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 "StringOp.hh"
9 #include "inline.hh"
10 #include "unreachable.hh"
11 #include <zlib.h>
12 #include <string>
13 #include <typeindex>
14 #include <type_traits>
15 #include <vector>
16 #include <map>
17 #include <sstream>
18 #include <cassert>
19 #include <memory>
20 
21 namespace openmsx {
22 
23 class LastDeltaBlocks;
24 class DeltaBlock;
25 
26 template<typename T> struct SerializeClassVersion;
27 
28 // In this section, the archive classes are defined.
29 //
30 // Archives can be categorized in two ways:
31 // - backing stream they use
32 // - input or output (each backing stream has exactly one input and one
33 // output variant)
34 //
35 // ATM these backing streams implemented:
36 // - Mem
37 // Stores stream in memory. Is meant to be very compact and very fast.
38 // It does not support versioning (it's not possible to load this stream
39 // in a newer version of openMSX). It is also not platform independent
40 // (e.g. integers are stored using native platform endianess).
41 // The main use case for this archive format is regular in memory
42 // snapshots, for example to support replay/rewind.
43 // - XML
44 // Stores the stream in a XML file. These files are meant to be portable
45 // to different architectures (e.g. little/big endian, 32/64 bit system).
46 // There is version information in the stream, so it should be possible
47 // to load streams created with older openMSX versions a newer openMSX.
48 // The XML files are meant to be human readable. Having editable XML files
49 // is not a design goal (e.g. simply changing a value will probably work,
50 // but swapping the position of two tag or adding or removing tags can
51 // easily break the stream).
52 // - Text
53 // This stores to stream in a flat ascii file (one item per line). This
54 // format is only written as a proof-of-concept to test the design. It's
55 // not meant to be used in practice.
56 //
57 // Archive code is heavily templatized. It uses the CRTP (curiously recuring
58 // template pattern ; a base class templatized on it's derived class). To
59 // implement static polymorphism. This means there is practically no run-time
60 // overhead of using this mechansim compared to 6 seperatly handcoded functions
61 // (Mem/XML/Text x input/output).
62 // TODO At least in theory, still need to verify this in practice.
63 // Though my experience is that gcc is generally very good in this.
64 
65 template<typename Derived> class ArchiveBase
66 {
67 public:
76  template<typename Base, typename T>
77  void serializeBase(T& t)
78  {
79  const char* tag = BaseClassName<Base>::getName();
80  self().serialize(tag, static_cast<Base&>(t));
81  }
82 
99  template<typename Base, typename T>
100  void serializeInlinedBase(T& t, unsigned version)
101  {
102  ::openmsx::serialize(self(), static_cast<Base&>(t), version);
103  }
104 
105  // Each concrete archive class also has the following methods:
106  // Because of the implementation with static polymorphism, this
107  // interface is not explictly visible in the base class.
108  //
109  //
110  // template<typename T> void serializeWithID(const char* tag, const T& t, ...)
111  //
112  // This is _the_most_important_ method of the serialization
113  // framework. Depending on the concrete archive type (loader/saver)
114  // this method will load or save the given type 't'. In case of an XML
115  // archive the 'tag' parameter will be used as tagname.
116  //
117  // At the end there are still a number of optional parameters (in the
118  // current implementation there can be between 0 and 3, but can be
119  // extened when needed). These are 'global' constructor parameters,
120  // constructor parameters that are not stored in the stream, but that
121  // are needed to reconstruct the object (for example can be references
122  // to structures that were already stored in the stream). So these
123  // parameters are only actually used while loading.
124  // TODO document this in more detail in some section where the
125  // (polymorphic) constructors are also described.
126  //
127  //
128  // void serialize_blob(const char* tag, const void* data, size_t len,
129  // bool diff = true)
130  //
131  // Serialize the given data as a binary blob.
132  // This cannot be part of the serialize() method above because we
133  // cannot know whether a byte-array should be serialized as a blob
134  // or as a collection of bytes (IOW we cannot decide it based on the
135  // type).
136  //
137  //
138  // template<typename T> void serialize(const char* tag, const T& t)
139  //
140  // This is much like the serializeWithID() method above, but it doesn't
141  // store an ID with this element. This means that it's not possible,
142  // later on in the stream, to refer to this element. For many elements
143  // you know this will not happen. This method results in a slightly
144  // more compact stream.
145  //
146  // Note that for primitive types we already don't store an ID, because
147  // pointers to primitive types are not supported (at least not ATM).
148  //
149  //
150  // template<typename T> void serializePointerID(const char* tag, const T& t)
151  //
152  // Serialize a pointer by storing the ID of the object it points to.
153  // This only works if the object was already serialized. The only
154  // reason to use this method instead of the more general serialize()
155  // method is that this one does not instantiate the object
156  // construction code. (So in some cases you can avoid having to
157  // provide specializations of SerializeConstructorArgs.)
158  //
159  //
160  // template<typename T> void serializePolymorphic(const char* tag, const T& t)
161  //
162  // Serialize a value-type whose concrete type is not yet known at
163  // compile-time (polymorphic pointers are already handled by the
164  // generic serialize() method).
165  //
166  // The difference between pointer and value-types is that for
167  // pointers, the de-serialize code also needs to construct the
168  // object, while for value-types, the object (with the correct
169  // concrete type) is already constructed, it only needs to be
170  // initialized.
171  //
172  //
173  // bool versionAtLeast(unsigned actual, unsigned required) const
174  // bool versionBelow (unsigned actual, unsigned required) const
175  //
176  // Checks whether the actual version is respective 'bigger or equal'
177  // or 'strictly lower' than the required version. So in fact these are
178  // equivalent to respectively:
179  // return actual >= required;
180  // return actual < required;
181  // Note that these two methods are the exact opposite of each other.
182  // Though for memory-archives and output-archives we know that the
183  // actual version is always equal to the latest class version and the
184  // required version can never be bigger than this latest version, so
185  // in these cases the methods can be optimized to respectively:
186  // return true;
187  // return false;
188  // By using these methods instead of direct comparisons, the compiler
189  // is able to perform more dead-code-elimination.
190 
191 /*internal*/
192  // These must be public for technical reasons, but they should only
193  // be used by the serialization framework.
194 
196  bool needVersion() const { return true; }
197 
199  bool isReverseSnapshot() const { return false; }
200 
204  bool translateEnumToString() const { return false; }
205 
212  template<typename T> void attribute(const char* name, T& t)
213  {
214  self().serialize(name, t);
215  }
216  void attribute(const char* name, const char* value);
217 
223  bool canHaveOptionalAttributes() const { return false; }
224 
229  bool hasAttribute(const char* /*name*/)
230  {
231  UNREACHABLE; return false;
232  }
233 
237  bool findAttribute(const char* /*name*/, unsigned& /*value*/)
238  {
239  UNREACHABLE; return false;
240  }
241 
250  bool canCountChildren() const { return false; }
251 
256  int countChildren() const
257  {
258  UNREACHABLE; return 0;
259  }
260 
269  void beginTag(const char* /*tag*/)
270  {
271  // nothing
272  }
279  void endTag(const char* /*tag*/)
280  {
281  // nothing
282  }
283 
284  // These (internal) methods should be implemented in the concrete
285  // archive classes.
286  //
287  // template<typename T> void save(const T& t)
288  //
289  // Should only be implemented for OuputArchives. Is called to
290  // store primitive types in the stream. In the end all structures
291  // are broken down to primitive types, so all data that ends up
292  // in the stream passes via this method (ok, depending on how
293  // attribute() and serialize_blob() is implemented, that data may
294  // not pass via save()).
295  //
296  // Often this method will be overloaded to handle certain types in a
297  // specific way.
298  //
299  //
300  // template<typename T> void load(T& t)
301  //
302  // Should only be implemented for InputArchives. This is similar (but
303  // opposite) to the save() method above. Loading of primitive types
304  // is done via this method.
305 
306  // void beginSection()
307  // void endSection()
308  // void skipSection(bool skip)
309  // The methods beginSection() and endSection() can only be used in
310  // output archives. These mark the location of a section that can
311  // later be skipped during loading.
312  // The method skipSection() can only be used in input archives. It
313  // optionally skips a section that was marked during saving.
314  // For every beginSection() call in the output, there must be a
315  // corresponding skipSection() call in the input (even if you don't
316  // actually want to skip the section).
317 
318 protected:
322  inline Derived& self()
323  {
324  return static_cast<Derived&>(*this);
325  }
326 };
327 
328 // The part of OutputArchiveBase that doesn't depend on the template parameter
330 {
331 public:
332  inline bool isLoader() const { return false; }
333  inline bool versionAtLeast(unsigned /*actual*/, unsigned /*required*/) const
334  {
335  return true;
336  }
337  inline bool versionBelow(unsigned /*actual*/, unsigned /*required*/) const
338  {
339  return false;
340  }
341 
342  void skipSection(bool /*skip*/)
343  {
344  UNREACHABLE;
345  }
346 
347 /*internal*/
348  #ifdef linux
349  // This routine is not portable, for example it breaks in
350  // windows (mingw) because there the location of the stack
351  // is _below_ the heap.
352  // But this is anyway only used to check assertions. So for now
353  // only do that in linux.
354  static NEVER_INLINE bool addressOnStack(const void* p)
355  {
356  // This is not portable, it assumes:
357  // - stack grows downwards
358  // - heap is at lower address than stack
359  // Also in c++ comparison between pointers is only defined when
360  // the two pointers point to objects in the same array.
361  int dummy;
362  return &dummy < p;
363  }
364  #endif
365 
366  // Generate a new ID for the given pointer and store this association
367  // for later (see getId()).
368  template<typename T> unsigned generateId(const T* p)
369  {
370  // For composed structures, for example
371  // struct A { ... };
372  // struct B { A a; ... };
373  // The pointer to the outer and inner structure can be the
374  // same while we still want a different ID to refer to these
375  // two. That's why we use a std::pair<void*, type_index> as key
376  // in the map.
377  // For polymorphic types you do sometimes use a base pointer
378  // to refer to a subtype. So there we only use the pointer
379  // value as key in the map.
380  if (std::is_polymorphic<T>::value) {
381  return generateID1(p);
382  } else {
383  return generateID2(p, typeid(T));
384  }
385  }
386 
387  template<typename T> unsigned getId(const T* p)
388  {
389  if (std::is_polymorphic<T>::value) {
390  return getID1(p);
391  } else {
392  return getID2(p, typeid(T));
393  }
394  }
395 
396 protected:
398 
399 private:
400  unsigned generateID1(const void* p);
401  unsigned generateID2(const void* p, const std::type_info& typeInfo);
402  unsigned getID1(const void* p);
403  unsigned getID2(const void* p, const std::type_info& typeInfo);
404 
405  std::map<std::pair<const void*, std::type_index>, unsigned> idMap;
406  std::map<const void*, unsigned> polyIdMap;
407  unsigned lastId;
408 };
409 
410 template<typename Derived>
411 class OutputArchiveBase : public ArchiveBase<Derived>, public OutputArchiveBase2
412 {
413 public:
414  template<typename Base, typename T>
415  void serializeInlinedBase(T& t, unsigned version)
416  {
417  // same implementation as base class, but with extra check
418  static_assert(SerializeClassVersion<Base>::value ==
420  "base and derived must have same version when "
421  "using serializeInlinedBase()");
422  ArchiveBase<Derived>::template serializeInlinedBase<Base>(t, version);
423  }
424  // Main saver method. Heavy lifting is done in the Saver class.
425  // The 'global constructor arguments' parameters are ignored because
426  // the saver archives also completely ignore those extra parameters.
427  // But we need to provide them because the same (templatized) code path
428  // is used both for saving and loading.
429  template<typename T, typename... Args>
430  void serializeWithID(const char* tag, const T& t, Args... /*globalConstrArgs*/)
431  {
432  this->self().beginTag(tag);
433  Saver<T> saver;
434  saver(this->self(), t, true);
435  this->self().endTag(tag);
436  }
437 
438  // Default implementation is to base64-encode the blob and serialize
439  // the resulting string. But memory archives will memcpy the blob.
440  void serialize_blob(const char* tag, const void* data, size_t len,
441  bool diff = true);
442 
443  template<typename T> void serialize(const char* tag, const T& t)
444  {
445  this->self().beginTag(tag);
446  Saver<T> saver;
447  saver(this->self(), t, false);
448  this->self().endTag(tag);
449  }
450  template<typename T> void serializePointerID(const char* tag, const T& t)
451  {
452  this->self().beginTag(tag);
453  IDSaver<T> saver;
454  saver(this->self(), t);
455  this->self().endTag(tag);
456  }
457  template<typename T> void serializePolymorphic(const char* tag, const T& t)
458  {
459  static_assert(std::is_polymorphic<T>::value,
460  "must be a polymorphic type");
461  PolymorphicSaverRegistry<Derived>::save(tag, this->self(), t);
462  }
463  template<typename T> void serializeOnlyOnce(const char* tag, const T& t)
464  {
465  if (!getId(&t)) {
466  serializeWithID(tag, t);
467  }
468  }
469 
470  // You shouldn't use this, it only exists for backwards compatibility
471  void serializeChar(const char* tag, char c)
472  {
473  this->self().beginTag(tag);
474  this->self().saveChar(c);
475  this->self().endTag(tag);
476  }
477 
478 protected:
480 };
481 
482 
483 // Part of InputArchiveBase that doesn't depend on the template parameter
485 {
486 public:
487  inline bool isLoader() const { return true; }
488 
490  {
491  UNREACHABLE;
492  }
493  void endSection()
494  {
495  UNREACHABLE;
496  }
497 
498 /*internal*/
499  void* getPointer(unsigned id);
500  void addPointer(unsigned id, const void* p);
501  unsigned getId(const void* p) const;
502 
503  template<typename T> void resetSharedPtr(std::shared_ptr<T>& s, T* r)
504  {
505  if (!r) {
506  s.reset();
507  return;
508  }
509  auto it = sharedPtrMap.find(r);
510  if (it == end(sharedPtrMap)) {
511  s.reset(r);
512  sharedPtrMap[r] = s;
513  } else {
514  s = std::static_pointer_cast<T>(it->second);
515  }
516  }
517 
518 protected:
520 
521 private:
522  std::map<unsigned, void*> idMap;
523  std::map<void*, std::shared_ptr<void>> sharedPtrMap;
524 };
525 
526 template<typename Derived>
527 class InputArchiveBase : public ArchiveBase<Derived>, public InputArchiveBase2
528 {
529 public:
530  template<typename T, typename... Args>
531  void serializeWithID(const char* tag, T& t, Args... args)
532  {
533  doSerialize(tag, t, std::tuple<Args...>(args...));
534  }
535  void serialize_blob(const char* tag, void* data, size_t len,
536  bool diff = true);
537 
538  template<typename T>
539  void serialize(const char* tag, T& t)
540  {
541  this->self().beginTag(tag);
542  using TNC = typename std::remove_const<T>::type;
543  auto& tnc = const_cast<TNC&>(t);
544  Loader<TNC> loader;
545  loader(this->self(), tnc, std::make_tuple(), -1); // don't load id
546  this->self().endTag(tag);
547  }
548  template<typename T> void serializePointerID(const char* tag, const T& t)
549  {
550  this->self().beginTag(tag);
551  using TNC = typename std::remove_const<T>::type;
552  auto& tnc = const_cast<TNC&>(t);
553  IDLoader<TNC> loader;
554  loader(this->self(), tnc);
555  this->self().endTag(tag);
556  }
557  template<typename T> void serializePolymorphic(const char* tag, T& t)
558  {
559  static_assert(std::is_polymorphic<T>::value,
560  "must be a polymorphic type");
561  PolymorphicInitializerRegistry<Derived>::init(tag, this->self(), &t);
562  }
563  template<typename T> void serializeOnlyOnce(const char* tag, const T& t)
564  {
565  if (!getId(&t)) {
566  serializeWithID(tag, t);
567  }
568  }
569 
570  // You shouldn't use this, it only exists for backwards compatibility
571  void serializeChar(const char* tag, char& c)
572  {
573  this->self().beginTag(tag);
574  this->self().loadChar(c);
575  this->self().endTag(tag);
576  }
577 
578 /*internal*/
579  // Actual loader method. Heavy lifting is done in the Loader class.
580  template<typename T, typename TUPLE>
581  void doSerialize(const char* tag, T& t, TUPLE args, int id = 0)
582  {
583  this->self().beginTag(tag);
584  using TNC = typename std::remove_const<T>::type;
585  auto& tnc = const_cast<TNC&>(t);
586  Loader<TNC> loader;
587  loader(this->self(), tnc, args, id);
588  this->self().endTag(tag);
589  }
590 
591 protected:
593 };
594 
595 
596 class MemOutputArchive final : public OutputArchiveBase<MemOutputArchive>
597 {
598 public:
600  std::vector<std::shared_ptr<DeltaBlock>>& deltaBlocks_,
601  bool reverseSnapshot_)
602  : lastDeltaBlocks(lastDeltaBlocks_)
603  , deltaBlocks(deltaBlocks_)
604  , reverseSnapshot(reverseSnapshot_)
605  {
606  }
607 
609  {
610  assert(openSections.empty());
611  }
612 
613  bool needVersion() const { return false; }
614  bool isReverseSnapshot() const { return reverseSnapshot; }
615 
616  template <typename T> void save(const T& t)
617  {
618  put(&t, sizeof(t));
619  }
620  inline void saveChar(char c)
621  {
622  save(c);
623  }
624  void save(const std::string& s);
625  void serialize_blob(const char*, const void* data, size_t len,
626  bool diff = true);
627 
629  {
630  size_t skip = 0; // filled in later
631  save(skip);
632  size_t beginPos = buffer.getPosition();
633  openSections.push_back(beginPos);
634  }
635  void endSection()
636  {
637  assert(!openSections.empty());
638  size_t endPos = buffer.getPosition();
639  size_t beginPos = openSections.back();
640  openSections.pop_back();
641  size_t skip = endPos - beginPos;
642  buffer.insertAt(beginPos - sizeof(skip),
643  &skip, sizeof(skip));
644  }
645 
646  MemBuffer<byte> releaseBuffer(size_t& size);
647 
648 private:
649  void put(const void* data, size_t len)
650  {
651  if (len) {
652  buffer.insert(data, len);
653  }
654  }
655 
656  OutputBuffer buffer;
657  std::vector<size_t> openSections;
658  LastDeltaBlocks& lastDeltaBlocks;
659  std::vector<std::shared_ptr<DeltaBlock>>& deltaBlocks;
660  const bool reverseSnapshot;
661 };
662 
663 class MemInputArchive final : public InputArchiveBase<MemInputArchive>
664 {
665 public:
666  MemInputArchive(const byte* data, size_t size,
667  const std::vector<std::shared_ptr<DeltaBlock>>& deltaBlocks_)
668  : buffer(data, size)
669  , deltaBlocks(deltaBlocks_)
670  {
671  }
672 
673  bool needVersion() const { return false; }
674  inline bool versionAtLeast(unsigned /*actual*/, unsigned /*required*/) const
675  {
676  return true;
677  }
678  inline bool versionBelow(unsigned /*actual*/, unsigned /*required*/) const
679  {
680  return false;
681  }
682 
683  template<typename T> void load(T& t)
684  {
685  get(&t, sizeof(t));
686  }
687  inline void loadChar(char& c)
688  {
689  load(c);
690  }
691  void load(std::string& s);
692  string_ref loadStr();
693  void serialize_blob(const char*, void* data, size_t len,
694  bool diff = true);
695 
696  void skipSection(bool skip)
697  {
698  size_t num;
699  load(num);
700  if (skip) {
701  buffer.skip(num);
702  }
703  }
704 
705 private:
706  void get(void* data, size_t len)
707  {
708  if (len) {
709  buffer.read(data, len);
710  }
711  }
712 
713  InputBuffer buffer;
714  const std::vector<std::shared_ptr<DeltaBlock>>& deltaBlocks;
715 };
716 
718 
719 class XmlOutputArchive final : public OutputArchiveBase<XmlOutputArchive>
720 {
721 public:
722  explicit XmlOutputArchive(const std::string& filename);
723  ~XmlOutputArchive();
724 
725  template <typename T> void saveImpl(const T& t)
726  {
727  // TODO make sure floating point is printed with enough digits
728  // maybe print as hex?
730  }
731  template <typename T> void save(const T& t)
732  {
733  saveImpl(t);
734  }
735  void saveChar(char c);
736  void save(const std::string& str);
737  void save(bool b);
738  void save(unsigned char b);
739  void save(signed char c);
740  void save(char c);
741  void save(int i); // these 3 are not strictly needed
742  void save(unsigned u); // but having them non-inline
743  void save(unsigned long long ull); // saves quite a bit of code
744 
745  void beginSection() { /*nothing*/ }
746  void endSection() { /*nothing*/ }
747 
748 //internal:
749  inline bool translateEnumToString() const { return true; }
750  inline bool canHaveOptionalAttributes() const { return true; }
751  inline bool canCountChildren() const { return true; }
752 
753  void beginTag(const char* tag);
754  void endTag(const char* tag);
755 
756  template<typename T> void attributeImpl(const char* name, const T& t)
757  {
758  attribute(name, StringOp::toString(t));
759  }
760  template<typename T> void attribute(const char* name, const T& t)
761  {
762  attributeImpl(name, t);
763  }
764  void attribute(const char* name, const std::string& str);
765  void attribute(const char* name, int i);
766  void attribute(const char* name, unsigned u);
767 
768 private:
769  gzFile file;
770  XMLElement root;
771  std::vector<XMLElement*> current;
772 };
773 
774 class XmlInputArchive final : public InputArchiveBase<XmlInputArchive>
775 {
776 public:
777  explicit XmlInputArchive(const std::string& filename);
778 
779  inline bool versionAtLeast(unsigned actual, unsigned required) const
780  {
781  return actual >= required;
782  }
783  inline bool versionBelow(unsigned actual, unsigned required) const
784  {
785  return actual < required;
786  }
787 
788  template<typename T> void load(T& t)
789  {
790  std::string str;
791  load(str);
792  std::istringstream is(str);
793  is >> t;
794  }
795  void loadChar(char& c);
796  void load(bool& b);
797  void load(unsigned char& b);
798  void load(signed char& c);
799  void load(char& c);
800  void load(int& i); // these 3 are not strictly needed
801  void load(unsigned& u); // but having them non-inline
802  void load(unsigned long long& ull); // saves quite a bit of code
803  void load(std::string& t);
804  string_ref loadStr();
805 
806  void skipSection(bool /*skip*/) { /*nothing*/ }
807 
808 //internal:
809  inline bool translateEnumToString() const { return true; }
810  inline bool canHaveOptionalAttributes() const { return true; }
811  inline bool canCountChildren() const { return true; }
812 
813  void beginTag(const char* tag);
814  void endTag(const char* tag);
815 
816  template<typename T> void attributeImpl(const char* name, T& t)
817  {
818  std::string str;
819  attribute(name, str);
820  std::istringstream is(str);
821  is >> t;
822  }
823  template<typename T> void attribute(const char* name, T& t)
824  {
825  attributeImpl(name, t);
826  }
827  void attribute(const char* name, std::string& t);
828  void attribute(const char* name, int& i);
829  void attribute(const char* name, unsigned& u);
830 
831  bool hasAttribute(const char* name);
832  bool findAttribute(const char* name, unsigned& value);
833  int countChildren() const;
834 
835 private:
836  XMLElement rootElem;
837  std::vector<std::pair<const XMLElement*, size_t>> elems;
838 };
839 
840 #define INSTANTIATE_SERIALIZE_METHODS(CLASS) \
841 template void CLASS::serialize(MemInputArchive&, unsigned); \
842 template void CLASS::serialize(MemOutputArchive&, unsigned); \
843 template void CLASS::serialize(XmlInputArchive&, unsigned); \
844 template void CLASS::serialize(XmlOutputArchive&, unsigned);
845 
846 } // namespace openmsx
847 
848 #endif
void serializeInlinedBase(T &t, unsigned version)
Serialize the base class of this classtype.
Definition: serialize.hh:100
unsigned getId(const T *p)
Definition: serialize.hh:387
void attribute(const char *name, T &t)
Definition: serialize.hh:823
void beginTag(const char *)
Indicate begin of a tag.
Definition: serialize.hh:269
string_ref::const_iterator end(const string_ref &x)
Definition: string_ref.hh:167
void serializeWithID(const char *tag, const T &t, Args...)
Definition: serialize.hh:430
void serialize(const char *tag, T &t)
Definition: serialize.hh:539
#define NEVER_INLINE
Definition: inline.hh:17
void serializePointerID(const char *tag, const T &t)
Definition: serialize.hh:548
bool canCountChildren() const
Definition: serialize.hh:751
void serializeInlinedBase(T &t, unsigned version)
Definition: serialize.hh:415
bool versionBelow(unsigned actual, unsigned required) const
Definition: serialize.hh:783
bool isReverseSnapshot() const
Is this a reverse-snapshot?
Definition: serialize.hh:199
void save(const T &t)
Definition: serialize.hh:616
string toString(long long a)
Definition: StringOp.cc:156
void resetSharedPtr(std::shared_ptr< T > &s, T *r)
Definition: serialize.hh:503
void doSerialize(const char *tag, T &t, TUPLE args, int id=0)
Definition: serialize.hh:581
uint8_t byte
8 bit unsigned integer
Definition: openmsx.hh:26
void skipSection(bool skip)
Definition: serialize.hh:696
This class implements a subset of the proposal for std::string_ref (proposed for the next c++ standar...
Definition: string_ref.hh:18
bool versionAtLeast(unsigned, unsigned) const
Definition: serialize.hh:333
int countChildren() const
Count the number of child tags.
Definition: serialize.hh:256
void serializePointerID(const char *tag, const T &t)
Definition: serialize.hh:450
void loadChar(char &c)
Definition: serialize.hh:687
bool versionBelow(unsigned, unsigned) const
Definition: serialize.hh:678
void serializePolymorphic(const char *tag, T &t)
Definition: serialize.hh:557
bool canCountChildren() const
Definition: serialize.hh:811
XMLElement load(string_ref filename, string_ref systemID)
Definition: XMLLoader.cc:31
MemOutputArchive(LastDeltaBlocks &lastDeltaBlocks_, std::vector< std::shared_ptr< DeltaBlock >> &deltaBlocks_, bool reverseSnapshot_)
Definition: serialize.hh:599
void save(SDL_Surface *surface, const std::string &filename)
Definition: PNG.cc:369
bool canHaveOptionalAttributes() const
Definition: serialize.hh:750
Memory output buffer.
MemInputArchive(const byte *data, size_t size, const std::vector< std::shared_ptr< DeltaBlock >> &deltaBlocks_)
Definition: serialize.hh:666
void attribute(const char *name, const T &t)
Definition: serialize.hh:760
void serializeWithID(const char *tag, T &t, Args... args)
Definition: serialize.hh:531
void attributeImpl(const char *name, const T &t)
Definition: serialize.hh:756
bool translateEnumToString() const
Definition: serialize.hh:809
void attribute(const char *name, T &t)
Load/store an attribute from/in the archive.
Definition: serialize.hh:212
void serializeBase(T &t)
Is this archive a loader or a saver.
Definition: serialize.hh:77
void save(const T &t)
Definition: serialize.hh:731
bool findAttribute(const char *, unsigned &)
Optimization: combination of hasAttribute() and getAttribute().
Definition: serialize.hh:237
bool canHaveOptionalAttributes() const
Some archives (like XML archives) can store optional attributes.
Definition: serialize.hh:223
void serializeOnlyOnce(const char *tag, const T &t)
Definition: serialize.hh:463
bool needVersion() const
Does this archive store version information.
Definition: serialize.hh:196
bool canHaveOptionalAttributes() const
Definition: serialize.hh:810
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
unsigned generateId(const T *p)
Definition: serialize.hh:368
bool translateEnumToString() const
Does this archive store enums as strings.
Definition: serialize.hh:204
size_t size() const
bool canCountChildren() const
Some archives (like XML archives) can count the number of subtags that belong to the current tag...
Definition: serialize.hh:250
bool needVersion() const
Definition: serialize.hh:613
uint8_t * data()
void serializePolymorphic(const char *tag, const T &t)
Definition: serialize.hh:457
bool versionAtLeast(unsigned actual, unsigned required) const
Definition: serialize.hh:779
void saveImpl(const T &t)
Definition: serialize.hh:725
void serializeOnlyOnce(const char *tag, const T &t)
Definition: serialize.hh:563
const string getName(KeyCode keyCode)
Translate key code to key name.
Definition: Keys.cc:373
bool versionAtLeast(unsigned, unsigned) const
Definition: serialize.hh:674
Store serialization-version number of a class.
Definition: serialize.hh:26
void endTag(const char *)
Indicate begin of a tag.
Definition: serialize.hh:279
void serializeChar(const char *tag, char &c)
Definition: serialize.hh:571
bool hasAttribute(const char *)
Check the presence of a (optional) attribute.
Definition: serialize.hh:229
This class is complementary to the OutputBuffer class.
void serialize(const char *tag, const T &t)
Definition: serialize.hh:443
void serializeChar(const char *tag, char c)
Definition: serialize.hh:471
void serialize(Archive &ar, T &t, unsigned version)
bool versionBelow(unsigned, unsigned) const
Definition: serialize.hh:337
bool isReverseSnapshot() const
Definition: serialize.hh:614
bool needVersion() const
Definition: serialize.hh:673
static void init(const char *tag, Archive &ar, void *t)
void attributeImpl(const char *name, T &t)
Definition: serialize.hh:816
bool translateEnumToString() const
Definition: serialize.hh:749
#define UNREACHABLE
Definition: unreachable.hh:35
static void save(Archive &ar, T *t)