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

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

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

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

Legend:
Removed from v.3139  
changed lines
  Added in v.3776

  ViewVC Help
Powered by ViewVC