From 44c6b8c6091a32c2f356e7dbfb290cad03d9e189 Mon Sep 17 00:00:00 2001 From: Martchus Date: Sun, 25 Jul 2021 19:19:02 +0200 Subject: [PATCH] Throw exception during binary deserialization when version is not supported --- README.md | 11 +++++++---- generator/binaryserializationcodegenerator.cpp | 9 ++++++--- lib/binary/serializable.h | 3 +++ lib/versioning.h | 16 ++++++++++++++++ 4 files changed, 32 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 6dc71e2..8b9d394 100644 --- a/README.md +++ b/README.md @@ -364,7 +364,7 @@ The binary (de)serializer supports *very* experimental versioning. Otherwise add members is a breaking change. The versioning looks like this:
-// enable definition of the macros shown below (otherwise use long macros defined  in
+// enable definition of the macros shown below (otherwise use long macros defined in
 // `lib/versioning.h`)
 #define REFLECTIVE_RAPIDJSON_SHORT_MACROS
 
@@ -379,7 +379,7 @@ as_of_version(3):
     std::uint32_t bar; // will be read/written if outer scope version is >= 3
 };
 
-// example struct where version is serialized/deserialized; defaults to version when writing
+// example struct where version is serialized/deserialized; defaults to version 3 when writing
 struct Example : public BinarySerializable<Example, 3> {
     Nested nested;      // will be read/written in any case, version is "propagated down"
     std::uint32_t a, b; // will be read/written in any case
@@ -395,8 +395,11 @@ as_of_version(4):
 };
 
-A mechanism to catch unsupported versions during deserialization is yet to be implemented. -Additionally, the versioning is completely untested at this point. +The version specified as template argument is also assumed to be the highest supported version. +If a higher version is encountered during deserialization, `BinaryVersionNotSupported` is thrown +and the deserialization aborted. + +Note that the versioning is mostly untested at this point. ### Remarks * Static member variables and member functions are currently ignored by the generator. diff --git a/generator/binaryserializationcodegenerator.cpp b/generator/binaryserializationcodegenerator.cpp index b8b9b9b..83bce11 100644 --- a/generator/binaryserializationcodegenerator.cpp +++ b/generator/binaryserializationcodegenerator.cpp @@ -308,10 +308,13 @@ void BinarySerializationCodeGenerator::generate(std::ostream &os) const << ">(BinaryDeserializer &deserializer, ::" << relevantClass.qualifiedName << " &customObject, BinaryVersion version)\n{\n"; if (!relevantClass.relevantBase.empty()) { os << " // read version\n" - " if constexpr (Versioning<" + " using V = Versioning<" << relevantClass.relevantBase - << ">::enabled) {\n" - " version = deserializer.readVariableLengthUIntBE();\n" + << ">;\n" + " if constexpr (V::enabled) {\n" + " V::assertVersion(version = deserializer.readVariableLengthUIntBE(), \"" + << relevantClass.qualifiedName + << "\");\n" " }\n"; } os << " // read base classes\n"; diff --git a/lib/binary/serializable.h b/lib/binary/serializable.h index ff0cd26..e5f564d 100644 --- a/lib/binary/serializable.h +++ b/lib/binary/serializable.h @@ -14,10 +14,13 @@ namespace ReflectiveRapidJSON { +using BinaryVersionNotSupported = VersionNotSupported; + /*! * \brief The BinarySerializable class provides the CRTP-base for (de)serializable objects. */ template struct BinarySerializable { + using VersionNotSupported = BinaryVersionNotSupported; void toBinary(std::ostream &outputStream, BinaryVersion version = 0) const; BinaryVersion restoreFromBinary(std::istream &inputStream); static Type fromBinary(std::istream &inputStream); diff --git a/lib/versioning.h b/lib/versioning.h index ec7fa39..bd05de7 100644 --- a/lib/versioning.h +++ b/lib/versioning.h @@ -26,6 +26,11 @@ public CPP_UTILITIES_TRAITS_DEFINE_TYPE_CHECK(IsVersioned, T::version); +template struct VersionNotSupported { + VersionType presentVersion = 0, maxVersion = 0; + const char *record = nullptr; +}; + template ::value> struct Versioning { static constexpr auto enabled = false; }; @@ -33,10 +38,21 @@ template ::value> struct Versi template struct Versioning { static constexpr auto enabled = Type::version != 0; static constexpr auto serializationDefault = Type::version; + static constexpr auto maxSupported = Type::version; static constexpr auto applyDefault(decltype(serializationDefault) version) { return version ? version : serializationDefault; } + static constexpr auto isSupported(decltype(maxSupported) version) + { + return version <= maxSupported; + } + static constexpr auto assertVersion(decltype(maxSupported) version, const char *record = nullptr) + { + if (!isSupported(version)) { + throw typename Type::VersionNotSupported({ .presentVersion = version, .maxVersion = maxSupported, .record = record }); + } + } }; } // namespace ReflectiveRapidJSON