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