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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3777 - (hide annotations) (download)
Sat May 23 19:55:32 2020 UTC (3 years, 10 months ago) by schoenebeck
File size: 105325 byte(s)
Serialization.cpp/.h: Added built-in support for C++ Map<> objects

* Introduced out of the box support for serialising / deserialising
  variables of C++ Map<> data types (a.k.a. std::map from the STL).

* DataType: Added optional 2nd custom type name.

* Bumped version (4.2.0.svn15).

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     s += _encodeBlob(type.baseTypeName());
1350     s += _encodeBlob(type.customTypeName());
1351 schoenebeck 3777 if (!type.customTypeName2().empty())
1352     s += _encodeBlob(type.customTypeName2());
1353 schoenebeck 3138 s += _encodeBlob(ToString(type.size()));
1354     s += _encodeBlob(ToString(type.isPointer()));
1355     return _encodeBlob(s);
1356     }
1357    
1358     static String _encode(const UIDChain& chain) {
1359     String s;
1360     for (int i = 0; i < chain.size(); ++i)
1361     s += _encode(chain[i]);
1362     return _encodeBlob(s);
1363     }
1364    
1365     static String _encode(const Member& member) {
1366     String s;
1367     s += _encode(member.uid());
1368     s += _encodeBlob(ToString(member.offset()));
1369     s += _encodeBlob(member.name());
1370     s += _encode(member.type());
1371     return _encodeBlob(s);
1372     }
1373    
1374     static String _encode(const std::vector<Member>& members) {
1375     String s;
1376     for (int i = 0; i < members.size(); ++i)
1377     s += _encode(members[i]);
1378     return _encodeBlob(s);
1379     }
1380    
1381 schoenebeck 3150 static String _primitiveObjectValueToString(const Object& obj) {
1382 schoenebeck 3138 String s;
1383     const DataType& type = obj.type();
1384     const ID& id = obj.uid().id;
1385 schoenebeck 3150 void* ptr = obj.m_data.empty() ? (void*)id : (void*)&obj.m_data[0];
1386     if (!obj.m_data.empty())
1387     assert(type.size() == obj.m_data.size());
1388 schoenebeck 3138 if (type.isPrimitive() && !type.isPointer()) {
1389     if (type.isInteger() || type.isEnum()) {
1390     if (type.isSigned()) {
1391     if (type.size() == 1)
1392 schoenebeck 3150 s = ToString((int16_t)*(int8_t*)ptr); // int16_t: prevent ToString() to render an ASCII character
1393 schoenebeck 3138 else if (type.size() == 2)
1394 schoenebeck 3150 s = ToString(*(int16_t*)ptr);
1395 schoenebeck 3138 else if (type.size() == 4)
1396 schoenebeck 3150 s = ToString(*(int32_t*)ptr);
1397 schoenebeck 3138 else if (type.size() == 8)
1398 schoenebeck 3150 s = ToString(*(int64_t*)ptr);
1399 schoenebeck 3138 else
1400     assert(false /* unknown signed int type size */);
1401     } else {
1402     if (type.size() == 1)
1403 schoenebeck 3150 s = ToString((uint16_t)*(uint8_t*)ptr); // uint16_t: prevent ToString() to render an ASCII character
1404 schoenebeck 3138 else if (type.size() == 2)
1405 schoenebeck 3150 s = ToString(*(uint16_t*)ptr);
1406 schoenebeck 3138 else if (type.size() == 4)
1407 schoenebeck 3150 s = ToString(*(uint32_t*)ptr);
1408 schoenebeck 3138 else if (type.size() == 8)
1409 schoenebeck 3150 s = ToString(*(uint64_t*)ptr);
1410 schoenebeck 3138 else
1411     assert(false /* unknown unsigned int type size */);
1412     }
1413     } else if (type.isReal()) {
1414     if (type.size() == sizeof(float))
1415 schoenebeck 3150 s = ToString(*(float*)ptr);
1416 schoenebeck 3138 else if (type.size() == sizeof(double))
1417 schoenebeck 3150 s = ToString(*(double*)ptr);
1418 schoenebeck 3138 else
1419     assert(false /* unknown floating point type */);
1420     } else if (type.isBool()) {
1421 schoenebeck 3150 s = ToString(*(bool*)ptr);
1422 schoenebeck 3771 } else if (type.isString()) {
1423     s = obj.m_data.empty() ? *(String*)ptr : String((const char*)ptr);
1424 schoenebeck 3138 } else {
1425     assert(false /* unknown primitive type */);
1426     }
1427     }
1428 schoenebeck 3150 return s;
1429 schoenebeck 3138 }
1430    
1431 schoenebeck 3169 template<typename T>
1432 schoenebeck 3771 inline T _stringToNumber(const String& s) {
1433     assert(false /* String cast to unknown primitive number type */);
1434     }
1435    
1436     template<>
1437     inline int64_t _stringToNumber(const String& s) {
1438     return atoll(s.c_str());
1439     }
1440    
1441     template<>
1442     inline double _stringToNumber(const String& s) {
1443     return atof(s.c_str());
1444     }
1445    
1446     template<>
1447     inline bool _stringToNumber(const String& s) {
1448     return (bool) atoll(s.c_str());
1449     }
1450    
1451     template<typename T>
1452 schoenebeck 3169 static T _primitiveObjectValueToNumber(const Object& obj) {
1453     T value = 0;
1454     const DataType& type = obj.type();
1455     const ID& id = obj.uid().id;
1456     void* ptr = obj.m_data.empty() ? (void*)id : (void*)&obj.m_data[0];
1457     if (!obj.m_data.empty())
1458     assert(type.size() == obj.m_data.size());
1459     if (type.isPrimitive() && !type.isPointer()) {
1460     if (type.isInteger() || type.isEnum()) {
1461     if (type.isSigned()) {
1462     if (type.size() == 1)
1463     value = (T)*(int8_t*)ptr;
1464     else if (type.size() == 2)
1465     value = (T)*(int16_t*)ptr;
1466     else if (type.size() == 4)
1467     value = (T)*(int32_t*)ptr;
1468     else if (type.size() == 8)
1469     value = (T)*(int64_t*)ptr;
1470     else
1471     assert(false /* unknown signed int type size */);
1472     } else {
1473     if (type.size() == 1)
1474     value = (T)*(uint8_t*)ptr;
1475     else if (type.size() == 2)
1476     value = (T)*(uint16_t*)ptr;
1477     else if (type.size() == 4)
1478     value = (T)*(uint32_t*)ptr;
1479     else if (type.size() == 8)
1480     value = (T)*(uint64_t*)ptr;
1481     else
1482     assert(false /* unknown unsigned int type size */);
1483     }
1484     } else if (type.isReal()) {
1485     if (type.size() == sizeof(float))
1486     value = (T)*(float*)ptr;
1487     else if (type.size() == sizeof(double))
1488     value = (T)*(double*)ptr;
1489     else
1490     assert(false /* unknown floating point type */);
1491     } else if (type.isBool()) {
1492     value = (T)*(bool*)ptr;
1493 schoenebeck 3771 } else if (type.isString()) {
1494     value = _stringToNumber<T>(
1495     obj.m_data.empty() ? *(String*)ptr : String((const char*)ptr)
1496     );
1497 schoenebeck 3169 } else {
1498     assert(false /* unknown primitive type */);
1499     }
1500     }
1501     return value;
1502     }
1503    
1504 schoenebeck 3150 static String _encodePrimitiveValue(const Object& obj) {
1505     return _encodeBlob( _primitiveObjectValueToString(obj) );
1506     }
1507    
1508 schoenebeck 3138 static String _encode(const Object& obj) {
1509     String s;
1510     s += _encode(obj.type());
1511     s += _encodeBlob(ToString(obj.version()));
1512     s += _encodeBlob(ToString(obj.minVersion()));
1513     s += _encode(obj.uidChain());
1514     s += _encode(obj.members());
1515     s += _encodePrimitiveValue(obj);
1516     return _encodeBlob(s);
1517     }
1518    
1519     String _encode(const Archive::ObjectPool& objects) {
1520     String s;
1521     for (Archive::ObjectPool::const_iterator itObject = objects.begin();
1522     itObject != objects.end(); ++itObject)
1523     {
1524     const Object& obj = itObject->second;
1525     s += _encode(obj);
1526     }
1527     return _encodeBlob(s);
1528     }
1529    
1530 schoenebeck 3771 /*
1531     * Srx format history:
1532     * - 1.0: Initial version.
1533 schoenebeck 3777 * - 1.1: Adds "String", "Array", "Set" and "Map" data types and an optional
1534     * 2nd custom type name (e.g. "Map" types which always contain two
1535     * user defined types).
1536 schoenebeck 3771 */
1537 schoenebeck 3138 #define MAGIC_START "Srx1v"
1538 schoenebeck 3771 #define ENCODING_FORMAT_MINOR_VERSION 1
1539 schoenebeck 3138
1540     String Archive::_encodeRootBlob() {
1541     String s;
1542     s += _encodeBlob(ToString(ENCODING_FORMAT_MINOR_VERSION));
1543     s += _encode(m_root);
1544     s += _encode(m_allObjects);
1545 schoenebeck 3156 s += _encodeBlob(m_name);
1546     s += _encodeBlob(m_comment);
1547     s += _encode(m_timeCreated);
1548     s += _encode(m_timeModified);
1549 schoenebeck 3138 return _encodeBlob(s);
1550     }
1551    
1552     void Archive::encode() {
1553     m_rawData.clear();
1554     String s = MAGIC_START;
1555 schoenebeck 3159 m_timeModified = time(NULL);
1556     if (m_timeCreated == LIBGIG_EPOCH_TIME)
1557     m_timeCreated = m_timeModified;
1558 schoenebeck 3138 s += _encodeRootBlob();
1559     m_rawData.resize(s.length() + 1);
1560     memcpy(&m_rawData[0], &s[0], s.length() + 1);
1561 schoenebeck 3150 m_isModified = false;
1562 schoenebeck 3138 }
1563    
1564     struct _Blob {
1565     const char* p;
1566     const char* end;
1567     };
1568    
1569     static _Blob _decodeBlob(const char* p, const char* end, bool bThrow = true) {
1570 schoenebeck 3168 if (!bThrow && p >= end) {
1571     const _Blob blob = { p, end };
1572     return blob;
1573     }
1574 schoenebeck 3138 size_t sz = 0;
1575     for (; true; ++p) {
1576     if (p >= end)
1577     throw Exception("Decode Error: Missing blob");
1578     const char& c = *p;
1579     if (c == ':') break;
1580     if (c < '0' || c > '9')
1581     throw Exception("Decode Error: Missing blob size");
1582     sz *= 10;
1583     sz += size_t(c - '0');
1584     }
1585     ++p;
1586     if (p + sz > end)
1587     throw Exception("Decode Error: Premature end of blob");
1588 schoenebeck 3168 const _Blob blob = { p, p + sz };
1589     return blob;
1590 schoenebeck 3138 }
1591    
1592     template<typename T_int>
1593     static T_int _popIntBlob(const char*& p, const char* end) {
1594     _Blob blob = _decodeBlob(p, end);
1595     p = blob.p;
1596     end = blob.end;
1597    
1598     T_int sign = 1;
1599     T_int i = 0;
1600     if (p >= end)
1601     throw Exception("Decode Error: premature end of int blob");
1602     if (*p == '-') {
1603     sign = -1;
1604     ++p;
1605     }
1606     for (; p < end; ++p) {
1607     const char& c = *p;
1608     if (c < '0' || c > '9')
1609     throw Exception("Decode Error: Invalid int blob format");
1610     i *= 10;
1611     i += size_t(c - '0');
1612     }
1613     return i * sign;
1614     }
1615    
1616     template<typename T_int>
1617     static void _popIntBlob(const char*& p, const char* end, RawData& rawData) {
1618     const T_int i = _popIntBlob<T_int>(p, end);
1619     *(T_int*)&rawData[0] = i;
1620     }
1621    
1622     template<typename T_real>
1623     static T_real _popRealBlob(const char*& p, const char* end) {
1624     _Blob blob = _decodeBlob(p, end);
1625     p = blob.p;
1626     end = blob.end;
1627    
1628     if (p >= end || (end - p) < 1)
1629     throw Exception("Decode Error: premature end of real blob");
1630    
1631     String s(p, size_t(end - p));
1632    
1633     T_real r;
1634 schoenebeck 3139 if (sizeof(T_real) <= sizeof(double))
1635 schoenebeck 3138 r = atof(s.c_str());
1636     else
1637     assert(false /* unknown real type */);
1638    
1639     p += s.length();
1640    
1641     return r;
1642     }
1643    
1644     template<typename T_real>
1645     static void _popRealBlob(const char*& p, const char* end, RawData& rawData) {
1646     const T_real r = _popRealBlob<T_real>(p, end);
1647     *(T_real*)&rawData[0] = r;
1648     }
1649    
1650     static String _popStringBlob(const char*& p, const char* end) {
1651     _Blob blob = _decodeBlob(p, end);
1652     p = blob.p;
1653     end = blob.end;
1654     if (end - p < 0)
1655     throw Exception("Decode Error: missing String blob");
1656     String s;
1657     const size_t sz = end - p;
1658     s.resize(sz);
1659     memcpy(&s[0], p, sz);
1660     p += sz;
1661     return s;
1662     }
1663    
1664 schoenebeck 3771 static void _popStringBlob(const char*& p, const char* end, RawData& rawData) {
1665     String s = _popStringBlob(p, end);
1666     rawData.resize(s.length() + 1);
1667     strcpy((char*)&rawData[0], &s[0]);
1668     }
1669    
1670 schoenebeck 3156 static time_t _popTimeBlob(const char*& p, const char* end) {
1671     const uint64_t i = _popIntBlob<uint64_t>(p, end);
1672     return (time_t) i;
1673     }
1674    
1675 schoenebeck 3392 static DataType _popDataTypeBlob(const char*& p, const char* end) {
1676 schoenebeck 3138 _Blob blob = _decodeBlob(p, end);
1677     p = blob.p;
1678     end = blob.end;
1679    
1680     DataType type;
1681     type.m_baseTypeName = _popStringBlob(p, end);
1682     type.m_customTypeName = _popStringBlob(p, end);
1683 schoenebeck 3777 if (type.isMap())
1684     type.m_customTypeName2 = _popStringBlob(p, end);
1685 schoenebeck 3138 type.m_size = _popIntBlob<int>(p, end);
1686     type.m_isPointer = _popIntBlob<bool>(p, end);
1687     return type;
1688     }
1689    
1690     static UID _popUIDBlob(const char*& p, const char* end) {
1691     _Blob blob = _decodeBlob(p, end);
1692     p = blob.p;
1693     end = blob.end;
1694    
1695     if (p >= end)
1696     throw Exception("Decode Error: premature end of UID blob");
1697    
1698     const ID id = (ID) _popIntBlob<size_t>(p, end);
1699     const size_t size = _popIntBlob<size_t>(p, end);
1700    
1701 schoenebeck 3168 const UID uid = { id, size };
1702     return uid;
1703 schoenebeck 3138 }
1704    
1705     static UIDChain _popUIDChainBlob(const char*& p, const char* end) {
1706     _Blob blob = _decodeBlob(p, end);
1707     p = blob.p;
1708     end = blob.end;
1709    
1710     UIDChain chain;
1711     while (p < end) {
1712     const UID uid = _popUIDBlob(p, end);
1713     chain.push_back(uid);
1714     }
1715     assert(!chain.empty());
1716     return chain;
1717     }
1718    
1719 schoenebeck 3146 static Member _popMemberBlob(const char*& p, const char* end) {
1720 schoenebeck 3138 _Blob blob = _decodeBlob(p, end, false);
1721     p = blob.p;
1722     end = blob.end;
1723    
1724     Member m;
1725     if (p >= end) return m;
1726    
1727     m.m_uid = _popUIDBlob(p, end);
1728 schoenebeck 3775 m.m_offset = _popIntBlob<ssize_t>(p, end);
1729 schoenebeck 3138 m.m_name = _popStringBlob(p, end);
1730     m.m_type = _popDataTypeBlob(p, end);
1731     assert(m.type());
1732     assert(!m.name().empty());
1733 schoenebeck 3146 assert(m.uid().isValid());
1734 schoenebeck 3138 return m;
1735     }
1736    
1737     static std::vector<Member> _popMembersBlob(const char*& p, const char* end) {
1738     _Blob blob = _decodeBlob(p, end, false);
1739     p = blob.p;
1740     end = blob.end;
1741    
1742     std::vector<Member> members;
1743     while (p < end) {
1744     const Member member = _popMemberBlob(p, end);
1745     if (member)
1746     members.push_back(member);
1747     else
1748     break;
1749     }
1750     return members;
1751     }
1752    
1753 schoenebeck 3146 static void _popPrimitiveValue(const char*& p, const char* end, Object& obj) {
1754 schoenebeck 3138 const DataType& type = obj.type();
1755     if (type.isPrimitive() && !type.isPointer()) {
1756     obj.m_data.resize(type.size());
1757     if (type.isInteger() || type.isEnum()) {
1758     if (type.isSigned()) {
1759     if (type.size() == 1)
1760     _popIntBlob<int8_t>(p, end, obj.m_data);
1761     else if (type.size() == 2)
1762     _popIntBlob<int16_t>(p, end, obj.m_data);
1763     else if (type.size() == 4)
1764     _popIntBlob<int32_t>(p, end, obj.m_data);
1765     else if (type.size() == 8)
1766     _popIntBlob<int64_t>(p, end, obj.m_data);
1767     else
1768     assert(false /* unknown signed int type size */);
1769     } else {
1770     if (type.size() == 1)
1771     _popIntBlob<uint8_t>(p, end, obj.m_data);
1772     else if (type.size() == 2)
1773     _popIntBlob<uint16_t>(p, end, obj.m_data);
1774     else if (type.size() == 4)
1775     _popIntBlob<uint32_t>(p, end, obj.m_data);
1776     else if (type.size() == 8)
1777     _popIntBlob<uint64_t>(p, end, obj.m_data);
1778     else
1779     assert(false /* unknown unsigned int type size */);
1780     }
1781     } else if (type.isReal()) {
1782     if (type.size() == sizeof(float))
1783     _popRealBlob<float>(p, end, obj.m_data);
1784     else if (type.size() == sizeof(double))
1785     _popRealBlob<double>(p, end, obj.m_data);
1786     else
1787     assert(false /* unknown floating point type */);
1788     } else if (type.isBool()) {
1789     _popIntBlob<uint8_t>(p, end, obj.m_data);
1790 schoenebeck 3771 } else if (type.isString()) {
1791     _popStringBlob(p, end, obj.m_data);
1792 schoenebeck 3138 } else {
1793     assert(false /* unknown primitive type */);
1794     }
1795    
1796     } else {
1797     // don't whine if the empty blob was not added on encoder side
1798     _Blob blob = _decodeBlob(p, end, false);
1799     p = blob.p;
1800     end = blob.end;
1801     }
1802     }
1803    
1804 schoenebeck 3146 static Object _popObjectBlob(const char*& p, const char* end) {
1805 schoenebeck 3138 _Blob blob = _decodeBlob(p, end, false);
1806     p = blob.p;
1807     end = blob.end;
1808    
1809     Object obj;
1810     if (p >= end) return obj;
1811    
1812     obj.m_type = _popDataTypeBlob(p, end);
1813     obj.m_version = _popIntBlob<Version>(p, end);
1814     obj.m_minVersion = _popIntBlob<Version>(p, end);
1815     obj.m_uid = _popUIDChainBlob(p, end);
1816     obj.m_members = _popMembersBlob(p, end);
1817     _popPrimitiveValue(p, end, obj);
1818     assert(obj.type());
1819     return obj;
1820     }
1821    
1822     void Archive::_popObjectsBlob(const char*& p, const char* end) {
1823     _Blob blob = _decodeBlob(p, end, false);
1824     p = blob.p;
1825     end = blob.end;
1826    
1827     if (p >= end)
1828     throw Exception("Decode Error: Premature end of objects blob");
1829    
1830     while (true) {
1831     const Object obj = _popObjectBlob(p, end);
1832     if (!obj) break;
1833     m_allObjects[obj.uid()] = obj;
1834     }
1835     }
1836    
1837     void Archive::_popRootBlob(const char*& p, const char* end) {
1838     _Blob blob = _decodeBlob(p, end, false);
1839     p = blob.p;
1840     end = blob.end;
1841    
1842     if (p >= end)
1843     throw Exception("Decode Error: Premature end of root blob");
1844    
1845     // just in case this encoding format will be extended in future
1846     // (currently not used)
1847     const int formatMinorVersion = _popIntBlob<int>(p, end);
1848    
1849     m_root = _popUIDBlob(p, end);
1850     if (!m_root)
1851     throw Exception("Decode Error: No root object");
1852    
1853     _popObjectsBlob(p, end);
1854     if (!m_allObjects[m_root])
1855     throw Exception("Decode Error: Missing declared root object");
1856 schoenebeck 3156
1857     m_name = _popStringBlob(p, end);
1858     m_comment = _popStringBlob(p, end);
1859     m_timeCreated = _popTimeBlob(p, end);
1860     m_timeModified = _popTimeBlob(p, end);
1861 schoenebeck 3138 }
1862    
1863 schoenebeck 3183 /** @brief Fill this archive with the given serialized raw data.
1864     *
1865     * Calling this method will decode the given raw @a data and constructs a
1866     * (non-empty) Archive object according to that given serialized @a data
1867     * stream.
1868     *
1869     * After this method returned, you may then traverse the individual
1870     * objects by starting with accessing the rootObject() for example. Finally
1871     * you might call deserialize() to restore your native C++ objects with the
1872     * content of this archive.
1873     *
1874     * @param data - the previously serialized raw data stream to be decoded
1875     * @throws Exception if the provided raw @a data uses an invalid, unknown,
1876     * incompatible or corrupt data stream or format.
1877     */
1878 schoenebeck 3138 void Archive::decode(const RawData& data) {
1879     m_rawData = data;
1880     m_allObjects.clear();
1881 schoenebeck 3150 m_isModified = false;
1882 schoenebeck 3156 m_timeCreated = m_timeModified = LIBGIG_EPOCH_TIME;
1883 schoenebeck 3138 const char* p = (const char*) &data[0];
1884     const char* end = p + data.size();
1885     if (memcmp(p, MAGIC_START, std::min(strlen(MAGIC_START), data.size())))
1886     throw Exception("Decode Error: Magic start missing!");
1887     p += strlen(MAGIC_START);
1888     _popRootBlob(p, end);
1889     }
1890    
1891 schoenebeck 3183 /** @brief Fill this archive with the given serialized raw C-buffer data.
1892     *
1893     * This method essentially works like the decode() method above, but just
1894     * uses another data type for the serialized raw data stream being passed to
1895     * this method.
1896     *
1897     * Calling this method will decode the given raw @a data and constructs a
1898     * (non-empty) Archive object according to that given serialized @a data
1899     * stream.
1900     *
1901     * After this method returned, you may then traverse the individual
1902     * objects by starting with accessing the rootObject() for example. Finally
1903     * you might call deserialize() to restore your native C++ objects with the
1904     * content of this archive.
1905     *
1906     * @param data - the previously serialized raw data stream to be decoded
1907     * @param size - size of @a data in bytes
1908     * @throws Exception if the provided raw @a data uses an invalid, unknown,
1909     * incompatible or corrupt data stream or format.
1910     */
1911 schoenebeck 3138 void Archive::decode(const uint8_t* data, size_t size) {
1912     RawData rawData;
1913     rawData.resize(size);
1914     memcpy(&rawData[0], data, size);
1915     decode(rawData);
1916     }
1917    
1918 schoenebeck 3183 /** @brief Raw data stream of this archive content.
1919     *
1920     * Call this method to get a raw data stream for the current content of this
1921     * archive, which you may use to i.e. store on disk or send vie network to
1922     * another machine for deserializing there. This method only returns a
1923     * meaningful content if this is a non-empty archive, that is if you either
1924     * serialized with this Archive object or decoded a raw data stream to this
1925     * Archive object before. If this is an empty archive instead, then this
1926     * method simply returns an empty raw data stream (of size 0) instead.
1927     *
1928     * Note that whenever you call this method, the "modified" state of this
1929     * archive will be reset to @c false.
1930     *
1931     * @see isModified()
1932     */
1933 schoenebeck 3150 const RawData& Archive::rawData() {
1934     if (m_isModified) encode();
1935     return m_rawData;
1936     }
1937    
1938 schoenebeck 3183 /** @brief Name of the encoding format used by this Archive class.
1939     *
1940     * This method returns the name of the encoding format used to encode
1941     * serialized raw data streams.
1942     */
1943 schoenebeck 3138 String Archive::rawDataFormat() const {
1944     return MAGIC_START;
1945     }
1946    
1947 schoenebeck 3183 /** @brief Whether this archive was modified.
1948     *
1949     * This method returns the current "modified" state of this archive. When
1950     * either decoding a previously serialized raw data stream or after
1951     * serializing native C++ objects to this archive the modified state will
1952     * initially be set to @c false. However whenever you are modifying the
1953     * abstract data model of this archive afterwards, for example by removing
1954     * objects from this archive by calling remove() or removeMember(), or by
1955     * altering object values for example by calling setIntValue(), then the
1956     * "modified" state of this archive will automatically be set to @c true.
1957     *
1958     * You can reset the "modified" state explicitly at any time, by calling
1959     * rawData().
1960     */
1961 schoenebeck 3150 bool Archive::isModified() const {
1962     return m_isModified;
1963     }
1964    
1965 schoenebeck 3183 /** @brief Clear content of this archive.
1966     *
1967     * Drops the entire content of this archive and thus resets this archive
1968     * back to become an empty archive.
1969     */
1970 schoenebeck 3138 void Archive::clear() {
1971     m_allObjects.clear();
1972     m_operation = OPERATION_NONE;
1973     m_root = NO_UID;
1974     m_rawData.clear();
1975 schoenebeck 3150 m_isModified = false;
1976 schoenebeck 3156 m_timeCreated = m_timeModified = LIBGIG_EPOCH_TIME;
1977 schoenebeck 3138 }
1978    
1979 schoenebeck 3183 /** @brief Optional name of this archive.
1980     *
1981     * Returns the optional name of this archive that you might have assigned
1982     * to this archive before by calling setName(). If you haven't assigned any
1983     * name to this archive before, then this method simply returns an empty
1984     * string instead.
1985     */
1986 schoenebeck 3156 String Archive::name() const {
1987     return m_name;
1988     }
1989    
1990 schoenebeck 3183 /** @brief Assign a name to this archive.
1991     *
1992     * You may optionally assign an arbitrary name to this archive. The name
1993     * will be stored along with the archive, that is it will encoded with the
1994     * resulting raw data stream, and accordingly it will be decoded from the
1995     * raw data stream later on.
1996     *
1997     * @param name - arbitrary new name for this archive
1998     */
1999 schoenebeck 3156 void Archive::setName(String name) {
2000     if (m_name == name) return;
2001     m_name = name;
2002     m_isModified = true;
2003     }
2004    
2005 schoenebeck 3183 /** @brief Optional comments for this archive.
2006     *
2007     * Returns the optional comments for this archive that you might have
2008     * assigned to this archive before by calling setComment(). If you haven't
2009     * assigned any comment to this archive before, then this method simply
2010     * returns an empty string instead.
2011     */
2012 schoenebeck 3156 String Archive::comment() const {
2013     return m_comment;
2014     }
2015    
2016 schoenebeck 3183 /** @brief Assign a comment to this archive.
2017     *
2018     * You may optionally assign arbitrary comments to this archive. The comment
2019     * will be stored along with the archive, that is it will encoded with the
2020     * resulting raw data stream, and accordingly it will be decoded from the
2021     * raw data stream later on.
2022     *
2023     * @param comment - arbitrary new comment for this archive
2024     */
2025 schoenebeck 3156 void Archive::setComment(String comment) {
2026     if (m_comment == comment) return;
2027     m_comment = comment;
2028     m_isModified = true;
2029     }
2030    
2031     static tm _convertTimeStamp(const time_t& time, time_base_t base) {
2032     tm* pTm;
2033     switch (base) {
2034     case LOCAL_TIME:
2035     pTm = localtime(&time);
2036     break;
2037     case UTC_TIME:
2038     pTm = gmtime(&time);
2039     break;
2040     default:
2041     throw Exception("Time stamp with unknown time base (" + ToString((int64_t)base) + ") requested");
2042     }
2043     if (!pTm)
2044     throw Exception("Failed assembling time stamp structure");
2045     return *pTm;
2046     }
2047    
2048 schoenebeck 3183 /** @brief Date and time when this archive was initially created.
2049     *
2050     * Returns a UTC time stamp (date and time) when this archive was initially
2051     * created.
2052     */
2053 schoenebeck 3156 time_t Archive::timeStampCreated() const {
2054     return m_timeCreated;
2055     }
2056    
2057 schoenebeck 3183 /** @brief Date and time when this archive was modified for the last time.
2058     *
2059     * Returns a UTC time stamp (date and time) when this archive was modified
2060     * for the last time.
2061     */
2062 schoenebeck 3156 time_t Archive::timeStampModified() const {
2063     return m_timeModified;
2064     }
2065    
2066 schoenebeck 3183 /** @brief Date and time when this archive was initially created.
2067     *
2068     * Returns a calendar time information representing the date and time when
2069     * this archive was initially created. The optional @a base parameter may
2070     * be used to define to which time zone the returned data and time shall be
2071     * related to.
2072     *
2073     * @param base - (optional) time zone the result shall relate to, by default
2074     * UTC time (Greenwhich Mean Time) is assumed instead
2075     */
2076 schoenebeck 3156 tm Archive::dateTimeCreated(time_base_t base) const {
2077     return _convertTimeStamp(m_timeCreated, base);
2078     }
2079    
2080 schoenebeck 3183 /** @brief Date and time when this archive was modified for the last time.
2081     *
2082     * Returns a calendar time information representing the date and time when
2083     * this archive has been modified for the last time. The optional @a base
2084     * parameter may be used to define to which time zone the returned date and
2085     * time shall be related to.
2086     *
2087     * @param base - (optional) time zone the result shall relate to, by default
2088     * UTC time (Greenwhich Mean Time) is assumed instead
2089     */
2090 schoenebeck 3156 tm Archive::dateTimeModified(time_base_t base) const {
2091     return _convertTimeStamp(m_timeModified, base);
2092     }
2093    
2094 schoenebeck 3183 /** @brief Remove a member variable from the given object.
2095     *
2096     * Removes the member variable @a member from its containing object
2097     * @a parent and sets the modified state of this archive to @c true.
2098     * If the given @a parent object does not contain the given @a member then
2099     * this method does nothing.
2100     *
2101     * This method provides a means of "partial" deserialization. By removing
2102     * either objects or members from this archive before calling deserialize(),
2103     * only the remaining objects and remaining members will be restored by this
2104     * framework, all other data of your C++ classes remain untouched.
2105     *
2106     * @param parent - Object which contains @a member
2107     * @param member - member to be removed
2108     * @see isModified() for details about the modified state.
2109     * @see Object for more details about the overall object reflection concept.
2110     */
2111 schoenebeck 3153 void Archive::removeMember(Object& parent, const Member& member) {
2112     parent.remove(member);
2113     m_isModified = true;
2114     }
2115    
2116 schoenebeck 3183 /** @brief Remove an object from this archive.
2117     *
2118     * Removes the object @obj from this archive and sets the modified state of
2119     * this archive to @c true. If the passed object is either invalid, or does
2120     * not exist in this archive, then this method does nothing.
2121     *
2122     * This method provides a means of "partial" deserialization. By removing
2123     * either objects or members from this archive before calling deserialize(),
2124     * only the remaining objects and remaining members will be restored by this
2125     * framework, all other data of your C++ classes remain untouched.
2126     *
2127     * @param obj - the object to be removed from this archive
2128     * @see isModified() for details about the modified state.
2129     * @see Object for more details about the overall object reflection concept.
2130     */
2131 schoenebeck 3138 void Archive::remove(const Object& obj) {
2132 schoenebeck 3153 //FIXME: Should traverse from root object and remove all members associated with this object
2133 schoenebeck 3138 if (!obj.uid()) return;
2134     m_allObjects.erase(obj.uid());
2135 schoenebeck 3150 m_isModified = true;
2136 schoenebeck 3138 }
2137    
2138 schoenebeck 3183 /** @brief Access object by its unique identifier.
2139     *
2140     * Returns the object of this archive with the given unique identifier
2141     * @a uid. If the given @a uid is invalid, or if this archive does not
2142     * contain an object with the given unique identifier, then this method
2143     * returns an invalid object instead.
2144     *
2145     * @param uid - unique identifier of sought object
2146     * @see Object for more details about the overall object reflection concept.
2147     * @see Object::isValid() for valid/invalid objects
2148     */
2149 schoenebeck 3138 Object& Archive::objectByUID(const UID& uid) {
2150     return m_allObjects[uid];
2151     }
2152    
2153 schoenebeck 3183 /** @brief Set the current version for the given object.
2154     *
2155     * Essentially behaves like above's setVersion() method, it just uses the
2156     * abstract reflection data type instead for the respective @a object being
2157     * passed to this method. Refer to above's setVersion() documentation about
2158     * the precise behavior details of setVersion().
2159     *
2160     * @param object - object to set the current version for
2161     * @param v - new current version to set for @a object
2162     */
2163 schoenebeck 3182 void Archive::setVersion(Object& object, Version v) {
2164     if (!object) return;
2165     object.setVersion(v);
2166     m_isModified = true;
2167     }
2168    
2169 schoenebeck 3183 /** @brief Set the minimum version for the given object.
2170     *
2171     * Essentially behaves like above's setMinVersion() method, it just uses the
2172     * abstract reflection data type instead for the respective @a object being
2173     * passed to this method. Refer to above's setMinVersion() documentation
2174     * about the precise behavior details of setMinVersion().
2175     *
2176     * @param object - object to set the minimum version for
2177     * @param v - new minimum version to set for @a object
2178     */
2179 schoenebeck 3182 void Archive::setMinVersion(Object& object, Version v) {
2180     if (!object) return;
2181     object.setMinVersion(v);
2182     m_isModified = true;
2183     }
2184    
2185 schoenebeck 3183 /** @brief Set new value for given @c enum object.
2186     *
2187     * Sets the new @a value to the given @c enum @a object.
2188     *
2189     * @param object - the @c enum object to be changed
2190     * @param value - the new value to be assigned to the @a object
2191     * @throws Exception if @a object is not an @c enum type.
2192     */
2193 schoenebeck 3150 void Archive::setEnumValue(Object& object, uint64_t value) {
2194     if (!object) return;
2195     if (!object.type().isEnum())
2196     throw Exception("Not an enum data type");
2197     Object* pObject = &object;
2198     if (object.type().isPointer()) {
2199     Object& obj = objectByUID(object.uid(1));
2200     if (!obj) return;
2201     pObject = &obj;
2202     }
2203     const int nativeEnumSize = sizeof(enum operation_t);
2204     DataType& type = const_cast<DataType&>( pObject->type() );
2205     // original serializer ("sender") might have had a different word size
2206     // than this machine, adjust type object in this case
2207     if (type.size() != nativeEnumSize) {
2208     type.m_size = nativeEnumSize;
2209     }
2210     pObject->m_data.resize(type.size());
2211     void* ptr = &pObject->m_data[0];
2212     if (type.size() == 1)
2213     *(uint8_t*)ptr = (uint8_t)value;
2214     else if (type.size() == 2)
2215     *(uint16_t*)ptr = (uint16_t)value;
2216     else if (type.size() == 4)
2217     *(uint32_t*)ptr = (uint32_t)value;
2218     else if (type.size() == 8)
2219     *(uint64_t*)ptr = (uint64_t)value;
2220     else
2221     assert(false /* unknown enum type size */);
2222     m_isModified = true;
2223     }
2224    
2225 schoenebeck 3183 /** @brief Set new integer value for given integer object.
2226     *
2227     * Sets the new integer @a value to the given integer @a object. Currently
2228     * this framework handles any integer data type up to 64 bit. For larger
2229     * integer types an assertion failure will be raised.
2230     *
2231     * @param object - the integer object to be changed
2232     * @param value - the new value to be assigned to the @a object
2233     * @throws Exception if @a object is not an integer type.
2234     */
2235 schoenebeck 3150 void Archive::setIntValue(Object& object, int64_t value) {
2236     if (!object) return;
2237     if (!object.type().isInteger())
2238     throw Exception("Not an integer data type");
2239     Object* pObject = &object;
2240     if (object.type().isPointer()) {
2241     Object& obj = objectByUID(object.uid(1));
2242     if (!obj) return;
2243     pObject = &obj;
2244     }
2245     const DataType& type = pObject->type();
2246     pObject->m_data.resize(type.size());
2247     void* ptr = &pObject->m_data[0];
2248     if (type.isSigned()) {
2249     if (type.size() == 1)
2250     *(int8_t*)ptr = (int8_t)value;
2251     else if (type.size() == 2)
2252     *(int16_t*)ptr = (int16_t)value;
2253     else if (type.size() == 4)
2254     *(int32_t*)ptr = (int32_t)value;
2255     else if (type.size() == 8)
2256     *(int64_t*)ptr = (int64_t)value;
2257     else
2258     assert(false /* unknown signed int type size */);
2259     } else {
2260     if (type.size() == 1)
2261     *(uint8_t*)ptr = (uint8_t)value;
2262     else if (type.size() == 2)
2263     *(uint16_t*)ptr = (uint16_t)value;
2264     else if (type.size() == 4)
2265     *(uint32_t*)ptr = (uint32_t)value;
2266     else if (type.size() == 8)
2267     *(uint64_t*)ptr = (uint64_t)value;
2268     else
2269     assert(false /* unknown unsigned int type size */);
2270     }
2271     m_isModified = true;
2272     }
2273    
2274 schoenebeck 3183 /** @brief Set new floating point value for given floating point object.
2275     *
2276     * Sets the new floating point @a value to the given floating point
2277     * @a object. Currently this framework supports single precision @c float
2278     * and double precision @c double floating point data types. For all other
2279     * floating point types this method will raise an assertion failure.
2280     *
2281     * @param object - the floating point object to be changed
2282     * @param value - the new value to be assigned to the @a object
2283     * @throws Exception if @a object is not a floating point based type.
2284     */
2285 schoenebeck 3150 void Archive::setRealValue(Object& object, double value) {
2286     if (!object) return;
2287     if (!object.type().isReal())
2288     throw Exception("Not a real data type");
2289     Object* pObject = &object;
2290     if (object.type().isPointer()) {
2291     Object& obj = objectByUID(object.uid(1));
2292     if (!obj) return;
2293     pObject = &obj;
2294     }
2295     const DataType& type = pObject->type();
2296     pObject->m_data.resize(type.size());
2297     void* ptr = &pObject->m_data[0];
2298     if (type.size() == sizeof(float))
2299     *(float*)ptr = (float)value;
2300     else if (type.size() == sizeof(double))
2301     *(double*)ptr = (double)value;
2302     else
2303     assert(false /* unknown real type size */);
2304     m_isModified = true;
2305     }
2306    
2307 schoenebeck 3183 /** @brief Set new boolean value for given boolean object.
2308     *
2309     * Sets the new boolean @a value to the given boolean @a object.
2310     *
2311     * @param object - the boolean object to be changed
2312     * @param value - the new value to be assigned to the @a object
2313     * @throws Exception if @a object is not a boolean type.
2314     */
2315 schoenebeck 3150 void Archive::setBoolValue(Object& object, bool value) {
2316     if (!object) return;
2317     if (!object.type().isBool())
2318     throw Exception("Not a bool data type");
2319     Object* pObject = &object;
2320     if (object.type().isPointer()) {
2321     Object& obj = objectByUID(object.uid(1));
2322     if (!obj) return;
2323     pObject = &obj;
2324     }
2325     const DataType& type = pObject->type();
2326     pObject->m_data.resize(type.size());
2327     bool* ptr = (bool*)&pObject->m_data[0];
2328     *ptr = value;
2329     m_isModified = true;
2330     }
2331    
2332 schoenebeck 3771 /** @brief Set new textual string for given String object.
2333     *
2334     * Sets the new textual string @a value to the given String @a object.
2335     *
2336     * @param object - the String object to be changed
2337     * @param value - the new textual string to be assigned to the @a object
2338     * @throws Exception if @a object is not a String type.
2339     */
2340     void Archive::setStringValue(Object& object, String value) {
2341     if (!object) return;
2342     if (!object.type().isString())
2343     throw Exception("Not a String data type");
2344     Object* pObject = &object;
2345     if (object.type().isPointer()) {
2346     Object& obj = objectByUID(object.uid(1));
2347     if (!obj) return;
2348     pObject = &obj;
2349     }
2350     pObject->m_data.resize(value.length() + 1);
2351     char* ptr = (char*) &pObject->m_data[0];
2352     strcpy(ptr, &value[0]);
2353     m_isModified = true;
2354     }
2355    
2356 schoenebeck 3183 /** @brief Automatically cast and assign appropriate value to object.
2357     *
2358     * This method automatically converts the given @a value from textual string
2359     * representation into the appropriate data format of the requested
2360     * @a object. So this method is a convenient way to change values of objects
2361     * in this archive with your applications in automated way, i.e. for
2362     * implementing an editor where the user is able to edit values of objects
2363     * in this archive by entering the values as text with a keyboard.
2364     *
2365     * @throws Exception if the passed @a object is not a fundamental, primitive
2366     * data type or if the provided textual value cannot be converted
2367     * into an appropriate value for the requested object.
2368     */
2369 schoenebeck 3150 void Archive::setAutoValue(Object& object, String value) {
2370     if (!object) return;
2371     const DataType& type = object.type();
2372     if (type.isInteger())
2373     setIntValue(object, atoll(value.c_str()));
2374     else if (type.isReal())
2375     setRealValue(object, atof(value.c_str()));
2376 schoenebeck 3185 else if (type.isBool()) {
2377     String val = toLowerCase(value);
2378     if (val == "true" || val == "yes" || val == "1")
2379     setBoolValue(object, true);
2380     else if (val == "false" || val == "no" || val == "0")
2381     setBoolValue(object, false);
2382     else
2383     setBoolValue(object, atof(value.c_str()));
2384 schoenebeck 3771 } else if (type.isString())
2385     setStringValue(object, value);
2386     else if (type.isEnum())
2387 schoenebeck 3150 setEnumValue(object, atoll(value.c_str()));
2388     else
2389     throw Exception("Not a primitive data type");
2390     }
2391    
2392 schoenebeck 3183 /** @brief Get value of object as string.
2393     *
2394     * Converts the current value of the given @a object into a textual string
2395     * and returns that string.
2396     *
2397     * @param object - object whose value shall be retrieved
2398     * @throws Exception if the given object is either invalid, or if the object
2399     * is not a fundamental, primitive data type.
2400     */
2401 schoenebeck 3150 String Archive::valueAsString(const Object& object) {
2402     if (!object)
2403     throw Exception("Invalid object");
2404     if (object.type().isClass())
2405     throw Exception("Object is class type");
2406     const Object* pObject = &object;
2407     if (object.type().isPointer()) {
2408     const Object& obj = objectByUID(object.uid(1));
2409     if (!obj) return "";
2410     pObject = &obj;
2411     }
2412     return _primitiveObjectValueToString(*pObject);
2413     }
2414    
2415 schoenebeck 3183 /** @brief Get integer value of object.
2416     *
2417     * Returns the current integer value of the requested integer @a object or
2418     * @c enum object.
2419     *
2420     * @param object - object whose value shall be retrieved
2421     * @throws Exception if the given object is either invalid, or if the object
2422     * is neither an integer nor @c enum data type.
2423     */
2424 schoenebeck 3169 int64_t Archive::valueAsInt(const Object& object) {
2425     if (!object)
2426     throw Exception("Invalid object");
2427     if (!object.type().isInteger() && !object.type().isEnum())
2428     throw Exception("Object is neither an integer nor an enum");
2429     const Object* pObject = &object;
2430     if (object.type().isPointer()) {
2431     const Object& obj = objectByUID(object.uid(1));
2432     if (!obj) return 0;
2433     pObject = &obj;
2434     }
2435     return _primitiveObjectValueToNumber<int64_t>(*pObject);
2436     }
2437    
2438 schoenebeck 3183 /** @brief Get floating point value of object.
2439     *
2440     * Returns the current floating point value of the requested floating point
2441     * @a object.
2442     *
2443     * @param object - object whose value shall be retrieved
2444     * @throws Exception if the given object is either invalid, or if the object
2445     * is not a floating point based type.
2446     */
2447 schoenebeck 3169 double Archive::valueAsReal(const Object& object) {
2448     if (!object)
2449     throw Exception("Invalid object");
2450     if (!object.type().isReal())
2451     throw Exception("Object is not an real type");
2452     const Object* pObject = &object;
2453     if (object.type().isPointer()) {
2454     const Object& obj = objectByUID(object.uid(1));
2455     if (!obj) return 0;
2456     pObject = &obj;
2457     }
2458     return _primitiveObjectValueToNumber<double>(*pObject);
2459     }
2460    
2461 schoenebeck 3183 /** @brief Get boolean value of object.
2462     *
2463     * Returns the current boolean value of the requested boolean @a object.
2464     *
2465     * @param object - object whose value shall be retrieved
2466     * @throws Exception if the given object is either invalid, or if the object
2467     * is not a boolean data type.
2468     */
2469 schoenebeck 3169 bool Archive::valueAsBool(const Object& object) {
2470     if (!object)
2471     throw Exception("Invalid object");
2472     if (!object.type().isBool())
2473     throw Exception("Object is not a bool");
2474     const Object* pObject = &object;
2475     if (object.type().isPointer()) {
2476     const Object& obj = objectByUID(object.uid(1));
2477     if (!obj) return 0;
2478     pObject = &obj;
2479     }
2480     return _primitiveObjectValueToNumber<bool>(*pObject);
2481     }
2482    
2483 schoenebeck 3774 Archive::operation_t Archive::operation() const {
2484     return m_operation;
2485     }
2486    
2487 schoenebeck 3138 // *************** Archive::Syncer ***************
2488     // *
2489    
2490     Archive::Syncer::Syncer(Archive& dst, Archive& src)
2491     : m_dst(dst), m_src(src)
2492     {
2493     const Object srcRootObj = src.rootObject();
2494     const Object dstRootObj = dst.rootObject();
2495     if (!srcRootObj)
2496     throw Exception("No source root object!");
2497     if (!dstRootObj)
2498     throw Exception("Expected destination root object not found!");
2499     syncObject(dstRootObj, srcRootObj);
2500     }
2501    
2502     void Archive::Syncer::syncPrimitive(const Object& dstObj, const Object& srcObj) {
2503     assert(srcObj.rawData().size() == dstObj.type().size());
2504     void* pDst = (void*)dstObj.uid().id;
2505     memcpy(pDst, &srcObj.rawData()[0], dstObj.type().size());
2506     }
2507    
2508 schoenebeck 3771 void Archive::Syncer::syncString(const Object& dstObj, const Object& srcObj) {
2509     assert(dstObj.type().isString());
2510     assert(dstObj.type() == srcObj.type());
2511     String* pDst = (String*)(void*)dstObj.uid().id;
2512     *pDst = (String) (const char*) &srcObj.rawData()[0];
2513     }
2514    
2515 schoenebeck 3775 void Archive::Syncer::syncArray(const Object& dstObj, const Object& srcObj) {
2516     assert(dstObj.type().isArray());
2517     assert(dstObj.type() == srcObj.type());
2518     dstObj.m_sync(const_cast<Object&>(dstObj), srcObj, this);
2519     }
2520    
2521 schoenebeck 3776 void Archive::Syncer::syncSet(const Object& dstObj, const Object& srcObj) {
2522     assert(dstObj.type().isSet());
2523     assert(dstObj.type() == srcObj.type());
2524     dstObj.m_sync(const_cast<Object&>(dstObj), srcObj, this);
2525     }
2526    
2527 schoenebeck 3777 void Archive::Syncer::syncMap(const Object& dstObj, const Object& srcObj) {
2528     assert(dstObj.type().isMap());
2529     assert(dstObj.type() == srcObj.type());
2530     dstObj.m_sync(const_cast<Object&>(dstObj), srcObj, this);
2531     }
2532    
2533 schoenebeck 3138 void Archive::Syncer::syncPointer(const Object& dstObj, const Object& srcObj) {
2534     assert(dstObj.type().isPointer());
2535     assert(dstObj.type() == srcObj.type());
2536     const Object& pointedDstObject = m_dst.m_allObjects[dstObj.uid(1)];
2537     const Object& pointedSrcObject = m_src.m_allObjects[srcObj.uid(1)];
2538     syncObject(pointedDstObject, pointedSrcObject);
2539     }
2540    
2541     void Archive::Syncer::syncObject(const Object& dstObj, const Object& srcObj) {
2542     if (!dstObj || !srcObj) return; // end of recursion
2543     if (!dstObj.isVersionCompatibleTo(srcObj))
2544     throw Exception("Version incompatible (destination version " +
2545     ToString(dstObj.version()) + " [min. version " +
2546     ToString(dstObj.minVersion()) + "], source version " +
2547     ToString(srcObj.version()) + " [min. version " +
2548     ToString(srcObj.minVersion()) + "])");
2549     if (dstObj.type() != srcObj.type())
2550     throw Exception("Incompatible data structure type (destination type " +
2551     dstObj.type().asLongDescr() + " vs. source type " +
2552     srcObj.type().asLongDescr() + ")");
2553    
2554     // prevent syncing this object again, and thus also prevent endless
2555     // loop on data structures with cyclic relations
2556     m_dst.m_allObjects.erase(dstObj.uid());
2557    
2558     if (dstObj.type().isPrimitive() && !dstObj.type().isPointer()) {
2559 schoenebeck 3771 if (dstObj.type().isString())
2560     syncString(dstObj, srcObj);
2561     else
2562     syncPrimitive(dstObj, srcObj);
2563 schoenebeck 3138 return; // end of recursion
2564     }
2565    
2566 schoenebeck 3775 if (dstObj.type().isArray()) {
2567     syncArray(dstObj, srcObj);
2568     return;
2569     }
2570    
2571 schoenebeck 3776 if (dstObj.type().isSet()) {
2572     syncSet(dstObj, srcObj);
2573     return;
2574     }
2575    
2576 schoenebeck 3777 if (dstObj.type().isMap()) {
2577     syncMap(dstObj, srcObj);
2578     return;
2579     }
2580    
2581 schoenebeck 3138 if (dstObj.type().isPointer()) {
2582     syncPointer(dstObj, srcObj);
2583     return;
2584     }
2585    
2586     assert(dstObj.type().isClass());
2587     for (int iMember = 0; iMember < srcObj.members().size(); ++iMember) {
2588     const Member& srcMember = srcObj.members()[iMember];
2589     Member dstMember = dstMemberMatching(dstObj, srcObj, srcMember);
2590     if (!dstMember)
2591     throw Exception("Expected member missing in destination object");
2592     syncMember(dstMember, srcMember);
2593     }
2594     }
2595    
2596     Member Archive::Syncer::dstMemberMatching(const Object& dstObj, const Object& srcObj, const Member& srcMember) {
2597     Member dstMember = dstObj.memberNamed(srcMember.name());
2598     if (dstMember)
2599     return (dstMember.type() == srcMember.type()) ? dstMember : Member();
2600     std::vector<Member> members = dstObj.membersOfType(srcMember.type());
2601     if (members.size() <= 0)
2602     return Member();
2603     if (members.size() == 1)
2604     return members[0];
2605     for (int i = 0; i < members.size(); ++i)
2606     if (members[i].offset() == srcMember.offset())
2607     return members[i];
2608     const int srcSeqNr = srcObj.sequenceIndexOf(srcMember);
2609     assert(srcSeqNr >= 0); // should never happen, otherwise there is a bug
2610     for (int i = 0; i < members.size(); ++i) {
2611     const int dstSeqNr = dstObj.sequenceIndexOf(members[i]);
2612     if (dstSeqNr == srcSeqNr)
2613     return members[i];
2614     }
2615     return Member(); // give up!
2616     }
2617    
2618     void Archive::Syncer::syncMember(const Member& dstMember, const Member& srcMember) {
2619     assert(dstMember && srcMember);
2620     assert(dstMember.type() == srcMember.type());
2621     const Object dstObj = m_dst.m_allObjects[dstMember.uid()];
2622     const Object srcObj = m_src.m_allObjects[srcMember.uid()];
2623     syncObject(dstObj, srcObj);
2624     }
2625    
2626     // *************** Exception ***************
2627     // *
2628    
2629 schoenebeck 3198 Exception::Exception() {
2630     }
2631    
2632     Exception::Exception(String format, ...) {
2633     va_list arg;
2634     va_start(arg, format);
2635     Message = assemble(format, arg);
2636     va_end(arg);
2637     }
2638    
2639     Exception::Exception(String format, va_list arg) {
2640     Message = assemble(format, arg);
2641     }
2642    
2643 schoenebeck 3183 /** @brief Print exception message to stdout.
2644     *
2645     * Prints the message of this Exception to the currently defined standard
2646     * output (that is to the terminal console for example).
2647     */
2648 schoenebeck 3138 void Exception::PrintMessage() {
2649     std::cout << "Serialization::Exception: " << Message << std::endl;
2650     }
2651    
2652 schoenebeck 3198 String Exception::assemble(String format, va_list arg) {
2653     char* buf = NULL;
2654     vasprintf(&buf, format.c_str(), arg);
2655     String s = buf;
2656     free(buf);
2657     return s;
2658     }
2659    
2660 schoenebeck 3138 } // namespace Serialization

  ViewVC Help
Powered by ViewVC