/[svn]/libgig/trunk/src/Serialization.cpp
ViewVC logotype

Annotation of /libgig/trunk/src/Serialization.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3776 - (hide annotations) (download)
Sat May 23 19:26:07 2020 UTC (3 years, 10 months ago) by schoenebeck
File size: 102909 byte(s)
* Serialization.cpp/.h: Added built-in support for C++ Set<>
  objects (a.k.a. std::set from the STL).

* Bumped version (4.2.0.svn14).

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

  ViewVC Help
Powered by ViewVC