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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3778 - (hide annotations) (download)
Sun May 24 11:20:11 2020 UTC (3 years, 10 months ago) by schoenebeck
File size: 105424 byte(s)
Serialization.cpp: Fix compatibility issues with new 2nd custom data type

* When encoding serialised data stream: always write 2nd custom data type
  name to data type blob, and append it actually to the end of the data
  type blob (not somewhere in the middle), no matter if a 2nd custom type
  is actually present (i.e. empty string) in object's data type or not.

* When decoding a serialised data stream: check the size of the data type
  blob, if the blob size is longer than a Srx v1.0 format data type blob,
  then assume there is a 2nd custom data type name, so read it (it might
  be an empty string if 2nd custom data type was not present on encoding
  side's object).

* Bumped version (4.2.0.svn16).

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

  ViewVC Help
Powered by ViewVC