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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3774 - (hide annotations) (download)
Tue May 19 14:55:48 2020 UTC (3 years, 11 months ago) by schoenebeck
File size: 97224 byte(s)
* Serialization.cpp/.h: Added new method Archive::operation() which
  allows applications to distinguish between serialization vs.
  deserialization in their serialize() method implementations.

* Bumped version (4.2.0.svn12).

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

  ViewVC Help
Powered by ViewVC