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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3774 - (show annotations) (download)
Tue May 19 14:55:48 2020 UTC (3 years, 11 months ago) by schoenebeck
File size: 97224 byte(s)
* Serialization.cpp/.h: Added new method Archive::operation() which
  allows applications to distinguish between serialization vs.
  deserialization in their serialize() method implementations.

* Bumped version (4.2.0.svn12).

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

  ViewVC Help
Powered by ViewVC