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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3464 - (show annotations) (download)
Sun Feb 10 20:25:05 2019 UTC (5 years, 1 month ago) by schoenebeck
File size: 91813 byte(s)
* Serialization.cpp: Fixed memory leak in DataType::customTypeName().
* Bumped version (4.1.0.svn10).

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

  ViewVC Help
Powered by ViewVC