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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3333 - (show annotations) (download)
Sat Jul 29 09:57:08 2017 UTC (6 years, 8 months ago) by schoenebeck
File size: 91560 byte(s)
* Serialization::DataType fix: Retain backward compatiblity to older
  versions of native C++ classes/strcts.
* Bumped version (4.0.0.svn30).

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

  ViewVC Help
Powered by ViewVC