openMSX
serialize.hh
Go to the documentation of this file.
1 #ifndef SERIALIZE_HH
2 #define SERIALIZE_HH
3 
4 #include "serialize_core.hh"
5 #include "SerializeBuffer.hh"
6 #include "XMLElement.hh"
7 #include "MemBuffer.hh"
8 #include "hash_map.hh"
9 #include "inline.hh"
10 #include "strCat.hh"
11 #include "unreachable.hh"
12 #include <zlib.h>
13 #include <string>
14 #include <typeindex>
15 #include <type_traits>
16 #include <vector>
17 #include <sstream>
18 #include <cassert>
19 #include <memory>
20 
21 namespace openmsx {
22 
23 class LastDeltaBlocks;
24 class DeltaBlock;
25 
26 // TODO move somewhere in utils once we use this more often
27 struct HashPair {
28  template<typename T1, typename T2>
29  size_t operator()(const std::pair<T1, T2>& p) const {
30  return 31 * std::hash<T1>()(p.first) +
31  std::hash<T2>()(p.second);
32  }
33 };
34 
35 
36 template<typename T> struct SerializeClassVersion;
37 
38 // In this section, the archive classes are defined.
39 //
40 // Archives can be categorized in two ways:
41 // - backing stream they use
42 // - input or output (each backing stream has exactly one input and one
43 // output variant)
44 //
45 // ATM these backing streams implemented:
46 // - Mem
47 // Stores stream in memory. Is meant to be very compact and very fast.
48 // It does not support versioning (it's not possible to load this stream
49 // in a newer version of openMSX). It is also not platform independent
50 // (e.g. integers are stored using native platform endianess).
51 // The main use case for this archive format is regular in memory
52 // snapshots, for example to support replay/rewind.
53 // - XML
54 // Stores the stream in a XML file. These files are meant to be portable
55 // to different architectures (e.g. little/big endian, 32/64 bit system).
56 // There is version information in the stream, so it should be possible
57 // to load streams created with older openMSX versions a newer openMSX.
58 // The XML files are meant to be human readable. Having editable XML files
59 // is not a design goal (e.g. simply changing a value will probably work,
60 // but swapping the position of two tag or adding or removing tags can
61 // easily break the stream).
62 // - Text
63 // This stores to stream in a flat ascii file (one item per line). This
64 // format is only written as a proof-of-concept to test the design. It's
65 // not meant to be used in practice.
66 //
67 // Archive code is heavily templatized. It uses the CRTP (curiously recuring
68 // template pattern ; a base class templatized on it's derived class). To
69 // implement static polymorphism. This means there is practically no run-time
70 // overhead of using this mechansim compared to 6 seperatly handcoded functions
71 // (Mem/XML/Text x input/output).
72 // TODO At least in theory, still need to verify this in practice.
73 // Though my experience is that gcc is generally very good in this.
74 
75 template<typename Derived> class ArchiveBase
76 {
77 public:
86  template<typename Base, typename T>
87  void serializeBase(T& t)
88  {
89  const char* tag = BaseClassName<Base>::getName();
90  self().serialize(tag, static_cast<Base&>(t));
91  }
92 
109  template<typename Base, typename T>
110  void serializeInlinedBase(T& t, unsigned version)
111  {
112  ::openmsx::serialize(self(), static_cast<Base&>(t), version);
113  }
114 
115  // Each concrete archive class also has the following methods:
116  // Because of the implementation with static polymorphism, this
117  // interface is not explictly visible in the base class.
118  //
119  //
120  // template<typename T> void serializeWithID(const char* tag, const T& t, ...)
121  //
122  // This is _the_most_important_ method of the serialization
123  // framework. Depending on the concrete archive type (loader/saver)
124  // this method will load or save the given type 't'. In case of an XML
125  // archive the 'tag' parameter will be used as tagname.
126  //
127  // At the end there are still a number of optional parameters (in the
128  // current implementation there can be between 0 and 3, but can be
129  // extened when needed). These are 'global' constructor parameters,
130  // constructor parameters that are not stored in the stream, but that
131  // are needed to reconstruct the object (for example can be references
132  // to structures that were already stored in the stream). So these
133  // parameters are only actually used while loading.
134  // TODO document this in more detail in some section where the
135  // (polymorphic) constructors are also described.
136  //
137  //
138  // void serialize_blob(const char* tag, const void* data, size_t len,
139  // bool diff = true)
140  //
141  // Serialize the given data as a binary blob.
142  // This cannot be part of the serialize() method above because we
143  // cannot know whether a byte-array should be serialized as a blob
144  // or as a collection of bytes (IOW we cannot decide it based on the
145  // type).
146  //
147  //
148  // template<typename T> void serialize(const char* tag, const T& t)
149  //
150  // This is much like the serializeWithID() method above, but it doesn't
151  // store an ID with this element. This means that it's not possible,
152  // later on in the stream, to refer to this element. For many elements
153  // you know this will not happen. This method results in a slightly
154  // more compact stream.
155  //
156  // Note that for primitive types we already don't store an ID, because
157  // pointers to primitive types are not supported (at least not ATM).
158  //
159  //
160  // template<typename T, typename ...Args>
161  // void serialize(const char* tag, const T& t, Args&& ...args)
162  //
163  // Optionally serialize() accepts more than one tag-variable pair.
164  // This does conceptually the same as repeated calls to serialize()
165  // with each time a single pair, but it might be more efficient. E.g.
166  // the MemOutputArchive implementation is more efficient when called
167  // with multiple simple types.
168  //
169  // template<typename T> void serializePointerID(const char* tag, const T& t)
170  //
171  // Serialize a pointer by storing the ID of the object it points to.
172  // This only works if the object was already serialized. The only
173  // reason to use this method instead of the more general serialize()
174  // method is that this one does not instantiate the object
175  // construction code. (So in some cases you can avoid having to
176  // provide specializations of SerializeConstructorArgs.)
177  //
178  //
179  // template<typename T> void serializePolymorphic(const char* tag, const T& t)
180  //
181  // Serialize a value-type whose concrete type is not yet known at
182  // compile-time (polymorphic pointers are already handled by the
183  // generic serialize() method).
184  //
185  // The difference between pointer and value-types is that for
186  // pointers, the de-serialize code also needs to construct the
187  // object, while for value-types, the object (with the correct
188  // concrete type) is already constructed, it only needs to be
189  // initialized.
190  //
191  //
192  // bool versionAtLeast(unsigned actual, unsigned required) const
193  // bool versionBelow (unsigned actual, unsigned required) const
194  //
195  // Checks whether the actual version is respective 'bigger or equal'
196  // or 'strictly lower' than the required version. So in fact these are
197  // equivalent to respectively:
198  // return actual >= required;
199  // return actual < required;
200  // Note that these two methods are the exact opposite of each other.
201  // Though for memory-archives and output-archives we know that the
202  // actual version is always equal to the latest class version and the
203  // required version can never be bigger than this latest version, so
204  // in these cases the methods can be optimized to respectively:
205  // return true;
206  // return false;
207  // By using these methods instead of direct comparisons, the compiler
208  // is able to perform more dead-code-elimination.
209 
210 /*internal*/
211  // These must be public for technical reasons, but they should only
212  // be used by the serialization framework.
213 
215  bool needVersion() const { return true; }
216 
218  bool isReverseSnapshot() const { return false; }
219 
223  bool translateEnumToString() const { return false; }
224 
231  template<typename T> void attribute(const char* name, T& t)
232  {
233  self().serialize(name, t);
234  }
235  void attribute(const char* name, const char* value);
236 
242  bool canHaveOptionalAttributes() const { return false; }
243 
248  bool hasAttribute(const char* /*name*/)
249  {
250  UNREACHABLE; return false;
251  }
252 
256  bool findAttribute(const char* /*name*/, unsigned& /*value*/)
257  {
258  UNREACHABLE; return false;
259  }
260 
269  bool canCountChildren() const { return false; }
270 
275  int countChildren() const
276  {
277  UNREACHABLE; return 0;
278  }
279 
288  void beginTag(const char* /*tag*/)
289  {
290  // nothing
291  }
298  void endTag(const char* /*tag*/)
299  {
300  // nothing
301  }
302 
303  // These (internal) methods should be implemented in the concrete
304  // archive classes.
305  //
306  // template<typename T> void save(const T& t)
307  //
308  // Should only be implemented for OuputArchives. Is called to
309  // store primitive types in the stream. In the end all structures
310  // are broken down to primitive types, so all data that ends up
311  // in the stream passes via this method (ok, depending on how
312  // attribute() and serialize_blob() is implemented, that data may
313  // not pass via save()).
314  //
315  // Often this method will be overloaded to handle certain types in a
316  // specific way.
317  //
318  //
319  // template<typename T> void load(T& t)
320  //
321  // Should only be implemented for InputArchives. This is similar (but
322  // opposite) to the save() method above. Loading of primitive types
323  // is done via this method.
324 
325  // void beginSection()
326  // void endSection()
327  // void skipSection(bool skip)
328  // The methods beginSection() and endSection() can only be used in
329  // output archives. These mark the location of a section that can
330  // later be skipped during loading.
331  // The method skipSection() can only be used in input archives. It
332  // optionally skips a section that was marked during saving.
333  // For every beginSection() call in the output, there must be a
334  // corresponding skipSection() call in the input (even if you don't
335  // actually want to skip the section).
336 
337 protected:
341  inline Derived& self()
342  {
343  return static_cast<Derived&>(*this);
344  }
345 };
346 
347 // The part of OutputArchiveBase that doesn't depend on the template parameter
349 {
350 public:
351  inline bool isLoader() const { return false; }
352  inline bool versionAtLeast(unsigned /*actual*/, unsigned /*required*/) const
353  {
354  return true;
355  }
356  inline bool versionBelow(unsigned /*actual*/, unsigned /*required*/) const
357  {
358  return false;
359  }
360 
361  void skipSection(bool /*skip*/)
362  {
363  UNREACHABLE;
364  }
365 
366 /*internal*/
367  #ifdef linux
368  // This routine is not portable, for example it breaks in
369  // windows (mingw) because there the location of the stack
370  // is _below_ the heap.
371  // But this is anyway only used to check assertions. So for now
372  // only do that in linux.
373  static NEVER_INLINE bool addressOnStack(const void* p)
374  {
375  // This is not portable, it assumes:
376  // - stack grows downwards
377  // - heap is at lower address than stack
378  // Also in c++ comparison between pointers is only defined when
379  // the two pointers point to objects in the same array.
380  int dummy;
381  return &dummy < p;
382  }
383  #endif
384 
385  // Generate a new ID for the given pointer and store this association
386  // for later (see getId()).
387  template<typename T> unsigned generateId(const T* p)
388  {
389  // For composed structures, for example
390  // struct A { ... };
391  // struct B { A a; ... };
392  // The pointer to the outer and inner structure can be the
393  // same while we still want a different ID to refer to these
394  // two. That's why we use a std::pair<void*, type_index> as key
395  // in the map.
396  // For polymorphic types you do sometimes use a base pointer
397  // to refer to a subtype. So there we only use the pointer
398  // value as key in the map.
399  if (std::is_polymorphic<T>::value) {
400  return generateID1(p);
401  } else {
402  return generateID2(p, typeid(T));
403  }
404  }
405 
406  template<typename T> unsigned getId(const T* p)
407  {
408  if (std::is_polymorphic<T>::value) {
409  return getID1(p);
410  } else {
411  return getID2(p, typeid(T));
412  }
413  }
414 
415 protected:
416  OutputArchiveBase2() = default;
417 
418 private:
419  unsigned generateID1(const void* p);
420  unsigned generateID2(const void* p, const std::type_info& typeInfo);
421  unsigned getID1(const void* p);
422  unsigned getID2(const void* p, const std::type_info& typeInfo);
423 
426  unsigned lastId = 0;
427 };
428 
429 template<typename Derived>
430 class OutputArchiveBase : public ArchiveBase<Derived>, public OutputArchiveBase2
431 {
432 public:
433  template<typename Base, typename T>
434  void serializeInlinedBase(T& t, unsigned version)
435  {
436  // same implementation as base class, but with extra check
437  static_assert(SerializeClassVersion<Base>::value ==
439  "base and derived must have same version when "
440  "using serializeInlinedBase()");
441  ArchiveBase<Derived>::template serializeInlinedBase<Base>(t, version);
442  }
443  // Main saver method. Heavy lifting is done in the Saver class.
444  // The 'global constructor arguments' parameters are ignored because
445  // the saver archives also completely ignore those extra parameters.
446  // But we need to provide them because the same (templatized) code path
447  // is used both for saving and loading.
448  template<typename T, typename... Args>
449  void serializeWithID(const char* tag, const T& t, Args... /*globalConstrArgs*/)
450  {
451  this->self().beginTag(tag);
452  Saver<T> saver;
453  saver(this->self(), t, true);
454  this->self().endTag(tag);
455  }
456 
457  // Default implementation is to base64-encode the blob and serialize
458  // the resulting string. But memory archives will memcpy the blob.
459  void serialize_blob(const char* tag, const void* data, size_t len,
460  bool diff = true);
461 
462  template<typename T> void serialize(const char* tag, const T& t)
463  {
464  this->self().beginTag(tag);
465  Saver<T> saver;
466  saver(this->self(), t, false);
467  this->self().endTag(tag);
468  }
469  template<typename T> void serializePointerID(const char* tag, const T& t)
470  {
471  this->self().beginTag(tag);
472  IDSaver<T> saver;
473  saver(this->self(), t);
474  this->self().endTag(tag);
475  }
476  template<typename T> void serializePolymorphic(const char* tag, const T& t)
477  {
478  static_assert(std::is_polymorphic<T>::value,
479  "must be a polymorphic type");
480  PolymorphicSaverRegistry<Derived>::save(tag, this->self(), t);
481  }
482  template<typename T> void serializeOnlyOnce(const char* tag, const T& t)
483  {
484  if (!getId(&t)) {
485  serializeWithID(tag, t);
486  }
487  }
488 
489  // You shouldn't use this, it only exists for backwards compatibility
490  void serializeChar(const char* tag, char c)
491  {
492  this->self().beginTag(tag);
493  this->self().saveChar(c);
494  this->self().endTag(tag);
495  }
496 
497 protected:
498  OutputArchiveBase() = default;
499 };
500 
501 
502 // Part of InputArchiveBase that doesn't depend on the template parameter
504 {
505 public:
506  inline bool isLoader() const { return true; }
507 
509  {
510  UNREACHABLE;
511  }
512  void endSection()
513  {
514  UNREACHABLE;
515  }
516 
517 /*internal*/
518  void* getPointer(unsigned id);
519  void addPointer(unsigned id, const void* p);
520  unsigned getId(const void* p) const;
521 
522  template<typename T> void resetSharedPtr(std::shared_ptr<T>& s, T* r)
523  {
524  if (!r) {
525  s.reset();
526  return;
527  }
528  auto it = sharedPtrMap.find(r);
529  if (it == end(sharedPtrMap)) {
530  s.reset(r);
531  sharedPtrMap.emplace_noDuplicateCheck(r, s);
532  } else {
533  s = std::static_pointer_cast<T>(it->second);
534  }
535  }
536 
537 protected:
538  InputArchiveBase2() = default;
539 
540 private:
543 };
544 
545 template<typename Derived>
546 class InputArchiveBase : public ArchiveBase<Derived>, public InputArchiveBase2
547 {
548 public:
549  template<typename T, typename... Args>
550  void serializeWithID(const char* tag, T& t, Args... args)
551  {
552  doSerialize(tag, t, std::tuple<Args...>(args...));
553  }
554  void serialize_blob(const char* tag, void* data, size_t len,
555  bool diff = true);
556 
557  template<typename T>
558  void serialize(const char* tag, T& t)
559  {
560  this->self().beginTag(tag);
561  using TNC = std::remove_const_t<T>;
562  auto& tnc = const_cast<TNC&>(t);
563  Loader<TNC> loader;
564  loader(this->self(), tnc, std::make_tuple(), -1); // don't load id
565  this->self().endTag(tag);
566  }
567  template<typename T> void serializePointerID(const char* tag, const T& t)
568  {
569  this->self().beginTag(tag);
570  using TNC = std::remove_const_t<T>;
571  auto& tnc = const_cast<TNC&>(t);
572  IDLoader<TNC> loader;
573  loader(this->self(), tnc);
574  this->self().endTag(tag);
575  }
576  template<typename T> void serializePolymorphic(const char* tag, T& t)
577  {
578  static_assert(std::is_polymorphic<T>::value,
579  "must be a polymorphic type");
580  PolymorphicInitializerRegistry<Derived>::init(tag, this->self(), &t);
581  }
582  template<typename T> void serializeOnlyOnce(const char* tag, const T& t)
583  {
584  if (!getId(&t)) {
585  serializeWithID(tag, t);
586  }
587  }
588 
589  // You shouldn't use this, it only exists for backwards compatibility
590  void serializeChar(const char* tag, char& c)
591  {
592  this->self().beginTag(tag);
593  this->self().loadChar(c);
594  this->self().endTag(tag);
595  }
596 
597 /*internal*/
598  // Actual loader method. Heavy lifting is done in the Loader class.
599  template<typename T, typename TUPLE>
600  void doSerialize(const char* tag, T& t, TUPLE args, int id = 0)
601  {
602  this->self().beginTag(tag);
603  using TNC = std::remove_const_t<T>;
604  auto& tnc = const_cast<TNC&>(t);
605  Loader<TNC> loader;
606  loader(this->self(), tnc, args, id);
607  this->self().endTag(tag);
608  }
609 
610 protected:
611  InputArchiveBase() = default;
612 };
613 
614 
615 // Enumerate all types which can be serialized using a simple memcpy. This
616 // trait can be used by MemOutputArchive to apply certain optimizations.
617 template<typename T> struct SerializeAsMemcpy : std::false_type {};
618 template<> struct SerializeAsMemcpy< bool > : std::true_type {};
619 template<> struct SerializeAsMemcpy< char > : std::true_type {};
620 template<> struct SerializeAsMemcpy< signed char > : std::true_type {};
621 template<> struct SerializeAsMemcpy<unsigned char > : std::true_type {};
622 template<> struct SerializeAsMemcpy< short > : std::true_type {};
623 template<> struct SerializeAsMemcpy<unsigned short > : std::true_type {};
624 template<> struct SerializeAsMemcpy< int > : std::true_type {};
625 template<> struct SerializeAsMemcpy<unsigned int > : std::true_type {};
626 template<> struct SerializeAsMemcpy< long > : std::true_type {};
627 template<> struct SerializeAsMemcpy<unsigned long > : std::true_type {};
628 template<> struct SerializeAsMemcpy< long long> : std::true_type {};
629 template<> struct SerializeAsMemcpy<unsigned long long> : std::true_type {};
630 template<> struct SerializeAsMemcpy< float > : std::true_type {};
631 template<> struct SerializeAsMemcpy< double > : std::true_type {};
632 template<> struct SerializeAsMemcpy< long double > : std::true_type {};
633 template<typename T, size_t N> struct SerializeAsMemcpy<T[N]> : SerializeAsMemcpy<T> {};
634 
635 class MemOutputArchive final : public OutputArchiveBase<MemOutputArchive>
636 {
637 public:
639  std::vector<std::shared_ptr<DeltaBlock>>& deltaBlocks_,
640  bool reverseSnapshot_)
641  : lastDeltaBlocks(lastDeltaBlocks_)
642  , deltaBlocks(deltaBlocks_)
643  , reverseSnapshot(reverseSnapshot_)
644  {
645  }
646 
648  {
649  assert(openSections.empty());
650  }
651 
652  bool needVersion() const { return false; }
653  bool isReverseSnapshot() const { return reverseSnapshot; }
654 
655  template <typename T> void save(const T& t)
656  {
657  put(&t, sizeof(t));
658  }
659  inline void saveChar(char c)
660  {
661  save(c);
662  }
663  void save(const std::string& s);
664  void serialize_blob(const char* tag, const void* data, size_t len,
665  bool diff = true);
666 
668  template<typename T, typename ...Args>
669  ALWAYS_INLINE void serialize(const char* tag, const T& t, Args&& ...args)
670  {
671  // - Walk over all elements. Process non-memcpy-able elements at
672  // once. Collect memcpy-able elements in a tuple. At the end
673  // process the collected tuple with a single call.
674  // - Only do this when there are at least two pairs (it is
675  // correct for a single pair, but it's less tuned for that
676  // case).
677  serialize_group(std::make_tuple(), tag, t, std::forward<Args>(args)...);
678  }
679  template<typename T, size_t N>
680  ALWAYS_INLINE void serialize(const char* /*tag*/, const T(&t)[N],
681  typename std::enable_if<SerializeAsMemcpy<T>::value>::type* = nullptr)
682  {
683  buffer.insert(&t[0], N * sizeof(T));
684  }
685 
687  {
688  size_t skip = 0; // filled in later
689  save(skip);
690  size_t beginPos = buffer.getPosition();
691  openSections.push_back(beginPos);
692  }
693  void endSection()
694  {
695  assert(!openSections.empty());
696  size_t endPos = buffer.getPosition();
697  size_t beginPos = openSections.back();
698  openSections.pop_back();
699  size_t skip = endPos - beginPos;
700  buffer.insertAt(beginPos - sizeof(skip),
701  &skip, sizeof(skip));
702  }
703 
704  MemBuffer<uint8_t> releaseBuffer(size_t& size);
705 
706 private:
707  void put(const void* data, size_t len)
708  {
709  if (len) {
710  buffer.insert(data, len);
711  }
712  }
713 
714  ALWAYS_INLINE void serialize_group(const std::tuple<>& /*tuple*/)
715  {
716  // done categorizing, there were no memcpy-able elements
717  }
718  template<typename ...Args>
719  ALWAYS_INLINE void serialize_group(const std::tuple<Args...>& tuple)
720  {
721  // done categorizing, process all memcpy-able elements
722  buffer.insert_tuple_ptr(tuple);
723  }
724  template<typename TUPLE, typename T, typename ...Args>
725  ALWAYS_INLINE void serialize_group_impl(std::true_type, const TUPLE& tuple, const char* /*tag*/, const T& t, Args&& ...args)
726  {
727  // add to the group and continue categorizing
728  serialize_group(std::tuple_cat(tuple, std::make_tuple(&t)), std::forward<Args>(args)...);
729  }
730  template<typename TUPLE, typename T, typename ...Args>
731  ALWAYS_INLINE void serialize_group_impl(std::false_type, const TUPLE& tuple, const char* tag, const T& t, Args&& ...args)
732  {
733  serialize(tag, t); // process single (ungroupable) element
734  serialize_group(tuple, std::forward<Args>(args)...); // continue categorizing
735  }
736  template<typename TUPLE, typename T, typename ...Args>
737  ALWAYS_INLINE void serialize_group(const TUPLE& tuple, const char* tag, const T& t, Args&& ...args)
738  {
739  // categorize one element
740  serialize_group_impl(typename SerializeAsMemcpy<T>::type(), tuple, tag, t, std::forward<Args>(args)...);
741  }
742 
743 private:
744  OutputBuffer buffer;
745  std::vector<size_t> openSections;
746  LastDeltaBlocks& lastDeltaBlocks;
747  std::vector<std::shared_ptr<DeltaBlock>>& deltaBlocks;
748  const bool reverseSnapshot;
749 };
750 
751 class MemInputArchive final : public InputArchiveBase<MemInputArchive>
752 {
753 public:
754  MemInputArchive(const uint8_t* data, size_t size,
755  const std::vector<std::shared_ptr<DeltaBlock>>& deltaBlocks_)
756  : buffer(data, size)
757  , deltaBlocks(deltaBlocks_)
758  {
759  }
760 
761  bool needVersion() const { return false; }
762  inline bool versionAtLeast(unsigned /*actual*/, unsigned /*required*/) const
763  {
764  return true;
765  }
766  inline bool versionBelow(unsigned /*actual*/, unsigned /*required*/) const
767  {
768  return false;
769  }
770 
771  template<typename T> void load(T& t)
772  {
773  get(&t, sizeof(t));
774  }
775  inline void loadChar(char& c)
776  {
777  load(c);
778  }
779  void load(std::string& s);
780  string_view loadStr();
781  void serialize_blob(const char* tag, void* data, size_t len,
782  bool diff = true);
783 
785  template<typename T, typename ...Args>
786  ALWAYS_INLINE void serialize(const char* tag, T& t, Args&& ...args)
787  {
788  // see comments in MemOutputArchive
789  serialize_group(std::make_tuple(), tag, t, std::forward<Args>(args)...);
790  }
791 
792  template<typename T, size_t N>
793  ALWAYS_INLINE void serialize(const char* /*tag*/, T(&t)[N],
794  typename std::enable_if<SerializeAsMemcpy<T>::value>::type* = nullptr)
795  {
796  buffer.read(&t[0], N * sizeof(T));
797  }
798 
799  void skipSection(bool skip)
800  {
801  size_t num;
802  load(num);
803  if (skip) {
804  buffer.skip(num);
805  }
806  }
807 
808 private:
809  void get(void* data, size_t len)
810  {
811  if (len) {
812  buffer.read(data, len);
813  }
814  }
815 
816  template<int I, int N, typename TUPLE> struct GroupLoader {
817  ALWAYS_INLINE void operator()(InputBuffer& buf, const TUPLE& tuple) const {
818  using ElemPtr = typename std::tuple_element<I, TUPLE>::type;
819  using Elem = typename std::remove_pointer<ElemPtr>::type;
820  buf.read(std::get<I>(tuple), sizeof(Elem));
821  GroupLoader<I + 1, N, TUPLE> nextLoader;
822  nextLoader(buf, tuple);
823  }
824  };
825  template<int N, typename TUPLE> struct GroupLoader<N, N, TUPLE> {
826  ALWAYS_INLINE void operator()(InputBuffer& /*buf*/, const TUPLE& /*tuple*/) const {
827  // nothing
828  }
829  };
830 
831  // See comments in MemOutputArchive
832  template<typename TUPLE>
833  ALWAYS_INLINE void serialize_group(const TUPLE& tuple)
834  {
835  GroupLoader<0, std::tuple_size<TUPLE>::value, TUPLE> groupLoader;
836  groupLoader(buffer, tuple);
837  }
838  template<typename TUPLE, typename T, typename ...Args>
839  ALWAYS_INLINE void serialize_group_impl(std::true_type, const TUPLE& tuple, const char* /*tag*/, T& t, Args&& ...args)
840  {
841  serialize_group(std::tuple_cat(tuple, std::make_tuple(&t)), std::forward<Args>(args)...);
842  }
843  template<typename TUPLE, typename T, typename ...Args>
844  ALWAYS_INLINE void serialize_group_impl(std::false_type, const TUPLE& tuple, const char* tag, T& t, Args&& ...args)
845  {
846  serialize(tag, t);
847  serialize_group(tuple, std::forward<Args>(args)...);
848  }
849  template<typename TUPLE, typename T, typename ...Args>
850  ALWAYS_INLINE void serialize_group(const TUPLE& tuple, const char* tag, T& t, Args&& ...args)
851  {
852  serialize_group_impl(typename SerializeAsMemcpy<T>::type(), tuple, tag, t, std::forward<Args>(args)...);
853  }
854 
855 private:
856  InputBuffer buffer;
857  const std::vector<std::shared_ptr<DeltaBlock>>& deltaBlocks;
858 };
859 
861 
862 class XmlOutputArchive final : public OutputArchiveBase<XmlOutputArchive>
863 {
864 public:
865  explicit XmlOutputArchive(const std::string& filename);
866  void close();
867  ~XmlOutputArchive();
868 
869  template <typename T> void saveImpl(const T& t)
870  {
871  // TODO make sure floating point is printed with enough digits
872  // maybe print as hex?
873  save(strCat(t));
874  }
875  template <typename T> void save(const T& t)
876  {
877  saveImpl(t);
878  }
879  void saveChar(char c);
880  void save(const std::string& str);
881  void save(bool b);
882  void save(unsigned char b);
883  void save(signed char c);
884  void save(char c);
885  void save(int i); // these 3 are not strictly needed
886  void save(unsigned u); // but having them non-inline
887  void save(unsigned long long ull); // saves quite a bit of code
888 
889  void beginSection() { /*nothing*/ }
890  void endSection() { /*nothing*/ }
891 
892  // workaround(?) for visual studio 2015:
893  // put the default here instead of in the base class
895  template<typename T, typename ...Args>
896  ALWAYS_INLINE void serialize(const char* tag, const T& t, Args&& ...args)
897  {
898  // by default just repeatedly call the single-pair serialize() variant
899  this->self().serialize(tag, t);
900  this->self().serialize(std::forward<Args>(args)...);
901  }
902 
903 //internal:
904  inline bool translateEnumToString() const { return true; }
905  inline bool canHaveOptionalAttributes() const { return true; }
906  inline bool canCountChildren() const { return true; }
907 
908  void beginTag(const char* tag);
909  void endTag(const char* tag);
910 
911  template<typename T> void attributeImpl(const char* name, const T& t)
912  {
913  attribute(name, strCat(t));
914  }
915  template<typename T> void attribute(const char* name, const T& t)
916  {
917  attributeImpl(name, t);
918  }
919  void attribute(const char* name, const std::string& str);
920  void attribute(const char* name, int i);
921  void attribute(const char* name, unsigned u);
922 
923 private:
924  gzFile file;
925  XMLElement root;
926  std::vector<XMLElement*> current;
927 };
928 
929 class XmlInputArchive final : public InputArchiveBase<XmlInputArchive>
930 {
931 public:
932  explicit XmlInputArchive(const std::string& filename);
933 
934  inline bool versionAtLeast(unsigned actual, unsigned required) const
935  {
936  return actual >= required;
937  }
938  inline bool versionBelow(unsigned actual, unsigned required) const
939  {
940  return actual < required;
941  }
942 
943  template<typename T> void load(T& t)
944  {
945  std::string str;
946  load(str);
947  std::istringstream is(str);
948  is >> t;
949  }
950  void loadChar(char& c);
951  void load(bool& b);
952  void load(unsigned char& b);
953  void load(signed char& c);
954  void load(char& c);
955  void load(int& i); // these 3 are not strictly needed
956  void load(unsigned& u); // but having them non-inline
957  void load(unsigned long long& ull); // saves quite a bit of code
958  void load(std::string& t);
959  string_view loadStr();
960 
961  void skipSection(bool /*skip*/) { /*nothing*/ }
962 
963  // workaround(?) for visual studio 2015:
964  // put the default here instead of in the base class
966  template<typename T, typename ...Args>
967  ALWAYS_INLINE void serialize(const char* tag, T& t, Args&& ...args)
968  {
969  // by default just repeatedly call the single-pair serialize() variant
970  this->self().serialize(tag, t);
971  this->self().serialize(std::forward<Args>(args)...);
972  }
973 
974 //internal:
975  inline bool translateEnumToString() const { return true; }
976  inline bool canHaveOptionalAttributes() const { return true; }
977  inline bool canCountChildren() const { return true; }
978 
979  void beginTag(const char* tag);
980  void endTag(const char* tag);
981 
982  template<typename T> void attributeImpl(const char* name, T& t)
983  {
984  std::string str;
985  attribute(name, str);
986  std::istringstream is(str);
987  is >> t;
988  }
989  template<typename T> void attribute(const char* name, T& t)
990  {
991  attributeImpl(name, t);
992  }
993  void attribute(const char* name, std::string& t);
994  void attribute(const char* name, int& i);
995  void attribute(const char* name, unsigned& u);
996 
997  bool hasAttribute(const char* name);
998  bool findAttribute(const char* name, unsigned& value);
999  int countChildren() const;
1000 
1001 private:
1002  XMLElement rootElem;
1003  std::vector<std::pair<const XMLElement*, size_t>> elems;
1004 };
1005 
1006 #define INSTANTIATE_SERIALIZE_METHODS(CLASS) \
1007 template void CLASS::serialize(MemInputArchive&, unsigned); \
1008 template void CLASS::serialize(MemOutputArchive&, unsigned); \
1009 template void CLASS::serialize(XmlInputArchive&, unsigned); \
1010 template void CLASS::serialize(XmlOutputArchive&, unsigned);
1011 
1012 } // namespace openmsx
1013 
1014 #endif
void serializeInlinedBase(T &t, unsigned version)
Serialize the base class of this classtype.
Definition: serialize.hh:110
unsigned getId(const T *p)
Definition: serialize.hh:406
void attribute(const char *name, T &t)
Definition: serialize.hh:989
void beginTag(const char *)
Indicate begin of a tag.
Definition: serialize.hh:288
void serializeWithID(const char *tag, const T &t, Args...)
Definition: serialize.hh:449
void serialize(const char *tag, T &t)
Definition: serialize.hh:558
#define NEVER_INLINE
Definition: inline.hh:17
void serializePointerID(const char *tag, const T &t)
Definition: serialize.hh:567
#define ALWAYS_INLINE
Definition: inline.hh:16
bool canCountChildren() const
Definition: serialize.hh:906
void serializeInlinedBase(T &t, unsigned version)
Definition: serialize.hh:434
bool versionBelow(unsigned actual, unsigned required) const
Definition: serialize.hh:938
bool isReverseSnapshot() const
Is this a reverse-snapshot?
Definition: serialize.hh:218
void save(const T &t)
Definition: serialize.hh:655
ALWAYS_INLINE void serialize(const char *tag, const T &t, Args &&...args)
Definition: serialize.hh:896
void resetSharedPtr(std::shared_ptr< T > &s, T *r)
Definition: serialize.hh:522
void doSerialize(const char *tag, T &t, TUPLE args, int id=0)
Definition: serialize.hh:600
void read(void *result, size_t len)
Read the given number of bytes.
void skipSection(bool skip)
Definition: serialize.hh:799
ALWAYS_INLINE void serialize(const char *tag, T &t, Args &&...args)
Definition: serialize.hh:967
bool versionAtLeast(unsigned, unsigned) const
Definition: serialize.hh:352
int countChildren() const
Count the number of child tags.
Definition: serialize.hh:275
ALWAYS_INLINE void serialize(const char *tag, const T &t, Args &&...args)
Definition: serialize.hh:669
void serializePointerID(const char *tag, const T &t)
Definition: serialize.hh:469
void loadChar(char &c)
Definition: serialize.hh:775
bool versionBelow(unsigned, unsigned) const
Definition: serialize.hh:766
void serializePolymorphic(const char *tag, T &t)
Definition: serialize.hh:576
bool canCountChildren() const
Definition: serialize.hh:977
MemOutputArchive(LastDeltaBlocks &lastDeltaBlocks_, std::vector< std::shared_ptr< DeltaBlock >> &deltaBlocks_, bool reverseSnapshot_)
Definition: serialize.hh:638
bool canHaveOptionalAttributes() const
Definition: serialize.hh:905
Memory output buffer.
constexpr auto data(C &c) -> decltype(c.data())
Definition: span.hh:69
void attribute(const char *name, const T &t)
Definition: serialize.hh:915
void serializeWithID(const char *tag, T &t, Args... args)
Definition: serialize.hh:550
void attributeImpl(const char *name, const T &t)
Definition: serialize.hh:911
bool translateEnumToString() const
Definition: serialize.hh:975
ALWAYS_INLINE void serialize(const char *, const T(&t)[N], typename std::enable_if< SerializeAsMemcpy< T >::value >::type *=nullptr)
Definition: serialize.hh:680
void attribute(const char *name, T &t)
Load/store an attribute from/in the archive.
Definition: serialize.hh:231
void serializeBase(T &t)
Is this archive a loader or a saver.
Definition: serialize.hh:87
void save(const T &t)
Definition: serialize.hh:875
bool findAttribute(const char *, unsigned &)
Optimization: combination of hasAttribute() and getAttribute().
Definition: serialize.hh:256
bool canHaveOptionalAttributes() const
Some archives (like XML archives) can store optional attributes.
Definition: serialize.hh:242
void serializeOnlyOnce(const char *tag, const T &t)
Definition: serialize.hh:482
bool needVersion() const
Does this archive store version information.
Definition: serialize.hh:215
bool canHaveOptionalAttributes() const
Definition: serialize.hh:976
ALWAYS_INLINE void serialize(const char *tag, T &t, Args &&...args)
Definition: serialize.hh:786
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
XMLElement load(string_view filename, string_view systemID)
Definition: XMLLoader.cc:31
unsigned generateId(const T *p)
Definition: serialize.hh:387
bool translateEnumToString() const
Does this archive store enums as strings.
Definition: serialize.hh:223
bool canCountChildren() const
Some archives (like XML archives) can count the number of subtags that belong to the current tag...
Definition: serialize.hh:269
ALWAYS_INLINE void serialize(const char *, T(&t)[N], typename std::enable_if< SerializeAsMemcpy< T >::value >::type *=nullptr)
Definition: serialize.hh:793
This class implements a (close approximation) of the std::string_view class.
Definition: string_view.hh:16
bool needVersion() const
Definition: serialize.hh:652
MemInputArchive(const uint8_t *data, size_t size, const std::vector< std::shared_ptr< DeltaBlock >> &deltaBlocks_)
Definition: serialize.hh:754
string getName(KeyCode keyCode)
Translate key code to key name.
Definition: Keys.cc:589
void serializePolymorphic(const char *tag, const T &t)
Definition: serialize.hh:476
bool versionAtLeast(unsigned actual, unsigned required) const
Definition: serialize.hh:934
size_t operator()(const std::pair< T1, T2 > &p) const
Definition: serialize.hh:29
void saveImpl(const T &t)
Definition: serialize.hh:869
void serializeOnlyOnce(const char *tag, const T &t)
Definition: serialize.hh:582
std::string strCat(Ts &&...ts)
Definition: strCat.hh:577
bool versionAtLeast(unsigned, unsigned) const
Definition: serialize.hh:762
Store serialization-version number of a class.
Definition: serialize.hh:36
void endTag(const char *)
Indicate begin of a tag.
Definition: serialize.hh:298
void serializeChar(const char *tag, char &c)
Definition: serialize.hh:590
bool hasAttribute(const char *)
Check the presence of a (optional) attribute.
Definition: serialize.hh:248
constexpr auto size(const C &c) -> decltype(c.size())
Definition: span.hh:62
This class is complementary to the OutputBuffer class.
void serialize(const char *tag, const T &t)
Definition: serialize.hh:462
TclObject t
void serializeChar(const char *tag, char c)
Definition: serialize.hh:490
void serialize(Archive &ar, T &t, unsigned version)
bool versionBelow(unsigned, unsigned) const
Definition: serialize.hh:356
bool isReverseSnapshot() const
Definition: serialize.hh:653
auto end(const string_view &x)
Definition: string_view.hh:152
bool needVersion() const
Definition: serialize.hh:761
static void init(const char *tag, Archive &ar, void *t)
void attributeImpl(const char *name, T &t)
Definition: serialize.hh:982
bool translateEnumToString() const
Definition: serialize.hh:904
#define UNREACHABLE
Definition: unreachable.hh:38
static void save(Archive &ar, T *t)