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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3454 - (show annotations) (download)
Sun Jan 27 10:07:48 2019 UTC (4 months, 3 weeks ago) by persson
File size: 91763 byte(s)
* src/Serialization.cpp: Fixed comparision logic bug

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

  ViewVC Help
Powered by ViewVC