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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3771 - (show annotations) (download)
Sun May 17 17:14:31 2020 UTC (3 years, 11 months ago) by schoenebeck
File size: 97155 byte(s)
Serialization.cpp/.h: Added support for "String" data type.

* Added method DataType::isString().

* Added method Object::setStringValue().

* Implemented built-in detection and serialization / deserialization of
  C++ String objects (a.k.a. std::string from the STL).

* Bumped Srx format version to 1.1.

* Bumped version (4.2.0.svn10).

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

  ViewVC Help
Powered by ViewVC