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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3476 - (hide annotations) (download)
Wed Feb 20 19:12:49 2019 UTC (5 years, 1 month ago) by schoenebeck
File size: 92253 byte(s)
* Added MSVC build support
  (anonymous patch from mailing list).
* Introduced CMake build support (yet constrained for building with MSVC)
  (anonymous patch from mailing list).
* Bumped version (4.1.0.svn12).

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

  ViewVC Help
Powered by ViewVC