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