--- libgig/trunk/src/Serialization.cpp 2017/05/05 18:42:06 3150 +++ libgig/trunk/src/Serialization.cpp 2017/05/14 20:40:02 3182 @@ -27,16 +27,20 @@ #include #include // for memcpy() #include // for atof() +#include #include "helper.h" +#define LIBGIG_EPOCH_TIME ((time_t)0) + namespace Serialization { // *************** DataType *************** // * static UID _createNullUID() { - return (UID) { NULL, 0 }; + const UID uid = { NULL, 0 }; + return uid; } const UID NO_UID = _createNullUID(); @@ -124,15 +128,22 @@ } String DataType::asLongDescr() const { - //TODO: Demangling of C++ raw type names String s = m_baseTypeName; if (!m_customTypeName.empty()) - s += " " + m_customTypeName; + s += " " + customTypeName(true); if (isPointer()) s += " pointer"; return s; } + String DataType::customTypeName(bool demangle) const { + if (!demangle) return m_customTypeName; + int status; + const char* result = + abi::__cxa_demangle(m_customTypeName.c_str(), 0, 0, &status); + return (status == 0) ? result : m_customTypeName; + } + // *************** Member *************** // * @@ -229,6 +240,14 @@ return other.minVersion() <= this->version(); } + void Object::setVersion(Version v) { + m_version = v; + } + + void Object::setMinVersion(Version v) { + m_minVersion = v; + } + Member Object::memberNamed(String name) const { for (int i = 0; i < m_members.size(); ++i) if (m_members[i].name() == name) @@ -236,6 +255,14 @@ return Member(); } + Member Object::memberByUID(const UID& uid) const { + if (!uid) return Member(); + for (int i = 0; i < m_members.size(); ++i) + if (m_members[i].uid() == uid) + return m_members[i]; + return Member(); + } + void Object::remove(const Member& member) { for (int i = 0; i < m_members.size(); ++i) { if (m_members[i] == member) { @@ -269,12 +296,14 @@ m_operation = OPERATION_NONE; m_root = NO_UID; m_isModified = false; + m_timeCreated = m_timeModified = LIBGIG_EPOCH_TIME; } Archive::Archive(const RawData& data) { m_operation = OPERATION_NONE; m_root = NO_UID; m_isModified = false; + m_timeCreated = m_timeModified = LIBGIG_EPOCH_TIME; decode(m_rawData); } @@ -282,6 +311,7 @@ m_operation = OPERATION_NONE; m_root = NO_UID; m_isModified = false; + m_timeCreated = m_timeModified = LIBGIG_EPOCH_TIME; decode(data, size); } @@ -303,6 +333,10 @@ return _encodeBlob(s); } + static String _encode(const time_t& time) { + return _encodeBlob(ToString(time)); + } + static String _encode(const DataType& type) { String s; s += _encodeBlob(type.baseTypeName()); @@ -384,6 +418,55 @@ return s; } + template + static T _primitiveObjectValueToNumber(const Object& obj) { + T value = 0; + const DataType& type = obj.type(); + const ID& id = obj.uid().id; + void* ptr = obj.m_data.empty() ? (void*)id : (void*)&obj.m_data[0]; + if (!obj.m_data.empty()) + assert(type.size() == obj.m_data.size()); + if (type.isPrimitive() && !type.isPointer()) { + if (type.isInteger() || type.isEnum()) { + if (type.isSigned()) { + if (type.size() == 1) + value = (T)*(int8_t*)ptr; + else if (type.size() == 2) + value = (T)*(int16_t*)ptr; + else if (type.size() == 4) + value = (T)*(int32_t*)ptr; + else if (type.size() == 8) + value = (T)*(int64_t*)ptr; + else + assert(false /* unknown signed int type size */); + } else { + if (type.size() == 1) + value = (T)*(uint8_t*)ptr; + else if (type.size() == 2) + value = (T)*(uint16_t*)ptr; + else if (type.size() == 4) + value = (T)*(uint32_t*)ptr; + else if (type.size() == 8) + value = (T)*(uint64_t*)ptr; + else + assert(false /* unknown unsigned int type size */); + } + } else if (type.isReal()) { + if (type.size() == sizeof(float)) + value = (T)*(float*)ptr; + else if (type.size() == sizeof(double)) + value = (T)*(double*)ptr; + else + assert(false /* unknown floating point type */); + } else if (type.isBool()) { + value = (T)*(bool*)ptr; + } else { + assert(false /* unknown primitive type */); + } + } + return value; + } + static String _encodePrimitiveValue(const Object& obj) { return _encodeBlob( _primitiveObjectValueToString(obj) ); } @@ -418,12 +501,19 @@ s += _encodeBlob(ToString(ENCODING_FORMAT_MINOR_VERSION)); s += _encode(m_root); s += _encode(m_allObjects); + s += _encodeBlob(m_name); + s += _encodeBlob(m_comment); + s += _encode(m_timeCreated); + s += _encode(m_timeModified); return _encodeBlob(s); } void Archive::encode() { m_rawData.clear(); String s = MAGIC_START; + m_timeModified = time(NULL); + if (m_timeCreated == LIBGIG_EPOCH_TIME) + m_timeCreated = m_timeModified; s += _encodeRootBlob(); m_rawData.resize(s.length() + 1); memcpy(&m_rawData[0], &s[0], s.length() + 1); @@ -436,8 +526,10 @@ }; static _Blob _decodeBlob(const char* p, const char* end, bool bThrow = true) { - if (!bThrow && p >= end) - return (_Blob) { p, end }; + if (!bThrow && p >= end) { + const _Blob blob = { p, end }; + return blob; + } size_t sz = 0; for (; true; ++p) { if (p >= end) @@ -452,7 +544,8 @@ ++p; if (p + sz > end) throw Exception("Decode Error: Premature end of blob"); - return (_Blob) { p, p + sz }; + const _Blob blob = { p, p + sz }; + return blob; } template @@ -527,6 +620,11 @@ return s; } + static time_t _popTimeBlob(const char*& p, const char* end) { + const uint64_t i = _popIntBlob(p, end); + return (time_t) i; + } + DataType _popDataTypeBlob(const char*& p, const char* end) { _Blob blob = _decodeBlob(p, end); p = blob.p; @@ -551,7 +649,8 @@ const ID id = (ID) _popIntBlob(p, end); const size_t size = _popIntBlob(p, end); - return (UID) { id, size }; + const UID uid = { id, size }; + return uid; } static UIDChain _popUIDChainBlob(const char*& p, const char* end) { @@ -703,12 +802,18 @@ _popObjectsBlob(p, end); if (!m_allObjects[m_root]) throw Exception("Decode Error: Missing declared root object"); + + m_name = _popStringBlob(p, end); + m_comment = _popStringBlob(p, end); + m_timeCreated = _popTimeBlob(p, end); + m_timeModified = _popTimeBlob(p, end); } void Archive::decode(const RawData& data) { m_rawData = data; m_allObjects.clear(); m_isModified = false; + m_timeCreated = m_timeModified = LIBGIG_EPOCH_TIME; const char* p = (const char*) &data[0]; const char* end = p + data.size(); if (memcmp(p, MAGIC_START, std::min(strlen(MAGIC_START), data.size()))) @@ -743,9 +848,69 @@ m_root = NO_UID; m_rawData.clear(); m_isModified = false; + m_timeCreated = m_timeModified = LIBGIG_EPOCH_TIME; + } + + String Archive::name() const { + return m_name; + } + + void Archive::setName(String name) { + if (m_name == name) return; + m_name = name; + m_isModified = true; + } + + String Archive::comment() const { + return m_comment; + } + + void Archive::setComment(String comment) { + if (m_comment == comment) return; + m_comment = comment; + m_isModified = true; + } + + static tm _convertTimeStamp(const time_t& time, time_base_t base) { + tm* pTm; + switch (base) { + case LOCAL_TIME: + pTm = localtime(&time); + break; + case UTC_TIME: + pTm = gmtime(&time); + break; + default: + throw Exception("Time stamp with unknown time base (" + ToString((int64_t)base) + ") requested"); + } + if (!pTm) + throw Exception("Failed assembling time stamp structure"); + return *pTm; + } + + time_t Archive::timeStampCreated() const { + return m_timeCreated; + } + + time_t Archive::timeStampModified() const { + return m_timeModified; + } + + tm Archive::dateTimeCreated(time_base_t base) const { + return _convertTimeStamp(m_timeCreated, base); + } + + tm Archive::dateTimeModified(time_base_t base) const { + return _convertTimeStamp(m_timeModified, base); + } + + void Archive::removeMember(Object& parent, const Member& member) { + parent.remove(member); + m_isModified = true; } void Archive::remove(const Object& obj) { + //FIXME: Should traverse from root object and remove all members associated with this object if (!obj.uid()) return; m_allObjects.erase(obj.uid()); m_isModified = true; @@ -755,6 +920,18 @@ return m_allObjects[uid]; } + void Archive::setVersion(Object& object, Version v) { + if (!object) return; + object.setVersion(v); + m_isModified = true; + } + + void Archive::setMinVersion(Object& object, Version v) { + if (!object) return; + object.setMinVersion(v); + m_isModified = true; + } + void Archive::setEnumValue(Object& object, uint64_t value) { if (!object) return; if (!object.type().isEnum()) @@ -894,6 +1071,48 @@ return _primitiveObjectValueToString(*pObject); } + int64_t Archive::valueAsInt(const Object& object) { + if (!object) + throw Exception("Invalid object"); + if (!object.type().isInteger() && !object.type().isEnum()) + throw Exception("Object is neither an integer nor an enum"); + const Object* pObject = &object; + if (object.type().isPointer()) { + const Object& obj = objectByUID(object.uid(1)); + if (!obj) return 0; + pObject = &obj; + } + return _primitiveObjectValueToNumber(*pObject); + } + + double Archive::valueAsReal(const Object& object) { + if (!object) + throw Exception("Invalid object"); + if (!object.type().isReal()) + throw Exception("Object is not an real type"); + const Object* pObject = &object; + if (object.type().isPointer()) { + const Object& obj = objectByUID(object.uid(1)); + if (!obj) return 0; + pObject = &obj; + } + return _primitiveObjectValueToNumber(*pObject); + } + + bool Archive::valueAsBool(const Object& object) { + if (!object) + throw Exception("Invalid object"); + if (!object.type().isBool()) + throw Exception("Object is not a bool"); + const Object* pObject = &object; + if (object.type().isPointer()) { + const Object& obj = objectByUID(object.uid(1)); + if (!obj) return 0; + pObject = &obj; + } + return _primitiveObjectValueToNumber(*pObject); + } + // *************** Archive::Syncer *************** // *