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