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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3777 - (show annotations) (download)
Sat May 23 19:55:32 2020 UTC (3 years, 10 months ago) by schoenebeck
File size: 105325 byte(s)
Serialization.cpp/.h: Added built-in support for C++ Map<> objects

* Introduced out of the box support for serialising / deserialising
  variables of C++ Map<> data types (a.k.a. std::map from the STL).

* DataType: Added optional 2nd custom type name.

* Bumped version (4.2.0.svn15).

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

  ViewVC Help
Powered by ViewVC