1 |
/*************************************************************************** |
/*************************************************************************** |
2 |
* * |
* * |
3 |
* Copyright (C) 2017 Christian Schoenebeck * |
* Copyright (C) 2017-2020 Christian Schoenebeck * |
4 |
* <cuse@users.sourceforge.net> * |
* <cuse@users.sourceforge.net> * |
5 |
* * |
* * |
6 |
* This library is part of libgig. * |
* This library is part of libgig. * |
7 |
* * |
* * |
21 |
* MA 02111-1307 USA * |
* 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" |
#include "Serialization.h" |
29 |
|
|
30 |
#include <iostream> |
#include <iostream> |
|
#include <assert.h> |
|
31 |
#include <string.h> // for memcpy() |
#include <string.h> // for memcpy() |
32 |
#include <stdlib.h> // for atof() |
#include <stdlib.h> // for atof() |
33 |
#include <cxxabi.h> |
#ifdef _MSC_VER |
34 |
|
# include <windows.h> |
35 |
|
# include <dbghelp.h> |
36 |
|
#else |
37 |
|
# include <cxxabi.h> |
38 |
|
#endif |
39 |
#include "helper.h" |
#include "helper.h" |
40 |
|
|
41 |
#define LIBGIG_EPOCH_TIME ((time_t)0) |
#define LIBGIG_EPOCH_TIME ((time_t)0) |
70 |
// *************** DataType *************** |
// *************** DataType *************** |
71 |
// * |
// * |
72 |
|
|
73 |
/** @brief Default constructor. |
/** @brief Default constructor (as "invalid" DataType). |
74 |
* |
* |
75 |
* Initializes a DataType object as being an "invalid" DataType object. |
* Initializes a DataType object as being an "invalid" DataType object. |
76 |
* Thus calling isValid(), after creating a DataType object with this |
* Thus calling isValid(), after creating a DataType object with this |
84 |
m_isPointer = false; |
m_isPointer = false; |
85 |
} |
} |
86 |
|
|
87 |
DataType::DataType(bool isPointer, int size, String baseType, String customType) { |
/** @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; |
m_size = size; |
118 |
m_isPointer = isPointer; |
m_isPointer = isPointer; |
119 |
m_baseTypeName = baseType; |
m_baseTypeName = baseType; |
120 |
m_customTypeName = customType; |
m_customTypeName = customType1; |
121 |
|
m_customTypeName2 = customType2; |
122 |
} |
} |
123 |
|
|
124 |
/** @brief Check if this is a valid DataType object. |
/** @brief Check if this is a valid DataType object. |
150 |
* (this DataType instance is reflecting) is a C/C++ @c struct or @c class |
* (this DataType instance is reflecting) is a C/C++ @c struct or @c class |
151 |
* type. |
* 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: |
* Note that in the following example: |
159 |
* @code |
* @code |
160 |
* struct Foo { |
* struct Foo { |
179 |
* (this DataType instance is reflecting) is a primitive, fundamental C/C++ |
* (this DataType instance is reflecting) is a primitive, fundamental C/C++ |
180 |
* data type. Those are fundamental data types which are already predefined |
* 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, |
* by the C/C++ language, for example: @c char, @c int, @c float, @c double, |
182 |
* @c bool, but also @b any pointer types like @c int*, @c double**, but |
* @c bool, but also @c String objects and @b any pointer types like |
183 |
* including pointers to user defined types like: |
* @c int*, @c double**, but including pointers to user defined types like: |
184 |
* @code |
* @code |
185 |
* struct Foo { |
* struct Foo { |
186 |
* int a; |
* int a; |
194 |
* @see isPointer() |
* @see isPointer() |
195 |
*/ |
*/ |
196 |
bool DataType::isPrimitive() const { |
bool DataType::isPrimitive() const { |
197 |
return !isClass(); |
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. |
/** @brief Whether this is an integer C/C++ data type. |
276 |
return m_baseTypeName == "enum"; |
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. |
/** @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 |
* Returns @c true if the respective native C/C++ object, member or variable |
354 |
* characteristic is compared as well. So a @c double type and @c double* |
* 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 |
* type are also considered to be not equal data types and hence this method |
356 |
* would return @c false. |
* 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 { |
bool DataType::operator==(const DataType& other) const { |
364 |
return m_baseTypeName == other.m_baseTypeName && |
return m_baseTypeName == other.m_baseTypeName && |
365 |
m_customTypeName == other.m_customTypeName && |
m_customTypeName == other.m_customTypeName && |
366 |
m_size == other.m_size && |
m_customTypeName2 == other.m_customTypeName2 && |
367 |
|
(m_size == other.m_size || (isClass() && other.isClass())) && |
368 |
m_isPointer == other.m_isPointer; |
m_isPointer == other.m_isPointer; |
369 |
} |
} |
370 |
|
|
391 |
bool DataType::operator<(const DataType& other) const { |
bool DataType::operator<(const DataType& other) const { |
392 |
return m_baseTypeName < other.m_baseTypeName || |
return m_baseTypeName < other.m_baseTypeName || |
393 |
(m_baseTypeName == other.m_baseTypeName && |
(m_baseTypeName == other.m_baseTypeName && |
394 |
m_customTypeName < other.m_customTypeName || |
(m_customTypeName < other.m_customTypeName || |
395 |
(m_customTypeName == other.m_customTypeName && |
(m_customTypeName == other.m_customTypeName && |
396 |
m_size < other.m_size || |
(m_customTypeName2 < other.m_customTypeName2 || |
397 |
|
(m_customTypeName2 == other.m_customTypeName2 && |
398 |
|
(m_size < other.m_size || |
399 |
(m_size == other.m_size && |
(m_size == other.m_size && |
400 |
m_isPointer < other.m_isPointer))); |
m_isPointer < other.m_isPointer))))))); |
401 |
} |
} |
402 |
|
|
403 |
/** @brief Greater than comparison. |
/** @brief Greater than comparison. |
433 |
String s = m_baseTypeName; |
String s = m_baseTypeName; |
434 |
if (!m_customTypeName.empty()) |
if (!m_customTypeName.empty()) |
435 |
s += " " + customTypeName(true); |
s += " " + customTypeName(true); |
436 |
|
if (!m_customTypeName2.empty()) |
437 |
|
s += " " + customTypeName2(true); |
438 |
if (isPointer()) |
if (isPointer()) |
439 |
s += " pointer"; |
s += " pointer"; |
440 |
return s; |
return s; |
467 |
* @endcode |
* @endcode |
468 |
* this method would return for both @c i and @c pi the string @c "uint64" ! |
* this method would return for both @c i and @c pi the string @c "uint64" ! |
469 |
* |
* |
470 |
* @see isPointer(), customTypeName() |
* @see isPointer(), customTypeName(), customTypeName2() |
471 |
*/ |
*/ |
472 |
String DataType::baseTypeName() const { |
String DataType::baseTypeName() const { |
473 |
return m_baseTypeName; |
return m_baseTypeName; |
474 |
} |
} |
475 |
|
|
476 |
/** @brief The user defined C/C++ data type name of this data type. |
static String _demangleTypeName(const char* name) { |
477 |
* |
#ifdef _MSC_VER |
478 |
* Call this method on user defined C/C++ data types like @c enum, @c struct |
const size_t MAXLENGTH = 1024; |
479 |
* and @c class types to retrieve the user defined type name portion of |
char result[MAXLENGTH]; |
480 |
* those data types. Note that this method is only intended for such user |
|
481 |
* defined data types. For all fundamental, primitive data types (like i.e. |
//FIXME: calling UnDecorateSymbolName() is not thread safe! |
482 |
* @c int) this method returns an empty string instead. |
//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 |
* 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 |
* you define whether you are interested in the raw C++ type name or rather |
527 |
* In the latter example @c customTypeName(true) would return for both |
* 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. |
* @c foo and @c pFoo the string @c "Foo" as return value of this method. |
529 |
* |
* |
530 |
* @see isPointer(), baseTypeName() |
* @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 { |
String DataType::customTypeName(bool demangle) const { |
536 |
if (!demangle) return m_customTypeName; |
if (!demangle) return m_customTypeName; |
537 |
int status; |
return _demangleTypeName(m_customTypeName.c_str()); |
538 |
const char* result = |
} |
539 |
abi::__cxa_demangle(m_customTypeName.c_str(), 0, 0, &status); |
|
540 |
return (status == 0) ? result : m_customTypeName; |
/** @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 *************** |
// *************** Member *************** |
569 |
m_offset = 0; |
m_offset = 0; |
570 |
} |
} |
571 |
|
|
572 |
Member::Member(String name, UID uid, size_t offset, DataType type) { |
Member::Member(String name, UID uid, ssize_t offset, DataType type) { |
573 |
m_name = name; |
m_name = name; |
574 |
m_uid = uid; |
m_uid = uid; |
575 |
m_offset = offset; |
m_offset = offset; |
580 |
* |
* |
581 |
* Returns the unique identifier of the original C/C++ member instance of |
* 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 |
* your C++ class. It is important to know that this unique identifier is |
583 |
* not generated particularly for Member objects. That means no matter how |
* not meant to be unique for Member instances themselves, but it is rather |
584 |
* many individual Member objects are created, as long as they are |
* meant to be unique for the original native C/C++ data these Member |
585 |
* representing the same member variable of the same original native |
* 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 |
* instance of your C++ class, then all those separately created Member |
589 |
* objects return the same unique identifier here. |
* objects return the same unique identifier here. |
590 |
* |
* |
651 |
* @c b and @c c instead. For most 64 bit architectures this example would |
* @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 |
* now still return @c 0 for member @c a, but @c 8 for member @c b and @c 16 |
653 |
* for member @c c. |
* 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 |
size_t Member::offset() const { |
ssize_t Member::offset() const { |
663 |
return m_offset; |
return m_offset; |
664 |
} |
} |
665 |
|
|
724 |
bool Member::operator<(const Member& other) const { |
bool Member::operator<(const Member& other) const { |
725 |
return m_uid < other.m_uid || |
return m_uid < other.m_uid || |
726 |
(m_uid == other.m_uid && |
(m_uid == other.m_uid && |
727 |
m_offset < other.m_offset || |
(m_offset < other.m_offset || |
728 |
(m_offset == other.m_offset && |
(m_offset == other.m_offset && |
729 |
m_name < other.m_name || |
(m_name < other.m_name || |
730 |
(m_name == other.m_name && |
(m_name == other.m_name && |
731 |
m_type < other.m_type))); |
m_type < other.m_type))))); |
732 |
} |
} |
733 |
|
|
734 |
/** @brief Greater than comparison. |
/** @brief Greater than comparison. |
820 |
return (index < m_uid.size()) ? m_uid[index] : NO_UID; |
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. |
/** @brief Unique identifier chain of this Object. |
890 |
* |
* |
891 |
* Returns the entire unique identifier chain of this Object. |
* Returns the entire unique identifier chain of this Object. |
936 |
* In case this Object is reflecting a native C/C++ @c struct or @c class |
* 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 |
* 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++ |
* or @c class layout or implementation. For primitive, fundamental C/C++ |
939 |
* data types the return value of this method has no meaning. |
* 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. |
* @see Archive::setVersion() for more details about this overall topic. |
943 |
*/ |
*/ |
950 |
* In case this Object is reflecting a native C/C++ @c struct or @c class |
* 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++ |
* 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 |
* @c struct or @c class layout or implementation which it may be compatible |
953 |
* with. For primitive, fundamental C/C++ data types the return value of |
* with. For primitive, fundamental C/C++ data types (including @c String |
954 |
* this method has no meaning. |
* objects) the return value of this method has no meaning. |
955 |
* |
* |
956 |
* @see Archive::setVersion() and Archive::setMinVersion() for more details |
* @see Archive::setVersion() and Archive::setMinVersion() for more details |
957 |
* about this overall topic. |
* about this overall topic. |
1281 |
m_root = NO_UID; |
m_root = NO_UID; |
1282 |
m_isModified = false; |
m_isModified = false; |
1283 |
m_timeCreated = m_timeModified = LIBGIG_EPOCH_TIME; |
m_timeCreated = m_timeModified = LIBGIG_EPOCH_TIME; |
1284 |
decode(m_rawData); |
decode(data); |
1285 |
} |
} |
1286 |
|
|
1287 |
/** @brief Create and fill the archive with the given serialized raw C-buffer data. |
/** @brief Create and fill the archive with the given serialized raw C-buffer data. |
1346 |
|
|
1347 |
static String _encode(const DataType& type) { |
static String _encode(const DataType& type) { |
1348 |
String s; |
String s; |
1349 |
|
|
1350 |
|
// Srx v1.0 format (mandatory): |
1351 |
s += _encodeBlob(type.baseTypeName()); |
s += _encodeBlob(type.baseTypeName()); |
1352 |
s += _encodeBlob(type.customTypeName()); |
s += _encodeBlob(type.customTypeName()); |
1353 |
s += _encodeBlob(ToString(type.size())); |
s += _encodeBlob(ToString(type.size())); |
1354 |
s += _encodeBlob(ToString(type.isPointer())); |
s += _encodeBlob(ToString(type.isPointer())); |
1355 |
|
|
1356 |
|
// Srx v1.1 format: |
1357 |
|
s += _encodeBlob(type.customTypeName2()); |
1358 |
|
|
1359 |
return _encodeBlob(s); |
return _encodeBlob(s); |
1360 |
} |
} |
1361 |
|
|
1423 |
assert(false /* unknown floating point type */); |
assert(false /* unknown floating point type */); |
1424 |
} else if (type.isBool()) { |
} else if (type.isBool()) { |
1425 |
s = ToString(*(bool*)ptr); |
s = ToString(*(bool*)ptr); |
1426 |
|
} else if (type.isString()) { |
1427 |
|
s = obj.m_data.empty() ? *(String*)ptr : String((const char*)ptr); |
1428 |
} else { |
} else { |
1429 |
assert(false /* unknown primitive type */); |
assert(false /* unknown primitive type */); |
1430 |
} |
} |
|
|
|
1431 |
} |
} |
1432 |
return s; |
return s; |
1433 |
} |
} |
1434 |
|
|
1435 |
template<typename T> |
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) { |
static T _primitiveObjectValueToNumber(const Object& obj) { |
1457 |
T value = 0; |
T value = 0; |
1458 |
const DataType& type = obj.type(); |
const DataType& type = obj.type(); |
1494 |
assert(false /* unknown floating point type */); |
assert(false /* unknown floating point type */); |
1495 |
} else if (type.isBool()) { |
} else if (type.isBool()) { |
1496 |
value = (T)*(bool*)ptr; |
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 { |
} else { |
1502 |
assert(false /* unknown primitive type */); |
assert(false /* unknown primitive type */); |
1503 |
} |
} |
1531 |
return _encodeBlob(s); |
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" |
#define MAGIC_START "Srx1v" |
1542 |
#define ENCODING_FORMAT_MINOR_VERSION 0 |
#define ENCODING_FORMAT_MINOR_VERSION 1 |
1543 |
|
|
1544 |
String Archive::_encodeRootBlob() { |
String Archive::_encodeRootBlob() { |
1545 |
String s; |
String s; |
1665 |
return s; |
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) { |
static time_t _popTimeBlob(const char*& p, const char* end) { |
1675 |
const uint64_t i = _popIntBlob<uint64_t>(p, end); |
const uint64_t i = _popIntBlob<uint64_t>(p, end); |
1676 |
return (time_t) i; |
return (time_t) i; |
1677 |
} |
} |
1678 |
|
|
1679 |
DataType _popDataTypeBlob(const char*& p, const char* end) { |
static DataType _popDataTypeBlob(const char*& p, const char* end) { |
1680 |
_Blob blob = _decodeBlob(p, end); |
_Blob blob = _decodeBlob(p, end); |
1681 |
p = blob.p; |
p = blob.p; |
1682 |
end = blob.end; |
end = blob.end; |
1683 |
|
|
1684 |
DataType type; |
DataType type; |
1685 |
|
|
1686 |
|
// Srx v1.0 format (mandatory): |
1687 |
type.m_baseTypeName = _popStringBlob(p, end); |
type.m_baseTypeName = _popStringBlob(p, end); |
1688 |
type.m_customTypeName = _popStringBlob(p, end); |
type.m_customTypeName = _popStringBlob(p, end); |
1689 |
type.m_size = _popIntBlob<int>(p, end); |
type.m_size = _popIntBlob<int>(p, end); |
1690 |
type.m_isPointer = _popIntBlob<bool>(p, end); |
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; |
return type; |
1697 |
} |
} |
1698 |
|
|
1734 |
if (p >= end) return m; |
if (p >= end) return m; |
1735 |
|
|
1736 |
m.m_uid = _popUIDBlob(p, end); |
m.m_uid = _popUIDBlob(p, end); |
1737 |
m.m_offset = _popIntBlob<size_t>(p, end); |
m.m_offset = _popIntBlob<ssize_t>(p, end); |
1738 |
m.m_name = _popStringBlob(p, end); |
m.m_name = _popStringBlob(p, end); |
1739 |
m.m_type = _popDataTypeBlob(p, end); |
m.m_type = _popDataTypeBlob(p, end); |
1740 |
assert(m.type()); |
assert(m.type()); |
1796 |
assert(false /* unknown floating point type */); |
assert(false /* unknown floating point type */); |
1797 |
} else if (type.isBool()) { |
} else if (type.isBool()) { |
1798 |
_popIntBlob<uint8_t>(p, end, obj.m_data); |
_popIntBlob<uint8_t>(p, end, obj.m_data); |
1799 |
|
} else if (type.isString()) { |
1800 |
|
_popStringBlob(p, end, obj.m_data); |
1801 |
} else { |
} else { |
1802 |
assert(false /* unknown primitive type */); |
assert(false /* unknown primitive type */); |
1803 |
} |
} |
2338 |
m_isModified = true; |
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. |
/** @brief Automatically cast and assign appropriate value to object. |
2366 |
* |
* |
2367 |
* This method automatically converts the given @a value from textual string |
* This method automatically converts the given @a value from textual string |
2382 |
setIntValue(object, atoll(value.c_str())); |
setIntValue(object, atoll(value.c_str())); |
2383 |
else if (type.isReal()) |
else if (type.isReal()) |
2384 |
setRealValue(object, atof(value.c_str())); |
setRealValue(object, atof(value.c_str())); |
2385 |
else if (type.isBool()) |
else if (type.isBool()) { |
2386 |
setBoolValue(object, atof(value.c_str())); |
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()) |
else if (type.isEnum()) |
2396 |
setEnumValue(object, atoll(value.c_str())); |
setEnumValue(object, atoll(value.c_str())); |
2397 |
else |
else |
2489 |
return _primitiveObjectValueToNumber<bool>(*pObject); |
return _primitiveObjectValueToNumber<bool>(*pObject); |
2490 |
} |
} |
2491 |
|
|
2492 |
|
Archive::operation_t Archive::operation() const { |
2493 |
|
return m_operation; |
2494 |
|
} |
2495 |
|
|
2496 |
// *************** Archive::Syncer *************** |
// *************** Archive::Syncer *************** |
2497 |
// * |
// * |
2498 |
|
|
2514 |
memcpy(pDst, &srcObj.rawData()[0], dstObj.type().size()); |
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) { |
void Archive::Syncer::syncPointer(const Object& dstObj, const Object& srcObj) { |
2543 |
assert(dstObj.type().isPointer()); |
assert(dstObj.type().isPointer()); |
2544 |
assert(dstObj.type() == srcObj.type()); |
assert(dstObj.type() == srcObj.type()); |
2565 |
m_dst.m_allObjects.erase(dstObj.uid()); |
m_dst.m_allObjects.erase(dstObj.uid()); |
2566 |
|
|
2567 |
if (dstObj.type().isPrimitive() && !dstObj.type().isPointer()) { |
if (dstObj.type().isPrimitive() && !dstObj.type().isPointer()) { |
2568 |
syncPrimitive(dstObj, srcObj); |
if (dstObj.type().isString()) |
2569 |
|
syncString(dstObj, srcObj); |
2570 |
|
else |
2571 |
|
syncPrimitive(dstObj, srcObj); |
2572 |
return; // end of recursion |
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()) { |
if (dstObj.type().isPointer()) { |
2591 |
syncPointer(dstObj, srcObj); |
syncPointer(dstObj, srcObj); |
2592 |
return; |
return; |
2635 |
// *************** Exception *************** |
// *************** 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. |
/** @brief Print exception message to stdout. |
2653 |
* |
* |
2654 |
* Prints the message of this Exception to the currently defined standard |
* Prints the message of this Exception to the currently defined standard |
2658 |
std::cout << "Serialization::Exception: " << Message << std::endl; |
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 |
} // namespace Serialization |