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 _createNullUID()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 */ isValid() const66 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 */ DataType()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 */ DataType(bool isPointer,int size,String baseType,String customType1,String customType2)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 */ isValid() const134 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 */ isPointer() const143 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 */ isClass() const172 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 */ isPrimitive() const196 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 */ isString() const210 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 */ isInteger() const228 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 */ isReal() const245 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 */ isBool() const260 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 */ isEnum() const275 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 */ isArray() const291 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 */ isSet() const307 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 */ isMap() const323 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 */ isSigned() const340 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 */ operator ==(const DataType & other) const363 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 */ operator !=(const DataType & other) const376 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 */ operator <(const DataType & other) const391 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 */ operator >(const DataType & other) const414 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 */ asLongDescr() const432 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 */ baseTypeName() const472 String DataType::baseTypeName() const { 473 return m_baseTypeName; 474 } 475 _demangleTypeName(const char * name)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 */ customTypeName(bool demangle) const535 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 */ customTypeName2(bool demangle) const547 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 */ Member()567 Member::Member() { 568 m_uid = NO_UID; 569 m_offset = 0; 570 } 571 Member(String name,UID uid,ssize_t offset,DataType type)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 */ uid() const593 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 */ name() const617 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 */ offset() const662 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 */ type() const670 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 */ isValid() const684 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 */ operator ==(const Member & other) const696 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 */ operator !=(const Member & other) const708 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 */ operator <(const Member & other) const724 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 */ operator >(const Member & other) const746 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 */ Object()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 */ Object(UIDChain uidChain,DataType type)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 */ isValid() const804 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 */ uid(int index) const819 UID Object::uid(int index) const { 820 return (index < m_uid.size()) ? m_uid[index] : NO_UID; 821 } 822 _setNativeValueFromString(void * ptr,const DataType & type,const char * s)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 */ setNativeValueFromString(const String & s)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 */ uidChain() const895 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 */ type() const904 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 */ rawData() const930 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 */ version() const944 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 */ minVersion() const959 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 */ members()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 */ members() const1005 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 */ operator ==(const Object & other) const1019 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 */ operator !=(const Object & other) const1031 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 */ operator <(const Object & other) const1047 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 */ operator >(const Object & other) const1067 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 */ isVersionCompatibleTo(const Object & other) const1089 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 setVersion(Version v)1098 void Object::setVersion(Version v) { 1099 m_version = v; 1100 } 1101 setMinVersion(Version v)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 */ memberNamed(String name) const1135 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 */ memberByUID(const UID & uid) const1156 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 remove(const Member & member)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 */ membersOfType(const DataType & type) const1188 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 */ sequenceIndexOf(const Member & member) const1229 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 */ Archive()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 */ Archive(const RawData & data)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 */ Archive(const uint8_t * data,size_t size)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 ~Archive()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 */ rootObject()1328 Object& Archive::rootObject() { 1329 return m_allObjects[m_root]; 1330 } 1331 _encodeBlob(String data)1332 static String _encodeBlob(String data) { 1333 return ToString(data.length()) + ":" + data; 1334 } 1335 _encode(const UID & uid)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 _encode(const time_t & time)1343 static String _encode(const time_t& time) { 1344 return _encodeBlob(ToString(time)); 1345 } 1346 _encode(const DataType & type)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 _encode(const UIDChain & chain)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 _encode(const Member & member)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 _encode(const std::vector<Member> & members)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 _primitiveObjectValueToString(const Object & obj)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> _stringToNumber(const String & s)1436 inline T _stringToNumber(const String& s) { 1437 assert(false /* String cast to unknown primitive number type */); 1438 } 1439 1440 template<> _stringToNumber(const String & s)1441 inline int64_t _stringToNumber(const String& s) { 1442 return atoll(s.c_str()); 1443 } 1444 1445 template<> _stringToNumber(const String & s)1446 inline double _stringToNumber(const String& s) { 1447 return atof(s.c_str()); 1448 } 1449 1450 template<> _stringToNumber(const String & s)1451 inline bool _stringToNumber(const String& s) { 1452 return (bool) atoll(s.c_str()); 1453 } 1454 1455 template<typename T> _primitiveObjectValueToNumber(const Object & obj)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 _encodePrimitiveValue(const Object & obj)1508 static String _encodePrimitiveValue(const Object& obj) { 1509 return _encodeBlob( _primitiveObjectValueToString(obj) ); 1510 } 1511 _encode(const Object & obj)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 _encode(const Archive::ObjectPool & objects)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 _encodeRootBlob()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 encode()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 _decodeBlob(const char * p,const char * end,bool bThrow=true)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> _popIntBlob(const char * & p,const char * end)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> _popIntBlob(const char * & p,const char * end,RawData & rawData)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> _popRealBlob(const char * & p,const char * end)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> _popRealBlob(const char * & p,const char * end,RawData & rawData)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 _popStringBlob(const char * & p,const char * end)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 _popStringBlob(const char * & p,const char * end,RawData & rawData)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 _popTimeBlob(const char * & p,const char * end)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 _popDataTypeBlob(const char * & p,const char * end)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 _popUIDBlob(const char * & p,const char * end)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 _popUIDChainBlob(const char * & p,const char * end)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 _popMemberBlob(const char * & p,const char * end)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 _popMembersBlob(const char * & p,const char * end)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 _popPrimitiveValue(const char * & p,const char * end,Object & obj)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 _popObjectBlob(const char * & p,const char * end)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 _popObjectsBlob(const char * & p,const char * end)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 _popRootBlob(const char * & p,const char * end)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 */ decode(const RawData & data)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 */ decode(const uint8_t * data,size_t size)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 */ rawData()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 */ rawDataFormat() const1952 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 */ isModified() const1970 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 */ clear()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 */ name() const1995 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 */ setName(String name)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 */ comment() const2021 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 */ setComment(String comment)2034 void Archive::setComment(String comment) { 2035 if (m_comment == comment) return; 2036 m_comment = comment; 2037 m_isModified = true; 2038 } 2039 _convertTimeStamp(const time_t & time,time_base_t base)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 */ timeStampCreated() const2062 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 */ timeStampModified() const2071 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 */ dateTimeCreated(time_base_t base) const2085 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 */ dateTimeModified(time_base_t base) const2099 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 */ removeMember(Object & parent,const Member & member)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 */ remove(const Object & obj)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 */ objectByUID(const UID & uid)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 */ setVersion(Object & object,Version v)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 */ setMinVersion(Object & object,Version v)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 */ setEnumValue(Object & object,uint64_t value)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 */ setIntValue(Object & object,int64_t value)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 */ setRealValue(Object & object,double value)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 */ setBoolValue(Object & object,bool value)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 */ setStringValue(Object & object,String value)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 */ setAutoValue(Object & object,String value)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 */ valueAsString(const Object & object)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 */ valueAsInt(const Object & object)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 */ valueAsReal(const Object & object)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 */ valueAsBool(const Object & object)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 operation() const2492 Archive::operation_t Archive::operation() const { 2493 return m_operation; 2494 } 2495 2496 // *************** Archive::Syncer *************** 2497 // * 2498 Syncer(Archive & dst,Archive & src)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 syncPrimitive(const Object & dstObj,const Object & srcObj)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 syncString(const Object & dstObj,const Object & srcObj)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 syncArray(const Object & dstObj,const Object & srcObj)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 syncSet(const Object & dstObj,const Object & srcObj)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 syncMap(const Object & dstObj,const Object & srcObj)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 syncPointer(const Object & dstObj,const Object & srcObj)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 syncObject(const Object & dstObj,const Object & srcObj)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 dstMemberMatching(const Object & dstObj,const Object & srcObj,const Member & srcMember)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 syncMember(const Member & dstMember,const Member & srcMember)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 Exception()2638 Exception::Exception() { 2639 } 2640 Exception(String format,...)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 Exception(String format,va_list arg)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 */ PrintMessage()2657 void Exception::PrintMessage() { 2658 std::cout << "Serialization::Exception: " << Message << std::endl; 2659 } 2660 assemble(String format,va_list arg)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 2670