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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3771 - (hide annotations) (download)
Sun May 17 17:14:31 2020 UTC (3 years, 10 months ago) by schoenebeck
File size: 97155 byte(s)
Serialization.cpp/.h: Added support for "String" data type.

* Added method DataType::isString().

* Added method Object::setStringValue().

* Implemented built-in detection and serialization / deserialization of
  C++ String objects (a.k.a. std::string from the STL).

* Bumped Srx format version to 1.1.

* Bumped version (4.2.0.svn10).

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

  ViewVC Help
Powered by ViewVC