#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 /// \cond class BinaryReflectorTests; /// \endcond 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"; }; using BinaryVersion = std::uint64_t; 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, IsVariant>; template using IsCustomType = Traits::Not>; class BinaryDeserializer; class BinarySerializer; template > * = nullptr> BinaryVersion readCustomType(BinaryDeserializer &deserializer, Type &customType); template > * = nullptr> void writeCustomType(BinarySerializer &serializer, const Type &customType, BinaryVersion version = 0); class BinaryDeserializer : public CppUtilities::BinaryReader { friend class ::BinaryReflectorTests; public: explicit BinaryDeserializer(std::istream *stream); using CppUtilities::BinaryReader::read; template > * = nullptr> void read(Type &pair); template > * = nullptr> void read(Type &pointer); template > * = nullptr> void read(Type &pointer); 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 &enumValue); template > * = nullptr> void read(Type &variant); template > * = nullptr> BinaryVersion read(Type &customType); private: std::unordered_map m_pointer; }; class BinarySerializer : public CppUtilities::BinaryWriter { friend class ::BinaryReflectorTests; public: explicit BinarySerializer(std::ostream *stream); using CppUtilities::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 &enumValue); template > * = nullptr> void write(const Type &variant); template > * = nullptr> void write(const Type &builtInType, BinaryVersion version); template > * = nullptr> void write(const Type &customType, BinaryVersion version = 0); private: std::unordered_map m_pointer; }; inline BinaryDeserializer::BinaryDeserializer(std::istream *stream) : CppUtilities::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) { auto mode = readByte(); if (!mode) { // pointer not set pointer.reset(); return; } const auto id = (mode & 0x4) ? readUInt64BE() : readVariableLengthUIntBE(); // the 3rd bit being flagged indicates a big ID if ((mode & 0x3) == 1) { // first occurrence: make a new pointer m_pointer[id] = pointer = std::make_shared(); read(*pointer); return; } // further occurrences: copy previous pointer try { pointer = std::any_cast(m_pointer[id]); } catch (const std::bad_any_cast &) { throw CppUtilities::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); } /// \cond namespace Detail { template void readVariantValueByRuntimeIndex(std::size_t runtimeIndex, Variant &variant, BinaryDeserializer &deserializer) { if constexpr (compiletimeIndex < std::variant_size_v) { if (compiletimeIndex == runtimeIndex) { if constexpr (std::is_same_v, std::monostate>) { variant = std::monostate{}; } else { deserializer.read(variant.template emplace()); } } else { readVariantValueByRuntimeIndex(runtimeIndex, variant, deserializer); } } else { throw CppUtilities::ConversionException("Variant index is out of expected range"); } } } // namespace Detail /// \endcond template > *> void BinaryDeserializer::read(Type &variant) { Detail::readVariantValueByRuntimeIndex(readByte(), variant, *this); } template > *> BinaryVersion BinaryDeserializer::read(Type &customType) { return readCustomType(*this, customType); } inline BinarySerializer::BinarySerializer(std::ostream *stream) : CppUtilities::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()); const auto bigId = id >= 0x80000000000000; auto &alreadyWritten = m_pointer[id]; std::uint8_t mode = alreadyWritten ? 2 : 1; if (bigId) { mode = mode | 0x4; // "flag" 3rd bit to indicate big ID } writeByte(mode); if (bigId) { writeUInt64BE(id); } else { 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 &variant) { static_assert(std::variant_size_v < std::numeric_limits::max(), "index will not exceed limit"); writeByte(static_cast(variant.index())); std::visit( [this](const auto &valueOfActualType) { if constexpr (!std::is_same_v, std::monostate>) { write(valueOfActualType); } else { CPP_UTILITIES_UNUSED(this) } }, variant); } template > *> void BinarySerializer::write(const Type &builtInType, BinaryVersion version) { CPP_UTILITIES_UNUSED(version) write(builtInType); } template > *> void BinarySerializer::write(const Type &customType, BinaryVersion version) { writeCustomType(*this, customType, version); } } // namespace BinaryReflector } // namespace ReflectiveRapidJSON #endif // REFLECTIVE_RAPIDJSON_BINARY_REFLECTOR_H