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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3770 - (hide annotations) (download)
Sun May 17 14:05:08 2020 UTC (3 years, 10 months ago) by schoenebeck
File size: 92442 byte(s)
* Serialization.cpp: Fixed broken Archive(RawData) constructor which
  always threw an Exception.

* Bumped version (4.2.0.svn9).

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

  ViewVC Help
Powered by ViewVC