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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3333 - (hide annotations) (download)
Sat Jul 29 09:57:08 2017 UTC (6 years, 8 months ago) by schoenebeck
File size: 91560 byte(s)
* Serialization::DataType fix: Retain backward compatiblity to older
  versions of native C++ classes/strcts.
* Bumped version (4.0.0.svn30).

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

  ViewVC Help
Powered by ViewVC