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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3776 - (show annotations) (download)
Sat May 23 19:26:07 2020 UTC (3 years, 10 months ago) by schoenebeck
File size: 102909 byte(s)
* Serialization.cpp/.h: Added built-in support for C++ Set<>
  objects (a.k.a. std::set from the STL).

* Bumped version (4.2.0.svn14).

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

  ViewVC Help
Powered by ViewVC