libgig  4.1.0
Serialization.h
1 /***************************************************************************
2  * *
3  * Copyright (C) 2017 Christian Schoenebeck *
4  * <cuse@users.sourceforge.net> *
5  * *
6  * This library is part of libgig. *
7  * *
8  * This library is free software; you can redistribute it and/or modify *
9  * it under the terms of the GNU General Public License as published by *
10  * the Free Software Foundation; either version 2 of the License, or *
11  * (at your option) any later version. *
12  * *
13  * This library is distributed in the hope that it will be useful, *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16  * GNU General Public License for more details. *
17  * *
18  * You should have received a copy of the GNU General Public License *
19  * along with this library; if not, write to the Free Software *
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
21  * MA 02111-1307 USA *
22  ***************************************************************************/
23 
24 #ifndef LIBGIG_SERIALIZATION_H
25 #define LIBGIG_SERIALIZATION_H
26 
27 #ifdef HAVE_CONFIG_H
28 # include <config.h>
29 #endif
30 
31 #include <stdint.h>
32 #include <stdio.h>
33 #include <typeinfo>
34 #include <string>
35 #include <vector>
36 #include <map>
37 #include <time.h>
38 #include <stdarg.h>
39 
40 #ifndef __has_extension
41 # define __has_extension(x) 0
42 #endif
43 
44 #ifndef HAS_BUILTIN_TYPE_TRAITS
45 # if __cplusplus >= 201103L
46 # define HAS_BUILTIN_TYPE_TRAITS 1
47 # elif ( __has_extension(is_class) && __has_extension(is_enum) )
48 # define HAS_BUILTIN_TYPE_TRAITS 1
49 # elif ( __GNUC__ > 4 || ( __GNUC__ == 4 && __GNUC_MINOR__ >= 3 ) )
50 # define HAS_BUILTIN_TYPE_TRAITS 1
51 # elif _MSC_VER >= 1400 /* MS Visual C++ 8.0 (Visual Studio 2005) */
52 # define HAS_BUILTIN_TYPE_TRAITS 1
53 # elif __INTEL_COMPILER >= 1100
54 # define HAS_BUILTIN_TYPE_TRAITS 1
55 # else
56 # define HAS_BUILTIN_TYPE_TRAITS 0
57 # endif
58 #endif
59 
60 #if !HAS_BUILTIN_TYPE_TRAITS
61 # include <tr1/type_traits>
62 # define LIBGIG_IS_CLASS(type) std::tr1::__is_union_or_class<type>::value //NOTE: without compiler support we cannot distinguish union from class
63 #else
64 # define LIBGIG_IS_CLASS(type) __is_class(type)
65 #endif
66 
110 namespace Serialization {
111 
112  // just symbol prototyping
113  class DataType;
114  class Object;
115  class Member;
116  class Archive;
117  class ObjectPool;
118  class Exception;
119 
120  typedef std::string String;
121 
130  typedef std::vector<uint8_t> RawData;
131 
142  typedef void* ID;
143 
151  typedef uint32_t Version;
152 
158  enum time_base_t {
161  };
162 
170  template<typename T>
171  bool IsEnum(const T& data) {
172  #if !HAS_BUILTIN_TYPE_TRAITS
173  return std::tr1::is_enum<T>::value;
174  #else
175  return __is_enum(T);
176  #endif
177  }
178 
189  template<typename T>
190  bool IsUnion(const T& data) {
191  #if !HAS_BUILTIN_TYPE_TRAITS
192  return false; // without compiler support we cannot distinguish union from class
193  #else
194  return __is_union(T);
195  #endif
196  }
197 
207  template<typename T>
208  bool IsClass(const T& data) {
209  #if !HAS_BUILTIN_TYPE_TRAITS
210  return std::tr1::__is_union_or_class<T>::value; // without compiler support we cannot distinguish union from class
211  #else
212  return __is_class(T);
213  #endif
214  }
215 
216  /*template<typename T>
217  bool IsTrivial(T data) {
218  return __is_trivial(T);
219  }*/
220 
221  /*template<typename T>
222  bool IsPOD(T data) {
223  return __is_pod(T);
224  }*/
225 
241  class UID {
242  public:
243  ID id;
244  size_t size;
245 
246  bool isValid() const;
247  operator bool() const { return isValid(); }
248  //bool operator()() const { return isValid(); }
249  bool operator==(const UID& other) const { return id == other.id && size == other.size; }
250  bool operator!=(const UID& other) const { return id != other.id || size != other.size; }
251  bool operator<(const UID& other) const { return id < other.id || (id == other.id && size < other.size); }
252  bool operator>(const UID& other) const { return id > other.id || (id == other.id && size > other.size); }
253 
261  template<typename T>
262  static UID from(const T& obj) {
263  return Resolver<T>::resolve(obj);
264  }
265 
266  protected:
267  // UID resolver for non-pointer types
268  template<typename T>
269  struct Resolver {
270  static UID resolve(const T& obj) {
271  const UID uid = { (ID) &obj, sizeof(obj) };
272  return uid;
273  }
274  };
275 
276  // UID resolver for pointer types (of 1st degree)
277  template<typename T>
278  struct Resolver<T*> {
279  static UID resolve(const T* const & obj) {
280  const UID uid = { (ID) obj, sizeof(*obj) };
281  return uid;
282  }
283  };
284  };
285 
291  extern const UID NO_UID;
292 
324  typedef std::vector<UID> UIDChain;
325 
326  // prototyping of private internal friend functions
327  static String _encodePrimitiveValue(const Object& obj);
328  static DataType _popDataTypeBlob(const char*& p, const char* end);
329  static Member _popMemberBlob(const char*& p, const char* end);
330  static Object _popObjectBlob(const char*& p, const char* end);
331  static void _popPrimitiveValue(const char*& p, const char* end, Object& obj);
332  static String _primitiveObjectValueToString(const Object& obj);
333  // |
334  template<typename T>
335  static T _primitiveObjectValueToNumber(const Object& obj);
336 
353  class DataType {
354  public:
355  DataType();
356  size_t size() const { return m_size; }
357  bool isValid() const;
358  bool isPointer() const;
359  bool isClass() const;
360  bool isPrimitive() const;
361  bool isInteger() const;
362  bool isReal() const;
363  bool isBool() const;
364  bool isEnum() const;
365  bool isSigned() const;
366  operator bool() const { return isValid(); }
367  //bool operator()() const { return isValid(); }
368  bool operator==(const DataType& other) const;
369  bool operator!=(const DataType& other) const;
370  bool operator<(const DataType& other) const;
371  bool operator>(const DataType& other) const;
372  String asLongDescr() const;
373  String baseTypeName() const;
374  String customTypeName(bool demangle = false) const;
375 
386  template<typename T>
387  static DataType dataTypeOf(const T& data) {
388  return Resolver<T>::resolve(data);
389  }
390 
391  protected:
392  DataType(bool isPointer, int size, String baseType, String customType = "");
393 
394  template<typename T, bool T_isPointer>
395  struct ResolverBase {
396  static DataType resolve(const T& data) {
397  const std::type_info& type = typeid(data);
398  const int sz = sizeof(data);
399 
400  // for primitive types we are using our own type names instead of
401  // using std:::type_info::name(), because the precise output of the
402  // latter may vary between compilers
403  if (type == typeid(int8_t)) return DataType(T_isPointer, sz, "int8");
404  if (type == typeid(uint8_t)) return DataType(T_isPointer, sz, "uint8");
405  if (type == typeid(int16_t)) return DataType(T_isPointer, sz, "int16");
406  if (type == typeid(uint16_t)) return DataType(T_isPointer, sz, "uint16");
407  if (type == typeid(int32_t)) return DataType(T_isPointer, sz, "int32");
408  if (type == typeid(uint32_t)) return DataType(T_isPointer, sz, "uint32");
409  if (type == typeid(int64_t)) return DataType(T_isPointer, sz, "int64");
410  if (type == typeid(uint64_t)) return DataType(T_isPointer, sz, "uint64");
411  if (type == typeid(bool)) return DataType(T_isPointer, sz, "bool");
412  if (type == typeid(float)) return DataType(T_isPointer, sz, "real32");
413  if (type == typeid(double)) return DataType(T_isPointer, sz, "real64");
414 
415  if (IsEnum(data)) return DataType(T_isPointer, sz, "enum", rawCppTypeNameOf(data));
416  if (IsUnion(data)) return DataType(T_isPointer, sz, "union", rawCppTypeNameOf(data));
417  if (IsClass(data)) return DataType(T_isPointer, sz, "class", rawCppTypeNameOf(data));
418 
419  return DataType();
420  }
421  };
422 
423  // DataType resolver for non-pointer types
424  template<typename T>
425  struct Resolver : ResolverBase<T,false> {
426  static DataType resolve(const T& data) {
427  return ResolverBase<T,false>::resolve(data);
428  }
429  };
430 
431  // DataType resolver for pointer types (of 1st degree)
432  template<typename T>
433  struct Resolver<T*> : ResolverBase<T,true> {
434  static DataType resolve(const T*& data) {
435  return ResolverBase<T,true>::resolve(*data);
436  }
437  };
438 
439  template<typename T>
440  static String rawCppTypeNameOf(const T& data) {
441  #if defined _MSC_VER // Microsoft compiler ...
442  # warning type_info::raw_name() demangling has not been tested yet with Microsoft compiler! Feedback appreciated!
443  String name = typeid(data).raw_name(); //NOTE: I haven't checked yet what MSC actually outputs here exactly
444  #else // i.e. especially GCC and clang ...
445  String name = typeid(data).name();
446  #endif
447  //while (!name.empty() && name[0] >= 0 && name[0] <= 9)
448  // name = name.substr(1);
449  return name;
450  }
451 
452  private:
453  String m_baseTypeName;
454  String m_customTypeName;
455  int m_size;
456  bool m_isPointer;
457 
458  friend DataType _popDataTypeBlob(const char*& p, const char* end);
459  friend class Archive;
460  };
461 
483  class Member {
484  public:
485  Member();
486  UID uid() const;
487  String name() const;
488  size_t offset() const;
489  const DataType& type() const;
490  bool isValid() const;
491  operator bool() const { return isValid(); }
492  //bool operator()() const { return isValid(); }
493  bool operator==(const Member& other) const;
494  bool operator!=(const Member& other) const;
495  bool operator<(const Member& other) const;
496  bool operator>(const Member& other) const;
497 
498  protected:
499  Member(String name, UID uid, size_t offset, DataType type);
500  friend class Archive;
501 
502  private:
503  UID m_uid;
504  size_t m_offset;
505  String m_name;
506  DataType m_type;
507 
508  friend Member _popMemberBlob(const char*& p, const char* end);
509  };
510 
535  class Object {
536  public:
537  Object();
538  Object(UIDChain uidChain, DataType type);
539 
540  UID uid(int index = 0) const;
541  const UIDChain& uidChain() const;
542  const DataType& type() const;
543  const RawData& rawData() const;
544  Version version() const;
545  Version minVersion() const;
546  bool isVersionCompatibleTo(const Object& other) const;
547  std::vector<Member>& members();
548  const std::vector<Member>& members() const;
549  Member memberNamed(String name) const;
550  Member memberByUID(const UID& uid) const;
551  std::vector<Member> membersOfType(const DataType& type) const;
552  int sequenceIndexOf(const Member& member) const;
553  bool isValid() const;
554  operator bool() const { return isValid(); }
555  //bool operator()() const { return isValid(); }
556  bool operator==(const Object& other) const;
557  bool operator!=(const Object& other) const;
558  bool operator<(const Object& other) const;
559  bool operator>(const Object& other) const;
560 
561  protected:
562  void remove(const Member& member);
563  void setVersion(Version v);
564  void setMinVersion(Version v);
565 
566  private:
567  DataType m_type;
568  UIDChain m_uid;
569  Version m_version;
570  Version m_minVersion;
571  RawData m_data;
572  std::vector<Member> m_members;
573 
574  friend String _encodePrimitiveValue(const Object& obj);
575  friend Object _popObjectBlob(const char*& p, const char* end);
576  friend void _popPrimitiveValue(const char*& p, const char* end, Object& obj);
577  friend String _primitiveObjectValueToString(const Object& obj);
578 
579  template<typename T>
580  friend T _primitiveObjectValueToNumber(const Object& obj);
581 
582  friend class Archive;
583  };
584 
702  class Archive {
703  public:
704  Archive();
705  Archive(const RawData& data);
706  Archive(const uint8_t* data, size_t size);
707  virtual ~Archive();
708 
734  template<typename T>
735  void serialize(const T* obj) {
736  m_operation = OPERATION_SERIALIZE;
737  m_allObjects.clear();
738  m_rawData.clear();
739  m_root = UID::from(obj);
740  const_cast<T*>(obj)->serialize(this);
741  encode();
742  m_operation = OPERATION_NONE;
743  }
744 
769  template<typename T>
770  void deserialize(T* obj) {
771  Archive a;
772  m_operation = OPERATION_DESERIALIZE;
773  obj->serialize(&a);
774  a.m_root = UID::from(obj);
775  Syncer s(a, *this);
776  m_operation = OPERATION_NONE;
777  }
778 
793  template<typename T>
794  void operator<<(const T& obj) {
795  serialize(&obj);
796  }
797 
816  template<typename T>
817  void operator>>(T& obj) {
818  deserialize(&obj);
819  }
820 
821  const RawData& rawData();
822  virtual String rawDataFormat() const;
823 
869  template<typename T_classType, typename T_memberType>
870  void serializeMember(const T_classType& nativeObject, const T_memberType& nativeMember, const char* memberName) {
871  const size_t offset =
872  ((const uint8_t*)(const void*)&nativeMember) -
873  ((const uint8_t*)(const void*)&nativeObject);
874  const UIDChain uids = UIDChainResolver<T_memberType>(nativeMember);
875  const DataType type = DataType::dataTypeOf(nativeMember);
876  const Member member(memberName, uids[0], offset, type);
877  const UID parentUID = UID::from(nativeObject);
878  Object& parent = m_allObjects[parentUID];
879  if (!parent) {
880  const UIDChain uids = UIDChainResolver<T_classType>(nativeObject);
881  const DataType type = DataType::dataTypeOf(nativeObject);
882  parent = Object(uids, type);
883  }
884  parent.members().push_back(member);
885  const Object obj(uids, type);
886  const bool bExistsAlready = m_allObjects.count(uids[0]);
887  const bool isValidObject = obj;
888  const bool bExistingObjectIsInvalid = !m_allObjects[uids[0]];
889  if (!bExistsAlready || (bExistingObjectIsInvalid && isValidObject)) {
890  m_allObjects[uids[0]] = obj;
891  // recurse serialization for all members of this member
892  // (only for struct/class types, noop for primitive types)
893  SerializationRecursion<T_memberType>::serializeObject(this, nativeMember);
894  }
895  }
896 
966  template<typename T_classType>
967  void setVersion(const T_classType& nativeObject, Version v) {
968  const UID uid = UID::from(nativeObject);
969  Object& obj = m_allObjects[uid];
970  if (!obj) {
971  const UIDChain uids = UIDChainResolver<T_classType>(nativeObject);
972  const DataType type = DataType::dataTypeOf(nativeObject);
973  obj = Object(uids, type);
974  }
975  setVersion(obj, v);
976  }
977 
1007  template<typename T_classType>
1008  void setMinVersion(const T_classType& nativeObject, Version v) {
1009  const UID uid = UID::from(nativeObject);
1010  Object& obj = m_allObjects[uid];
1011  if (!obj) {
1012  const UIDChain uids = UIDChainResolver<T_classType>(nativeObject);
1013  const DataType type = DataType::dataTypeOf(nativeObject);
1014  obj = Object(uids, type);
1015  }
1016  setMinVersion(obj, v);
1017  }
1018 
1019  virtual void decode(const RawData& data);
1020  virtual void decode(const uint8_t* data, size_t size);
1021  void clear();
1022  bool isModified() const;
1023  void removeMember(Object& parent, const Member& member);
1024  void remove(const Object& obj);
1025  Object& rootObject();
1026  Object& objectByUID(const UID& uid);
1027  void setAutoValue(Object& object, String value);
1028  void setIntValue(Object& object, int64_t value);
1029  void setRealValue(Object& object, double value);
1030  void setBoolValue(Object& object, bool value);
1031  void setEnumValue(Object& object, uint64_t value);
1032  String valueAsString(const Object& object);
1033  int64_t valueAsInt(const Object& object);
1034  double valueAsReal(const Object& object);
1035  bool valueAsBool(const Object& object);
1036  void setVersion(Object& object, Version v);
1037  void setMinVersion(Object& object, Version v);
1038  String name() const;
1039  void setName(String name);
1040  String comment() const;
1041  void setComment(String comment);
1042  time_t timeStampCreated() const;
1043  time_t timeStampModified() const;
1044  tm dateTimeCreated(time_base_t base = LOCAL_TIME) const;
1045  tm dateTimeModified(time_base_t base = LOCAL_TIME) const;
1046 
1047  protected:
1048  // UID resolver for non-pointer types
1049  template<typename T>
1050  class UIDChainResolver {
1051  public:
1052  UIDChainResolver(const T& data) {
1053  m_uid.push_back(UID::from(data));
1054  }
1055 
1056  operator UIDChain() const { return m_uid; }
1057  UIDChain operator()() const { return m_uid; }
1058  private:
1059  UIDChain m_uid;
1060  };
1061 
1062  // UID resolver for pointer types (of 1st degree)
1063  template<typename T>
1064  class UIDChainResolver<T*> {
1065  public:
1066  UIDChainResolver(const T*& data) {
1067  const UID uids[2] = {
1068  { &data, sizeof(data) },
1069  { data, sizeof(*data) }
1070  };
1071  m_uid.push_back(uids[0]);
1072  m_uid.push_back(uids[1]);
1073  }
1074 
1075  operator UIDChain() const { return m_uid; }
1076  UIDChain operator()() const { return m_uid; }
1077  private:
1078  UIDChain m_uid;
1079  };
1080 
1081  // SerializationRecursion for non-pointer class/struct types.
1082  template<typename T, bool T_isRecursive>
1083  struct SerializationRecursionImpl {
1084  static void serializeObject(Archive* archive, const T& obj) {
1085  const_cast<T&>(obj).serialize(archive);
1086  }
1087  };
1088 
1089  // SerializationRecursion for pointers (of 1st degree) to class/structs.
1090  template<typename T, bool T_isRecursive>
1091  struct SerializationRecursionImpl<T*,T_isRecursive> {
1092  static void serializeObject(Archive* archive, const T*& obj) {
1093  if (!obj) return;
1094  const_cast<T*&>(obj)->serialize(archive);
1095  }
1096  };
1097 
1098  // NOOP SerializationRecursion for primitive types.
1099  template<typename T>
1100  struct SerializationRecursionImpl<T,false> {
1101  static void serializeObject(Archive* archive, const T& obj) {}
1102  };
1103 
1104  // NOOP SerializationRecursion for pointers (of 1st degree) to primitive types.
1105  template<typename T>
1106  struct SerializationRecursionImpl<T*,false> {
1107  static void serializeObject(Archive* archive, const T*& obj) {}
1108  };
1109 
1110  // Automatically handles recursion for class/struct types, while ignoring all primitive types.
1111  template<typename T>
1112  struct SerializationRecursion : SerializationRecursionImpl<T, LIBGIG_IS_CLASS(T)> {
1113  };
1114 
1115  class ObjectPool : public std::map<UID,Object> {
1116  public:
1117  // prevent passing obvious invalid UID values from creating a new pair entry
1118  Object& operator[](const UID& k) {
1119  static Object invalid;
1120  if (!k.isValid()) {
1121  invalid = Object();
1122  return invalid;
1123  }
1124  return std::map<UID,Object>::operator[](k);
1125  }
1126  };
1127 
1128  friend String _encode(const ObjectPool& objects);
1129 
1130  private:
1131  String _encodeRootBlob();
1132  void _popRootBlob(const char*& p, const char* end);
1133  void _popObjectsBlob(const char*& p, const char* end);
1134 
1135  protected:
1136  class Syncer {
1137  public:
1138  Syncer(Archive& dst, Archive& src);
1139  protected:
1140  void syncObject(const Object& dst, const Object& src);
1141  void syncPrimitive(const Object& dst, const Object& src);
1142  void syncPointer(const Object& dst, const Object& src);
1143  void syncMember(const Member& dstMember, const Member& srcMember);
1144  static Member dstMemberMatching(const Object& dstObj, const Object& srcObj, const Member& srcMember);
1145  private:
1146  Archive& m_dst;
1147  Archive& m_src;
1148  };
1149 
1150  enum operation_t {
1151  OPERATION_NONE,
1152  OPERATION_SERIALIZE,
1153  OPERATION_DESERIALIZE
1154  };
1155 
1156  virtual void encode();
1157 
1158  ObjectPool m_allObjects;
1159  operation_t m_operation;
1160  UID m_root;
1161  RawData m_rawData;
1162  bool m_isModified;
1163  String m_name;
1164  String m_comment;
1165  time_t m_timeCreated;
1166  time_t m_timeModified;
1167  };
1168 
1173  class Exception {
1174  public:
1175  String Message;
1176 
1177  Exception(String format, ...);
1178  Exception(String format, va_list arg);
1179  void PrintMessage();
1180  virtual ~Exception() {}
1181 
1182  protected:
1183  Exception();
1184  static String assemble(String format, va_list arg);
1185  };
1186 
1187 } // namespace Serialization
1188 
1189 #endif // LIBGIG_SERIALIZATION_H
Abstract reflection of some native serialized C/C++ data.
static UID from(const T &obj)
Create an unique indentifier for a native C++ object/member/variable.
Destination container for serialization, and source container for deserialization.
std::vector< UID > UIDChain
Chain of UIDs.
static DataType dataTypeOf(const T &data)
Construct a DataType object for the given native C++ data.
bool IsUnion(const T &data)
Check whether data is a C++ union type.
void * ID
Abstract identifier for serialized C++ objects.
void serialize(const T *obj)
Initiate serialization.
Abstract reflection of a native C++ data type.
void setMinVersion(const T_classType &nativeObject, Version v)
Set a minimum version number for your C++ class.
void deserialize(T *obj)
Initiate deserialization.
uint32_t Version
Version number data type.
void serializeMember(const T_classType &nativeObject, const T_memberType &nativeMember, const char *memberName)
Serialize a native C/C++ member variable.
time_base_t
To which time zone a certain timing information relates to.
bool isValid() const
Check whether this is a valid unique identifier.
void setVersion(const T_classType &nativeObject, Version v)
Set current version number for your C++ class.
Unique identifier referring to one specific native C++ object, member, fundamental variable...
std::vector< Member > & members()
All members of the original native C/C++ struct or class instance.
void operator<<(const T &obj)
Initiate serialization of your C++ objects.
ID id
Abstract non-unique ID of the object or member in question.
std::vector< uint8_t > RawData
Raw data stream of serialized C++ objects.
The time stamp relates to the machine&#39;s local time zone. Request a time stamp in local time if you wa...
The time stamp relates to "Greenwhich Mean Time" zone, also known as "Coordinated Universal Time"...
Will be thrown whenever an error occurs during an serialization or deserialization process...
void operator>>(T &obj)
Initiate deserialization of your C++ objects.
bool IsEnum(const T &data)
Check whether data is a C/C++ enum type.
bool IsClass(const T &data)
Check whether data is a C/C++ struct or C++ class type.
size_t size() const
Returns native memory size of the respective C++ object or variable.
const UID NO_UID
Reflects an invalid UID and behaves similar to NULL as invalid value for pointer types.
Serialization / deserialization framework.
Definition: gig.h:91
size_t size
Memory size of the object or member in question.
Abstract reflection of a native C++ class/struct&#39;s member variable.