cereal_fwd
Fork of cereal library adding archive supporting forward compatibility
extendable_binary.hpp
Go to the documentation of this file.
1 
3 /*
4  Copyright (c) 2016, Randolph Voorhies, Shane Grant, Michal Breiter
5  All rights reserved.
6 
7  Redistribution and use in source and binary forms, with or without
8  modification, are permitted provided that the following conditions are met:
9  * Redistributions of source code must retain the above copyright
10  notice, this list of conditions and the following disclaimer.
11  * Redistributions in binary form must reproduce the above copyright
12  notice, this list of conditions and the following disclaimer in the
13  documentation and/or other materials provided with the distribution.
14  * Neither the name of cereal nor the
15  names of its contributors may be used to endorse or promote products
16  derived from this software without specific prior written permission.
17 
18  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT OR MICHAL BREITER BE LIABLE FOR ANY
22  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29 #ifndef CEREAL_ARCHIVES_EXTENDABLE_BINARY_HPP_
30 #define CEREAL_ARCHIVES_EXTENDABLE_BINARY_HPP_
31 
32 #include <cereal/cereal.hpp>
33 #include <cereal/types/memory.hpp>
34 #include <array>
35 #include <cstring>
36 #include <limits>
37 #include <map>
38 #include <set>
39 #include <sstream>
40 
42 
43 namespace cereal
44 {
45  // ######################################################################
47 
58  class ExtendableBinaryOutputArchive : public OutputArchive<ExtendableBinaryOutputArchive, Flags::ForwardSupport>
59  {
60  public:
62  class Options
63  {
64  public:
66  enum class Endianness : std::uint8_t
67  { big, little };
68 
70  static Options Default(){ return Options(); }
71 
73 
74  explicit Options( Endianness outputEndian_ = getEndianness() ) :
75  itsOutputEndianness( outputEndian_ ) { }
76 
78  Options& littleEndian(){ itsOutputEndianness = Endianness::little; return *this; }
80  Options& bigEndian(){ itsOutputEndianness = Endianness::big; return *this; }
81 
82  private:
84  inline static Endianness getEndianness()
85  { return extendable_binary_detail::is_little_endian() ? Endianness::little : Endianness::big; }
86 
88  inline std::uint8_t is_little_endian() const
89  { return itsOutputEndianness == Endianness::little; }
90 
91  friend class ExtendableBinaryOutputArchive;
92  Endianness itsOutputEndianness;
93  };
94 
96 
99  ExtendableBinaryOutputArchive(std::ostream & stream, Options const & options = Options::Default()) :
100  OutputArchive<ExtendableBinaryOutputArchive, Flags::ForwardSupport>(this),
101  itsStream(stream),
102  itsConvertEndianness( extendable_binary_detail::is_little_endian() ^ options.is_little_endian() )
103  {
104  const auto littleEndian = options.is_little_endian();
105  this->saveBinary<sizeof(std::uint8_t)>( &littleEndian, sizeof(std::uint8_t) );
106  }
107 
109 
111 
113  template <std::size_t DataSize> inline
114  void saveBinary( const void * data, std::size_t size )
115  {
116  std::size_t writtenSize = 0;
117 
118  if( itsConvertEndianness )
119  {
120  for( std::size_t i = 0; i < size; i += DataSize )
121  for( std::size_t j = 0; j < DataSize; ++j )
122  writtenSize += static_cast<std::size_t>( itsStream.rdbuf()->sputn( reinterpret_cast<const char*>( data ) + DataSize - j - 1 + i, 1 ) );
123  }
124  else
125  writtenSize = static_cast<std::size_t>( itsStream.rdbuf()->sputn( reinterpret_cast<const char*>( data ), size ) );
126 
127  if(writtenSize != size)
128  throw Exception("Failed to write " + std::to_string(size) + " bytes to output stream! Wrote " + std::to_string(writtenSize));
129  }
130 
132 
133  void saveBinaryNoSwap( const void * data, std::size_t size )
134  {
135  std::size_t writtenSize = 0;
136 
137  writtenSize = static_cast<std::size_t>( itsStream.rdbuf()->sputn( reinterpret_cast<const char*>( data ), size ) );
138 
139  if(writtenSize != size)
140  throw Exception("Failed to write " + std::to_string(size) + " bytes to output stream! Wrote " + std::to_string(writtenSize));
141  }
142 
144 
148  template<std::size_t DataSize> inline
149  void saveBinarySingle( const void * data, std::size_t size )
150  {
151  std::size_t writtenSize = 0;
152  const std::uint8_t * dataEndian = reinterpret_cast<const std::uint8_t*>(data) + (extendable_binary_detail::is_little_endian() ? 0 : DataSize - size);
153 
154  if( itsConvertEndianness )
155  {
156  for( std::size_t j = 0; j < size; ++j )
157  writtenSize += static_cast<std::size_t>( itsStream.rdbuf()->sputn( reinterpret_cast<const char*>( dataEndian ) + size - j - 1, 1 ) );
158  }
159  else
160  writtenSize = static_cast<std::size_t>( itsStream.rdbuf()->sputn( reinterpret_cast<const char*>( dataEndian ), size ) );
161 
162  if(writtenSize != size)
163  throw Exception("Failed to write " + std::to_string(size) + " bytes to output stream! Wrote " + std::to_string(writtenSize));
164  }
165 
167 
171  template <class T>
172  void saveVarint(T v)
173  {
174  static_assert(sizeof(T) <= (extendable_binary_detail::maxVarintSize*7)/8, "value is to big to be saved as varint");
175  static_assert(std::is_unsigned<T>::value, "only unsigned varints are supported");
176  std::array<std::uint8_t, extendable_binary_detail::maxVarintSize> buffer;
177  std::size_t size = 0;
178  while (v > 0x7F) {
179  buffer[size] = (static_cast<std::uint8_t>(v) & 0x7f) | 0x80;
180  v >>= 7;
181  ++size;
182  }
183  buffer[size] = static_cast<std::uint8_t>(v) & 0x7f;
184  // we don't want bit swap here
185  saveBinaryNoSwap(buffer.data(), size + 1);
186  }
187 
189 
190  void saveClassVersion(std::uint32_t version)
191  {
192  classVersion = version;
193  }
194 
196 
197  void saveObjectId(std::uint32_t objectId_)
198  {
199  isPointer = true;
200  objectId = objectId_;
201  }
202 
204 
205  void savePointerValidityTag(bool /*valid*/)
206  {
207  isPointer = true;
208  }
209 
211 
212  void savePolymorphicId(std::int32_t polymorphicId_)
213  {
214  isPointer = true;
215  polymorphicId = polymorphicId_;
216  }
217 
219 
220  void savePolymorphicName(const std::string& name)
221  {
222  polymorphicName = name;
223  }
224 
226 
228  {
229  if(objectDataNeedsSaving) {
230  saveObjectData(false);
231  }
232  objectDataNeedsSaving = true;
233  }
234 
236 
238  {
239  if(objectDataNeedsSaving) {
240  saveObjectData(false);
241  }
242  }
243 
245 
248  {
249  if(objectDataNeedsSaving) {
250  // no other fields were saved we have to save objectData with empty marker
251  saveObjectData(true);
252  } else{
253  // there were fields in class, have to save end marker
254  saveEndMarker();
255  /* TODO we are saving last_field marker for pointers even if it's not needed
256  * Easiest way to solve it wold be to make queue of save object types in archive to know that we don't have to save it.
257  * But that would mean that we would have to allocate some memory. We could also make specializations for prologue for pointer types
258  * but that would mean that we make specializations for pointer types.
259  * We could make generic wrappers around pointers and specialize for them.
260  */
261  }
262  }
263 
264  private:
265 
267 
268  void saveObjectData(bool endOfObject)
269  {
270  using namespace extendable_binary_detail;
271  if(isPointer) {
272  PointerMarkers finalMarker = PointerMarkers::None;
273  if(objectId > 0) {
274  finalMarker |= PointerMarkers::IsSharedPtr;
275  }
276  if(polymorphicId > 0) {
277  finalMarker |= PointerMarkers::IsPolymorphicPointer;
278  }
279  if(endOfObject) {
280  finalMarker |= PointerMarkers::Empty;
281  }
282  std::uint8_t t = writeType(FieldType::pointer, static_cast<std::uint8_t>(finalMarker));
283  saveBinary<sizeof(std::uint8_t)>(&t, sizeof(std::uint8_t));
284  if(objectId > 0) {
285  saveVarint(objectId);
286  }
287  if(polymorphicId > 0) {
288  saveBinary<sizeof(std::int32_t)>(&polymorphicId, sizeof(std::int32_t));
289  }
290  if(false == polymorphicName.empty()) {
291  saveVarint(polymorphicName.size());
292  saveBinary<sizeof(decltype(polymorphicName)::value_type)>( polymorphicName.c_str(),
293  polymorphicName.size() * sizeof(decltype(polymorphicName)::value_type));
294  }
295  } else {
296  ClassMarkers finalMarker = ClassMarkers::None;
297  if(classVersion > 0) {
298  finalMarker |= ClassMarkers::HasVersion;
299  }
300  // No fields in object were saved we can skip saving end of object marker.
301  if(endOfObject) {
302  finalMarker |= ClassMarkers::EmptyClass;
303  }
304  std::uint8_t t = writeType(FieldType::class_t, static_cast<std::uint8_t>(finalMarker));
305  saveBinary<sizeof(std::uint8_t)>(&t, sizeof(std::uint8_t));
306  // save needed data
307  if(classVersion > 0) {
308  saveVarint(classVersion);
309  }
310  }
311  // reset variables
312  objectDataNeedsSaving = false;
313  classVersion = 0;
314  isPointer = false;
315  objectId = 0;
316  // version is only saved when method has version argument so we have to reset it
317  polymorphicId = 0;
318  polymorphicName.clear();
319  }
320 
322 
323  void saveEndMarker()
324  {
325  using namespace extendable_binary_detail;
326  std::uint8_t t = writeType(FieldType::last_field, 0);
327  saveBinary<sizeof(std::uint8_t)>(&t, sizeof(std::uint8_t));
328  }
329 
330  private:
331  bool objectDataNeedsSaving = false;
332  std::uint32_t classVersion = 0;
333  bool isPointer = false;
334  std::uint32_t objectId = 0;
335  std::uint32_t polymorphicId = 0;
336  std::string polymorphicName;
337 
338  std::ostream & itsStream;
339  const uint8_t itsConvertEndianness;
340  };
341 
342  // ######################################################################
344 
361  class ExtendableBinaryInputArchive : public InputArchive<ExtendableBinaryInputArchive, Flags::ForwardSupport>
362  {
363  public:
365  class Options
366  {
367  public:
369  enum class Endianness : std::uint8_t
370  { big, little };
371 
373  static Options Default(){ return Options(); }
374 
376 
382  explicit Options( Endianness inputEndian_ = getEndianness(),
383  std::size_t maxSharedBufferSize_ = std::numeric_limits<std::size_t>::max(),
384  bool ignoreUnknownPolymorphicTypes_ = true) :
385  itsInputEndianness( inputEndian_ ),
386  itsMaxSharedBufferSize( maxSharedBufferSize_ ),
387  itsIgnoreUnknownPolymorphicTypes( ignoreUnknownPolymorphicTypes_ )
388  { }
389 
391  Options& littleEndian(){ itsInputEndianness = Endianness::little; return *this; }
393  Options & bigEndian(){ itsInputEndianness = Endianness::big; return *this; }
394 
396 
399  Options & maxSharedBufferSize(std::size_t maxSharedBufferSize_)
400  {
401  itsMaxSharedBufferSize = maxSharedBufferSize_;
402  return *this;
403  }
404 
406 
409  {
410  itsIgnoreUnknownPolymorphicTypes = ignore_;
411  return *this;
412  }
413 
414  private:
416  inline static Endianness getEndianness()
417  { return extendable_binary_detail::is_little_endian() ? Endianness::little : Endianness::big; }
418 
420  inline std::uint8_t is_little_endian() const
421  { return itsInputEndianness == Endianness::little; }
422 
424  inline std::size_t getMaxSharedBufferSize() const
425  { return itsMaxSharedBufferSize; }
426 
427  friend class ExtendableBinaryInputArchive;
428  Endianness itsInputEndianness; //<
429  std::size_t itsMaxSharedBufferSize;
430  bool itsIgnoreUnknownPolymorphicTypes;
431  };
432 
434 
437  ExtendableBinaryInputArchive(std::istream & stream, Options const & options = Options::Default()) :
438  InputArchive<ExtendableBinaryInputArchive, Flags::ForwardSupport>(this),
439  sharedObjectStream(std::ios::binary | std::ios::in | std::ios::out),
440  itsStream(stream, sharedObjectStream, options.itsMaxSharedBufferSize),
441  itsConvertEndianness( false )
442  {
443  uint8_t streamLittleEndian;
444  this->loadBinary<sizeof(std::uint8_t)>( &streamLittleEndian, sizeof(std::uint8_t));
445  itsConvertEndianness = options.is_little_endian() ^ streamLittleEndian;
446  itsIgnoreUnknownPolymorphicTypes = options.itsIgnoreUnknownPolymorphicTypes;
447  }
448 
450 
452 
455  template <std::size_t DataSize> inline
456  void loadBinary( void * const data, std::size_t size )
457  {
458  // load data
459  auto const readSize = itsStream.readBinary( reinterpret_cast<char*>( data ), size );
460  if(false == savedShared.saving.empty()) {
461  itsStream.checkIfMaxSize(size, sharedObjectStream);
462  sharedObjectStream.write(reinterpret_cast<char*>(data), size);
463  }
464 
465  if(readSize != size)
466  throw Exception("Failed to read " + std::to_string(size) + " bytes from input stream! Read " + std::to_string(readSize));
467 
468  // flip bytes if needed
469  if( itsConvertEndianness )
470  {
471  std::uint8_t * ptr = reinterpret_cast<std::uint8_t*>( data );
472  for( std::size_t i = 0; i < size; i += DataSize )
473  extendable_binary_detail::swap_bytes<DataSize>( ptr + i );
474  }
475  }
476 
478 
484  template <std::size_t DataSize> inline
485  void loadBinarySingle( void * const data, std::size_t size )
486  {
487  // load data
488  std::uint8_t* dataEndian = reinterpret_cast<std::uint8_t*>(data) + (extendable_binary_detail::is_little_endian() ? 0 : DataSize - size);
489  auto const readSize = itsStream.readBinary( reinterpret_cast<char*>( dataEndian ), size );
490  if(false == savedShared.saving.empty()) {
491  itsStream.checkIfMaxSize(size, sharedObjectStream);
492  sharedObjectStream.write(reinterpret_cast<char*>(data), size);
493  }
494 
495  if(readSize != size)
496  throw Exception("Failed to read " + std::to_string(size) + " bytes from input stream! Read " + std::to_string(readSize));
497 
498  // flip bits if needed
499  if( itsConvertEndianness ) {
500  std::uint8_t * ptr = reinterpret_cast<std::uint8_t*>( dataEndian );
501  for( std::size_t i = 0, end = size / 2; i < end; ++i )
502  std::swap( ptr[i], ptr[size - i - 1] );
503  }
504  }
505 
507 
512  template <std::size_t DataSize> inline
513  void copyBinarySingleNoSwap( void * dest_, void * const src_, std::size_t size_ )
514  {
515  std::uint8_t* src = reinterpret_cast<std::uint8_t*>(src_) + (extendable_binary_detail::is_little_endian() ? 0 : DataSize - size_);
516  std::uint8_t* dest = reinterpret_cast<std::uint8_t*>(dest_);
517  std::memcpy(dest, src, size_);
518  }
519 
520 
522 
524  inline void skipData(std::size_t size) {
525  if(savedShared.saving.empty()) {
526  itsStream.skipData(size);
527  } else {
528  itsStream.readToOtherStream(size, sharedObjectStream);
529  }
530  }
531 
533 
537  inline bool loadTypeTag()
538  {
539  using namespace extendable_binary_detail;
540  std::uint8_t v;
541  loadBinary<sizeof(std::uint8_t)>(&v, sizeof(std::uint8_t));
542  lastTypeTag = extendable_binary_detail::readType(v);
543  return lastTypeTag.first != FieldType::omitted_field;
544  }
545 
547 
551  template <extendable_binary_detail::FieldType expected_type>
552  inline auto getTypeTag() -> decltype(extendable_binary_detail::readType(std::uint8_t{}))
553  {
554  using namespace extendable_binary_detail;
555  static_assert(expected_type != FieldType::last_field, "should go to loadEndOfClass");
556  if(lastTypeTag.first != expected_type && lastTypeTag.first != FieldType::omitted_field)
557  throw Exception("Loaded wrong lastTypeTag, expected: " + std::to_string(static_cast<int>(expected_type))
558  + " got: " + std::to_string(static_cast<int>(lastTypeTag.first)) );
559  return lastTypeTag;
560  }
561 
563 
566  template <extendable_binary_detail::FieldType expected_type>
567  inline auto getTypeTagNoError() noexcept -> decltype(extendable_binary_detail::readType(std::uint8_t{}))
568  {
569  using namespace extendable_binary_detail;
570  static_assert(expected_type != FieldType::last_field, "should go to loadEndOfClass");
571  return lastTypeTag;
572  }
573 
575 
577  template <class T>
578  inline void loadVarint(T& v)
579  {
580  static_assert(sizeof(T) <= (extendable_binary_detail::maxVarintSize*7)/8, "value is to big to be a varint");
581  static_assert(std::is_unsigned<T>::value, "only unsigned varints are supported");
582 
583  std::uint32_t f = 0, s = 0;
584  auto load = [&]() {
585  std::uint8_t bytes = 1;
586  std::uint32_t f1 = 0, f2 = 0, f3 = 0, f4 = 0, s1 = 0, s2 = 0, s3 = 0, s4 = 0, s5 = 0, s6 = 0;
587  loadBinarySingle<sizeof(std::uint32_t)>(&f1, sizeof(std::uint8_t));
588  if(f1 < 0x80) {
589  f = f1;
590  return bytes;
591  }
592  loadBinarySingle<sizeof(std::uint32_t)>(&f2, sizeof(std::uint8_t));
593  ++bytes;
594  if(f2 < 0x80) {
595  f = (f1 - 0x80) | (f2 << 7);
596  return bytes;
597  }
598  loadBinarySingle<sizeof(std::uint32_t)>(&f3, sizeof(std::uint8_t));
599  ++bytes;
600  if(f3 < 0x80) {
601  f = (f1 - 0x80) | ((f2 - 0x80) << 7) | (f3 << 14);
602  return bytes;
603  }
604  loadBinarySingle<sizeof(std::uint32_t)>(&f4, sizeof(std::uint8_t));
605  ++bytes;
606  if(f4 < 0x80) {
607  f = (f1 - 0x80) | ((f2 - 0x80) << 7) | ((f3 - 0x80) << 14) | (f4 << 21);
608  return bytes;
609  }
610  loadBinarySingle<sizeof(std::uint32_t)>(&s1, sizeof(std::uint8_t));
611  ++bytes;
612  if(s1 < 0x80) {
613  f = (f1 - 0x80) | ((f2 - 0x80) << 7) | ((f3 - 0x80) << 14) | ((f4 - 0x80) << 21) | (s1 << 28);
614  s = s1 >> 3;
615  return bytes;
616  }
617  loadBinarySingle<sizeof(std::uint32_t)>(&s2, sizeof(std::uint8_t));
618  ++bytes;
619  if(s2 < 0x80) {
620  f = (f1 - 0x80) | ((f2 - 0x80) << 7) | ((f3 - 0x80) << 14) | ((f4 - 0x80) << 21) | ((s1 - 0x80) << 28);
621  s = ((s1 - 0x80) >> 4) | (s2 << 3);
622  return bytes;
623  }
624  loadBinarySingle<sizeof(std::uint32_t)>(&s3, sizeof(std::uint8_t));
625  ++bytes;
626  if(s3 < 0x80) {
627  f = (f1 - 0x80) | ((f2 - 0x80) << 7) | ((f3 - 0x80) << 14) | ((f4 - 0x80) << 21) | ((s1 - 0x80) << 28);
628  s = ((s1 - 0x80) >> 4) | ((s2 - 0x80) << 3) | s3 << 10;
629  return bytes;
630  }
631  loadBinarySingle<sizeof(std::uint32_t)>(&s4, sizeof(std::uint8_t));
632  ++bytes;
633  if(s4 < 0x80) {
634  f = (f1 - 0x80) | ((f2 - 0x80) << 7) | ((f3 - 0x80) << 14) | ((f4 - 0x80) << 21) | ((s1 - 0x80) << 28);
635  s = ((s1 - 0x80) >> 4) | ((s2 - 0x80) << 3) | ((s3 - 0x80) << 10) | s4 << 17;
636  return bytes;
637  }
638  loadBinarySingle<sizeof(std::uint32_t)>(&s5, sizeof(std::uint8_t));
639  ++bytes;
640  if(s5 < 0x80) {
641  f = (f1 - 0x80) | ((f2 - 0x80) << 7) | ((f3 - 0x80) << 14) | ((f4 - 0x80) << 21) | ((s1 - 0x80) << 28);
642  s = ((s1 - 0x80) >> 4) | ((s2 - 0x80) << 3) | ((s3 - 0x80) << 10) | ((s4 - 0x80) << 17) | s5 << 24;
643  return bytes;
644  }
645  loadBinarySingle<sizeof(std::uint32_t)>(&s6, sizeof(std::uint8_t));
646  ++bytes;
647  if(s6 < 0x80) {
648  f = (f1 - 0x80) | ((f2 - 0x80) << 7) | ((f3 - 0x80) << 14) | ((f4 - 0x80) << 21) | ((s1 - 0x80) << 28);
649  s = ((s1 - 0x80) >> 4) | ((s2 - 0x80) << 3) | ((s3 - 0x80) << 10) | ((s4 - 0x80) << 17) | ((s5 - 0x80) << 24) | s6 << 31;
650  return bytes;
651  } else {
652  throw Exception("Too big varint");
653  }
654  };
655  load();
656  if(sizeof(T) == 8) {
657  std::uint64_t final = s;
658  final <<= (sizeof(s)*8);
659  final |= f;
660  std::memcpy(&v, &final, sizeof(T));
661  } else {
662  copyBinarySingleNoSwap<sizeof(s)>(&v, &f, sizeof(T));
663  }
664  }
665 
667  inline void skipVarint()
668  {
669  // TODO can implement faster approach, without interpreting
670  std::uint64_t ignore;
671  loadVarint(ignore);
672  }
673 
675  inline void loadObjectBeginning()
676  {
677  resetObjectDetails();
678  using namespace extendable_binary_detail;
679  const auto type = getTypeTagNoError<FieldType::class_t>();
680  switch(type.first) {
681  case FieldType::class_t: {
682  loadClassData(type.second);
683  break;
684  }
685  case FieldType::pointer: {
686  loadPointerData(type.second);
687  break;
688  }
689  case FieldType::omitted_field: {
690  throw Exception("omitted class, should be read earlier");
691  }
692  default: {
693  throw Exception("Unexpected type expected class or pointer, got:" + std::to_string(static_cast<int>(type.first)));
694  }
695  }
696  }
697 
699 
701  inline void loadEndOfObjectData()
702  {
703  if(emptyClass) {
704  // empty class or shared pointer with object which was already saved
705  } else {
706  loadTypeTag();
707  loadEndOfClass(true);
708  }
709  resetObjectDetails();
710  }
711 
713  inline void loadOmittedObject()
714  {
715  loadEndOfClass(false);
716  }
717 
719  inline std::uint32_t getLoadedClassVersion() const
720  {
721  return classVersion;
722  }
723 
725  inline std::int32_t getLoadedPolymorphicId() const
726  {
727  return polymorphicId;
728  }
729 
731  inline const std::string& getLoadedPolymorphicName() const
732  {
733  return polymorphicName;
734  }
735 
737  inline std::uint32_t getLoadedObjectId() const
738  {
739  return objectId;
740  }
741 
743  inline bool getLoadedPointerValidity() const
744  {
745  return false == emptyClass;
746  }
747 
749  inline void setLastSizeTag(std::size_t size)
750  {
751  lastSizeTag = size;
752  }
753 
755  inline std::size_t getLastSizeTag() const
756  {
757  return lastSizeTag;
758  }
759 
760  private:
761 
763  struct SavedShared {
765 
767  std::vector<std::pair<std::uint32_t, extendable_binary_detail::StreamPos>> saving;
769 
770  std::map<std::uint32_t, extendable_binary_detail::StreamPos> saved;
771  std::set<std::uint32_t> loaded;
772  };
773 
775  inline void resetObjectDetails()
776  {
777  // should be reset on all fields?
778  classVersion = 0;
779  emptyClass = false;
780  objectId = 0;
781  polymorphicId = 0;
782  polymorphicName.clear();
783  }
784 
786  inline void loadClassData(std::uint8_t classMarkers)
787  {
788  using namespace extendable_binary_detail;
789  ClassMarkers markers = static_cast<ClassMarkers>(classMarkers);
790  if(markers & ClassMarkers::EmptyClass) {
791  emptyClass = true;
792  }
793  if(markers & ClassMarkers::HasVersion) {
794  loadVarint(classVersion);
795  }
796  }
797 
799  void loadSharedPointer()
800  {
801  const auto normalObjectId = objectId & ~detail::msb_32bit;
802  const auto wasSkipped = savedShared.saved.find(normalObjectId);
803  // usual path
804  if(wasSkipped == savedShared.saved.end())
805  return;
806 
807  const bool isNewObjectInStream = (objectId & detail::msb_32bit) != 0;
808  const auto alreadyLoaded = savedShared.loaded.find(normalObjectId);
809  if (false == isNewObjectInStream) {
810  /* Object was not loaded before according to stream order. */
811  if (alreadyLoaded == savedShared.loaded.end()) {
812  // TODO we can delete it here (if we made a copy)
813  savedShared.loaded.emplace(normalObjectId);
814  // we change objectId to indicate that we want to load it now
815  objectId = objectId | detail::msb_32bit;
816  pushLoadShared(wasSkipped->second);
817  emptyClass = false; // unneeded redundancy?
818  }
819  } else if (alreadyLoaded == savedShared.loaded.end()) {
820  /* NewObjectInStream, was skipped and not loaded before.
821  * Here we are loading it with stream order (stream indicates that it's new object) so there's no need to
822  * push additional stream position, it is naturally next in stream.
823  * We just have to mark that that object is now being loaded so we don't load it again and make duplicate with
824  * different address. */
825  savedShared.loaded.emplace(normalObjectId);
826  } else if (alreadyLoaded != savedShared.loaded.end()) {
827  /* NewObjectInStream, was skipped but was loaded before.
828  * We don't want to load it for the second time. We have move forward in the stream to the end of object. */
829  objectId = normalObjectId;
830  emptyClass = true;
831  // move forward
832  itsStream.skipData(wasSkipped->second.end - wasSkipped->second.start);
833  }
834  }
835 
836 
837  inline void loadPointerData(std::uint8_t pointerMarkers)
838  {
839  using namespace extendable_binary_detail;
840  PointerMarkers markers = static_cast<PointerMarkers>(pointerMarkers);
841  if (markers & PointerMarkers::Empty) {
842  emptyClass = true;
843  }
844  if (markers & PointerMarkers::IsSharedPtr) {
845  loadVarint(objectId);
846  loadSharedPointer();
847  }
848  if (markers & PointerMarkers::IsPolymorphicPointer) {
849  loadBinary<sizeof(std::int32_t)>(&polymorphicId, sizeof(std::int32_t));
850  const bool isNewId = polymorphicId & detail::msb_32bit;
851  const bool noPolymorphicCast = polymorphicId & detail::msb2_32bit;
852  if (isNewId) {
853  std::uint32_t nameSize;
854  loadVarint(nameSize);
855  // TODO limit max size for safety
856  polymorphicName.resize(nameSize);
857  using char_type = decltype(polymorphicName)::value_type;
858  loadBinary<sizeof(char_type)>(&polymorphicName[0u],
859  nameSize * sizeof(char_type));
860  // TODO change to uint8_t (may not match on sending side)
861  // normally it would be multiply by one, but on other platforms we could just have problems
862  } else if(noPolymorphicCast) {
863  return;
864  } else {
865  polymorphicName = getPolymorphicName(polymorphicId);
866  }
867  if (itsIgnoreUnknownPolymorphicTypes &&
868  false == emptyClass &&
869  false == polymorphic_detail::hasPolymorphicBinding<ExtendableBinaryInputArchive>(polymorphicName)
870  ) {
871  if(isNewId) {
872  /* We will skip pointer loading do name would've not been registered.
873  * note: resetObjectDetails will reset name and id */
874  registerPolymorphicName(polymorphicId, polymorphicName);
875  }
876  resetObjectDetails();
877  emptyClass = true;
878  loadTypeTag();
879  loadEndOfClass(true);
880  }
881  }
882  }
883 
885 
889  inline void loadEndOfClass(bool isInObject)
890  {
891  using namespace extendable_binary_detail;
892  using return_type = decltype(extendable_binary_detail::readType(std::uint8_t{}));
893  int class_depth = isInObject ? 1 : 0; // we are in an object
894  std::size_t lastIgnoredSizeTag = 0; // TODO use other size type?
895 
896  bool firstPass = true;
897  do {
898  if(false == firstPass) {
899  loadTypeTag();
900  }
901  firstPass = false;
902 
903  return_type type = getTypeTagNoError<FieldType::class_t>();
904  switch (type.first) {
905  case FieldType::positive_integer:
906  case FieldType::negative_integer: {
907  skipData(getIntSizeFromTagSize(type.second));
908  break;
909  }
910  case FieldType::floating_point: {
911  skipData(getFloatSizeFromTagSize(type.second));
912  break;
913  }
914  case FieldType::integer_packed:
915  break; // one byte
916  case FieldType::omitted_field:
917  break; // one byte
918  case FieldType::last_field: {
919  /* what we expected, but only if we didn't go into next class_field */
920  if(isSkippedSharedObjectEnd(class_depth)) {
921  popSaveShared();
922  }
923  --class_depth;
924  break; // one byte
925  }
926  case FieldType::class_t: {
927  ClassMarkers markers = static_cast<ClassMarkers>(type.second);
928  if(false == (markers & ClassMarkers::EmptyClass)) {
929  ++class_depth;
930  }
931  if(markers & ClassMarkers::HasVersion) {
932  skipVarint();
933  }
934  break;
935  }
936  case FieldType::pointer: {
937  PointerMarkers markers = static_cast<PointerMarkers>(type.second);
938  if(false == (markers & PointerMarkers::Empty)) {
939  ++class_depth;
940  }
941  if(markers & PointerMarkers::IsSharedPtr) {
942  std::uint32_t objectIdTmp;
943  loadVarint(objectIdTmp);
944  if(objectIdTmp & detail::msb_32bit) {
945  pushSaveShared(objectIdTmp & ~detail::msb_32bit, class_depth);
946  }
947  }
948  if(markers & PointerMarkers::IsPolymorphicPointer) {
949  loadBinary<sizeof(std::int32_t)>(&polymorphicId, sizeof(std::int32_t));
950  if(polymorphicId & detail::msb_32bit) { // TODO change msb to lsb?
951  std::uint32_t nameSize;
952  loadVarint(nameSize);
953  // TODO limit max size for safety
954  polymorphicName.resize(nameSize);
955  using char_type = decltype(polymorphicName)::value_type;
956  loadBinary<sizeof(char_type)>(&polymorphicName[0u],
957  nameSize * sizeof(char_type));
958  // TODO change to uint8_t (may not match on sending side)
959  // normally it would be multiply by one, but on other platforms we could just have problems
960  registerPolymorphicName(polymorphicId, polymorphicName);
961  /* Needed if polymorphic pointer's class name is saved but field is not loaded.
962  * If polymorphic pointer of the same class is saved later class name would be unknown since class name is saved only once. */
963  }
964  }
965  break;
966  }
967  case FieldType::packed_array: {
968  std::size_t size;
969  loadVarint(size);
970  skipData(type.second * size);
971  break;
972  }
973  case FieldType::packed_struct: {
974  throw Exception("packed_struct is not supported yet");
975  }
976  case FieldType::size_tag: {
977  /* We need to load size tag because it can be needed to load BinaryData (packed_array) later */
978  const auto sizeTagSize = getIntSizeFromTagSize(type.second);
979  if(sizeTagSize > sizeof(lastIgnoredSizeTag)) {
980  throw Exception("Size tag is to big to be loaded");
981  }
982  loadBinarySingle<sizeof(lastIgnoredSizeTag)>(&lastIgnoredSizeTag, sizeTagSize);
983  break;
984  }
985  default:
986  throw Exception("Unknown type of field: " + std::to_string(static_cast<int>(type.first)));
987  // delete, it's already present in readType function
988  }
989  } while(class_depth > 0);
990  }
991 
992  inline void pushSaveShared(std::uint32_t skippedObjectId, int classDepth)
993  {
994  savedShared.saving.emplace_back(
995  std::make_pair(skippedObjectId,
996  extendable_binary_detail::StreamPos{sharedObjectStream.tellp(),
997  static_cast<std::streamoff>(classDepth)
998  }));
999  }
1000 
1002  inline bool isSkippedSharedObjectEnd(int classDepth)
1003  {
1004  return false == savedShared.saving.empty()
1005  && savedShared.saving.back().second.end == static_cast<std::streamoff>(classDepth);
1006  }
1007 
1009  inline void popSaveShared()
1010  {
1011  if (savedShared.saving.empty()) // maybe check earlier
1012  throw Exception("unexpected end of shared object");
1013  auto &last = savedShared.saving.back();
1014  last.second.end = sharedObjectStream.tellp();
1015  savedShared.saved.emplace(last.first, last.second);
1016  savedShared.saving.pop_back();
1017  }
1018 
1019  inline void pushLoadShared(extendable_binary_detail::StreamPos &pos)
1020  {
1021  itsStream.pushReadingPos(pos);
1022  }
1023 
1024 
1025  private:
1026  std::uint32_t classVersion = 0;
1027  bool emptyClass = false;
1028  std::uint32_t objectId = 0;
1029  std::int32_t polymorphicId = 0;
1030  std::string polymorphicName;
1031 
1032  std::size_t lastSizeTag = 0;
1033  std::pair<extendable_binary_detail::FieldType, std::uint8_t> lastTypeTag =
1034  {extendable_binary_detail::FieldType::last_field, 0};
1035 
1036  SavedShared savedShared;
1037  std::stringstream sharedObjectStream;
1039 
1040  uint8_t itsConvertEndianness;
1041  bool itsIgnoreUnknownPolymorphicTypes;
1043  };
1044 
1045  // ######################################################################
1046  // Common ExtendableBinaryArchive serialization functions
1047 
1049  template <class T> inline
1050  typename std::enable_if<std::is_same<T, bool>::value, void>::type
1052  {
1053  using namespace extendable_binary_detail;
1054  std::uint8_t v = writeType(FieldType::integer_packed, (t ? 1 : 0));
1055  ar.template saveBinary<sizeof(std::uint8_t)>(&v, sizeof(std::uint8_t));
1056  // sizeof bool is implementation defined
1057  }
1058 
1060  template <class T> inline
1061  typename std::enable_if<std::is_same<T, bool>::value, void>::type
1063  {
1064  using namespace extendable_binary_detail;
1065  const auto type = ar.getTypeTag<FieldType::integer_packed>();
1066  if( type.first == FieldType::omitted_field )
1067  return;
1068  t = type.second;
1069  }
1070 
1072  template<class T> inline
1073  typename std::enable_if<std::is_integral<T>::value &&
1074  !std::is_same<T, bool>::value, void>::type
1076  {
1077  static_assert(sizeof(T) <= 32, "Only integers up to 32 bytes are supported");
1078  using namespace extendable_binary_detail;
1079  using unsigned_type = typename std::make_unsigned<T>::type;
1080  // can be stored in the same byte as type
1081  if( t <= 0xf && t >= 0 ) {
1082  std::uint8_t v = writeType(FieldType::integer_packed, static_cast<std::uint8_t>(t));
1083  ar.template saveBinary<sizeof(std::uint8_t)>(&v, sizeof(v));
1084  } else {
1085  // note that abs of minimal value for signed type may not to stored the same signed type
1086  // http://stackoverflow.com/questions/17313579/is-there-a-safe-way-to-get-the-unsigned-absolute-value-of-a-signed-integer-with
1087  const unsigned_type absolute = t >= 0 ? t : -static_cast<unsigned_type>(t);
1088  const auto neededBytes = getIntSizeTagFromByteCount(getHighestBit(absolute));
1089  const auto fieldType = t >= 0 ? FieldType::positive_integer : FieldType::negative_integer;
1090  std::uint8_t v = writeType(fieldType, neededBytes);
1091  ar.template saveBinary<sizeof(std::uint8_t)>(&v, sizeof(v));
1092  ar.saveBinarySingle<sizeof(T)>(std::addressof(absolute), neededBytes);
1093  }
1094  }
1095 
1097  template<class T> inline
1098  typename std::enable_if<std::is_integral<T>::value &&
1099  !std::is_same<T, bool>::value, void>::type
1101  {
1102  using namespace extendable_binary_detail;
1103  auto type = ar.getTypeTagNoError<FieldType::integer_packed>();
1104  switch(type.first) {
1105  case FieldType::omitted_field:
1106  return;
1107  case FieldType::integer_packed: {
1108  t = type.second;
1109  break;
1110  }
1111  case FieldType::positive_integer: {
1112  const auto neededByteSize = getIntSizeFromTagSize(type.second);
1113  if(neededByteSize > sizeof(T)) {
1114  throw Exception("Integer is to big to be loaded");
1115  }
1116  t = 0;
1117  ar.loadBinarySingle<sizeof(T)>(std::addressof(t), neededByteSize);
1118  break;
1119  }
1120  case FieldType::negative_integer: {
1121  const auto neededByteSize = getIntSizeFromTagSize(type.second);
1122  if(neededByteSize > sizeof(T)) {
1123  throw Exception("Integer is to big to be loaded");
1124  }
1125  if(std::is_unsigned<T>::value) {
1126  throw Exception("Negative value cannot be loaded to unsigned type");
1127  }
1128  t = 0;
1129  ar.loadBinarySingle<sizeof(T)>(std::addressof(t), neededByteSize);
1130  t = -t;
1131  break;
1132  }
1133  default:
1134  throw Exception("Unexpected type expected: integer got:" + std::to_string(static_cast<int>(type.first)));
1135  }
1136  }
1137 
1139  template<class T> inline
1140  typename std::enable_if<std::is_floating_point<T>::value, void>::type
1142  {
1143  static_assert( !std::is_floating_point<T>::value ||
1144  (std::is_floating_point<T>::value && std::numeric_limits<T>::is_iec559),
1145  "Extendable binary only supports IEEE 754 standardized floating point" );
1146  using namespace extendable_binary_detail;
1147  std::uint8_t floatSize = getTagSizeFromFloatType<T>();
1148  std::uint8_t v = writeType(FieldType::floating_point, floatSize);
1149  ar.template saveBinary<sizeof(std::uint8_t)>(&v, sizeof(std::uint8_t));
1150  if (t == t) {
1151  ar.template saveBinary<sizeof(T)>(std::addressof(t), sizeof(t));
1152  } else {
1153  auto qNaN = getqNaN<T>();
1154  ar.template saveBinary<sizeof(qNaN)>(std::addressof(qNaN), sizeof(qNaN));
1155  }
1156  }
1157 
1159  template<class T> inline
1160  typename std::enable_if<std::is_floating_point<T>::value, void>::type
1162  {
1163  static_assert( !std::is_floating_point<T>::value ||
1164  (std::is_floating_point<T>::value && std::numeric_limits<T>::is_iec559),
1165  "Extendable binary only supports IEEE 754 standardized floating point" );
1166  static_assert((sizeof(t) == 4) || (sizeof(t) == 8), "unsupported float size");
1167  using namespace extendable_binary_detail;
1168  auto type = ar.getTypeTag<FieldType::floating_point>();
1169  if(type.first == FieldType::omitted_field)
1170  return;
1171  switch(type.second) {
1172  case 1: {
1173  float dest;
1174  ar.template loadBinary<sizeof(dest)>(std::addressof(dest), sizeof(dest));
1175  // in case of double/long double, TODO make separate specialization
1176  t = dest;
1177  break;
1178  }
1179  case 2: {
1180  double dest;
1181  ar.template loadBinary<sizeof(dest)>(std::addressof(dest), sizeof(dest));
1182  // in case of float/long double, TODO make separate specialization
1183  t = dest;
1184  break;
1185  }
1186  // https://en.wikipedia.org/wiki/Long_double
1187  // can be different size on different platforms
1188  default:
1189  throw Exception("Not supported size of floating point: " + std::to_string(type.second));
1190  }
1191  }
1192 
1194  template <class Archive, class T> inline
1197  {
1198  ar( t.value );
1199  }
1200 
1202  template <class T> inline
1204  {
1205  using namespace extendable_binary_detail;
1206  static_assert(sizeof(t.size) <= 32, "Only integers up to 32 bytes are supported");
1207  if(t.size < 0) {
1208  throw Exception("Negative SizeTag is not suppported");
1209  }
1210  const auto neededBytes = getIntSizeTagFromByteCount(getHighestBit(t.size));
1211  const auto fieldType = FieldType::size_tag;
1212  std::uint8_t v = writeType(fieldType, neededBytes);
1213  ar.template saveBinary<sizeof(std::uint8_t)>(&v, sizeof(v));
1214  ar.saveBinarySingle<sizeof(T)>(std::addressof(t.size), static_cast<std::size_t>(neededBytes));
1215  }
1216 
1218  template <class T> inline
1220  {
1221  using namespace extendable_binary_detail;
1222  auto type = ar.getTypeTag<FieldType::size_tag>();
1223  if(type.first == FieldType::omitted_field) {
1224  return;
1225  } else {
1226  const auto neededByteSize = getIntSizeFromTagSize(type.second);
1227  if(neededByteSize > sizeof(t.size)) {
1228  throw Exception("Size tag integer is to big to be loaded");
1229  }
1230  t.size = 0;
1231  ar.loadBinarySingle<sizeof(T)>(std::addressof(t.size), neededByteSize);
1232  ar.setLastSizeTag(t.size);
1233  }
1234  }
1235 
1237  inline
1239  {
1240  using namespace extendable_binary_detail;
1241  std::uint8_t v = writeType(FieldType::omitted_field, 0);
1242  ar.template saveBinary<sizeof(std::uint8_t)>(&v, sizeof(std::uint8_t));
1243  }
1244 
1246  inline
1248  {
1249  ar.loadOmittedObject();
1250  }
1251 
1253  template <class T> inline
1255  {
1256  using TT = typename std::remove_pointer<T>::type;
1257  using namespace extendable_binary_detail;
1258  std::uint8_t packedSizeOfElem;
1259  if(sizeof(TT) < 0xf) {
1260  packedSizeOfElem = sizeof(TT);
1261  } else {
1262  packedSizeOfElem = 0xf;
1263  }
1264  std::uint8_t v = writeType(FieldType::packed_array, packedSizeOfElem);
1265  ar.template saveBinary<sizeof(std::uint8_t)>(&v, sizeof(std::uint8_t));
1266  if(sizeof(TT) >= 0xf) {
1267  ar.saveVarint(sizeof(TT));
1268  }
1269  // TODO size is saved twice for string, once before in size tag @see types/string.hpp
1270  ar.saveVarint(bd.size / sizeof(TT));
1271  ar.template saveBinary<sizeof(TT)>( bd.data, static_cast<std::size_t>( bd.size ) );
1272  }
1273 
1275 
1276  template <class T> inline
1278  {
1279  typedef typename std::remove_pointer<T>::type TT;
1280  using namespace extendable_binary_detail;
1281  const auto type = ar.getTypeTag<FieldType::packed_array>();
1282  if(type.first == FieldType::omitted_field)
1283  return;
1284  std::uint64_t sizeOfElem;
1285  if(type.second == 0xf) {
1286  ar.loadVarint(sizeOfElem);
1287  } else {
1288  sizeOfElem = type.second;
1289  }
1290  if(sizeof(TT) != sizeOfElem) {
1291  // We could allow mismatch here, only problem would be how to make endian swap
1292  throw Exception("Wrong dest type size, expected:" + std::to_string(sizeOfElem) + " got:" + std::to_string(sizeof(TT)));
1293  }
1294  std::uint64_t numberOfElements;
1295  ar.loadVarint(numberOfElements);
1296  std::uint64_t wholeSize = numberOfElements * sizeOfElem;
1297  if( wholeSize > bd.size ) {
1298  throw Exception("BinaryData is bigger than dest var");
1299  }
1300  ar.template loadBinary<sizeof(TT)>( bd.data, wholeSize );
1301  }
1302 
1304  template <class T> inline
1306  {
1307  ar.saveClassVersion(version.version_id);
1308  }
1309 
1311  template <class T> inline
1313  {
1314  version.version_id = ar.getLoadedClassVersion();
1315  }
1316 
1317  template <class T>
1318  struct specialize<ExtendableBinaryOutputArchive, detail::VersionIdTag<T>, specialization::non_member_load_save> {};
1319  template <class T>
1320  struct specialize<ExtendableBinaryInputArchive, detail::VersionIdTag<T>, specialization::non_member_load_save> {};
1321 
1323  template <class T> inline
1325  {
1326  ar.savePolymorphicId(polymorphicIdTag.polymorphic_id);
1327  }
1328 
1330  template <class T> inline
1332  {
1333  polymorphicIdTag.polymorphic_id = ar.getLoadedPolymorphicId();
1334  }
1335 
1336  template <class T>
1337  struct specialize<ExtendableBinaryOutputArchive, detail::PolymorphicIdTag<T>, specialization::non_member_load_save> {};
1338  template <class T>
1339  struct specialize<ExtendableBinaryInputArchive, detail::PolymorphicIdTag<T>, specialization::non_member_load_save> {};
1340 
1342  template <class T> inline
1344  {
1345  ar.savePolymorphicName(polymorphicKeyTag.polymorphic_key);
1346  }
1347 
1349  template <class T> inline
1351  {
1352  polymorphicKeyTag.polymorphic_key = ar.getLoadedPolymorphicName();
1353  }
1354 
1355  template <class T>
1356  struct specialize<ExtendableBinaryOutputArchive, detail::PolymorphicKeyTag<T>, specialization::non_member_load_save> {};
1357  template <class T>
1358  struct specialize<ExtendableBinaryInputArchive, detail::PolymorphicKeyTag<T>, specialization::non_member_load_save> {};
1359 
1361  template <class T> inline
1363  {
1364  ar.saveObjectId(objectIdTag.object_id);
1365  }
1366 
1368  template <class T> inline
1370  {
1371  objectIdTag.object_id = ar.getLoadedObjectId();
1372  }
1373 
1374  template <class T>
1375  struct specialize<ExtendableBinaryOutputArchive, detail::ObjectIdTag<T>, specialization::non_member_load_save> {};
1376  template <class T>
1377  struct specialize<ExtendableBinaryInputArchive, detail::ObjectIdTag<T>, specialization::non_member_load_save> {};
1378 
1380  template <class T> inline
1382  {
1383  ar.savePointerValidityTag(pointerValidityTag.valid != 0);
1384  }
1385 
1387  template <class T> inline
1389  {
1390  pointerValidityTag.valid = ar.getLoadedPointerValidity();
1391  }
1392 
1393  template <class T>
1394  struct specialize<ExtendableBinaryOutputArchive, detail::PointerValidityTag<T>, specialization::non_member_load_save> {};
1395  template <class T>
1396  struct specialize<ExtendableBinaryInputArchive, detail::PointerValidityTag<T>, specialization::non_member_load_save> {};
1397 
1398  // ######################################################################
1399  // ExtendableBinaryArchive prologue and epilogue functions
1400  // ######################################################################
1401 
1402  // ###SimpleTypes########################################################
1404 
1407  template<class T>
1410  std::is_arithmetic<T>::value,
1411  std::is_same<T, std::nullptr_t>::value,
1412  std::is_same<T, OmittedFieldTag>::value
1413  > {};
1414  template <class T>
1416  template <class T>
1418 
1420  template <class T, traits::EnableIf<is_extendablebinary_empty_prologue_and_epilogue1<T>::value> = traits::sfinae> inline
1421  void prologue( ExtendableBinaryOutputArchive & ar, T const & )
1422  {
1423  ar.savingOtherField();
1424  }
1425 
1427  template <class T, traits::EnableIf<is_extendablebinary_empty_prologue_and_epilogue1<T>::value> = traits::sfinae> inline
1428  FieldSerialized prologueLoad( ExtendableBinaryInputArchive & ar, T const & )
1429  {
1430  return ar.loadTypeTag() ? FieldSerialized::YES : FieldSerialized::NO;
1431  }
1432 
1434  template <class T, traits::EnableIf<is_extendablebinary_empty_prologue_and_epilogue1<T>::value> = traits::sfinae> inline
1436  { }
1437 
1439  template <class T, traits::EnableIf<is_extendablebinary_empty_prologue_and_epilogue1<T>::value> = traits::sfinae> inline
1441  { }
1442 
1443  // ###Internal Types#####################################################
1445 
1447  template<class T>
1449  template <class T>
1454  template <class T>
1456  : std::integral_constant<bool, std::is_polymorphic<T>::value>::type {};
1457  template <class T>
1458  struct is_extendablebinary_internal_prologue_and_epilogue1<memory_detail::PtrWrapper<T>> : std::true_type {};
1459  template <class T>
1460  struct is_extendablebinary_internal_prologue_and_epilogue1<detail::VersionIdTag<T>> : std::true_type {};
1461  template <class T>
1462  struct is_extendablebinary_internal_prologue_and_epilogue1<detail::ObjectIdTag<T>> : std::true_type {};
1463  template <class T>
1464  struct is_extendablebinary_internal_prologue_and_epilogue1<detail::PointerValidityTag<T>> : std::true_type {};
1465  template <class T>
1466  struct is_extendablebinary_internal_prologue_and_epilogue1<detail::PolymorphicIdTag<T>> : std::true_type {};
1467  template <class T>
1468  struct is_extendablebinary_internal_prologue_and_epilogue1<detail::PolymorphicKeyTag<T>> : std::true_type {};
1469  template <class T, std::size_t V>
1470  struct is_extendablebinary_internal_prologue_and_epilogue1<std::array<T, V>> : std::true_type {};
1471 
1473  template <class T, traits::EnableIf<is_extendablebinary_internal_prologue_and_epilogue1<T>::value> = traits::sfinae> inline
1474  void prologue( ExtendableBinaryOutputArchive &, T const & )
1475  { }
1476 
1478  template <class T, traits::EnableIf<is_extendablebinary_internal_prologue_and_epilogue1<T>::value> = traits::sfinae> inline
1479  FieldSerialized prologueLoad( ExtendableBinaryInputArchive &, T const & )
1480  {
1481  return FieldSerialized::INTERNAL;
1482  }
1483 
1485  template <class T, traits::EnableIf<is_extendablebinary_internal_prologue_and_epilogue1<T>::value> = traits::sfinae> inline
1486  void epilogue( ExtendableBinaryOutputArchive &, T const & )
1487  { }
1488 
1490  template <class T, traits::EnableIf<is_extendablebinary_internal_prologue_and_epilogue1<T>::value> = traits::sfinae> inline
1491  void epilogue( ExtendableBinaryInputArchive &, T const & )
1492  { }
1493 
1494 
1495  // ###Objects###########################################################
1497 
1498  template <class T, traits::EnableIf<!traits::has_minimal_base_class_serialization<T, traits::has_minimal_output_serialization, ExtendableBinaryOutputArchive>::value,
1502  inline void prologue( ExtendableBinaryOutputArchive & ar, T const & )
1503  {
1504  ar.saveObjectBeginning();
1505  }
1506 
1508  template <class T, traits::EnableIf<!traits::has_minimal_base_class_serialization<T, traits::has_minimal_input_serialization, ExtendableBinaryInputArchive>::value,
1512  inline FieldSerialized prologueLoad( ExtendableBinaryInputArchive & ar, T const & )
1513  {
1514  const bool load = ar.loadTypeTag();
1515  if(load) {
1516  ar.loadObjectBeginning();
1517  }
1518  return load ? FieldSerialized::YES : FieldSerialized::NO;
1519  }
1520 
1522 
1523  template <class T, traits::EnableIf<!traits::has_minimal_base_class_serialization<T, traits::has_minimal_output_serialization, ExtendableBinaryOutputArchive>::value,
1527  inline void epilogue( ExtendableBinaryOutputArchive & ar, T const & )
1528  {
1529  ar.saveObjectEnd();
1530  }
1531 
1533  template <class T, traits::EnableIf<!traits::has_minimal_base_class_serialization<T, traits::has_minimal_input_serialization, ExtendableBinaryInputArchive>::value,
1537  inline void epilogue( ExtendableBinaryInputArchive & ar, T const & )
1538  {
1539  ar.loadEndOfObjectData();
1540  }
1541 
1542  template <>
1544  using type = double;
1545  };
1546 
1547  template <>
1549  using type = double;
1550  };
1551 } // namespace cereal
1552 
1553 // register archives for polymorphic support
1556 
1557 // tie input and output archives together
1559 
1560 namespace cereal { namespace traits {
1561 
1562 template <>
1564 { };
1565 
1566 template <>
1568 { };
1569 
1570 }} // namespace cereal::traits
1571 
1572 #endif // CEREAL_ARCHIVES_EXTENDABLE_BINARY_HPP_
bool getLoadedPointerValidity() const
Returns if last pointer was not nullptr.
Definition: extendable_binary.hpp:743
auto getTypeTag() -> decltype(extendable_binary_detail::readType(std::uint8_t
Gets last type tag loaded from input stream.
Definition: extendable_binary.hpp:552
Options & littleEndian()
Save with little endian order.
Definition: extendable_binary.hpp:78
Implementation details for extendable binary archive.
#define CEREAL_SETUP_ARCHIVE_TRAITS(InputArchive, OutputArchive)
Sets up traits that relate an input archive to an output archive.
Definition: traits.hpp:169
PT data
pointer to beginning of data
Definition: helpers.hpp:217
void saveClassVersion(std::uint32_t version)
Store temporarily class version to be saved later.
Definition: extendable_binary.hpp:190
std::int32_t getLoadedPolymorphicId() const
Gets last loaded polymorphic id.
Definition: extendable_binary.hpp:725
A class containing various advanced options for the ExtendableBinaryInput archive.
Definition: extendable_binary.hpp:365
A wrapper around size metadata.
Definition: helpers.hpp:271
Wrap polymorphic weak_ptr in shared_ptr.
Definition: traits.hpp:1313
A wrapper around object id.
Definition: helpers.hpp:485
FieldSerialized prologueLoad(ExtendableBinaryInputArchive &ar, T const &)
Prologue for arithmetic types for ExtendableBinary archives.
Definition: extendable_binary.hpp:1428
Definition: traits.hpp:73
Tag used to indicate that field is ommited from the archive.
Definition: helpers.hpp:292
A class containing various advanced options for the ExtendableBinaryOutput archive.
Definition: extendable_binary.hpp:62
A wrapper around pointer validity marker.
Definition: helpers.hpp:528
Options & bigEndian()
Save with big endian order.
Definition: extendable_binary.hpp:80
ExtendableBinaryOutputArchive(std::ostream &stream, Options const &options=Options::Default())
Construct, outputting to the provided stream.
Definition: extendable_binary.hpp:99
void loadObjectBeginning()
Load metadata of new object.
Definition: extendable_binary.hpp:675
auto getTypeTagNoError() noexcept -> decltype(extendable_binary_detail::readType(std::uint8_t
Gets last type tag loaded from input stream.
Definition: extendable_binary.hpp:567
Options & bigEndian()
Set desired endianess of loaded data to big endian.
Definition: extendable_binary.hpp:393
void skipVarint()
Load varint from the stream, discard input value.
Definition: extendable_binary.hpp:667
STL namespace.
void saveBinary(const void *data, std::size_t size)
Writes size bytes of data to the output stream.
Definition: extendable_binary.hpp:114
std::size_t getLastSizeTag() const
Returns value of last loaded size tag.
Definition: extendable_binary.hpp:755
The base input archive class.
Definition: cereal.hpp:659
void savePolymorphicId(std::int32_t polymorphicId_)
Store temporarily polymorphic id of type to be saved later.
Definition: extendable_binary.hpp:212
#define CEREAL_SERIALIZE_FUNCTION_NAME
The serialization/deserialization function name to search for.
Definition: macros.hpp:64
Options & littleEndian()
Set desired endianess of loaded data to little endian.
Definition: extendable_binary.hpp:391
A wrapper around polymorphic id.
Definition: helpers.hpp:369
static Options Default()
Default options, preserve system endianness.
Definition: extendable_binary.hpp:70
bool loadTypeTag()
Load type tag from input stream.
Definition: extendable_binary.hpp:537
void skipData(std::size_t size)
Discards size bytes from the input stream.
Definition: extendable_binary.hpp:524
Options & ignoreUnknownPolymorphicTypes(bool ignore_)
Don&#39;t throw exception when pointer to object of unknown polymorphic type is loaded.
Definition: extendable_binary.hpp:408
void saveBinaryNoSwap(const void *data, std::size_t size)
Writes size bytes of data to the output stream without any byte order swapping.
Definition: extendable_binary.hpp:133
void savePointerValidityTag(bool)
Store temporarily validity of pointer (!=nullptr) to be saved later.
Definition: extendable_binary.hpp:205
void saveVarint(T v)
Writes varint to the stream.
Definition: extendable_binary.hpp:172
Flags
Special flags for archives.
Definition: cereal.hpp:158
Class used as an adapter to queue of streams.
Definition: extendable_binary_details.hpp:299
Options & maxSharedBufferSize(std::size_t maxSharedBufferSize_)
Set max buffer size for shared data.
Definition: extendable_binary.hpp:399
void saveObjectBeginning()
Indicate beginning of new object saving.
Definition: extendable_binary.hpp:227
Types which has empty internal prologue and epilogue (version with one template argument) ...
Definition: extendable_binary.hpp:1448
void epilogue(ExtendableBinaryOutputArchive &, T const &)
Epilogue for arithmetic types for ExtendableBinary archives.
Definition: extendable_binary.hpp:1435
Definition: access.hpp:40
#define CEREAL_REGISTER_ARCHIVE(Archive)
Registers a specific Archive type with cereal.
Definition: cereal.hpp:171
#define CEREAL_ARCHIVE_RESTRICT(INTYPE, OUTTYPE)
A macro to use to restrict which types of archives your function will work for.
Definition: traits.hpp:1293
A wrapper around polymorphic id.
Definition: helpers.hpp:399
A wrapper around polymorphic key.
Definition: helpers.hpp:442
void loadEndOfObjectData()
Read all remaining data from current object.
Definition: extendable_binary.hpp:701
Options(Endianness outputEndian_=getEndianness())
Specify specific options for the ExtendableBinaryOutputArchive.
Definition: extendable_binary.hpp:74
void loadBinarySingle(void *const data, std::size_t size)
Load least significant size bytes of type which sizeof is DataSize.
Definition: extendable_binary.hpp:485
Nearest (by precision) floating point type to longdouble supported by Archive.
Definition: common.hpp:134
void saveObjectId(std::uint32_t objectId_)
Store temporarily object id for shared objects to be saved later.
Definition: extendable_binary.hpp:197
const std::string & getLoadedPolymorphicName() const
Gets last loaded polymorphic name.
Definition: extendable_binary.hpp:731
std::uint32_t getLoadedObjectId() const
Gets last loaded object id for shared pointers.
Definition: extendable_binary.hpp:737
void savingOtherField()
Indicate that some field (not beginning of new object) is being saved.
Definition: extendable_binary.hpp:237
Main cereal functionality.
For holding name value pairs.
Definition: helpers.hpp:135
Endianness
Represents desired endianness.
Definition: extendable_binary.hpp:369
Endianness
Represents desired endianness.
Definition: extendable_binary.hpp:66
#define CEREAL_LOAD_FUNCTION_NAME
The deserialization (load) function name to search for.
Definition: macros.hpp:71
void prologue(ExtendableBinaryOutputArchive &ar, T const &)
Prologue for arithmetic types for ExtendableBinary archives.
Definition: extendable_binary.hpp:1421
void saveObjectEnd()
End of object saving. Called in prologue for objects.
Definition: extendable_binary.hpp:247
static Options Default()
Default options, preserve system endianness.
Definition: extendable_binary.hpp:373
std::uint8_t is_little_endian()
Returns true if the current machine is little endian.
Definition: portable_binary.hpp:42
#define CEREAL_NOEXCEPT
Defines the CEREAL_NOEXCEPT macro to use instead of noexcept.
Definition: macros.hpp:116
void loadOmittedObject()
Read/Skip object which was not loaded explicitly.
Definition: extendable_binary.hpp:713
A wrapper around data that can be serialized in a binary fashion.
Definition: helpers.hpp:207
std::uint32_t getLoadedClassVersion() const
Gets last loaded class version.
Definition: extendable_binary.hpp:719
void copyBinarySingleNoSwap(void *dest_, void *const src_, std::size_t size_)
Copies size bytes from input buffer to the output buffer without any byte order swapping.
Definition: extendable_binary.hpp:513
Struct to keep position of start and end in stream.
Definition: extendable_binary_details.hpp:283
void setLastSizeTag(std::size_t size)
Sets value of loaded size tag.
Definition: extendable_binary.hpp:749
The base output archive class.
Definition: cereal.hpp:264
Types which has empty prologue and epilogoue (version with one template argument) ...
Definition: extendable_binary.hpp:1408
An output archive designed to save data in a portable binary representation with forward compatibilit...
Definition: extendable_binary.hpp:58
void loadBinary(void *const data, std::size_t size)
Reads size bytes of data from the input stream.
Definition: extendable_binary.hpp:456
#define CEREAL_SAVE_FUNCTION_NAME
The serialization (save) function name to search for.
Definition: macros.hpp:78
uint64_t size
size in bytes
Definition: helpers.hpp:218
Options(Endianness inputEndian_=getEndianness(), std::size_t maxSharedBufferSize_=std::numeric_limits< std::size_t >::max(), bool ignoreUnknownPolymorphicTypes_=true)
Specify specific options for the ExtendableBinaryInputArchive.
Definition: extendable_binary.hpp:382
void savePolymorphicName(const std::string &name)
Store temporarily polymorphic name of type to be saved later.
Definition: extendable_binary.hpp:220
An input archive designed to load data saved using ExtendableBinaryOutputArchive. ...
Definition: extendable_binary.hpp:361
void loadVarint(T &v)
Load varint from the stream.
Definition: extendable_binary.hpp:578
void saveBinarySingle(const void *data, std::size_t size)
Writes size bytes of data to the output stream.
Definition: extendable_binary.hpp:149
Support for types found in <memory>
A class used to disambiguate cases where cereal cannot detect a unique way of serializing a class...
Definition: access.hpp:404
An exception class thrown when things go wrong at runtime.
Definition: helpers.hpp:48
ExtendableBinaryInputArchive(std::istream &stream, Options const &options=Options::Default())
Construct, loading from the provided stream.
Definition: extendable_binary.hpp:437