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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3186 - (hide annotations) (download)
Wed May 17 15:55:18 2017 UTC (6 years, 10 months ago) by schoenebeck
File size: 90734 byte(s)
- Fixed compile error.

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

  ViewVC Help
Powered by ViewVC