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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3778 - (show annotations) (download)
Sun May 24 11:20:11 2020 UTC (4 months ago) by schoenebeck
File size: 105424 byte(s)
Serialization.cpp: Fix compatibility issues with new 2nd custom data type

* When encoding serialised data stream: always write 2nd custom data type
  name to data type blob, and append it actually to the end of the data
  type blob (not somewhere in the middle), no matter if a 2nd custom type
  is actually present (i.e. empty string) in object's data type or not.

* When decoding a serialised data stream: check the size of the data type
  blob, if the blob size is longer than a Srx v1.0 format data type blob,
  then assume there is a 2nd custom data type name, so read it (it might
  be an empty string if 2nd custom data type was not present on encoding
  side's object).

* Bumped version (4.2.0.svn16).

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

  ViewVC Help
Powered by ViewVC