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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3464 - (hide annotations) (download)
Sun Feb 10 20:25:05 2019 UTC (5 years, 1 month ago) by schoenebeck
File size: 91813 byte(s)
* Serialization.cpp: Fixed memory leak in DataType::customTypeName().
* Bumped version (4.1.0.svn10).

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

  ViewVC Help
Powered by ViewVC