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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3480 - (show annotations) (download)
Fri Feb 22 11:17:58 2019 UTC (5 years, 1 month ago) by schoenebeck
File size: 92447 byte(s)
- Just added a comment about UnDecorateSymbolName() not being thread safe.

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

  ViewVC Help
Powered by ViewVC