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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3775 - (show annotations) (download)
Tue May 19 15:23:11 2020 UTC (3 years, 11 months ago) by schoenebeck
File size: 98871 byte(s)
Serialization.cpp/.h: Added built-in support for C++ Array<> objects

* Introduced out of the box support for serialising / deserialising
  variables of C++ Array<> data types (a.k.a. std::vector from the STL).

* Member offsets are now signed and for (newly added support of) member
  variables on the heap -1 is always used as offset instead.

* Bumped version (4.2.0.svn13).

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

  ViewVC Help
Powered by ViewVC