1 /***************************************************************************
2  *                                                                         *
3  *   Copyright (C) 2017-2020 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 // enable implementation specific declarations in Serialization.h required to
25 // build this C++ unit, which should be ignored in the public API though
26 #define LIBGIG_SERIALIZATION_INTERNAL 1
27 
28 #include "Serialization.h"
29 
30 #include <iostream>
31 #include <string.h> // for memcpy()
32 #include <stdlib.h> // for atof()
33 #ifdef _MSC_VER
34 # include <windows.h>
35 # include <dbghelp.h>
36 #else
37 # include <cxxabi.h>
38 #endif
39 #include "helper.h"
40 
41 #define LIBGIG_EPOCH_TIME ((time_t)0)
42 
43 namespace Serialization {
44 
45     // *************** DataType ***************
46     // *
47 
_createNullUID()48     static UID _createNullUID() {
49         const UID uid = { NULL, 0 };
50         return uid;
51     }
52 
53     const UID NO_UID = _createNullUID();
54 
55     /** @brief Check whether this is a valid unique identifier.
56      *
57      * Returns @c false if this UID can be considered an invalid unique
58      * identifier. This is for example the case if this UID object was not
59      * explicitly set to some certain meaningful unique identifier value, or if
60      * this UID object was intentionally assigned the constant @c NO_UID value.
61      * Both represent essentially an UID object which is all zero.
62      *
63      * Note that this class also implements the @c bool operator, both return
64      * the same boolean result.
65      */
isValid() const66     bool UID::isValid() const {
67         return id != NULL && id != (void*)-1 && size;
68     }
69 
70     // *************** DataType ***************
71     // *
72 
73     /** @brief Default constructor (as "invalid" DataType).
74      *
75      * Initializes a DataType object as being an "invalid" DataType object.
76      * Thus calling isValid(), after creating a DataType object with this
77      * constructor, would return @c false.
78      *
79      * To create a valid and meaningful DataType object instead, call the static
80      * function DataType::dataTypeOf() instead.
81      */
DataType()82     DataType::DataType() {
83         m_size = 0;
84         m_isPointer = false;
85     }
86 
87     /** @brief Constructs a valid DataType object.
88      *
89      * Initializes this object as "valid" DataType object, with specific and
90      * useful data type information.
91      *
92      * This is a protected constructor which should not be called directly by
93      * applications, as its argument list is somewhat implementation specific
94      * and might change at any time. Applications should call the static
95      * function DataType::dataTypeOf() instead.
96      *
97      * @param isPointer - whether pointer type (i.e. a simple memory address)
98      * @param size - native size of data type in bytes (i.e. according to
99      *               @c sizeof() C/C++ keyword)
100      * @param baseType - this framework's internal name for specifying the base
101      *                   type in a coarse way, which must be either one of:
102      *                   "int8", "uint8", "int16", "uint16", "int32", "uint32",
103      *                   "int64", "uint64", "bool", "real32", "real64",
104      *                   "String", "Array", "Set", "enum", "union" or "class"
105      * @param customType1 - this is only used for base types "enum", "union",
106      *                     "class", "Array", "Set" or "Map", in which case this
107      *                      identifies the user defined type name (e.g. "Foo" for
108      *                      @c class @c Foo or e.g. "Bar" for @c Array<Bar>
109      *                      respectively), for all other types this is empty
110      * @param customType2 - this is only used for @c Map<> objects in which case
111      *                      it identifies the map's value type (i.e. 2nd
112      *                      template parameter of map)
113      */
DataType(bool isPointer,int size,String baseType,String customType1,String customType2)114     DataType::DataType(bool isPointer, int size, String baseType,
115                        String customType1, String customType2)
116     {
117         m_size = size;
118         m_isPointer = isPointer;
119         m_baseTypeName = baseType;
120         m_customTypeName = customType1;
121         m_customTypeName2 = customType2;
122     }
123 
124     /** @brief Check if this is a valid DataType object.
125      *
126      * Returns @c true if this DataType object is reflecting a valid data type.
127      * The default constructor creates DataType objects initialized to be
128      * "invalid" DataType objects by default. That way one can detect whether
129      * a DataType object was ever assigned to something meaningful.
130      *
131      * Note that this class also implements the @c bool operator, both return
132      * the same boolean result.
133      */
isValid() const134     bool DataType::isValid() const {
135         return m_size;
136     }
137 
138     /** @brief Whether this is reflecting a C/C++ pointer type.
139      *
140      * Returns @true if the respective native C/C++ object, member or variable
141      * (this DataType instance is reflecting) is a C/C++ pointer type.
142      */
isPointer() const143     bool DataType::isPointer() const {
144         return m_isPointer;
145     }
146 
147     /** @brief Whether this is reflecting a C/C++ @c struct or @c class type.
148      *
149      * Returns @c true if the respective native C/C++ object, member or variable
150      * (this DataType instance is reflecting) is a C/C++ @c struct or @c class
151      * type.
152      *
153      * @note: Data types which enjoy out of the box serialization support by
154      * this framework, like @c String and @c Array<> are @b NOT handled as class
155      * data types by this framwork. So @c isClass() returns @c false for e.g.
156      * @c String and any @c Array<> based data type.
157      *
158      * Note that in the following example:
159      * @code
160      * struct Foo {
161      *     int  a;
162      *     bool b;
163      * };
164      * Foo foo;
165      * Foo* pFoo;
166      * @endcode
167      * the DataType objects of both @c foo, as well as of the C/C++ pointer
168      * @c pFoo would both return @c true for isClass() here!
169      *
170      * @see isPointer()
171      */
isClass() const172     bool DataType::isClass() const {
173         return m_baseTypeName == "class";
174     }
175 
176     /** @brief Whether this is reflecting a fundamental C/C++ data type.
177      *
178      * Returns @c true if the respective native C/C++ object, member or variable
179      * (this DataType instance is reflecting) is a primitive, fundamental C/C++
180      * data type. Those are fundamental data types which are already predefined
181      * by the C/C++ language, for example: @c char, @c int, @c float, @c double,
182      * @c bool, but also @c String objects and @b any pointer types like
183      * @c int*, @c double**, but including pointers to user defined types like:
184      * @code
185      * struct Foo {
186      *     int  a;
187      *     bool b;
188      * };
189      * Foo* pFoo;
190      * @endcode
191      * So the DataType object of @c pFoo in the latter example would also return
192      * @c true for isPrimitive() here!
193      *
194      * @see isPointer()
195      */
isPrimitive() const196     bool DataType::isPrimitive() const {
197         return !isClass() && !isArray() && !isSet() && !isMap();
198     }
199 
200     /** @brief Whether this is a C++ @c String data type.
201      *
202      * Returns @c true if the respective native C/C++ object, member or variable
203      * (this DataType instance is reflecting) is a C++ @c String object (a.k.a.
204      * @c std::string from the C++ STL).
205      *
206      * Note that this framework handles @c String objects as if they were a
207      * fundamental, primitive C/C++ data type, so @c isPrimitive() returns
208      * @c true for strings.
209      */
isString() const210     bool DataType::isString() const {
211         return m_baseTypeName == "String";
212     }
213 
214     /** @brief Whether this is an integer C/C++ data type.
215      *
216      * Returns @c true if the respective native C/C++ object, member or variable
217      * (this DataType instance is reflecting) is a (fundamental, primitive)
218      * integer data type. So these are all @c int and @c unsigned @c int types
219      * of any size. It does not include floating point ("real") types though.
220      *
221      * You may use isSigned() to further check whether this data type allows
222      * negative numbers.
223      *
224      * Note that this method also returns @c true on integer pointer types!
225      *
226      * @see isPointer()
227      */
isInteger() const228     bool DataType::isInteger() const {
229         return m_baseTypeName.substr(0, 3) == "int" ||
230                m_baseTypeName.substr(0, 4) == "uint";
231     }
232 
233     /** @brief Whether this is a floating point based C/C++ data type.
234      *
235      * Returns @c true if the respective native C/C++ object, member or variable
236      * (this DataType instance is reflecting) is a (fundamental, primitive)
237      * floating point based data type. So these are currently the C/C++ @c float
238      * and @c double types. It does not include integer types though.
239      *
240      * Note that this method also returns @c true on @c float pointer and
241      * @c double pointer types!
242      *
243      * @see isPointer()
244      */
isReal() const245     bool DataType::isReal() const {
246         return m_baseTypeName.substr(0, 4) == "real";
247     }
248 
249     /** @brief Whether this is a boolean C/C++ data type.
250      *
251      * Returns @c true if the respective native C/C++ object, member or variable
252      * (this DataType instance is reflecting) is a (fundamental, primitive)
253      * boolean data type. So this is the case for the C++ @c bool data type.
254      * It does not include integer or floating point types though.
255      *
256      * Note that this method also returns @c true on @c bool pointer types!
257      *
258      * @see isPointer()
259      */
isBool() const260     bool DataType::isBool() const {
261         return m_baseTypeName == "bool";
262     }
263 
264     /** @brief Whether this is a C/C++ @c enum data type.
265      *
266      * Returns @c true if the respective native C/C++ object, member or variable
267      * (this DataType instance is reflecting) is a user defined enumeration
268      * data type. So this is the case for all C/C++ @c enum data types.
269      * It does not include integer (or even floating point) types though.
270      *
271      * Note that this method also returns @c true on @c enum pointer types!
272      *
273      * @see isPointer()
274      */
isEnum() const275     bool DataType::isEnum() const {
276         return m_baseTypeName == "enum";
277     }
278 
279     /** @brief Whether this is a C++ @c Array<> object type.
280      *
281      * Returns @c true if the respective native C/C++ object, member or variable
282      * (this DataType instance is reflecting) is a C++ @c Array<> container
283      * object type.
284      *
285      * @note: This framework handles @c Array<> types neither as primitive
286      * types, nor as class types. So @c isPrimitive() and @c isClass() both
287      * return @c false for arrays.
288      *
289      * @see isPointer()
290      */
isArray() const291     bool DataType::isArray() const {
292         return m_baseTypeName == "Array";
293     }
294 
295     /** @brief Whether this is a C++ @c Set<> object type.
296      *
297      * Returns @c true if the respective native C/C++ object, member or variable
298      * (this DataType instance is reflecting) is a C++ @c Set<> unique container
299      * object type.
300      *
301      * @note: This framework handles @c Set<> types neither as primitive
302      * types, nor as class types. So @c isPrimitive() and @c isClass() both
303      * return @c false for sets.
304      *
305      * @see isPointer()
306      */
isSet() const307     bool DataType::isSet() const {
308         return m_baseTypeName == "Set";
309     }
310 
311     /** @brief Whether this is a C++ @c Map<> object type.
312      *
313      * Returns @c true if the respective native C/C++ object, member or variable
314      * (this DataType instance is reflecting) is an associative sorted C++
315      * @c Map<> container object type.
316      *
317      * @note: This framework handles @c Map<> types neither as primitive
318      * types, nor as class types. So @c isPrimitive() and @c isClass() both
319      * return @c false for maps.
320      *
321      * @see isPointer()
322      */
isMap() const323     bool DataType::isMap() const {
324         return m_baseTypeName == "Map";
325     }
326 
327     /** @brief Whether this is a signed integer C/C++ data type.
328      *
329      * Returns @c true if the respective native C/C++ object, member or variable
330      * (this DataType instance is reflecting) is a (fundamental, primitive)
331      * signed integer data type. This is the case for are all @c unsigned
332      * @c int C/C++ types of any size. For all floating point ("real") based
333      * types this method returns @c false though!
334      *
335      * Note that this method also returns @c true on signed integer pointer
336      * types!
337      *
338      * @see isInteger();
339      */
isSigned() const340     bool DataType::isSigned() const {
341         return m_baseTypeName.substr(0, 3) == "int" ||
342                isReal();
343     }
344 
345     /** @brief Comparison for equalness.
346      *
347      * Returns @c true if the two DataType objects being compared can be
348      * considered to be "equal" C/C++ data types. They are considered to be
349      * equal if their underlying C/C++ data types are exactly identical. For
350      * example comparing @c int and @c unsigned int data types are considere to
351      * be @b not equal, since they are differently signed. Furthermore @c short
352      * @c int and @c long @c int would also not be considered to be equal, since
353      * they do have a different memory size. Additionally pointer type
354      * characteristic is compared as well. So a @c double type and @c double*
355      * type are also considered to be not equal data types and hence this method
356      * would return @c false.
357      *
358      * As an exception here, classes and structs with the same class/struct name
359      * but different sizes are also considered to be "equal". This relaxed
360      * requirement is necessary to retain backward compatiblity to older
361      * versions of the same native C++ classes/structs.
362      */
operator ==(const DataType & other) const363     bool DataType::operator==(const DataType& other) const {
364         return m_baseTypeName   == other.m_baseTypeName &&
365                m_customTypeName == other.m_customTypeName &&
366                m_customTypeName2 == other.m_customTypeName2 &&
367                (m_size == other.m_size || (isClass() && other.isClass())) &&
368                m_isPointer      == other.m_isPointer;
369     }
370 
371     /** @brief Comparison for inequalness.
372      *
373      * Returns the inverse result of what DataType::operator==() would return.
374      * So refer to the latter for more details.
375      */
operator !=(const DataType & other) const376     bool DataType::operator!=(const DataType& other) const {
377         return !operator==(other);
378     }
379 
380     /** @brief Smaller than comparison.
381      *
382      * Returns @c true if this DataType object can be consider to be "smaller"
383      * than the @a other DataType object being compared with. This operator
384      * is actually quite arbitrarily implemented and may change at any time,
385      * and thus result for the same data types may change in future at any time.
386      *
387      * This operator is basically implemented for allowing this DataType class
388      * to be used with various standard template library (STL) classes, which
389      * require sorting operators to be implemented.
390      */
operator <(const DataType & other) const391     bool DataType::operator<(const DataType& other) const {
392         return m_baseTypeName  < other.m_baseTypeName ||
393               (m_baseTypeName == other.m_baseTypeName &&
394               (m_customTypeName  < other.m_customTypeName ||
395               (m_customTypeName == other.m_customTypeName &&
396               (m_customTypeName2  < other.m_customTypeName2 ||
397               (m_customTypeName2 == other.m_customTypeName2 &&
398               (m_size  < other.m_size ||
399               (m_size == other.m_size &&
400                m_isPointer < other.m_isPointer)))))));
401     }
402 
403     /** @brief Greater than comparison.
404      *
405      * Returns @c true if this DataType object can be consider to be "greater"
406      * than the @a other DataType object being compared with. This operator
407      * is actually quite arbitrarily implemented and may change at any time,
408      * and thus result for the same data types may change in future at any time.
409      *
410      * This operator is basically implemented for allowing this DataType class
411      * to be used with various standard template library (STL) classes, which
412      * require sorting operators to be implemented.
413      */
operator >(const DataType & other) const414     bool DataType::operator>(const DataType& other) const {
415         return !(operator==(other) || operator<(other));
416     }
417 
418     /** @brief Human readable long description for this data type.
419      *
420      * Returns a human readable long description for this data type, designed
421      * for the purpose for being displayed to the user. Note that the
422      * implementation for this method and thus the precise textual strings
423      * returned by this method, may change at any time. So you should not rely
424      * on precise strings for certain data types, and you should not use the
425      * return values of this method for comparing data types with each other.
426      *
427      * This class implements various comparison operators, so you should use
428      * them for comparing DataTypes objects instead.
429      *
430      * @see baseTypeName(), customTypeName()
431      */
asLongDescr() const432     String DataType::asLongDescr() const {
433         String s = m_baseTypeName;
434         if (!m_customTypeName.empty())
435             s += " " + customTypeName(true);
436         if (!m_customTypeName2.empty())
437             s += " " + customTypeName2(true);
438         if (isPointer())
439             s += " pointer";
440         return s;
441     }
442 
443     /** @brief The base type name of this data type.
444      *
445      * Returns a textual short string identifying the basic type of name of this
446      * data type. For example for a 32 bit signed integer data type this method
447      * would return @c "int32". For all user defined C/C++ @c enum types this
448      * method would return "enum". For all user defined C/C++ @c struct @b and
449      * @c class types this method would return "class" for both. Note that the
450      * precise user defined type name (of i.e. @c enum, @c struct and @c class
451      * types) is not included in the string returned by this method, use
452      * customTypeName() to retrieve that information instead.
453      *
454      * The precise textual strings returned by this method are guaranteed to
455      * retain equal with future versions of this framework. So you can rely on
456      * them for using the return values of this method for comparison tasks in
457      * your application. Note however that this class also implements various
458      * comparison operators.
459      *
460      * Further it is important to know that this method returns the same string
461      * for pointers and non-pointers of the same underlying data type. So in the
462      * following example:
463      * @code
464      * #include <stdint.h>
465      * uint64_t i;
466      * uint64_t* pi;
467      * @endcode
468      * this method would return for both @c i and @c pi the string @c "uint64" !
469      *
470      * @see isPointer(), customTypeName(), customTypeName2()
471      */
baseTypeName() const472     String DataType::baseTypeName() const {
473         return m_baseTypeName;
474     }
475 
_demangleTypeName(const char * name)476     static String _demangleTypeName(const char* name) {
477 #ifdef _MSC_VER
478         const size_t MAXLENGTH = 1024;
479         char result[MAXLENGTH];
480 
481         //FIXME: calling UnDecorateSymbolName() is not thread safe!
482         //Skip the first char
483         size_t size = UnDecorateSymbolName(name + 1, result, MAXLENGTH, UNDNAME_32_BIT_DECODE | UNDNAME_NO_ARGUMENTS);
484         if (size)
485         {
486             return result;
487         }
488         return name;
489 #else
490         int status;
491         char* result =
492             abi::__cxa_demangle(name, 0, 0, &status);
493         String sResult = result;
494         free(result);
495         return (status == 0) ? sResult : name;
496 #endif
497     }
498 
499     /** @brief The 1st user defined C/C++ data type name of this data type.
500      *
501      * Call this method on user defined C/C++ data types like @c enum,
502      * @c struct, @c class or @c Array<> types to retrieve the user defined type
503      * name portion of those data types. Note that this method is only intended
504      * for such user defined data types. For all fundamental, primitive data
505      * types (like i.e. @c int) this method returns an empty string instead.
506      *
507      * This method takes an optional boolean argument @b demangle, which allows
508      * you define whether you are interested in the raw C++ type name or rather
509      * the demangled custom type name. By default this method returns the raw
510      * C++ type name. The raw C++ type name is the one that is actually used
511      * in the compiled binaries and should be preferred for comparions tasks.
512      * The demangled C++ type name is a human readable representation of the
513      * type name instead, which you may use for displaying the user defined type
514      * name portion to the user, however you should not use the demangled
515      * representation for comparison tasks.
516      *
517      * Note that in the following example:
518      * @code
519      * struct Foo {
520      *     int  a;
521      *     bool b;
522      * };
523      * Foo foo;
524      * Foo* pFoo;
525      * @endcode
526      * this method would return the same string for both @c foo and @c pFoo !
527      * In the latter example @c customTypeName(true) would return for both
528      * @c foo and @c pFoo the string @c "Foo" as return value of this method.
529      *
530      * @b Windows: please note that the current implementation of this method
531      * on Windows is @b not thread safe!
532      *
533      * @see baseTypeName(), customTypeName2(), isPointer()
534      */
customTypeName(bool demangle) const535     String DataType::customTypeName(bool demangle) const {
536         if (!demangle) return m_customTypeName;
537         return _demangleTypeName(m_customTypeName.c_str());
538     }
539 
540     /** @brief The 2nd user defined C/C++ data type name of this data type.
541      *
542      * This is currently only used for @c Map<> data types in which case this
543      * method returns the map's value type (i.e. map's 2nd template parameter).
544      *
545      * @see baseTypeName(), customTypeName()
546      */
customTypeName2(bool demangle) const547     String DataType::customTypeName2(bool demangle) const {
548         if (!demangle) return m_customTypeName2;
549         return _demangleTypeName(m_customTypeName2.c_str());
550     }
551 
552     // *************** Member ***************
553     // *
554 
555     /** @brief Default constructor.
556      *
557      * Initializes a Member object as being an "invalid" Member object.
558      * Thus calling isValid(), after creating a Member object with this
559      * constructor, would return @c false.
560      *
561      * You are currently not supposed to create (meaningful) Member objects on
562      * your own. This framework automatically create such Member objects for
563      * you instead.
564      *
565      * @see Object::members()
566      */
Member()567     Member::Member() {
568         m_uid = NO_UID;
569         m_offset = 0;
570     }
571 
Member(String name,UID uid,ssize_t offset,DataType type)572     Member::Member(String name, UID uid, ssize_t offset, DataType type) {
573         m_name = name;
574         m_uid  = uid;
575         m_offset = offset;
576         m_type = type;
577     }
578 
579     /** @brief Unique identifier of this member instance.
580      *
581      * Returns the unique identifier of the original C/C++ member instance of
582      * your C++ class. It is important to know that this unique identifier is
583      * not meant to be unique for Member instances themselves, but it is rather
584      * meant to be unique for the original native C/C++ data these Member
585      * instances are representing. So that means no matter how many individual
586      * Member objects are created, as long as they are representing the same
587      * original native member variable of the same original native
588      * instance of your C++ class, then all those separately created Member
589      * objects return the same unique identifier here.
590      *
591      * @see UID for more details
592      */
uid() const593     UID Member::uid() const {
594         return m_uid;
595     }
596 
597     /** @brief Name of the member.
598      *
599      * Returns the name of the native C/C++ member variable as originally typed
600      * in its C++ source code. So in the following example:
601      * @code
602      * struct Foo {
603      *     int  a;
604      *     bool b;
605      *     double someValue;
606      * };
607      * @endcode
608      * this method would usually return @c "a" for the first member of object
609      * instances of your native C/C++ @c struct @c Foo, and this method would
610      * usually return @c "someValue" for its third member.
611      *
612      * Note that when you implement the @c serialize() method of your own C/C++
613      * clases or strucs, you are able to override defining the precise name of
614      * your members. In that case this method would of course return the member
615      * names as explicitly forced by you instead.
616      */
name() const617     String Member::name() const {
618         return m_name;
619     }
620 
621     /** @brief Offset of member in its containing parent data structure.
622      *
623      * Returns the offset of this member (in bytes) within its containing parent
624      * user defined data structure or class. So in the following example:
625      * @code
626      * #include <stdint.h>
627      * struct Foo __attribute__ ((__packed__)) {
628      *     int32_t a;
629      *     bool b;
630      *     double c;
631      * };
632      * @endcode
633      * this method would typically return @c 0 for member @c a, @c 4 for member
634      * @c b and @c 5 for member @c c. As you have noted in the latter example,
635      * the structure @c Foo was declared to have "packed" data members. That
636      * means the compiler is instructed to add no memory spaces between the
637      * individual members. Because by default the compiler might add memory
638      * spaces between individual members to align them on certain memory address
639      * boundaries for increasing runtime performance while accessing the
640      * members. So if you declared the previous example without the "packed"
641      * attribute like:
642      * @code
643      * #include <stdint.h>
644      * struct Foo {
645      *     int32_t a;
646      *     bool b;
647      *     double c;
648      * };
649      * @endcode
650      * then this method would usually return a different offset for members
651      * @c b and @c c instead. For most 64 bit architectures this example would
652      * now still return @c 0 for member @c a, but @c 8 for member @c b and @c 16
653      * for member @c c.
654      *
655      * @note Offset is intended for native members only, that is member
656      * variables which are memory located directly within the associated parent
657      * data structure. For members allocated on the heap @c offset() always
658      * returns @c -1 instead since there is no constant, static offset
659      * relationship between data on the heap and the parent structure owning
660      * their life-time control.
661      */
offset() const662     ssize_t Member::offset() const {
663         return m_offset;
664     }
665 
666     /** @brief C/C++ Data type of this member.
667      *
668      * Returns the precise data type of the original native C/C++ member.
669      */
type() const670     const DataType& Member::type() const {
671         return m_type;
672     }
673 
674     /** @brief Check if this is a valid Member object.
675      *
676      * Returns @c true if this Member object is reflecting a "valid" member
677      * object. The default constructor creates Member objects initialized to be
678      * "invalid" Member objects by default. That way one can detect whether
679      * a Member object was ever assigned to something meaningful.
680      *
681      * Note that this class also implements the @c bool operator, both return
682      * the same boolean result value.
683      */
isValid() const684     bool Member::isValid() const {
685         return m_uid && !m_name.empty() && m_type;
686     }
687 
688     /** @brief Comparison for equalness.
689      *
690      * Returns @c true if the two Member objects being compared can be
691      * considered to be "equal" C/C++ members. They are considered to be
692      * equal if their data type, member name, their offset within their parent
693      * containing C/C++ data structure, as well as their original native C/C++
694      * instance were exactly identical.
695      */
operator ==(const Member & other) const696     bool Member::operator==(const Member& other) const {
697         return m_uid    == other.m_uid &&
698                m_offset == other.m_offset &&
699                m_name   == other.m_name &&
700                m_type   == other.m_type;
701     }
702 
703     /** @brief Comparison for inequalness.
704      *
705      * Returns the inverse result of what Member::operator==() would return.
706      * So refer to the latter for more details.
707      */
operator !=(const Member & other) const708     bool Member::operator!=(const Member& other) const {
709         return !operator==(other);
710     }
711 
712     /** @brief Smaller than comparison.
713      *
714      * Returns @c true if this Member object can be consider to be "smaller"
715      * than the @a other Member object being compared with. This operator
716      * is actually quite arbitrarily implemented and may change at any time,
717      * and thus result for the same member representations may change in
718      * future at any time.
719      *
720      * This operator is basically implemented for allowing this DataType class
721      * to be used with various standard template library (STL) classes, which
722      * require sorting operators to be implemented.
723      */
operator <(const Member & other) const724     bool Member::operator<(const Member& other) const {
725         return m_uid  < other.m_uid ||
726               (m_uid == other.m_uid &&
727               (m_offset  < other.m_offset ||
728               (m_offset == other.m_offset &&
729               (m_name  < other.m_name ||
730               (m_name == other.m_name &&
731                m_type < other.m_type)))));
732     }
733 
734     /** @brief Greater than comparison.
735      *
736      * Returns @c true if this Member object can be consider to be "greater"
737      * than the @a other Member object being compared with. This operator
738      * is actually quite arbitrarily implemented and may change at any time,
739      * and thus result for the same member representations may change in
740      * future at any time.
741      *
742      * This operator is basically implemented for allowing this DataType class
743      * to be used with various standard template library (STL) classes, which
744      * require sorting operators to be implemented.
745      */
operator >(const Member & other) const746     bool Member::operator>(const Member& other) const {
747         return !(operator==(other) || operator<(other));
748     }
749 
750     // *************** Object ***************
751     // *
752 
753     /** @brief Default constructor (for an "invalid" Object).
754      *
755      * Initializes an Object instance as being an "invalid" Object.
756      * Thus calling isValid(), after creating an Object instance with this
757      * constructor, would return @c false.
758      *
759      * Usually you are not supposed to create (meaningful) Object instances on
760      * your own. They are typically constructed by the Archive class for you.
761      *
762      * @see Archive::rootObject(), Archive::objectByUID()
763      */
Object()764     Object::Object() {
765         m_version = 0;
766         m_minVersion = 0;
767     }
768 
769     /** @brief Constructor for a "meaningful" Object.
770      *
771      * Initializes a "meaningful" Object instance as being. Thus calling
772      * isValid(), after creating an Object instance with this constructor,
773      * should return @c true, provided that the arguments passed to this
774      * constructor construe a valid object representation.
775      *
776      * Usually you are not supposed to create (meaningful) Object instances on
777      * your own. They are typically constructed by the Archive class for you.
778      *
779      * @see Archive::rootObject(), Archive::objectByUID()
780      *
781      * @param uidChain - unique identifier chain of the object to be constructed
782      * @param type - C/C++ data type of the actual native object this abstract
783      *               Object instance should reflect after calling this
784      *               constructor
785      */
Object(UIDChain uidChain,DataType type)786     Object::Object(UIDChain uidChain, DataType type) {
787         m_type = type;
788         m_uid  = uidChain;
789         m_version = 0;
790         m_minVersion = 0;
791         //m_data.resize(type.size());
792     }
793 
794     /** @brief Check if this is a valid Object instance.
795      *
796      * Returns @c true if this Object instance is reflecting a "valid" Object.
797      * The default constructor creates Object instances initialized to be
798      * "invalid" Objects by default. That way one can detect whether an Object
799      * instance was ever assigned to something meaningful.
800      *
801      * Note that this class also implements the @c bool operator, both return
802      * the same boolean result value.
803      */
isValid() const804     bool Object::isValid() const {
805         return m_type && !m_uid.empty();
806     }
807 
808     /** @brief Unique identifier of this Object.
809      *
810      * Returns the unique identifier for the original native C/C++ data this
811      * abstract Object instance is reflecting. If this Object is representing
812      * a C/C++ pointer (of first degree) then @c uid() (or @c uid(0) ) returns
813      * the unique identifier of the pointer itself, whereas @c uid(1) returns
814      * the unique identifier of the original C/C++ data that pointer was
815      * actually pointing to.
816      *
817      * @see UIDChain for more details about this overall topic.
818      */
uid(int index) const819     UID Object::uid(int index) const {
820         return (index < m_uid.size()) ? m_uid[index] : NO_UID;
821     }
822 
_setNativeValueFromString(void * ptr,const DataType & type,const char * s)823     static void _setNativeValueFromString(void* ptr, const DataType& type, const char* s) {
824         if (type.isPrimitive() && !type.isPointer()) {
825             if (type.isInteger() || type.isEnum()) {
826                 if (type.isSigned()) {
827                     if (type.size() == 1)
828                         *(int8_t*)ptr = (int8_t) atoll(s);
829                     else if (type.size() == 2)
830                         *(int16_t*)ptr = (int16_t) atoll(s);
831                     else if (type.size() == 4)
832                         *(int32_t*)ptr = (int32_t) atoll(s);
833                     else if (type.size() == 8)
834                         *(int64_t*)ptr = (int64_t) atoll(s);
835                     else
836                         assert(false /* unknown signed int type size */);
837                 } else {
838                     if (type.size() == 1)
839                         *(uint8_t*)ptr = (uint8_t) atoll(s);
840                     else if (type.size() == 2)
841                         *(uint16_t*)ptr = (uint16_t) atoll(s);
842                     else if (type.size() == 4)
843                         *(uint32_t*)ptr = (uint32_t) atoll(s);
844                     else if (type.size() == 8)
845                         *(uint64_t*)ptr = (uint64_t) atoll(s);
846                     else
847                         assert(false /* unknown unsigned int type size */);
848                 }
849             } else if (type.isReal()) {
850                 if (type.size() == sizeof(float))
851                     *(float*)ptr = (float) atof(s);
852                 else if (type.size() == sizeof(double))
853                     *(double*)ptr = (double) atof(s);
854                 else
855                     assert(false /* unknown floating point type */);
856             } else if (type.isBool()) {
857                 String lower = toLowerCase(s);
858                 const bool b = lower != "0" && lower != "false" && lower != "no";
859                 *(bool*)ptr = b;
860             } else if (type.isString()) {
861                 *(String*)ptr = s;
862             } else {
863                 assert(false /* no built-in cast from string support for this data type */);
864             }
865         }
866     }
867 
868     /** @brief Cast from string to object's data type and assign value natively.
869      *
870      * The passed String @a s is decoded from its string representation to this
871      * object's corresponding native data type, then that casted value is
872      * assigned to the native memory location this Object is referring to.
873      *
874      * Note: This method may only be called for data types which enjoy built-in
875      * support for casting from string to their native data type, which are
876      * basically primitive data types (e.g. @c int, @c bool, @c double, etc.) or
877      * @c String objects. For all other data types calling this method will
878      * cause an assertion fault at runtime.
879      *
880      * @param s - textual string representation of the value to be assigned to
881      *            this object
882      */
setNativeValueFromString(const String & s)883     void Object::setNativeValueFromString(const String& s) {
884         const ID& id = uid().id;
885         void* ptr = (void*)id;
886         _setNativeValueFromString(ptr, m_type, s.c_str());
887     }
888 
889     /** @brief Unique identifier chain of this Object.
890      *
891      * Returns the entire unique identifier chain of this Object.
892      *
893      * @see uid() and UIDChain for more details about this overall topic.
894      */
uidChain() const895     const UIDChain& Object::uidChain() const {
896         return m_uid;
897     }
898 
899     /** @brief C/C++ data type this Object is reflecting.
900      *
901      * Returns the precise original C/C++ data type of the original native
902      * C/C++ object or data this Object instance is reflecting.
903      */
type() const904     const DataType& Object::type() const {
905         return m_type;
906     }
907 
908     /** @brief Raw data of the original native C/C++ data.
909      *
910      * Returns the raw data value of the original C/C++ data this Object is
911      * reflecting. So the precise raw data value, layout and size is dependent
912      * to the precise C/C++ data type of the original native C/C++ data. However
913      * potentially required endian correction is already automatically applied
914      * for you. That means you can safely, directly C-cast the raw data returned
915      * by this method to the respective native C/C++ data type in order to
916      * access and use the value for some purpose, at least if the respective
917      * data is of any fundamental, primitive C/C++ data type, or also to a
918      * certain extent if the type is user defined @c enum type.
919      *
920      * However directly C-casting this raw data for user defined @c struct or
921      * @c class types is not possible. For those user defined data structures
922      * this method always returns empty raw data instead.
923      *
924      * Note however that there are more convenient methods in the Archive class
925      * to get the right value for the individual data types instead.
926      *
927      * @see Archive::valueAsInt(), Archive::valueAsReal(), Archive::valueAsBool(),
928      *      Archive::valueAsString()
929      */
rawData() const930     const RawData& Object::rawData() const {
931         return m_data;
932     }
933 
934     /** @brief Version of original user defined C/C++ @c struct or @c class.
935      *
936      * In case this Object is reflecting a native C/C++ @c struct or @c class
937      * type, then this method returns the version of that native C/C++ @c struct
938      * or @c class layout or implementation. For primitive, fundamental C/C++
939      * data types (including @c String objects) the return value of this method
940      * has no meaning.
941      *
942      * @see Archive::setVersion() for more details about this overall topic.
943      */
version() const944     Version Object::version() const {
945         return m_version;
946     }
947 
948     /** @brief Minimum version of original user defined C/C++ @c struct or @c class.
949      *
950      * In case this Object is reflecting a native C/C++ @c struct or @c class
951      * type, then this method returns the "minimum" version of that native C/C++
952      * @c struct or @c class layout or implementation which it may be compatible
953      * with. For primitive, fundamental C/C++ data types (including @c String
954      * objects) the return value of this method has no meaning.
955      *
956      * @see Archive::setVersion() and Archive::setMinVersion() for more details
957      *      about this overall topic.
958      */
minVersion() const959     Version Object::minVersion() const {
960         return m_minVersion;
961     }
962 
963     /** @brief All members of the original native C/C++ @c struct or @c class instance.
964      *
965      * In case this Object is reflecting a native C/C++ @c struct or @c class
966      * type, then this method returns all member variables of that original
967      * native C/C++ @c struct or @c class instance. For primitive, fundamental
968      * C/C++ data types this method returns an empty vector instead.
969      *
970      * Example:
971      * @code
972      * struct Foo {
973      *     int  a;
974      *     bool b;
975      *     double someValue;
976      * };
977      * @endcode
978      * Considering above's C++ code, a serialized Object representation of such
979      * a native @c Foo class would have 3 members @c a, @c b and @c someValue.
980      *
981      * Note that the respective serialize() method implementation of that
982      * fictional C++ @c struct @c Foo actually defines which members are going
983      * to be serialized and deserialized for instances of class @c Foo. So in
984      * practice the members returned by method members() here might return a
985      * different set of members as actually defined in the original C/C++ struct
986      * header declaration.
987      *
988      * The precise sequence of the members returned by this method here depends
989      * on the actual serialize() implementation of the user defined C/C++
990      * @c struct or @c class.
991      *
992      * @see Object::sequenceIndexOf() for more details about the precise order
993      *      of members returned by this method in the same way.
994      */
members()995     std::vector<Member>& Object::members() {
996         return m_members;
997     }
998 
999     /** @brief All members of the original native C/C++ @c struct or @c class instance (read only).
1000      *
1001      * Returns the same result as overridden members() method above, it just
1002      * returns a read-only result instead. See above's method description for
1003      * details for the return value of this method instead.
1004      */
members() const1005     const std::vector<Member>& Object::members() const {
1006         return m_members;
1007     }
1008 
1009     /** @brief Comparison for equalness.
1010      *
1011      * Returns @c true if the two Object instances being compared can be
1012      * considered to be "equal" native C/C++ object instances. They are
1013      * considered to be equal if they are representing the same original
1014      * C/C++ data instance, which is essentially the case if the original
1015      * reflecting native C/C++ data are sharing the same memory address and
1016      * memory size (thus the exact same memory space) and originally had the
1017      * exact same native C/C++ types.
1018      */
operator ==(const Object & other) const1019     bool Object::operator==(const Object& other) const {
1020         // ignoring all other member variables here
1021         // (since UID stands for "unique" ;-) )
1022         return m_uid  == other.m_uid &&
1023                m_type == other.m_type;
1024     }
1025 
1026     /** @brief Comparison for inequalness.
1027      *
1028      * Returns the inverse result of what Object::operator==() would return.
1029      * So refer to the latter for more details.
1030      */
operator !=(const Object & other) const1031     bool Object::operator!=(const Object& other) const {
1032         return !operator==(other);
1033     }
1034 
1035     /** @brief Smaller than comparison.
1036      *
1037      * Returns @c true if this Object instance can be consider to be "smaller"
1038      * than the @a other Object instance being compared with. This operator
1039      * is actually quite arbitrarily implemented and may change at any time,
1040      * and thus result for the same Object representations may change in future
1041      * at any time.
1042      *
1043      * This operator is basically implemented for allowing this DataType class
1044      * to be used with various standard template library (STL) classes, which
1045      * require sorting operators to be implemented.
1046      */
operator <(const Object & other) const1047     bool Object::operator<(const Object& other) const {
1048         // ignoring all other member variables here
1049         // (since UID stands for "unique" ;-) )
1050         return m_uid  < other.m_uid ||
1051               (m_uid == other.m_uid &&
1052                m_type < other.m_type);
1053     }
1054 
1055     /** @brief Greater than comparison.
1056      *
1057      * Returns @c true if this Object instance can be consider to be "greater"
1058      * than the @a other Object instance being compared with. This operator
1059      * is actually quite arbitrarily implemented and may change at any time,
1060      * and thus result for the same Object representations may change in future
1061      * at any time.
1062      *
1063      * This operator is basically implemented for allowing this DataType class
1064      * to be used with various standard template library (STL) classes, which
1065      * require sorting operators to be implemented.
1066      */
operator >(const Object & other) const1067     bool Object::operator>(const Object& other) const {
1068         return !(operator==(other) || operator<(other));
1069     }
1070 
1071     /** @brief Check version compatibility between Object instances.
1072      *
1073      * Use this method to check whether the two original C/C++ instances those
1074      * two Objects are reflecting, were using a C/C++ data type which are version
1075      * compatible with each other. By default all C/C++ Objects are considered
1076      * to be version compatible. They might only be version incompatible if you
1077      * enforced a certain backward compatibility constraint with your
1078      * serialize() method implementation of your custom C/C++ @c struct or
1079      * @c class types.
1080      *
1081      * You must only call this method on two Object instances which are
1082      * representing the same data type, for example if both Objects reflect
1083      * instances of the same user defined C++ class. Calling this method on
1084      * completely different data types does not cause an error or exception, but
1085      * its result would simply be useless for any purpose.
1086      *
1087      * @see Archive::setVersion() for more details about this overall topic.
1088      */
isVersionCompatibleTo(const Object & other) const1089     bool Object::isVersionCompatibleTo(const Object& other) const {
1090         if (this->version() == other.version())
1091             return true;
1092         if (this->version() > other.version())
1093             return this->minVersion() <= other.version();
1094         else
1095             return other.minVersion() <= this->version();
1096     }
1097 
setVersion(Version v)1098     void Object::setVersion(Version v) {
1099         m_version = v;
1100     }
1101 
setMinVersion(Version v)1102     void Object::setMinVersion(Version v) {
1103         m_minVersion = v;
1104     }
1105 
1106     /** @brief Get the member of this Object with given name.
1107      *
1108      * In case this Object is reflecting a native C/C++ @c struct or @c class
1109      * type, then this method returns the abstract reflection of the requested
1110      * member variable of the original native C/C++ @c struct or @c class
1111      * instance. For primitive, fundamental C/C++ data types this method always
1112      * returns an "invalid" Member instance instead.
1113      *
1114      * Example:
1115      * @code
1116      * struct Foo {
1117      *     int  a;
1118      *     bool b;
1119      *     double someValue;
1120      * };
1121      * @endcode
1122      * Consider that you serialized the native C/C++ @c struct as shown in this
1123      * example, and assuming that you implemented the respective serialize()
1124      * method of this C++ @c struct to serialize all its members, then you might
1125      * call memberNamed("someValue") to get the details of the third member in
1126      * this example for instance. In case the passed @a name is an unknown
1127      * member name, then this method will return an "invalid" Member object
1128      * instead.
1129      *
1130      * @param name - original name of the sought serialized member variable of
1131      *               this Object reflection
1132      * @returns abstract reflection of the sought member variable
1133      * @see Member::isValid(), Object::members()
1134      */
memberNamed(String name) const1135     Member Object::memberNamed(String name) const {
1136         for (int i = 0; i < m_members.size(); ++i)
1137             if (m_members[i].name() == name)
1138                 return m_members[i];
1139         return Member();
1140     }
1141 
1142     /** @brief Get the member of this Object with given unique identifier.
1143      *
1144      * This method behaves similar like method memberNamed() described above,
1145      * but instead of searching for a member variable by name, it searches for
1146      * a member with an abstract unique identifier instead. For primitive,
1147      * fundamental C/C++ data types, for invalid or unknown unique identifiers,
1148      * and for members which are actually not member instances of the original
1149      * C/C++ @c struct or @c class instance this Object is reflecting, this
1150      * method returns an "invalid" Member instance instead.
1151      *
1152      * @param uid - unique identifier of the member variable being sought
1153      * @returns abstract reflection of the sought member variable
1154      * @see Member::isValid(), Object::members(), Object::memberNamed()
1155      */
memberByUID(const UID & uid) const1156     Member Object::memberByUID(const UID& uid) const {
1157         if (!uid) return Member();
1158         for (int i = 0; i < m_members.size(); ++i)
1159             if (m_members[i].uid() == uid)
1160                 return m_members[i];
1161         return Member();
1162     }
1163 
remove(const Member & member)1164     void Object::remove(const Member& member) {
1165         for (int i = 0; i < m_members.size(); ++i) {
1166             if (m_members[i] == member) {
1167                 m_members.erase(m_members.begin() + i);
1168                 return;
1169             }
1170         }
1171     }
1172 
1173     /** @brief Get all members of this Object with given data type.
1174      *
1175      * In case this Object is reflecting a native C/C++ @c struct or @c class
1176      * type, then this method returns all member variables of that original
1177      * native C/C++ @c struct or @c class instance which are matching the given
1178      * requested data @a type. If this Object is reflecting a primitive,
1179      * fundamental data type, or if there are no members of this Object with the
1180      * requested precise C/C++ data type, then this method returns an empty
1181      * vector instead.
1182      *
1183      * @param type - the precise C/C++ data type of the sought member variables
1184      *               of this Object
1185      * @returns vector with abstract reflections of the sought member variables
1186      * @see Object::members(), Object::memberNamed()
1187      */
membersOfType(const DataType & type) const1188     std::vector<Member> Object::membersOfType(const DataType& type) const {
1189         std::vector<Member> v;
1190         for (int i = 0; i < m_members.size(); ++i) {
1191             const Member& member = m_members[i];
1192             if (member.type() == type)
1193                 v.push_back(member);
1194         }
1195         return v;
1196     }
1197 
1198     /** @brief Serialization/deserialization sequence number of the requested member.
1199      *
1200      * Returns the precise serialization/deserialization sequence number of the
1201      * requested @a member variable.
1202      *
1203      * Example:
1204      * @code
1205      * struct Foo {
1206      *     int  a;
1207      *     bool b;
1208      *     double c;
1209      *
1210      *     void serialize(Serialization::Archive* archive);
1211      * };
1212      * @endcode
1213      * Assuming the declaration of the user defined native C/C++ @c struct
1214      * @c Foo above, and assuming the following implementation of serialize():
1215      * @code
1216      * #define SRLZ(member) \
1217      *   archive->serializeMember(*this, member, #member);
1218      *
1219      * void Foo::serialize(Serialization::Archive* archive) {
1220      *     SRLZ(c);
1221      *     SRLZ(a);
1222      *     SRLZ(b);
1223      * }
1224      * @endcode
1225      * then @c sequenceIndexOf(obj.memberNamed("a")) returns 1,
1226      * @c sequenceIndexOf(obj.memberNamed("b")) returns 2, and
1227      * @c sequenceIndexOf(obj.memberNamed("c")) returns 0.
1228      */
sequenceIndexOf(const Member & member) const1229     int Object::sequenceIndexOf(const Member& member) const {
1230         for (int i = 0; i < m_members.size(); ++i)
1231             if (m_members[i] == member)
1232                 return i;
1233         return -1;
1234     }
1235 
1236     // *************** Archive ***************
1237     // *
1238 
1239     /** @brief Create an "empty" archive.
1240      *
1241      * This default constructor creates an "empty" archive which you then
1242      * subsequently for example might fill with serialized data like:
1243      * @code
1244      * Archive a;
1245      * a.serialize(&myRootObject);
1246      * @endcode
1247      * Or:
1248      * @code
1249      * Archive a;
1250      * a << myRootObject;
1251      * @endcode
1252      * Or you might also subsequently assign an already existing non-empty
1253      * to this empty archive, which effectively clones the other
1254      * archive (deep copy) or call decode() later on to assign a previously
1255      * serialized raw data stream.
1256      */
Archive()1257     Archive::Archive() {
1258         m_operation = OPERATION_NONE;
1259         m_root = NO_UID;
1260         m_isModified = false;
1261         m_timeCreated = m_timeModified = LIBGIG_EPOCH_TIME;
1262     }
1263 
1264     /** @brief Create and fill the archive with the given serialized raw data.
1265      *
1266      * This constructor decodes the given raw @a data and constructs a
1267      * (non-empty) Archive object according to that given serialized data
1268      * stream.
1269      *
1270      * After this constructor returned, you may then traverse the individual
1271      * objects by starting with accessing the rootObject() for example. Finally
1272      * you might call deserialize() to restore your native C++ objects with the
1273      * content of this archive.
1274      *
1275      * @param data - the previously serialized raw data stream to be decoded
1276      * @throws Exception if the provided raw @a data uses an invalid, unknown,
1277      *         incompatible or corrupt data stream or format.
1278      */
Archive(const RawData & data)1279     Archive::Archive(const RawData& data) {
1280         m_operation = OPERATION_NONE;
1281         m_root = NO_UID;
1282         m_isModified = false;
1283         m_timeCreated = m_timeModified = LIBGIG_EPOCH_TIME;
1284         decode(data);
1285     }
1286 
1287     /** @brief Create and fill the archive with the given serialized raw C-buffer data.
1288      *
1289      * This constructor essentially works like the constructor above, but just
1290      * uses another data type for the serialized raw data stream being passed to
1291      * this class.
1292      *
1293      * This constructor decodes the given raw @a data and constructs a
1294      * (non-empty) Archive object according to that given serialized data
1295      * stream.
1296      *
1297      * After this constructor returned, you may then traverse the individual
1298      * objects by starting with accessing the rootObject() for example. Finally
1299      * you might call deserialize() to restore your native C++ objects with the
1300      * content of this archive.
1301      *
1302      * @param data - the previously serialized raw data stream to be decoded
1303      * @param size - size of @a data in bytes
1304      * @throws Exception if the provided raw @a data uses an invalid, unknown,
1305      *         incompatible or corrupt data stream or format.
1306      */
Archive(const uint8_t * data,size_t size)1307     Archive::Archive(const uint8_t* data, size_t size) {
1308         m_operation = OPERATION_NONE;
1309         m_root = NO_UID;
1310         m_isModified = false;
1311         m_timeCreated = m_timeModified = LIBGIG_EPOCH_TIME;
1312         decode(data, size);
1313     }
1314 
~Archive()1315     Archive::~Archive() {
1316     }
1317 
1318     /** @brief Root C++ object of this archive.
1319      *
1320      * In case this is a non-empty Archive, then this method returns the so
1321      * called "root" C++ object. If this is an empty archive, then this method
1322      * returns an "invalid" Object instance instead.
1323      *
1324      * @see Archive::serialize() for more details about the "root" object concept.
1325      * @see Object for more details about the overall object reflection concept.
1326      * @returns reflection of the original native C++ root object
1327      */
rootObject()1328     Object& Archive::rootObject() {
1329         return m_allObjects[m_root];
1330     }
1331 
_encodeBlob(String data)1332     static String _encodeBlob(String data) {
1333         return ToString(data.length()) + ":" + data;
1334     }
1335 
_encode(const UID & uid)1336     static String _encode(const UID& uid) {
1337         String s;
1338         s += _encodeBlob(ToString(size_t(uid.id)));
1339         s += _encodeBlob(ToString(size_t(uid.size)));
1340         return _encodeBlob(s);
1341     }
1342 
_encode(const time_t & time)1343     static String _encode(const time_t& time) {
1344         return _encodeBlob(ToString(time));
1345     }
1346 
_encode(const DataType & type)1347     static String _encode(const DataType& type) {
1348         String s;
1349 
1350         // Srx v1.0 format (mandatory):
1351         s += _encodeBlob(type.baseTypeName());
1352         s += _encodeBlob(type.customTypeName());
1353         s += _encodeBlob(ToString(type.size()));
1354         s += _encodeBlob(ToString(type.isPointer()));
1355 
1356         // Srx v1.1 format:
1357         s += _encodeBlob(type.customTypeName2());
1358 
1359         return _encodeBlob(s);
1360     }
1361 
_encode(const UIDChain & chain)1362     static String _encode(const UIDChain& chain) {
1363         String s;
1364         for (int i = 0; i < chain.size(); ++i)
1365             s += _encode(chain[i]);
1366         return _encodeBlob(s);
1367     }
1368 
_encode(const Member & member)1369     static String _encode(const Member& member) {
1370         String s;
1371         s += _encode(member.uid());
1372         s += _encodeBlob(ToString(member.offset()));
1373         s += _encodeBlob(member.name());
1374         s += _encode(member.type());
1375         return _encodeBlob(s);
1376     }
1377 
_encode(const std::vector<Member> & members)1378     static String _encode(const std::vector<Member>& members) {
1379         String s;
1380         for (int i = 0; i < members.size(); ++i)
1381             s += _encode(members[i]);
1382         return _encodeBlob(s);
1383     }
1384 
_primitiveObjectValueToString(const Object & obj)1385     static String _primitiveObjectValueToString(const Object& obj) {
1386         String s;
1387         const DataType& type = obj.type();
1388         const ID& id = obj.uid().id;
1389         void* ptr = obj.m_data.empty() ? (void*)id : (void*)&obj.m_data[0];
1390         if (!obj.m_data.empty())
1391             assert(type.size() == obj.m_data.size());
1392         if (type.isPrimitive() && !type.isPointer()) {
1393             if (type.isInteger() || type.isEnum()) {
1394                 if (type.isSigned()) {
1395                     if (type.size() == 1)
1396                         s = ToString((int16_t)*(int8_t*)ptr); // int16_t: prevent ToString() to render an ASCII character
1397                     else if (type.size() == 2)
1398                         s = ToString(*(int16_t*)ptr);
1399                     else if (type.size() == 4)
1400                         s = ToString(*(int32_t*)ptr);
1401                     else if (type.size() == 8)
1402                         s = ToString(*(int64_t*)ptr);
1403                     else
1404                         assert(false /* unknown signed int type size */);
1405                 } else {
1406                     if (type.size() == 1)
1407                         s = ToString((uint16_t)*(uint8_t*)ptr); // uint16_t: prevent ToString() to render an ASCII character
1408                     else if (type.size() == 2)
1409                         s = ToString(*(uint16_t*)ptr);
1410                     else if (type.size() == 4)
1411                         s = ToString(*(uint32_t*)ptr);
1412                     else if (type.size() == 8)
1413                         s = ToString(*(uint64_t*)ptr);
1414                     else
1415                         assert(false /* unknown unsigned int type size */);
1416                 }
1417             } else if (type.isReal()) {
1418                 if (type.size() == sizeof(float))
1419                     s = ToString(*(float*)ptr);
1420                 else if (type.size() == sizeof(double))
1421                     s = ToString(*(double*)ptr);
1422                 else
1423                     assert(false /* unknown floating point type */);
1424             } else if (type.isBool()) {
1425                 s = ToString(*(bool*)ptr);
1426             } else if (type.isString()) {
1427                 s = obj.m_data.empty() ? *(String*)ptr : String((const char*)ptr);
1428             } else {
1429                 assert(false /* unknown primitive type */);
1430             }
1431         }
1432         return s;
1433     }
1434 
1435     template<typename T>
_stringToNumber(const String & s)1436     inline T _stringToNumber(const String& s) {
1437         assert(false /* String cast to unknown primitive number type */);
1438     }
1439 
1440     template<>
_stringToNumber(const String & s)1441     inline int64_t _stringToNumber(const String& s) {
1442         return atoll(s.c_str());
1443     }
1444 
1445     template<>
_stringToNumber(const String & s)1446     inline double _stringToNumber(const String& s) {
1447         return atof(s.c_str());
1448     }
1449 
1450     template<>
_stringToNumber(const String & s)1451     inline bool _stringToNumber(const String& s) {
1452         return (bool) atoll(s.c_str());
1453     }
1454 
1455     template<typename T>
_primitiveObjectValueToNumber(const Object & obj)1456     static T _primitiveObjectValueToNumber(const Object& obj) {
1457         T value = 0;
1458         const DataType& type = obj.type();
1459         const ID& id = obj.uid().id;
1460         void* ptr = obj.m_data.empty() ? (void*)id : (void*)&obj.m_data[0];
1461         if (!obj.m_data.empty())
1462             assert(type.size() == obj.m_data.size());
1463         if (type.isPrimitive() && !type.isPointer()) {
1464             if (type.isInteger() || type.isEnum()) {
1465                 if (type.isSigned()) {
1466                     if (type.size() == 1)
1467                         value = (T)*(int8_t*)ptr;
1468                     else if (type.size() == 2)
1469                         value = (T)*(int16_t*)ptr;
1470                     else if (type.size() == 4)
1471                         value = (T)*(int32_t*)ptr;
1472                     else if (type.size() == 8)
1473                         value = (T)*(int64_t*)ptr;
1474                     else
1475                         assert(false /* unknown signed int type size */);
1476                 } else {
1477                     if (type.size() == 1)
1478                         value = (T)*(uint8_t*)ptr;
1479                     else if (type.size() == 2)
1480                         value = (T)*(uint16_t*)ptr;
1481                     else if (type.size() == 4)
1482                         value = (T)*(uint32_t*)ptr;
1483                     else if (type.size() == 8)
1484                         value = (T)*(uint64_t*)ptr;
1485                     else
1486                         assert(false /* unknown unsigned int type size */);
1487                 }
1488             } else if (type.isReal()) {
1489                 if (type.size() == sizeof(float))
1490                     value = (T)*(float*)ptr;
1491                 else if (type.size() == sizeof(double))
1492                     value = (T)*(double*)ptr;
1493                 else
1494                     assert(false /* unknown floating point type */);
1495             } else if (type.isBool()) {
1496                 value = (T)*(bool*)ptr;
1497             } else if (type.isString()) {
1498                 value = _stringToNumber<T>(
1499                     obj.m_data.empty() ? *(String*)ptr : String((const char*)ptr)
1500                 );
1501             } else {
1502                 assert(false /* unknown primitive type */);
1503             }
1504         }
1505         return value;
1506     }
1507 
_encodePrimitiveValue(const Object & obj)1508     static String _encodePrimitiveValue(const Object& obj) {
1509         return _encodeBlob( _primitiveObjectValueToString(obj) );
1510     }
1511 
_encode(const Object & obj)1512     static String _encode(const Object& obj) {
1513         String s;
1514         s += _encode(obj.type());
1515         s += _encodeBlob(ToString(obj.version()));
1516         s += _encodeBlob(ToString(obj.minVersion()));
1517         s += _encode(obj.uidChain());
1518         s += _encode(obj.members());
1519         s += _encodePrimitiveValue(obj);
1520         return _encodeBlob(s);
1521     }
1522 
_encode(const Archive::ObjectPool & objects)1523     String _encode(const Archive::ObjectPool& objects) {
1524         String s;
1525         for (Archive::ObjectPool::const_iterator itObject = objects.begin();
1526              itObject != objects.end(); ++itObject)
1527         {
1528             const Object& obj = itObject->second;
1529             s += _encode(obj);
1530         }
1531         return _encodeBlob(s);
1532     }
1533 
1534     /*
1535      * Srx format history:
1536      * - 1.0: Initial version.
1537      * - 1.1: Adds "String", "Array", "Set" and "Map" data types and an optional
1538      *        2nd custom type name (e.g. "Map" types which always contain two
1539      *        user defined types).
1540      */
1541     #define MAGIC_START "Srx1v"
1542     #define ENCODING_FORMAT_MINOR_VERSION 1
1543 
_encodeRootBlob()1544     String Archive::_encodeRootBlob() {
1545         String s;
1546         s += _encodeBlob(ToString(ENCODING_FORMAT_MINOR_VERSION));
1547         s += _encode(m_root);
1548         s += _encode(m_allObjects);
1549         s += _encodeBlob(m_name);
1550         s += _encodeBlob(m_comment);
1551         s += _encode(m_timeCreated);
1552         s += _encode(m_timeModified);
1553         return _encodeBlob(s);
1554     }
1555 
encode()1556     void Archive::encode() {
1557         m_rawData.clear();
1558         String s = MAGIC_START;
1559         m_timeModified = time(NULL);
1560         if (m_timeCreated == LIBGIG_EPOCH_TIME)
1561             m_timeCreated = m_timeModified;
1562         s += _encodeRootBlob();
1563         m_rawData.resize(s.length() + 1);
1564         memcpy(&m_rawData[0], &s[0], s.length() + 1);
1565         m_isModified = false;
1566     }
1567 
1568     struct _Blob {
1569         const char* p;
1570         const char* end;
1571     };
1572 
_decodeBlob(const char * p,const char * end,bool bThrow=true)1573     static _Blob _decodeBlob(const char* p, const char* end, bool bThrow = true) {
1574         if (!bThrow && p >= end) {
1575             const _Blob blob =  { p, end };
1576             return blob;
1577         }
1578         size_t sz = 0;
1579         for (; true; ++p) {
1580             if (p >= end)
1581                 throw Exception("Decode Error: Missing blob");
1582             const char& c = *p;
1583             if (c == ':') break;
1584             if (c < '0' || c > '9')
1585                 throw Exception("Decode Error: Missing blob size");
1586             sz *= 10;
1587             sz += size_t(c - '0');
1588         }
1589         ++p;
1590         if (p + sz > end)
1591             throw Exception("Decode Error: Premature end of blob");
1592         const _Blob blob = { p, p + sz };
1593         return blob;
1594     }
1595 
1596     template<typename T_int>
_popIntBlob(const char * & p,const char * end)1597     static T_int _popIntBlob(const char*& p, const char* end) {
1598         _Blob blob = _decodeBlob(p, end);
1599         p   = blob.p;
1600         end = blob.end;
1601 
1602         T_int sign = 1;
1603         T_int i = 0;
1604         if (p >= end)
1605             throw Exception("Decode Error: premature end of int blob");
1606         if (*p == '-') {
1607             sign = -1;
1608             ++p;
1609         }
1610         for (; p < end; ++p) {
1611             const char& c = *p;
1612             if (c < '0' || c > '9')
1613                 throw Exception("Decode Error: Invalid int blob format");
1614             i *= 10;
1615             i += size_t(c - '0');
1616         }
1617         return i * sign;
1618     }
1619 
1620     template<typename T_int>
_popIntBlob(const char * & p,const char * end,RawData & rawData)1621     static void _popIntBlob(const char*& p, const char* end, RawData& rawData) {
1622         const T_int i = _popIntBlob<T_int>(p, end);
1623         *(T_int*)&rawData[0] = i;
1624     }
1625 
1626     template<typename T_real>
_popRealBlob(const char * & p,const char * end)1627     static T_real _popRealBlob(const char*& p, const char* end) {
1628         _Blob blob = _decodeBlob(p, end);
1629         p   = blob.p;
1630         end = blob.end;
1631 
1632         if (p >= end || (end - p) < 1)
1633             throw Exception("Decode Error: premature end of real blob");
1634 
1635         String s(p, size_t(end - p));
1636 
1637         T_real r;
1638         if (sizeof(T_real) <= sizeof(double))
1639             r = atof(s.c_str());
1640         else
1641             assert(false /* unknown real type */);
1642 
1643         p += s.length();
1644 
1645         return r;
1646     }
1647 
1648     template<typename T_real>
_popRealBlob(const char * & p,const char * end,RawData & rawData)1649     static void _popRealBlob(const char*& p, const char* end, RawData& rawData) {
1650         const T_real r = _popRealBlob<T_real>(p, end);
1651         *(T_real*)&rawData[0] = r;
1652     }
1653 
_popStringBlob(const char * & p,const char * end)1654     static String _popStringBlob(const char*& p, const char* end) {
1655         _Blob blob = _decodeBlob(p, end);
1656         p   = blob.p;
1657         end = blob.end;
1658         if (end - p < 0)
1659             throw Exception("Decode Error: missing String blob");
1660         String s;
1661         const size_t sz = end - p;
1662         s.resize(sz);
1663         memcpy(&s[0], p, sz);
1664         p += sz;
1665         return s;
1666     }
1667 
_popStringBlob(const char * & p,const char * end,RawData & rawData)1668     static void _popStringBlob(const char*& p, const char* end, RawData& rawData) {
1669         String s = _popStringBlob(p, end);
1670         rawData.resize(s.length() + 1);
1671         strcpy((char*)&rawData[0], &s[0]);
1672     }
1673 
_popTimeBlob(const char * & p,const char * end)1674     static time_t _popTimeBlob(const char*& p, const char* end) {
1675         const uint64_t i = _popIntBlob<uint64_t>(p, end);
1676         return (time_t) i;
1677     }
1678 
_popDataTypeBlob(const char * & p,const char * end)1679     static DataType _popDataTypeBlob(const char*& p, const char* end) {
1680         _Blob blob = _decodeBlob(p, end);
1681         p   = blob.p;
1682         end = blob.end;
1683 
1684         DataType type;
1685 
1686         // Srx v1.0 format (mandatory):
1687         type.m_baseTypeName   = _popStringBlob(p, end);
1688         type.m_customTypeName = _popStringBlob(p, end);
1689         type.m_size           = _popIntBlob<int>(p, end);
1690         type.m_isPointer      = _popIntBlob<bool>(p, end);
1691 
1692         // Srx v1.1 format (optional):
1693         if (p < end)
1694             type.m_customTypeName2 = _popStringBlob(p, end);
1695 
1696         return type;
1697     }
1698 
_popUIDBlob(const char * & p,const char * end)1699     static UID _popUIDBlob(const char*& p, const char* end) {
1700         _Blob blob = _decodeBlob(p, end);
1701         p   = blob.p;
1702         end = blob.end;
1703 
1704         if (p >= end)
1705             throw Exception("Decode Error: premature end of UID blob");
1706 
1707         const ID id = (ID) _popIntBlob<size_t>(p, end);
1708         const size_t size = _popIntBlob<size_t>(p, end);
1709 
1710         const UID uid = { id, size };
1711         return uid;
1712     }
1713 
_popUIDChainBlob(const char * & p,const char * end)1714     static UIDChain _popUIDChainBlob(const char*& p, const char* end) {
1715         _Blob blob = _decodeBlob(p, end);
1716         p   = blob.p;
1717         end = blob.end;
1718 
1719         UIDChain chain;
1720         while (p < end) {
1721             const UID uid = _popUIDBlob(p, end);
1722             chain.push_back(uid);
1723         }
1724         assert(!chain.empty());
1725         return chain;
1726     }
1727 
_popMemberBlob(const char * & p,const char * end)1728     static Member _popMemberBlob(const char*& p, const char* end) {
1729         _Blob blob = _decodeBlob(p, end, false);
1730         p   = blob.p;
1731         end = blob.end;
1732 
1733         Member m;
1734         if (p >= end) return m;
1735 
1736         m.m_uid    = _popUIDBlob(p, end);
1737         m.m_offset = _popIntBlob<ssize_t>(p, end);
1738         m.m_name   = _popStringBlob(p, end);
1739         m.m_type   = _popDataTypeBlob(p, end);
1740         assert(m.type());
1741         assert(!m.name().empty());
1742         assert(m.uid().isValid());
1743         return m;
1744     }
1745 
_popMembersBlob(const char * & p,const char * end)1746     static std::vector<Member> _popMembersBlob(const char*& p, const char* end) {
1747         _Blob blob = _decodeBlob(p, end, false);
1748         p   = blob.p;
1749         end = blob.end;
1750 
1751         std::vector<Member> members;
1752         while (p < end) {
1753             const Member member = _popMemberBlob(p, end);
1754             if (member)
1755                 members.push_back(member);
1756             else
1757                 break;
1758         }
1759         return members;
1760     }
1761 
_popPrimitiveValue(const char * & p,const char * end,Object & obj)1762     static void _popPrimitiveValue(const char*& p, const char* end, Object& obj) {
1763         const DataType& type = obj.type();
1764         if (type.isPrimitive() && !type.isPointer()) {
1765             obj.m_data.resize(type.size());
1766             if (type.isInteger() || type.isEnum()) {
1767                 if (type.isSigned()) {
1768                     if (type.size() == 1)
1769                         _popIntBlob<int8_t>(p, end, obj.m_data);
1770                     else if (type.size() == 2)
1771                         _popIntBlob<int16_t>(p, end, obj.m_data);
1772                     else if (type.size() == 4)
1773                         _popIntBlob<int32_t>(p, end, obj.m_data);
1774                     else if (type.size() == 8)
1775                         _popIntBlob<int64_t>(p, end, obj.m_data);
1776                     else
1777                         assert(false /* unknown signed int type size */);
1778                 } else {
1779                     if (type.size() == 1)
1780                         _popIntBlob<uint8_t>(p, end, obj.m_data);
1781                     else if (type.size() == 2)
1782                         _popIntBlob<uint16_t>(p, end, obj.m_data);
1783                     else if (type.size() == 4)
1784                         _popIntBlob<uint32_t>(p, end, obj.m_data);
1785                     else if (type.size() == 8)
1786                         _popIntBlob<uint64_t>(p, end, obj.m_data);
1787                     else
1788                         assert(false /* unknown unsigned int type size */);
1789                 }
1790             } else if (type.isReal()) {
1791                 if (type.size() == sizeof(float))
1792                     _popRealBlob<float>(p, end, obj.m_data);
1793                 else if (type.size() == sizeof(double))
1794                     _popRealBlob<double>(p, end, obj.m_data);
1795                 else
1796                     assert(false /* unknown floating point type */);
1797             } else if (type.isBool()) {
1798                 _popIntBlob<uint8_t>(p, end, obj.m_data);
1799             } else if (type.isString()) {
1800                 _popStringBlob(p, end, obj.m_data);
1801             } else {
1802                 assert(false /* unknown primitive type */);
1803             }
1804 
1805         } else {
1806             // don't whine if the empty blob was not added on encoder side
1807             _Blob blob = _decodeBlob(p, end, false);
1808             p   = blob.p;
1809             end = blob.end;
1810         }
1811     }
1812 
_popObjectBlob(const char * & p,const char * end)1813     static Object _popObjectBlob(const char*& p, const char* end) {
1814         _Blob blob = _decodeBlob(p, end, false);
1815         p   = blob.p;
1816         end = blob.end;
1817 
1818         Object obj;
1819         if (p >= end) return obj;
1820 
1821         obj.m_type       = _popDataTypeBlob(p, end);
1822         obj.m_version    = _popIntBlob<Version>(p, end);
1823         obj.m_minVersion = _popIntBlob<Version>(p, end);
1824         obj.m_uid        = _popUIDChainBlob(p, end);
1825         obj.m_members    = _popMembersBlob(p, end);
1826         _popPrimitiveValue(p, end, obj);
1827         assert(obj.type());
1828         return obj;
1829     }
1830 
_popObjectsBlob(const char * & p,const char * end)1831     void Archive::_popObjectsBlob(const char*& p, const char* end) {
1832         _Blob blob = _decodeBlob(p, end, false);
1833         p   = blob.p;
1834         end = blob.end;
1835 
1836         if (p >= end)
1837             throw Exception("Decode Error: Premature end of objects blob");
1838 
1839         while (true) {
1840             const Object obj = _popObjectBlob(p, end);
1841             if (!obj) break;
1842             m_allObjects[obj.uid()] = obj;
1843         }
1844     }
1845 
_popRootBlob(const char * & p,const char * end)1846     void Archive::_popRootBlob(const char*& p, const char* end) {
1847         _Blob blob = _decodeBlob(p, end, false);
1848         p   = blob.p;
1849         end = blob.end;
1850 
1851         if (p >= end)
1852             throw Exception("Decode Error: Premature end of root blob");
1853 
1854         // just in case this encoding format will be extended in future
1855         // (currently not used)
1856         const int formatMinorVersion = _popIntBlob<int>(p, end);
1857 
1858         m_root = _popUIDBlob(p, end);
1859         if (!m_root)
1860             throw Exception("Decode Error: No root object");
1861 
1862         _popObjectsBlob(p, end);
1863         if (!m_allObjects[m_root])
1864             throw Exception("Decode Error: Missing declared root object");
1865 
1866         m_name = _popStringBlob(p, end);
1867         m_comment = _popStringBlob(p, end);
1868         m_timeCreated = _popTimeBlob(p, end);
1869         m_timeModified = _popTimeBlob(p, end);
1870     }
1871 
1872     /** @brief Fill this archive with the given serialized raw data.
1873      *
1874      * Calling this method will decode the given raw @a data and constructs a
1875      * (non-empty) Archive object according to that given serialized @a data
1876      * stream.
1877      *
1878      * After this method returned, you may then traverse the individual
1879      * objects by starting with accessing the rootObject() for example. Finally
1880      * you might call deserialize() to restore your native C++ objects with the
1881      * content of this archive.
1882      *
1883      * @param data - the previously serialized raw data stream to be decoded
1884      * @throws Exception if the provided raw @a data uses an invalid, unknown,
1885      *         incompatible or corrupt data stream or format.
1886      */
decode(const RawData & data)1887     void Archive::decode(const RawData& data) {
1888         m_rawData = data;
1889         m_allObjects.clear();
1890         m_isModified = false;
1891         m_timeCreated = m_timeModified = LIBGIG_EPOCH_TIME;
1892         const char* p   = (const char*) &data[0];
1893         const char* end = p + data.size();
1894         if (memcmp(p, MAGIC_START, std::min(strlen(MAGIC_START), data.size())))
1895             throw Exception("Decode Error: Magic start missing!");
1896         p += strlen(MAGIC_START);
1897         _popRootBlob(p, end);
1898     }
1899 
1900     /** @brief Fill this archive with the given serialized raw C-buffer data.
1901      *
1902      * This method essentially works like the decode() method above, but just
1903      * uses another data type for the serialized raw data stream being passed to
1904      * this method.
1905      *
1906      * Calling this method will decode the given raw @a data and constructs a
1907      * (non-empty) Archive object according to that given serialized @a data
1908      * stream.
1909      *
1910      * After this method returned, you may then traverse the individual
1911      * objects by starting with accessing the rootObject() for example. Finally
1912      * you might call deserialize() to restore your native C++ objects with the
1913      * content of this archive.
1914      *
1915      * @param data - the previously serialized raw data stream to be decoded
1916      * @param size - size of @a data in bytes
1917      * @throws Exception if the provided raw @a data uses an invalid, unknown,
1918      *         incompatible or corrupt data stream or format.
1919      */
decode(const uint8_t * data,size_t size)1920     void Archive::decode(const uint8_t* data, size_t size) {
1921         RawData rawData;
1922         rawData.resize(size);
1923         memcpy(&rawData[0], data, size);
1924         decode(rawData);
1925     }
1926 
1927     /** @brief Raw data stream of this archive content.
1928      *
1929      * Call this method to get a raw data stream for the current content of this
1930      * archive, which you may use to i.e. store on disk or send vie network to
1931      * another machine for deserializing there. This method only returns a
1932      * meaningful content if this is a non-empty archive, that is if you either
1933      * serialized with this Archive object or decoded a raw data stream to this
1934      * Archive object before. If this is an empty archive instead, then this
1935      * method simply returns an empty raw data stream (of size 0) instead.
1936      *
1937      * Note that whenever you call this method, the "modified" state of this
1938      * archive will be reset to @c false.
1939      *
1940      * @see isModified()
1941      */
rawData()1942     const RawData& Archive::rawData() {
1943         if (m_isModified) encode();
1944         return m_rawData;
1945     }
1946 
1947     /** @brief Name of the encoding format used by this Archive class.
1948      *
1949      * This method returns the name of the encoding format used to encode
1950      * serialized raw data streams.
1951      */
rawDataFormat() const1952     String Archive::rawDataFormat() const {
1953         return MAGIC_START;
1954     }
1955 
1956     /** @brief Whether this archive was modified.
1957      *
1958      * This method returns the current "modified" state of this archive. When
1959      * either decoding a previously serialized raw data stream or after
1960      * serializing native C++ objects to this archive the modified state will
1961      * initially be set to @c false. However whenever you are modifying the
1962      * abstract data model of this archive afterwards, for example by removing
1963      * objects from this archive by calling remove() or removeMember(), or by
1964      * altering object values for example by calling setIntValue(), then the
1965      * "modified" state of this archive will automatically be set to @c true.
1966      *
1967      * You can reset the "modified" state explicitly at any time, by calling
1968      * rawData().
1969      */
isModified() const1970     bool Archive::isModified() const {
1971         return m_isModified;
1972     }
1973 
1974     /** @brief Clear content of this archive.
1975      *
1976      * Drops the entire content of this archive and thus resets this archive
1977      * back to become an empty archive.
1978      */
clear()1979     void Archive::clear() {
1980         m_allObjects.clear();
1981         m_operation = OPERATION_NONE;
1982         m_root = NO_UID;
1983         m_rawData.clear();
1984         m_isModified = false;
1985         m_timeCreated = m_timeModified = LIBGIG_EPOCH_TIME;
1986     }
1987 
1988     /** @brief Optional name of this archive.
1989      *
1990      * Returns the optional name of this archive that you might have assigned
1991      * to this archive before by calling setName(). If you haven't assigned any
1992      * name to this archive before, then this method simply returns an empty
1993      * string instead.
1994      */
name() const1995     String Archive::name() const {
1996         return m_name;
1997     }
1998 
1999     /** @brief Assign a name to this archive.
2000      *
2001      * You may optionally assign an arbitrary name to this archive. The name
2002      * will be stored along with the archive, that is it will encoded with the
2003      * resulting raw data stream, and accordingly it will be decoded from the
2004      * raw data stream later on.
2005      *
2006      * @param name - arbitrary new name for this archive
2007      */
setName(String name)2008     void Archive::setName(String name) {
2009         if (m_name == name) return;
2010         m_name = name;
2011         m_isModified = true;
2012     }
2013 
2014     /** @brief Optional comments for this archive.
2015      *
2016      * Returns the optional comments for this archive that you might have
2017      * assigned to this archive before by calling setComment(). If you haven't
2018      * assigned any comment to this archive before, then this method simply
2019      * returns an empty string instead.
2020      */
comment() const2021     String Archive::comment() const {
2022         return m_comment;
2023     }
2024 
2025     /** @brief Assign a comment to this archive.
2026      *
2027      * You may optionally assign arbitrary comments to this archive. The comment
2028      * will be stored along with the archive, that is it will encoded with the
2029      * resulting raw data stream, and accordingly it will be decoded from the
2030      * raw data stream later on.
2031      *
2032      * @param comment - arbitrary new comment for this archive
2033      */
setComment(String comment)2034     void Archive::setComment(String comment) {
2035         if (m_comment == comment) return;
2036         m_comment = comment;
2037         m_isModified = true;
2038     }
2039 
_convertTimeStamp(const time_t & time,time_base_t base)2040     static tm _convertTimeStamp(const time_t& time, time_base_t base) {
2041         tm* pTm;
2042         switch (base) {
2043             case LOCAL_TIME:
2044                 pTm = localtime(&time);
2045                 break;
2046             case UTC_TIME:
2047                 pTm = gmtime(&time);
2048                 break;
2049             default:
2050                 throw Exception("Time stamp with unknown time base (" + ToString((int64_t)base) + ") requested");
2051         }
2052         if (!pTm)
2053             throw Exception("Failed assembling time stamp structure");
2054         return *pTm;
2055     }
2056 
2057     /** @brief Date and time when this archive was initially created.
2058      *
2059      * Returns a UTC time stamp (date and time) when this archive was initially
2060      * created.
2061      */
timeStampCreated() const2062     time_t Archive::timeStampCreated() const {
2063         return m_timeCreated;
2064     }
2065 
2066     /** @brief Date and time when this archive was modified for the last time.
2067      *
2068      * Returns a UTC time stamp (date and time) when this archive was modified
2069      * for the last time.
2070      */
timeStampModified() const2071     time_t Archive::timeStampModified() const {
2072         return m_timeModified;
2073     }
2074 
2075     /** @brief Date and time when this archive was initially created.
2076      *
2077      * Returns a calendar time information representing the date and time when
2078      * this archive was initially created. The optional @a base parameter may
2079      * be used to define to which time zone the returned data and time shall be
2080      * related to.
2081      *
2082      * @param base - (optional) time zone the result shall relate to, by default
2083      *               UTC time (Greenwhich Mean Time) is assumed instead
2084      */
dateTimeCreated(time_base_t base) const2085     tm Archive::dateTimeCreated(time_base_t base) const {
2086         return _convertTimeStamp(m_timeCreated, base);
2087     }
2088 
2089     /** @brief Date and time when this archive was modified for the last time.
2090      *
2091      * Returns a calendar time information representing the date and time when
2092      * this archive has been modified for the last time. The optional @a base
2093      * parameter may be used to define to which time zone the returned date and
2094      * time shall be related to.
2095      *
2096      * @param base - (optional) time zone the result shall relate to, by default
2097      *               UTC time (Greenwhich Mean Time) is assumed instead
2098      */
dateTimeModified(time_base_t base) const2099     tm Archive::dateTimeModified(time_base_t base) const {
2100         return _convertTimeStamp(m_timeModified, base);
2101     }
2102 
2103     /** @brief Remove a member variable from the given object.
2104      *
2105      * Removes the member variable @a member from its containing object
2106      * @a parent and sets the modified state of this archive to @c true.
2107      * If the given @a parent object does not contain the given @a member then
2108      * this method does nothing.
2109      *
2110      * This method provides a means of "partial" deserialization. By removing
2111      * either objects or members from this archive before calling deserialize(),
2112      * only the remaining objects and remaining members will be restored by this
2113      * framework, all other data of your C++ classes remain untouched.
2114      *
2115      * @param parent - Object which contains @a member
2116      * @param member - member to be removed
2117      * @see isModified() for details about the modified state.
2118      * @see Object for more details about the overall object reflection concept.
2119      */
removeMember(Object & parent,const Member & member)2120     void Archive::removeMember(Object& parent, const Member& member) {
2121         parent.remove(member);
2122         m_isModified = true;
2123     }
2124 
2125     /** @brief Remove an object from this archive.
2126      *
2127      * Removes the object @obj from this archive and sets the modified state of
2128      * this archive to @c true. If the passed object is either invalid, or does
2129      * not exist in this archive, then this method does nothing.
2130      *
2131      * This method provides a means of "partial" deserialization. By removing
2132      * either objects or members from this archive before calling deserialize(),
2133      * only the remaining objects and remaining members will be restored by this
2134      * framework, all other data of your C++ classes remain untouched.
2135      *
2136      * @param obj - the object to be removed from this archive
2137      * @see isModified() for details about the modified state.
2138      * @see Object for more details about the overall object reflection concept.
2139      */
remove(const Object & obj)2140     void Archive::remove(const Object& obj) {
2141         //FIXME: Should traverse from root object and remove all members associated with this object
2142         if (!obj.uid()) return;
2143         m_allObjects.erase(obj.uid());
2144         m_isModified = true;
2145     }
2146 
2147     /** @brief Access object by its unique identifier.
2148      *
2149      * Returns the object of this archive with the given unique identifier
2150      * @a uid. If the given @a uid is invalid, or if this archive does not
2151      * contain an object with the given unique identifier, then this method
2152      * returns an invalid object instead.
2153      *
2154      * @param uid - unique identifier of sought object
2155      * @see Object for more details about the overall object reflection concept.
2156      * @see Object::isValid() for valid/invalid objects
2157      */
objectByUID(const UID & uid)2158     Object& Archive::objectByUID(const UID& uid) {
2159         return m_allObjects[uid];
2160     }
2161 
2162     /** @brief Set the current version for the given object.
2163      *
2164      * Essentially behaves like above's setVersion() method, it just uses the
2165      * abstract reflection data type instead for the respective @a object being
2166      * passed to this method. Refer to above's setVersion() documentation about
2167      * the precise behavior details of setVersion().
2168      *
2169      * @param object - object to set the current version for
2170      * @param v - new current version to set for @a object
2171      */
setVersion(Object & object,Version v)2172     void Archive::setVersion(Object& object, Version v) {
2173         if (!object) return;
2174         object.setVersion(v);
2175         m_isModified = true;
2176     }
2177 
2178     /** @brief Set the minimum version for the given object.
2179      *
2180      * Essentially behaves like above's setMinVersion() method, it just uses the
2181      * abstract reflection data type instead for the respective @a object being
2182      * passed to this method. Refer to above's setMinVersion() documentation
2183      * about the precise behavior details of setMinVersion().
2184      *
2185      * @param object - object to set the minimum version for
2186      * @param v - new minimum version to set for @a object
2187      */
setMinVersion(Object & object,Version v)2188     void Archive::setMinVersion(Object& object, Version v) {
2189         if (!object) return;
2190         object.setMinVersion(v);
2191         m_isModified = true;
2192     }
2193 
2194     /** @brief Set new value for given @c enum object.
2195      *
2196      * Sets the new @a value to the given @c enum @a object.
2197      *
2198      * @param object - the @c enum object to be changed
2199      * @param value - the new value to be assigned to the @a object
2200      * @throws Exception if @a object is not an @c enum type.
2201      */
setEnumValue(Object & object,uint64_t value)2202     void Archive::setEnumValue(Object& object, uint64_t value) {
2203         if (!object) return;
2204         if (!object.type().isEnum())
2205             throw Exception("Not an enum data type");
2206         Object* pObject = &object;
2207         if (object.type().isPointer()) {
2208             Object& obj = objectByUID(object.uid(1));
2209             if (!obj) return;
2210             pObject = &obj;
2211         }
2212         const int nativeEnumSize = sizeof(enum operation_t);
2213         DataType& type = const_cast<DataType&>( pObject->type() );
2214         // original serializer ("sender") might have had a different word size
2215         // than this machine, adjust type object in this case
2216         if (type.size() != nativeEnumSize) {
2217             type.m_size = nativeEnumSize;
2218         }
2219         pObject->m_data.resize(type.size());
2220         void* ptr = &pObject->m_data[0];
2221         if (type.size() == 1)
2222             *(uint8_t*)ptr = (uint8_t)value;
2223         else if (type.size() == 2)
2224             *(uint16_t*)ptr = (uint16_t)value;
2225         else if (type.size() == 4)
2226             *(uint32_t*)ptr = (uint32_t)value;
2227         else if (type.size() == 8)
2228             *(uint64_t*)ptr = (uint64_t)value;
2229         else
2230             assert(false /* unknown enum type size */);
2231         m_isModified = true;
2232     }
2233 
2234     /** @brief Set new integer value for given integer object.
2235      *
2236      * Sets the new integer @a value to the given integer @a object. Currently
2237      * this framework handles any integer data type up to 64 bit. For larger
2238      * integer types an assertion failure will be raised.
2239      *
2240      * @param object - the integer object to be changed
2241      * @param value - the new value to be assigned to the @a object
2242      * @throws Exception if @a object is not an integer type.
2243      */
setIntValue(Object & object,int64_t value)2244     void Archive::setIntValue(Object& object, int64_t value) {
2245         if (!object) return;
2246         if (!object.type().isInteger())
2247             throw Exception("Not an integer data type");
2248         Object* pObject = &object;
2249         if (object.type().isPointer()) {
2250             Object& obj = objectByUID(object.uid(1));
2251             if (!obj) return;
2252             pObject = &obj;
2253         }
2254         const DataType& type = pObject->type();
2255         pObject->m_data.resize(type.size());
2256         void* ptr = &pObject->m_data[0];
2257         if (type.isSigned()) {
2258             if (type.size() == 1)
2259                 *(int8_t*)ptr = (int8_t)value;
2260             else if (type.size() == 2)
2261                 *(int16_t*)ptr = (int16_t)value;
2262             else if (type.size() == 4)
2263                 *(int32_t*)ptr = (int32_t)value;
2264             else if (type.size() == 8)
2265                 *(int64_t*)ptr = (int64_t)value;
2266             else
2267                 assert(false /* unknown signed int type size */);
2268         } else {
2269             if (type.size() == 1)
2270                 *(uint8_t*)ptr = (uint8_t)value;
2271             else if (type.size() == 2)
2272                 *(uint16_t*)ptr = (uint16_t)value;
2273             else if (type.size() == 4)
2274                 *(uint32_t*)ptr = (uint32_t)value;
2275             else if (type.size() == 8)
2276                 *(uint64_t*)ptr = (uint64_t)value;
2277             else
2278                 assert(false /* unknown unsigned int type size */);
2279         }
2280         m_isModified = true;
2281     }
2282 
2283     /** @brief Set new floating point value for given floating point object.
2284      *
2285      * Sets the new floating point @a value to the given floating point
2286      * @a object. Currently this framework supports single precision @c float
2287      * and double precision @c double floating point data types. For all other
2288      * floating point types this method will raise an assertion failure.
2289      *
2290      * @param object - the floating point object to be changed
2291      * @param value - the new value to be assigned to the @a object
2292      * @throws Exception if @a object is not a floating point based type.
2293      */
setRealValue(Object & object,double value)2294     void Archive::setRealValue(Object& object, double value) {
2295         if (!object) return;
2296         if (!object.type().isReal())
2297             throw Exception("Not a real data type");
2298         Object* pObject = &object;
2299         if (object.type().isPointer()) {
2300             Object& obj = objectByUID(object.uid(1));
2301             if (!obj) return;
2302             pObject = &obj;
2303         }
2304         const DataType& type = pObject->type();
2305         pObject->m_data.resize(type.size());
2306         void* ptr = &pObject->m_data[0];
2307         if (type.size() == sizeof(float))
2308             *(float*)ptr = (float)value;
2309         else if (type.size() == sizeof(double))
2310             *(double*)ptr = (double)value;
2311         else
2312             assert(false /* unknown real type size */);
2313         m_isModified = true;
2314     }
2315 
2316     /** @brief Set new boolean value for given boolean object.
2317      *
2318      * Sets the new boolean @a value to the given boolean @a object.
2319      *
2320      * @param object - the boolean object to be changed
2321      * @param value - the new value to be assigned to the @a object
2322      * @throws Exception if @a object is not a boolean type.
2323      */
setBoolValue(Object & object,bool value)2324     void Archive::setBoolValue(Object& object, bool value) {
2325         if (!object) return;
2326         if (!object.type().isBool())
2327             throw Exception("Not a bool data type");
2328         Object* pObject = &object;
2329         if (object.type().isPointer()) {
2330             Object& obj = objectByUID(object.uid(1));
2331             if (!obj) return;
2332             pObject = &obj;
2333         }
2334         const DataType& type = pObject->type();
2335         pObject->m_data.resize(type.size());
2336         bool* ptr = (bool*)&pObject->m_data[0];
2337         *ptr = value;
2338         m_isModified = true;
2339     }
2340 
2341     /** @brief Set new textual string for given String object.
2342      *
2343      * Sets the new textual string @a value to the given String @a object.
2344      *
2345      * @param object - the String object to be changed
2346      * @param value - the new textual string to be assigned to the @a object
2347      * @throws Exception if @a object is not a String type.
2348      */
setStringValue(Object & object,String value)2349     void Archive::setStringValue(Object& object, String value) {
2350         if (!object) return;
2351         if (!object.type().isString())
2352             throw Exception("Not a String data type");
2353         Object* pObject = &object;
2354         if (object.type().isPointer()) {
2355             Object& obj = objectByUID(object.uid(1));
2356             if (!obj) return;
2357             pObject = &obj;
2358         }
2359         pObject->m_data.resize(value.length() + 1);
2360         char* ptr = (char*) &pObject->m_data[0];
2361         strcpy(ptr, &value[0]);
2362         m_isModified = true;
2363     }
2364 
2365     /** @brief Automatically cast and assign appropriate value to object.
2366      *
2367      * This method automatically converts the given @a value from textual string
2368      * representation into the appropriate data format of the requested
2369      * @a object. So this method is a convenient way to change values of objects
2370      * in this archive with your applications in automated way, i.e. for
2371      * implementing an editor where the user is able to edit values of objects
2372      * in this archive by entering the values as text with a keyboard.
2373      *
2374      * @throws Exception if the passed @a object is not a fundamental, primitive
2375      *         data type or if the provided textual value cannot be converted
2376      *         into an appropriate value for the requested object.
2377      */
setAutoValue(Object & object,String value)2378     void Archive::setAutoValue(Object& object, String value) {
2379         if (!object) return;
2380         const DataType& type = object.type();
2381         if (type.isInteger())
2382             setIntValue(object, atoll(value.c_str()));
2383         else if (type.isReal())
2384             setRealValue(object, atof(value.c_str()));
2385         else if (type.isBool()) {
2386             String val = toLowerCase(value);
2387             if (val == "true" || val == "yes" || val == "1")
2388                 setBoolValue(object, true);
2389             else if (val == "false" || val == "no" || val == "0")
2390                 setBoolValue(object, false);
2391             else
2392                 setBoolValue(object, atof(value.c_str()));
2393         } else if (type.isString())
2394             setStringValue(object, value);
2395         else if (type.isEnum())
2396             setEnumValue(object, atoll(value.c_str()));
2397         else
2398             throw Exception("Not a primitive data type");
2399     }
2400 
2401     /** @brief Get value of object as string.
2402      *
2403      * Converts the current value of the given @a object into a textual string
2404      * and returns that string.
2405      *
2406      * @param object - object whose value shall be retrieved
2407      * @throws Exception if the given object is either invalid, or if the object
2408      *         is not a fundamental, primitive data type.
2409      */
valueAsString(const Object & object)2410     String Archive::valueAsString(const Object& object) {
2411         if (!object)
2412             throw Exception("Invalid object");
2413         if (object.type().isClass())
2414             throw Exception("Object is class type");
2415         const Object* pObject = &object;
2416         if (object.type().isPointer()) {
2417             const Object& obj = objectByUID(object.uid(1));
2418             if (!obj) return "";
2419             pObject = &obj;
2420         }
2421         return _primitiveObjectValueToString(*pObject);
2422     }
2423 
2424     /** @brief Get integer value of object.
2425      *
2426      * Returns the current integer value of the requested integer @a object or
2427      * @c enum object.
2428      *
2429      * @param object - object whose value shall be retrieved
2430      * @throws Exception if the given object is either invalid, or if the object
2431      *         is neither an integer nor @c enum data type.
2432      */
valueAsInt(const Object & object)2433     int64_t Archive::valueAsInt(const Object& object) {
2434         if (!object)
2435             throw Exception("Invalid object");
2436         if (!object.type().isInteger() && !object.type().isEnum())
2437             throw Exception("Object is neither an integer nor an enum");
2438         const Object* pObject = &object;
2439         if (object.type().isPointer()) {
2440             const Object& obj = objectByUID(object.uid(1));
2441             if (!obj) return 0;
2442             pObject = &obj;
2443         }
2444         return _primitiveObjectValueToNumber<int64_t>(*pObject);
2445     }
2446 
2447     /** @brief Get floating point value of object.
2448      *
2449      * Returns the current floating point value of the requested floating point
2450      * @a object.
2451      *
2452      * @param object - object whose value shall be retrieved
2453      * @throws Exception if the given object is either invalid, or if the object
2454      *         is not a floating point based type.
2455      */
valueAsReal(const Object & object)2456     double Archive::valueAsReal(const Object& object) {
2457         if (!object)
2458             throw Exception("Invalid object");
2459         if (!object.type().isReal())
2460             throw Exception("Object is not an real type");
2461         const Object* pObject = &object;
2462         if (object.type().isPointer()) {
2463             const Object& obj = objectByUID(object.uid(1));
2464             if (!obj) return 0;
2465             pObject = &obj;
2466         }
2467         return _primitiveObjectValueToNumber<double>(*pObject);
2468     }
2469 
2470     /** @brief Get boolean value of object.
2471      *
2472      * Returns the current boolean value of the requested boolean @a object.
2473      *
2474      * @param object - object whose value shall be retrieved
2475      * @throws Exception if the given object is either invalid, or if the object
2476      *         is not a boolean data type.
2477      */
valueAsBool(const Object & object)2478     bool Archive::valueAsBool(const Object& object) {
2479         if (!object)
2480             throw Exception("Invalid object");
2481         if (!object.type().isBool())
2482             throw Exception("Object is not a bool");
2483         const Object* pObject = &object;
2484         if (object.type().isPointer()) {
2485             const Object& obj = objectByUID(object.uid(1));
2486             if (!obj) return 0;
2487             pObject = &obj;
2488         }
2489         return _primitiveObjectValueToNumber<bool>(*pObject);
2490     }
2491 
operation() const2492     Archive::operation_t Archive::operation() const {
2493         return m_operation;
2494     }
2495 
2496     // *************** Archive::Syncer ***************
2497     // *
2498 
Syncer(Archive & dst,Archive & src)2499     Archive::Syncer::Syncer(Archive& dst, Archive& src)
2500        : m_dst(dst), m_src(src)
2501     {
2502         const Object srcRootObj = src.rootObject();
2503         const Object dstRootObj = dst.rootObject();
2504         if (!srcRootObj)
2505             throw Exception("No source root object!");
2506         if (!dstRootObj)
2507             throw Exception("Expected destination root object not found!");
2508         syncObject(dstRootObj, srcRootObj);
2509     }
2510 
syncPrimitive(const Object & dstObj,const Object & srcObj)2511     void Archive::Syncer::syncPrimitive(const Object& dstObj, const Object& srcObj) {
2512         assert(srcObj.rawData().size() == dstObj.type().size());
2513         void* pDst = (void*)dstObj.uid().id;
2514         memcpy(pDst, &srcObj.rawData()[0], dstObj.type().size());
2515     }
2516 
syncString(const Object & dstObj,const Object & srcObj)2517     void Archive::Syncer::syncString(const Object& dstObj, const Object& srcObj) {
2518         assert(dstObj.type().isString());
2519         assert(dstObj.type() == srcObj.type());
2520         String* pDst = (String*)(void*)dstObj.uid().id;
2521         *pDst = (String) (const char*) &srcObj.rawData()[0];
2522     }
2523 
syncArray(const Object & dstObj,const Object & srcObj)2524     void Archive::Syncer::syncArray(const Object& dstObj, const Object& srcObj) {
2525         assert(dstObj.type().isArray());
2526         assert(dstObj.type() == srcObj.type());
2527         dstObj.m_sync(const_cast<Object&>(dstObj), srcObj, this);
2528     }
2529 
syncSet(const Object & dstObj,const Object & srcObj)2530     void Archive::Syncer::syncSet(const Object& dstObj, const Object& srcObj) {
2531         assert(dstObj.type().isSet());
2532         assert(dstObj.type() == srcObj.type());
2533         dstObj.m_sync(const_cast<Object&>(dstObj), srcObj, this);
2534     }
2535 
syncMap(const Object & dstObj,const Object & srcObj)2536     void Archive::Syncer::syncMap(const Object& dstObj, const Object& srcObj) {
2537         assert(dstObj.type().isMap());
2538         assert(dstObj.type() == srcObj.type());
2539         dstObj.m_sync(const_cast<Object&>(dstObj), srcObj, this);
2540     }
2541 
syncPointer(const Object & dstObj,const Object & srcObj)2542     void Archive::Syncer::syncPointer(const Object& dstObj, const Object& srcObj) {
2543         assert(dstObj.type().isPointer());
2544         assert(dstObj.type() == srcObj.type());
2545         const Object& pointedDstObject = m_dst.m_allObjects[dstObj.uid(1)];
2546         const Object& pointedSrcObject = m_src.m_allObjects[srcObj.uid(1)];
2547         syncObject(pointedDstObject, pointedSrcObject);
2548     }
2549 
syncObject(const Object & dstObj,const Object & srcObj)2550     void Archive::Syncer::syncObject(const Object& dstObj, const Object& srcObj) {
2551         if (!dstObj || !srcObj) return; // end of recursion
2552         if (!dstObj.isVersionCompatibleTo(srcObj))
2553             throw Exception("Version incompatible (destination version " +
2554                             ToString(dstObj.version()) + " [min. version " +
2555                             ToString(dstObj.minVersion()) + "], source version " +
2556                             ToString(srcObj.version()) + " [min. version " +
2557                             ToString(srcObj.minVersion()) + "])");
2558         if (dstObj.type() != srcObj.type())
2559             throw Exception("Incompatible data structure type (destination type " +
2560                             dstObj.type().asLongDescr() + " vs. source type " +
2561                             srcObj.type().asLongDescr() + ")");
2562 
2563         // prevent syncing this object again, and thus also prevent endless
2564         // loop on data structures with cyclic relations
2565         m_dst.m_allObjects.erase(dstObj.uid());
2566 
2567         if (dstObj.type().isPrimitive() && !dstObj.type().isPointer()) {
2568             if (dstObj.type().isString())
2569                 syncString(dstObj, srcObj);
2570             else
2571                 syncPrimitive(dstObj, srcObj);
2572             return; // end of recursion
2573         }
2574 
2575         if (dstObj.type().isArray()) {
2576             syncArray(dstObj, srcObj);
2577             return;
2578         }
2579 
2580         if (dstObj.type().isSet()) {
2581             syncSet(dstObj, srcObj);
2582             return;
2583         }
2584 
2585         if (dstObj.type().isMap()) {
2586             syncMap(dstObj, srcObj);
2587             return;
2588         }
2589 
2590         if (dstObj.type().isPointer()) {
2591             syncPointer(dstObj, srcObj);
2592             return;
2593         }
2594 
2595         assert(dstObj.type().isClass());
2596         for (int iMember = 0; iMember < srcObj.members().size(); ++iMember) {
2597             const Member& srcMember = srcObj.members()[iMember];
2598             Member dstMember = dstMemberMatching(dstObj, srcObj, srcMember);
2599             if (!dstMember)
2600                 throw Exception("Expected member missing in destination object");
2601             syncMember(dstMember, srcMember);
2602         }
2603     }
2604 
dstMemberMatching(const Object & dstObj,const Object & srcObj,const Member & srcMember)2605     Member Archive::Syncer::dstMemberMatching(const Object& dstObj, const Object& srcObj, const Member& srcMember) {
2606         Member dstMember = dstObj.memberNamed(srcMember.name());
2607         if (dstMember)
2608             return (dstMember.type() == srcMember.type()) ? dstMember : Member();
2609         std::vector<Member> members = dstObj.membersOfType(srcMember.type());
2610         if (members.size() <= 0)
2611             return Member();
2612         if (members.size() == 1)
2613             return members[0];
2614         for (int i = 0; i < members.size(); ++i)
2615             if (members[i].offset() == srcMember.offset())
2616                 return members[i];
2617         const int srcSeqNr = srcObj.sequenceIndexOf(srcMember);
2618         assert(srcSeqNr >= 0); // should never happen, otherwise there is a bug
2619         for (int i = 0; i < members.size(); ++i) {
2620             const int dstSeqNr = dstObj.sequenceIndexOf(members[i]);
2621             if (dstSeqNr == srcSeqNr)
2622                 return members[i];
2623         }
2624         return Member(); // give up!
2625     }
2626 
syncMember(const Member & dstMember,const Member & srcMember)2627     void Archive::Syncer::syncMember(const Member& dstMember, const Member& srcMember) {
2628         assert(dstMember && srcMember);
2629         assert(dstMember.type() == srcMember.type());
2630         const Object dstObj = m_dst.m_allObjects[dstMember.uid()];
2631         const Object srcObj = m_src.m_allObjects[srcMember.uid()];
2632         syncObject(dstObj, srcObj);
2633     }
2634 
2635     // *************** Exception ***************
2636     // *
2637 
Exception()2638     Exception::Exception() {
2639     }
2640 
Exception(String format,...)2641     Exception::Exception(String format, ...) {
2642         va_list arg;
2643         va_start(arg, format);
2644         Message = assemble(format, arg);
2645         va_end(arg);
2646     }
2647 
Exception(String format,va_list arg)2648     Exception::Exception(String format, va_list arg) {
2649         Message = assemble(format, arg);
2650     }
2651 
2652     /** @brief Print exception message to stdout.
2653      *
2654      * Prints the message of this Exception to the currently defined standard
2655      * output (that is to the terminal console for example).
2656      */
PrintMessage()2657     void Exception::PrintMessage() {
2658         std::cout << "Serialization::Exception: " << Message << std::endl;
2659     }
2660 
assemble(String format,va_list arg)2661     String Exception::assemble(String format, va_list arg) {
2662         char* buf = NULL;
2663         vasprintf(&buf, format.c_str(), arg);
2664         String s = buf;
2665         free(buf);
2666         return s;
2667     }
2668 
2669 } // namespace Serialization
2670