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> |
31 |
#include <assert.h> |
#include <assert.h> |
32 |
#include <string.h> // for memcpy() |
#include <string.h> // for memcpy() |
33 |
#include <stdlib.h> // for atof() |
#include <stdlib.h> // for atof() |
34 |
#include <cxxabi.h> |
#ifdef _MSC_VER |
35 |
|
# include <windows.h> |
36 |
|
# include <dbghelp.h> |
37 |
|
#else |
38 |
|
# include <cxxabi.h> |
39 |
|
#endif |
40 |
#include "helper.h" |
#include "helper.h" |
41 |
|
|
42 |
#define LIBGIG_EPOCH_TIME ((time_t)0) |
#define LIBGIG_EPOCH_TIME ((time_t)0) |
71 |
// *************** DataType *************** |
// *************** DataType *************** |
72 |
// * |
// * |
73 |
|
|
74 |
/** @brief Default constructor. |
/** @brief Default constructor (as "invalid" DataType). |
75 |
* |
* |
76 |
* Initializes a DataType object as being an "invalid" DataType object. |
* Initializes a DataType object as being an "invalid" DataType object. |
77 |
* Thus calling isValid(), after creating a DataType object with this |
* Thus calling isValid(), after creating a DataType object with this |
85 |
m_isPointer = false; |
m_isPointer = false; |
86 |
} |
} |
87 |
|
|
88 |
|
/** @brief Constructs a valid DataType object. |
89 |
|
* |
90 |
|
* Initializes this object as "valid" DataType object, with specific and |
91 |
|
* useful data type information. |
92 |
|
* |
93 |
|
* This is a protected constructor which should not be called directly by |
94 |
|
* applications, as its argument list is somewhat implementation specific |
95 |
|
* and might change at any time. Applications should call the static |
96 |
|
* function DataType::dataTypeOf() instead. |
97 |
|
* |
98 |
|
* @param isPointer - whether pointer type (i.e. a simple memory address) |
99 |
|
* @param size - native size of data type in bytes (i.e. according to |
100 |
|
* @c sizeof() C/C++ keyword) |
101 |
|
* @param baseType - this framework's internal name for specifying the base |
102 |
|
* type in a coarse way, which must be either one of: |
103 |
|
* "int8", "uint8", "int16", "uint16", "int32", "uint32", |
104 |
|
* "int64", "uint64", "bool", "real32", "real64", |
105 |
|
* "String", "enum", "union" or "class" |
106 |
|
* @param customType - this is only used for base types "enum", "union" or |
107 |
|
* "class", in which case this identifies the user |
108 |
|
* defined type name (e.g. "Foo" for @c class @c Foo ), |
109 |
|
* for all other types this is empty |
110 |
|
*/ |
111 |
DataType::DataType(bool isPointer, int size, String baseType, String customType) { |
DataType::DataType(bool isPointer, int size, String baseType, String customType) { |
112 |
m_size = size; |
m_size = size; |
113 |
m_isPointer = isPointer; |
m_isPointer = isPointer; |
142 |
* |
* |
143 |
* 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 |
144 |
* (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 |
145 |
* type. |
* type, with one exception: @c String objects are handled by this framework |
146 |
|
* as if they were a primitive type. |
147 |
* |
* |
148 |
* Note that in the following example: |
* Note that in the following example: |
149 |
* @code |
* @code |
169 |
* (this DataType instance is reflecting) is a primitive, fundamental C/C++ |
* (this DataType instance is reflecting) is a primitive, fundamental C/C++ |
170 |
* data type. Those are fundamental data types which are already predefined |
* data type. Those are fundamental data types which are already predefined |
171 |
* by the C/C++ language, for example: @c char, @c int, @c float, @c double, |
* by the C/C++ language, for example: @c char, @c int, @c float, @c double, |
172 |
* @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 |
173 |
* including pointers to user defined types like: |
* @c int*, @c double**, but including pointers to user defined types like: |
174 |
* @code |
* @code |
175 |
* struct Foo { |
* struct Foo { |
176 |
* int a; |
* int a; |
187 |
return !isClass(); |
return !isClass(); |
188 |
} |
} |
189 |
|
|
190 |
|
/** @brief Whether this is a C++ @c String data type. |
191 |
|
* |
192 |
|
* Returns @c true if the respective native C/C++ object, member or variable |
193 |
|
* (this DataType instance is reflecting) is a C++ @c String object (a.k.a. |
194 |
|
* @c std::string from the C++ STL). |
195 |
|
* |
196 |
|
* Note that this framework handles @c String objects as if they were a |
197 |
|
* fundamental, primitive C/C++ data type, so @c isPrimitive() returns |
198 |
|
* @c true for strings. |
199 |
|
*/ |
200 |
|
bool DataType::isString() const { |
201 |
|
return m_baseTypeName == "String"; |
202 |
|
} |
203 |
|
|
204 |
/** @brief Whether this is an integer C/C++ data type. |
/** @brief Whether this is an integer C/C++ data type. |
205 |
* |
* |
206 |
* Returns @c true if the respective native C/C++ object, member or variable |
* Returns @c true if the respective native C/C++ object, member or variable |
296 |
* 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* |
297 |
* 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 |
298 |
* would return @c false. |
* would return @c false. |
299 |
|
* |
300 |
|
* As an exception here, classes and structs with the same class/struct name |
301 |
|
* but different sizes are also considered to be "equal". This relaxed |
302 |
|
* requirement is necessary to retain backward compatiblity to older |
303 |
|
* versions of the same native C++ classes/structs. |
304 |
*/ |
*/ |
305 |
bool DataType::operator==(const DataType& other) const { |
bool DataType::operator==(const DataType& other) const { |
306 |
return m_baseTypeName == other.m_baseTypeName && |
return m_baseTypeName == other.m_baseTypeName && |
307 |
m_customTypeName == other.m_customTypeName && |
m_customTypeName == other.m_customTypeName && |
308 |
m_size == other.m_size && |
(m_size == other.m_size || (isClass() && other.isClass())) && |
309 |
m_isPointer == other.m_isPointer; |
m_isPointer == other.m_isPointer; |
310 |
} |
} |
311 |
|
|
332 |
bool DataType::operator<(const DataType& other) const { |
bool DataType::operator<(const DataType& other) const { |
333 |
return m_baseTypeName < other.m_baseTypeName || |
return m_baseTypeName < other.m_baseTypeName || |
334 |
(m_baseTypeName == other.m_baseTypeName && |
(m_baseTypeName == other.m_baseTypeName && |
335 |
m_customTypeName < other.m_customTypeName || |
(m_customTypeName < other.m_customTypeName || |
336 |
(m_customTypeName == other.m_customTypeName && |
(m_customTypeName == other.m_customTypeName && |
337 |
m_size < other.m_size || |
(m_size < other.m_size || |
338 |
(m_size == other.m_size && |
(m_size == other.m_size && |
339 |
m_isPointer < other.m_isPointer))); |
m_isPointer < other.m_isPointer))))); |
340 |
} |
} |
341 |
|
|
342 |
/** @brief Greater than comparison. |
/** @brief Greater than comparison. |
441 |
* In the latter example @c customTypeName(true) would return for both |
* In the latter example @c customTypeName(true) would return for both |
442 |
* @c foo and @c pFoo the string @c "Foo" as return value of this method. |
* @c foo and @c pFoo the string @c "Foo" as return value of this method. |
443 |
* |
* |
444 |
|
* @b Windows: please note that the current implementation of this method |
445 |
|
* on Windows is @b not thread safe! |
446 |
|
* |
447 |
* @see isPointer(), baseTypeName() |
* @see isPointer(), baseTypeName() |
448 |
*/ |
*/ |
449 |
String DataType::customTypeName(bool demangle) const { |
String DataType::customTypeName(bool demangle) const { |
450 |
if (!demangle) return m_customTypeName; |
if (!demangle) return m_customTypeName; |
451 |
|
#ifdef _MSC_VER |
452 |
|
const size_t MAXLENGTH = 1024; |
453 |
|
char result[MAXLENGTH]; |
454 |
|
|
455 |
|
//FIXME: calling UnDecorateSymbolName() is not thread safe! |
456 |
|
//Skip the first char |
457 |
|
size_t size = UnDecorateSymbolName(m_customTypeName.c_str() +1, result, MAXLENGTH, UNDNAME_32_BIT_DECODE | UNDNAME_NO_ARGUMENTS); |
458 |
|
if (size) |
459 |
|
{ |
460 |
|
return result; |
461 |
|
} |
462 |
|
return m_customTypeName; |
463 |
|
#else |
464 |
int status; |
int status; |
465 |
const char* result = |
char* result = |
466 |
abi::__cxa_demangle(m_customTypeName.c_str(), 0, 0, &status); |
abi::__cxa_demangle(m_customTypeName.c_str(), 0, 0, &status); |
467 |
return (status == 0) ? result : m_customTypeName; |
String sResult = result; |
468 |
|
free(result); |
469 |
|
return (status == 0) ? sResult : m_customTypeName; |
470 |
|
#endif |
471 |
} |
} |
472 |
|
|
473 |
// *************** Member *************** |
// *************** Member *************** |
638 |
bool Member::operator<(const Member& other) const { |
bool Member::operator<(const Member& other) const { |
639 |
return m_uid < other.m_uid || |
return m_uid < other.m_uid || |
640 |
(m_uid == other.m_uid && |
(m_uid == other.m_uid && |
641 |
m_offset < other.m_offset || |
(m_offset < other.m_offset || |
642 |
(m_offset == other.m_offset && |
(m_offset == other.m_offset && |
643 |
m_name < other.m_name || |
(m_name < other.m_name || |
644 |
(m_name == other.m_name && |
(m_name == other.m_name && |
645 |
m_type < other.m_type))); |
m_type < other.m_type))))); |
646 |
} |
} |
647 |
|
|
648 |
/** @brief Greater than comparison. |
/** @brief Greater than comparison. |
784 |
* 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 |
785 |
* 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 |
786 |
* or @c class layout or implementation. For primitive, fundamental C/C++ |
* or @c class layout or implementation. For primitive, fundamental C/C++ |
787 |
* data types the return value of this method has no meaning. |
* data types (including @c String objects) the return value of this method |
788 |
|
* has no meaning. |
789 |
* |
* |
790 |
* @see Archive::setVersion() for more details about this overall topic. |
* @see Archive::setVersion() for more details about this overall topic. |
791 |
*/ |
*/ |
798 |
* 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 |
799 |
* 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++ |
800 |
* @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 |
801 |
* with. For primitive, fundamental C/C++ data types the return value of |
* with. For primitive, fundamental C/C++ data types (including @c String |
802 |
* this method has no meaning. |
* objects) the return value of this method has no meaning. |
803 |
* |
* |
804 |
* @see Archive::setVersion() and Archive::setMinVersion() for more details |
* @see Archive::setVersion() and Archive::setMinVersion() for more details |
805 |
* about this overall topic. |
* about this overall topic. |
1129 |
m_root = NO_UID; |
m_root = NO_UID; |
1130 |
m_isModified = false; |
m_isModified = false; |
1131 |
m_timeCreated = m_timeModified = LIBGIG_EPOCH_TIME; |
m_timeCreated = m_timeModified = LIBGIG_EPOCH_TIME; |
1132 |
decode(m_rawData); |
decode(data); |
1133 |
} |
} |
1134 |
|
|
1135 |
/** @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. |
1265 |
assert(false /* unknown floating point type */); |
assert(false /* unknown floating point type */); |
1266 |
} else if (type.isBool()) { |
} else if (type.isBool()) { |
1267 |
s = ToString(*(bool*)ptr); |
s = ToString(*(bool*)ptr); |
1268 |
|
} else if (type.isString()) { |
1269 |
|
s = obj.m_data.empty() ? *(String*)ptr : String((const char*)ptr); |
1270 |
} else { |
} else { |
1271 |
assert(false /* unknown primitive type */); |
assert(false /* unknown primitive type */); |
1272 |
} |
} |
1276 |
} |
} |
1277 |
|
|
1278 |
template<typename T> |
template<typename T> |
1279 |
|
inline T _stringToNumber(const String& s) { |
1280 |
|
assert(false /* String cast to unknown primitive number type */); |
1281 |
|
} |
1282 |
|
|
1283 |
|
template<> |
1284 |
|
inline int64_t _stringToNumber(const String& s) { |
1285 |
|
return atoll(s.c_str()); |
1286 |
|
} |
1287 |
|
|
1288 |
|
template<> |
1289 |
|
inline double _stringToNumber(const String& s) { |
1290 |
|
return atof(s.c_str()); |
1291 |
|
} |
1292 |
|
|
1293 |
|
template<> |
1294 |
|
inline bool _stringToNumber(const String& s) { |
1295 |
|
return (bool) atoll(s.c_str()); |
1296 |
|
} |
1297 |
|
|
1298 |
|
template<typename T> |
1299 |
static T _primitiveObjectValueToNumber(const Object& obj) { |
static T _primitiveObjectValueToNumber(const Object& obj) { |
1300 |
T value = 0; |
T value = 0; |
1301 |
const DataType& type = obj.type(); |
const DataType& type = obj.type(); |
1337 |
assert(false /* unknown floating point type */); |
assert(false /* unknown floating point type */); |
1338 |
} else if (type.isBool()) { |
} else if (type.isBool()) { |
1339 |
value = (T)*(bool*)ptr; |
value = (T)*(bool*)ptr; |
1340 |
|
} else if (type.isString()) { |
1341 |
|
value = _stringToNumber<T>( |
1342 |
|
obj.m_data.empty() ? *(String*)ptr : String((const char*)ptr) |
1343 |
|
); |
1344 |
} else { |
} else { |
1345 |
assert(false /* unknown primitive type */); |
assert(false /* unknown primitive type */); |
1346 |
} |
} |
1374 |
return _encodeBlob(s); |
return _encodeBlob(s); |
1375 |
} |
} |
1376 |
|
|
1377 |
|
/* |
1378 |
|
* Srx format history: |
1379 |
|
* - 1.0: Initial version. |
1380 |
|
* - 1.1: Adds "String" data type. |
1381 |
|
*/ |
1382 |
#define MAGIC_START "Srx1v" |
#define MAGIC_START "Srx1v" |
1383 |
#define ENCODING_FORMAT_MINOR_VERSION 0 |
#define ENCODING_FORMAT_MINOR_VERSION 1 |
1384 |
|
|
1385 |
String Archive::_encodeRootBlob() { |
String Archive::_encodeRootBlob() { |
1386 |
String s; |
String s; |
1506 |
return s; |
return s; |
1507 |
} |
} |
1508 |
|
|
1509 |
|
static void _popStringBlob(const char*& p, const char* end, RawData& rawData) { |
1510 |
|
String s = _popStringBlob(p, end); |
1511 |
|
rawData.resize(s.length() + 1); |
1512 |
|
strcpy((char*)&rawData[0], &s[0]); |
1513 |
|
} |
1514 |
|
|
1515 |
static time_t _popTimeBlob(const char*& p, const char* end) { |
static time_t _popTimeBlob(const char*& p, const char* end) { |
1516 |
const uint64_t i = _popIntBlob<uint64_t>(p, end); |
const uint64_t i = _popIntBlob<uint64_t>(p, end); |
1517 |
return (time_t) i; |
return (time_t) i; |
1518 |
} |
} |
1519 |
|
|
1520 |
DataType _popDataTypeBlob(const char*& p, const char* end) { |
static DataType _popDataTypeBlob(const char*& p, const char* end) { |
1521 |
_Blob blob = _decodeBlob(p, end); |
_Blob blob = _decodeBlob(p, end); |
1522 |
p = blob.p; |
p = blob.p; |
1523 |
end = blob.end; |
end = blob.end; |
1630 |
assert(false /* unknown floating point type */); |
assert(false /* unknown floating point type */); |
1631 |
} else if (type.isBool()) { |
} else if (type.isBool()) { |
1632 |
_popIntBlob<uint8_t>(p, end, obj.m_data); |
_popIntBlob<uint8_t>(p, end, obj.m_data); |
1633 |
|
} else if (type.isString()) { |
1634 |
|
_popStringBlob(p, end, obj.m_data); |
1635 |
} else { |
} else { |
1636 |
assert(false /* unknown primitive type */); |
assert(false /* unknown primitive type */); |
1637 |
} |
} |
2172 |
m_isModified = true; |
m_isModified = true; |
2173 |
} |
} |
2174 |
|
|
2175 |
|
/** @brief Set new textual string for given String object. |
2176 |
|
* |
2177 |
|
* Sets the new textual string @a value to the given String @a object. |
2178 |
|
* |
2179 |
|
* @param object - the String object to be changed |
2180 |
|
* @param value - the new textual string to be assigned to the @a object |
2181 |
|
* @throws Exception if @a object is not a String type. |
2182 |
|
*/ |
2183 |
|
void Archive::setStringValue(Object& object, String value) { |
2184 |
|
if (!object) return; |
2185 |
|
if (!object.type().isString()) |
2186 |
|
throw Exception("Not a String data type"); |
2187 |
|
Object* pObject = &object; |
2188 |
|
if (object.type().isPointer()) { |
2189 |
|
Object& obj = objectByUID(object.uid(1)); |
2190 |
|
if (!obj) return; |
2191 |
|
pObject = &obj; |
2192 |
|
} |
2193 |
|
pObject->m_data.resize(value.length() + 1); |
2194 |
|
char* ptr = (char*) &pObject->m_data[0]; |
2195 |
|
strcpy(ptr, &value[0]); |
2196 |
|
m_isModified = true; |
2197 |
|
} |
2198 |
|
|
2199 |
/** @brief Automatically cast and assign appropriate value to object. |
/** @brief Automatically cast and assign appropriate value to object. |
2200 |
* |
* |
2201 |
* This method automatically converts the given @a value from textual string |
* This method automatically converts the given @a value from textual string |
2224 |
setBoolValue(object, false); |
setBoolValue(object, false); |
2225 |
else |
else |
2226 |
setBoolValue(object, atof(value.c_str())); |
setBoolValue(object, atof(value.c_str())); |
2227 |
} else if (type.isEnum()) |
} else if (type.isString()) |
2228 |
|
setStringValue(object, value); |
2229 |
|
else if (type.isEnum()) |
2230 |
setEnumValue(object, atoll(value.c_str())); |
setEnumValue(object, atoll(value.c_str())); |
2231 |
else |
else |
2232 |
throw Exception("Not a primitive data type"); |
throw Exception("Not a primitive data type"); |
2344 |
memcpy(pDst, &srcObj.rawData()[0], dstObj.type().size()); |
memcpy(pDst, &srcObj.rawData()[0], dstObj.type().size()); |
2345 |
} |
} |
2346 |
|
|
2347 |
|
void Archive::Syncer::syncString(const Object& dstObj, const Object& srcObj) { |
2348 |
|
assert(dstObj.type().isString()); |
2349 |
|
assert(dstObj.type() == srcObj.type()); |
2350 |
|
String* pDst = (String*)(void*)dstObj.uid().id; |
2351 |
|
*pDst = (String) (const char*) &srcObj.rawData()[0]; |
2352 |
|
} |
2353 |
|
|
2354 |
void Archive::Syncer::syncPointer(const Object& dstObj, const Object& srcObj) { |
void Archive::Syncer::syncPointer(const Object& dstObj, const Object& srcObj) { |
2355 |
assert(dstObj.type().isPointer()); |
assert(dstObj.type().isPointer()); |
2356 |
assert(dstObj.type() == srcObj.type()); |
assert(dstObj.type() == srcObj.type()); |
2377 |
m_dst.m_allObjects.erase(dstObj.uid()); |
m_dst.m_allObjects.erase(dstObj.uid()); |
2378 |
|
|
2379 |
if (dstObj.type().isPrimitive() && !dstObj.type().isPointer()) { |
if (dstObj.type().isPrimitive() && !dstObj.type().isPointer()) { |
2380 |
syncPrimitive(dstObj, srcObj); |
if (dstObj.type().isString()) |
2381 |
|
syncString(dstObj, srcObj); |
2382 |
|
else |
2383 |
|
syncPrimitive(dstObj, srcObj); |
2384 |
return; // end of recursion |
return; // end of recursion |
2385 |
} |
} |
2386 |
|
|
2432 |
// *************** Exception *************** |
// *************** Exception *************** |
2433 |
// * |
// * |
2434 |
|
|
2435 |
|
Exception::Exception() { |
2436 |
|
} |
2437 |
|
|
2438 |
|
Exception::Exception(String format, ...) { |
2439 |
|
va_list arg; |
2440 |
|
va_start(arg, format); |
2441 |
|
Message = assemble(format, arg); |
2442 |
|
va_end(arg); |
2443 |
|
} |
2444 |
|
|
2445 |
|
Exception::Exception(String format, va_list arg) { |
2446 |
|
Message = assemble(format, arg); |
2447 |
|
} |
2448 |
|
|
2449 |
/** @brief Print exception message to stdout. |
/** @brief Print exception message to stdout. |
2450 |
* |
* |
2451 |
* Prints the message of this Exception to the currently defined standard |
* Prints the message of this Exception to the currently defined standard |
2455 |
std::cout << "Serialization::Exception: " << Message << std::endl; |
std::cout << "Serialization::Exception: " << Message << std::endl; |
2456 |
} |
} |
2457 |
|
|
2458 |
|
String Exception::assemble(String format, va_list arg) { |
2459 |
|
char* buf = NULL; |
2460 |
|
vasprintf(&buf, format.c_str(), arg); |
2461 |
|
String s = buf; |
2462 |
|
free(buf); |
2463 |
|
return s; |
2464 |
|
} |
2465 |
|
|
2466 |
} // namespace Serialization |
} // namespace Serialization |