#ifndef REFLECTIVE_RAPIDJSON_BINARY_REFLECTOR_H #define REFLECTIVE_RAPIDJSON_BINARY_REFLECTOR_H /*! * \file reflector.h * \brief Contains BinaryReader and BinaryWriter supporting binary (de)serialization * of primitive and custom types. */ #include "../traits.h" #include #include #include #include #include #include #include #include #include namespace ReflectiveRapidJSON { /*! * \brief The AdaptedBinarySerializable class allows considering 3rd party classes as serializable. */ template struct AdaptedBinarySerializable : public Traits::Bool { static constexpr const char *name = "AdaptedBinarySerializable"; static constexpr const char *qualifiedName = "ReflectiveRapidJSON::AdaptedBinarySerializable"; }; template struct BinarySerializable; /*! * \brief The BinaryReflector namespace contains BinaryReader and BinaryWriter for automatic binary (de)serialization. */ namespace BinaryReflector { // define traits to distinguish between "built-in" types like int, std::string, std::vector, ... and custom structs/classes template using IsBuiltInType = Traits::Any, Traits::IsIteratable, Traits::IsSpecializingAnyOf, std::is_enum>; template using IsCustomType = Traits::Not>; class BinaryDeserializer; class BinarySerializer; template > * = nullptr> void readCustomType(BinaryDeserializer &deserializer, Type &customType); template > * = nullptr> void writeCustomType(BinarySerializer &serializer, const Type &customType); class BinaryDeserializer : public IoUtilities::BinaryReader { public: BinaryDeserializer(std::istream *stream); using IoUtilities::BinaryReader::read; template > * = nullptr> void read(Type &pair); template > * = nullptr> void read(Type &pair); template > * = nullptr> void read(Type &pair); template , Traits::IsResizable> * = nullptr> void read(Type &iteratable); template , IsMultiMapOrHash> * = nullptr> void read(Type &iteratable); template , Traits::None, IsMultiMapOrHash, Traits::All, Traits::IsResizable>>> * = nullptr> void read(Type &iteratable); template > * = nullptr> void read(Type &customType); template > * = nullptr> void read(Type &customType); std::unordered_map m_pointer; }; class BinarySerializer : public IoUtilities::BinaryWriter { public: BinarySerializer(std::ostream *stream); using IoUtilities::BinaryWriter::write; template > * = nullptr> void write(const Type &pair); template > * = nullptr> void write(const Type &pointer); template > * = nullptr> void write(const Type &pointer); template , Traits::HasSize> * = nullptr> void write(const Type &iteratable); template > * = nullptr> void write(const Type &customType); template > * = nullptr> void write(const Type &customType); std::unordered_map m_pointer; }; inline BinaryDeserializer::BinaryDeserializer(std::istream *stream) : IoUtilities::BinaryReader(stream) { } template > *> void BinaryDeserializer::read(Type &pair) { read(pair.first); read(pair.second); } template > *> void BinaryDeserializer::read(Type &pointer) { if (!readBool()) { pointer.reset(); return; } pointer = std::make_unique(); read(*pointer); } template > *> void BinaryDeserializer::read(Type &pointer) { const auto occurence = readByte(); if (!occurence) { // pointer not set pointer.reset(); return; } const auto id = readVariableLengthUIntBE(); if (occurence == 1) { // first occurence: make a new pointer m_pointer[id] = pointer = std::make_shared(); read(*pointer); return; } // further occurences: copy previous pointer try { pointer = std::any_cast(m_pointer[id]); } catch (const std::bad_any_cast) { throw ConversionUtilities::ConversionException("Referenced pointer type does not match"); } } template , Traits::IsResizable> *> void BinaryDeserializer::read(Type &iteratable) { const auto size = readVariableLengthUIntBE(); iteratable.resize(size); for (auto &element : iteratable) { read(element); } } template , IsMultiMapOrHash> *> void BinaryDeserializer::read(Type &iteratable) { const auto size = readVariableLengthUIntBE(); for (size_t i = 0; i != size; ++i) { std::pair::type, typename Type::value_type::second_type> value; read(value); iteratable.emplace(std::move(value)); } } template , Traits::None, IsMultiMapOrHash, Traits::All, Traits::IsResizable>>> *> void BinaryDeserializer::read(Type &iteratable) { const auto size = readVariableLengthUIntBE(); for (size_t i = 0; i != size; ++i) { typename Type::value_type value; read(value); iteratable.emplace(std::move(value)); } } template > *> void BinaryDeserializer::read(Type &enumValue) { typename std::underlying_type::type value; read(value); enumValue = static_cast(value); } template > *> void BinaryDeserializer::read(Type &customType) { readCustomType(*this, customType); } inline BinarySerializer::BinarySerializer(std::ostream *stream) : IoUtilities::BinaryWriter(stream) { } template > *> void BinarySerializer::write(const Type &pair) { write(pair.first); write(pair.second); } template > *> void BinarySerializer::write(const Type &pointer) { const bool hasValue = pointer != nullptr; writeBool(hasValue); if (hasValue) { write(*pointer); } } template > *> void BinarySerializer::write(const Type &pointer) { if (pointer == nullptr) { writeByte(0); return; } const auto id = reinterpret_cast(pointer.get()); auto &alreadyWritten = m_pointer[id]; writeByte(alreadyWritten ? 2 : 1); writeVariableLengthUIntBE(id); if (!alreadyWritten) { alreadyWritten = true; write(*pointer); } } template , Traits::HasSize> *> void BinarySerializer::write(const Type &iteratable) { writeVariableLengthUIntBE(iteratable.size()); for (const auto &element : iteratable) { write(element); } } template > *> void BinarySerializer::write(const Type &enumValue) { write(static_cast::type>(enumValue)); } template > *> void BinarySerializer::write(const Type &customType) { writeCustomType(*this, customType); } } // namespace BinaryReflector } // namespace ReflectiveRapidJSON #endif // REFLECTIVE_RAPIDJSON_BINARY_REFLECTOR_H