From a9c05372d526f35af31ae5906793f71fdcd4ea3e Mon Sep 17 00:00:00 2001 From: Nicholas Van Sickle Date: Tue, 30 Nov 2021 18:00:59 -0800 Subject: [PATCH] API tweaks - Use ref types - Support using rapidjson::Value in lieu of rapidjson::Document - Use the existing JSON comparison util function in tests Signed-off-by: Nicholas Van Sickle --- .../AzCore/DOM/Backends/JSON/JsonBackend.cpp | 6 +- .../AzCore/DOM/Backends/JSON/JsonBackend.h | 6 +- .../Backends/JSON/JsonSerializationUtils.cpp | 374 +++++++++--------- .../Backends/JSON/JsonSerializationUtils.h | 57 ++- .../AzCore/AzCore/DOM/DomBackend.cpp | 14 +- Code/Framework/AzCore/AzCore/DOM/DomBackend.h | 12 +- .../AzCore/Tests/DOM/DomJsonBenchmarks.cpp | 4 +- .../AzCore/Tests/DOM/DomJsonTests.cpp | 88 +---- 8 files changed, 265 insertions(+), 296 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/DOM/Backends/JSON/JsonBackend.cpp b/Code/Framework/AzCore/AzCore/DOM/Backends/JSON/JsonBackend.cpp index db7b209310..288d3c0699 100644 --- a/Code/Framework/AzCore/AzCore/DOM/Backends/JSON/JsonBackend.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/Backends/JSON/JsonBackend.cpp @@ -10,17 +10,17 @@ namespace AZ::Dom { - Visitor::Result JsonBackend::ReadFromStringInPlace(AZStd::string& buffer, Visitor* visitor) + Visitor::Result JsonBackend::ReadFromStringInPlace(AZStd::string& buffer, Visitor& visitor) { return Json::VisitSerializedJsonInPlace(buffer, visitor); } - Visitor::Result JsonBackend::ReadFromString(AZStd::string_view buffer, Lifetime lifetime, Visitor* visitor) + Visitor::Result JsonBackend::ReadFromString(AZStd::string_view buffer, Lifetime lifetime, Visitor& visitor) { return Json::VisitSerializedJson(buffer, lifetime, visitor); } - AZStd::unique_ptr JsonBackend::CreateStreamWriter(AZ::IO::GenericStream* stream) + AZStd::unique_ptr JsonBackend::CreateStreamWriter(AZ::IO::GenericStream& stream) { return Json::GetJsonStreamWriter(stream, Json::OutputFormatting::PrettyPrintedJson); } diff --git a/Code/Framework/AzCore/AzCore/DOM/Backends/JSON/JsonBackend.h b/Code/Framework/AzCore/AzCore/DOM/Backends/JSON/JsonBackend.h index 9de3db573d..536fec7418 100644 --- a/Code/Framework/AzCore/AzCore/DOM/Backends/JSON/JsonBackend.h +++ b/Code/Framework/AzCore/AzCore/DOM/Backends/JSON/JsonBackend.h @@ -16,8 +16,8 @@ namespace AZ::Dom class JsonBackend final : public Backend { public: - Visitor::Result ReadFromStringInPlace(AZStd::string& buffer, Visitor* visitor) override; - Visitor::Result ReadFromString(AZStd::string_view buffer, Lifetime lifetime, Visitor* visitor) override; - AZStd::unique_ptr CreateStreamWriter(AZ::IO::GenericStream* stream) override; + Visitor::Result ReadFromStringInPlace(AZStd::string& buffer, Visitor& visitor) override; + Visitor::Result ReadFromString(AZStd::string_view buffer, Lifetime lifetime, Visitor& visitor) override; + AZStd::unique_ptr CreateStreamWriter(AZ::IO::GenericStream& stream) override; }; } // namespace AZ::Dom diff --git a/Code/Framework/AzCore/AzCore/DOM/Backends/JSON/JsonSerializationUtils.cpp b/Code/Framework/AzCore/AzCore/DOM/Backends/JSON/JsonSerializationUtils.cpp index 4cdf680aff..743ef3df43 100644 --- a/Code/Framework/AzCore/AzCore/DOM/Backends/JSON/JsonSerializationUtils.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/Backends/JSON/JsonSerializationUtils.cpp @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -24,202 +23,185 @@ namespace AZ::Dom::Json { // - // class DocumentWriter + // class RapidJsonValueWriter // - // Visitor that produces a rapidjson::Document - class DocumentWriter final : public Visitor + RapidJsonValueWriter::RapidJsonValueWriter(rapidjson::Value& outputValue, rapidjson::Value::AllocatorType& allocator) + : m_result(outputValue) + , m_allocator(allocator) { - public: - VisitorFlags GetVisitorFlags() const override + } + + VisitorFlags RapidJsonValueWriter::GetVisitorFlags() const + { + return VisitorFlags::SupportsRawKeys | VisitorFlags::SupportsArrays | VisitorFlags::SupportsObjects; + } + + Visitor::Result RapidJsonValueWriter::Null() + { + CurrentValue().SetNull(); + return FinishWrite(); + } + + Visitor::Result RapidJsonValueWriter::Bool(bool value) + { + CurrentValue().SetBool(value); + return FinishWrite(); + } + + Visitor::Result RapidJsonValueWriter::Int64(AZ::s64 value) + { + CurrentValue().SetInt64(value); + return FinishWrite(); + } + + Visitor::Result RapidJsonValueWriter::Uint64(AZ::u64 value) + { + CurrentValue().SetUint64(value); + return FinishWrite(); + } + + Visitor::Result RapidJsonValueWriter::Double(double value) + { + CurrentValue().SetDouble(value); + return FinishWrite(); + } + + Visitor::Result RapidJsonValueWriter::String(AZStd::string_view value, Lifetime lifetime) + { + if (lifetime == Lifetime::Temporary) { - return VisitorFlags::SupportsRawKeys | VisitorFlags::SupportsArrays | VisitorFlags::SupportsObjects; + CurrentValue().SetString(value.data(), static_cast(value.length()), m_allocator); + } + else + { + CurrentValue().SetString(value.data(), static_cast(value.length())); + } + return FinishWrite(); + } + + Visitor::Result RapidJsonValueWriter::StartObject() + { + CurrentValue().SetObject(); + + const bool isObject = true; + m_entryStack.emplace_front(isObject, CurrentValue()); + return VisitorSuccess(); + } + + Visitor::Result RapidJsonValueWriter::EndObject(AZ::u64 attributeCount) + { + if (m_entryStack.empty()) + { + return VisitorFailure(VisitorErrorCode::InternalError, "EndObject called without a matching BeginObject call"); } - Result Null() override + if (!m_entryStack.front().m_isObject) { - CurrentValue().SetNull(); - return FinishWrite(); + return VisitorFailure(VisitorErrorCode::InternalError, "Expected EndArray and received EndObject instead"); } - Result Bool(bool value) override + if (m_entryStack.front().m_entryCount != attributeCount) { - CurrentValue().SetBool(value); - return FinishWrite(); + return FormatVisitorFailure( + VisitorErrorCode::InternalError, "EndObject: Expected %lu attributes but received %lu attributes instead", attributeCount, + m_entryStack.front().m_entryCount); } - Result Int64(AZ::s64 value) override + m_entryStack.pop_front(); + return FinishWrite(); + } + + Visitor::Result RapidJsonValueWriter::Key(AZ::Name key) + { + return RawKey(key.GetStringView(), Lifetime::Persistent); + } + + Visitor::Result RapidJsonValueWriter::RawKey(AZStd::string_view key, Lifetime lifetime) + { + AZ_Assert(!m_entryStack.empty(), "Attempmted to push a key with no object"); + AZ_Assert(m_entryStack.front().m_isObject, "Attempted to push a key to an array"); + if (lifetime == Lifetime::Persistent) { - CurrentValue().SetInt64(value); - return FinishWrite(); + m_entryStack.front().m_key.SetString(key.data(), static_cast(key.size())); + } + else + { + m_entryStack.front().m_key.SetString(key.data(), static_cast(key.size()), m_allocator); + } + return VisitorSuccess(); + } + + Visitor::Result RapidJsonValueWriter::StartArray() + { + CurrentValue().SetArray(); + + const bool isObject = false; + m_entryStack.emplace_front(isObject, CurrentValue()); + return VisitorSuccess(); + } + + Visitor::Result RapidJsonValueWriter::EndArray(AZ::u64 elementCount) + { + if (m_entryStack.empty()) + { + return VisitorFailure(VisitorErrorCode::InternalError, "EndArray called without a matching BeginArray call"); } - Result Uint64(AZ::u64 value) override + if (m_entryStack.front().m_isObject) { - CurrentValue().SetUint64(value); - return FinishWrite(); + return VisitorFailure(VisitorErrorCode::InternalError, "Expected EndObject and received EndArray instead"); } - Result Double(double value) override + if (m_entryStack.front().m_entryCount != elementCount) { - CurrentValue().SetDouble(value); - return FinishWrite(); + return FormatVisitorFailure( + VisitorErrorCode::InternalError, "EndArray: Expected %lu elements but received %lu elements instead", elementCount, + m_entryStack.front().m_entryCount); } - Result String(AZStd::string_view value, Lifetime lifetime) override - { - if (lifetime == Lifetime::Temporary) - { - CurrentValue().SetString(value.data(), static_cast(value.length()), m_result.GetAllocator()); - } - else - { - CurrentValue().SetString(value.data(), static_cast(value.length())); - } - return FinishWrite(); - } + m_entryStack.pop_front(); + return FinishWrite(); + } - Result StartObject() override + Visitor::Result RapidJsonValueWriter::FinishWrite() + { + if (m_entryStack.empty()) { - CurrentValue().SetObject(); - - const bool isObject = true; - m_entryStack.emplace_front(isObject, CurrentValue()); return VisitorSuccess(); } - Result EndObject(AZ::u64 attributeCount) override + // Retrieve the top value of the stack and replace it with a null value + rapidjson::Value value; + m_entryStack.front().m_value.Swap(value); + ++m_entryStack.front().m_entryCount; + + if (m_entryStack.front().m_key.IsString()) { - if (m_entryStack.empty()) - { - return VisitorFailure(VisitorErrorCode::InternalError, "EndObject called without a matching BeginObject call"); - } - - if (!m_entryStack.front().m_isObject) - { - return VisitorFailure(VisitorErrorCode::InternalError, "Expected EndArray and received EndObject instead"); - } - - if (m_entryStack.front().m_entryCount != attributeCount) - { - return FormatVisitorFailure( - VisitorErrorCode::InternalError, "EndObject: Expected %lu attributes but received %lu attributes instead", - attributeCount, m_entryStack.front().m_entryCount); - } - - m_entryStack.pop_front(); - return FinishWrite(); + m_entryStack.front().m_container.AddMember(m_entryStack.front().m_key.Move(), AZStd::move(value), m_allocator); + m_entryStack.front().m_key.SetNull(); + } + else + { + m_entryStack.front().m_container.PushBack(AZStd::move(value), m_allocator); } - Result Key(AZ::Name key) override + return VisitorSuccess(); + } + + rapidjson::Value& RapidJsonValueWriter::CurrentValue() + { + if (m_entryStack.empty()) { - return RawKey(key.GetStringView(), Lifetime::Persistent); + return m_result; } + return m_entryStack.front().m_value; + } - Result RawKey(AZStd::string_view key, Lifetime lifetime) override - { - AZ_Assert(!m_entryStack.empty(), "Attempmted to push a key with no object"); - AZ_Assert(m_entryStack.front().m_isObject, "Attempted to push a key to an array"); - if (lifetime == Lifetime::Persistent) - { - m_entryStack.front().m_key.SetString(key.data(), static_cast(key.size())); - } - else - { - m_entryStack.front().m_key.SetString(key.data(), static_cast(key.size()), m_result.GetAllocator()); - } - return VisitorSuccess(); - } - - Result StartArray() override - { - CurrentValue().SetArray(); - - const bool isObject = false; - m_entryStack.emplace_front(isObject, CurrentValue()); - return VisitorSuccess(); - } - - Result EndArray(AZ::u64 elementCount) override - { - if (m_entryStack.empty()) - { - return VisitorFailure(VisitorErrorCode::InternalError, "EndArray called without a matching BeginArray call"); - } - - if (m_entryStack.front().m_isObject) - { - return VisitorFailure(VisitorErrorCode::InternalError, "Expected EndObject and received EndArray instead"); - } - - if (m_entryStack.front().m_entryCount != elementCount) - { - return FormatVisitorFailure( - VisitorErrorCode::InternalError, "EndArray: Expected %lu elements but received %lu elements instead", elementCount, - m_entryStack.front().m_entryCount); - } - - m_entryStack.pop_front(); - return FinishWrite(); - } - - rapidjson::Document&& TakeDocument() - { - return AZStd::move(m_result); - } - - private: - Result FinishWrite() - { - if (m_entryStack.empty()) - { - return VisitorSuccess(); - } - - // Retrieve the top value of the stack and replace it with a null value - rapidjson::Value value; - m_entryStack.front().m_value.Swap(value); - ++m_entryStack.front().m_entryCount; - - if (m_entryStack.front().m_key.IsString()) - { - m_entryStack.front().m_container.AddMember(m_entryStack.front().m_key.Move(), AZStd::move(value), m_result.GetAllocator()); - m_entryStack.front().m_key.SetNull(); - } - else - { - m_entryStack.front().m_container.PushBack(AZStd::move(value), m_result.GetAllocator()); - } - - return VisitorSuccess(); - } - - rapidjson::Value& CurrentValue() - { - if (m_entryStack.empty()) - { - return m_result; - } - return m_entryStack.front().m_value; - } - - struct ValueInfo - { - ValueInfo(bool isObject, rapidjson::Value& container) - : m_isObject(isObject) - , m_container(container) - { - } - - rapidjson::Value m_key; - rapidjson::Value m_value; - rapidjson::Value& m_container; - AZ::u64 m_entryCount = 0; - bool m_isObject; - }; - - rapidjson::Document m_result; - AZStd::deque m_entryStack; - }; + RapidJsonValueWriter::ValueInfo::ValueInfo(bool isObject, rapidjson::Value& container) + : m_isObject(isObject) + , m_container(container) + { + } // // class StreamWriter @@ -503,36 +485,36 @@ namespace AZ::Dom::Json // // Serialized JSON util functions // - AZStd::unique_ptr GetJsonStreamWriter(AZ::IO::GenericStream* stream, OutputFormatting format) + AZStd::unique_ptr GetJsonStreamWriter(AZ::IO::GenericStream& stream, OutputFormatting format) { if (format == OutputFormatting::MinifiedJson) { using WriterType = rapidjson::Writer; - return AZStd::make_unique>(stream); + return AZStd::make_unique>(&stream); } else { using WriterType = rapidjson::PrettyWriter; - return AZStd::make_unique>(stream); + return AZStd::make_unique>(&stream); } } - Visitor::Result VisitSerializedJson(AZStd::string_view buffer, Lifetime lifetime, Visitor* visitor) + Visitor::Result VisitSerializedJson(AZStd::string_view buffer, Lifetime lifetime, Visitor& visitor) { rapidjson::Reader reader; rapidjson::MemoryStream stream(buffer.data(), buffer.size()); - JsonReadHandler handler(visitor, lifetime); + JsonReadHandler handler(&visitor, lifetime); constexpr int flags = rapidjson::kParseCommentsFlag; reader.Parse(stream, handler); return handler.TakeOutcome(); } - Visitor::Result VisitSerializedJsonInPlace(AZStd::string& buffer, Visitor* visitor) + Visitor::Result VisitSerializedJsonInPlace(AZStd::string& buffer, Visitor& visitor) { rapidjson::Reader reader; AzStringStream stream(buffer); - JsonReadHandler handler(visitor, Lifetime::Persistent); + JsonReadHandler handler(&visitor, Lifetime::Persistent); constexpr int flags = rapidjson::kParseCommentsFlag | rapidjson::kParseInsituFlag; reader.Parse(stream, handler); @@ -544,16 +526,24 @@ namespace AZ::Dom::Json // AZ::Outcome WriteToRapidJsonDocument(Backend::WriteCallback writeCallback) { - DocumentWriter writer; - auto result = writeCallback(&writer); + rapidjson::Document document; + RapidJsonValueWriter writer(document, document.GetAllocator()); + auto result = writeCallback(writer); if (!result.IsSuccess()) { return AZ::Failure(result.TakeError().FormatVisitorErrorMessage()); } - return AZ::Success(writer.TakeDocument()); + return AZ::Success(AZStd::move(document)); } - Visitor::Result VisitRapidJsonValue(const rapidjson::Value& value, Visitor* visitor, Lifetime lifetime) + Visitor::Result WriteToRapidJsonValue( + rapidjson::Value& value, rapidjson::Value::AllocatorType& allocator, Backend::WriteCallback writeCallback) + { + RapidJsonValueWriter writer(value, allocator); + return writeCallback(writer); + } + + Visitor::Result VisitRapidJsonValue(const rapidjson::Value& value, Visitor& visitor, Lifetime lifetime) { struct EndArrayMarker { @@ -579,7 +569,7 @@ namespace AZ::Dom::Json Visitor::Result result = AZ::Success(); AZStd::visit( - [visitor, &entryStack, &entryCountStack, &result, lifetime](auto&& arg) + [&visitor, &entryStack, &entryCountStack, &result, lifetime](auto&& arg) { using Alternative = AZStd::decay_t; if constexpr (AZStd::is_same_v) @@ -593,18 +583,18 @@ namespace AZ::Dom::Json switch (currentValue.GetType()) { case rapidjson::kNullType: - result = visitor->Null(); + result = visitor.Null(); break; case rapidjson::kFalseType: - result = visitor->Bool(false); + result = visitor.Bool(false); break; case rapidjson::kTrueType: - result = visitor->Bool(true); + result = visitor.Bool(true); break; case rapidjson::kObjectType: entryStack.push(EndObjectMarker{}); entryCountStack.push(0); - result = visitor->StartObject(); + result = visitor.StartObject(); for (auto it = currentValue.MemberEnd(); it != currentValue.MemberBegin(); --it) { auto entry = (it - 1); @@ -616,7 +606,7 @@ namespace AZ::Dom::Json case rapidjson::kArrayType: entryStack.push(EndArrayMarker{}); entryCountStack.push(0); - result = visitor->StartArray(); + result = visitor.StartArray(); for (auto it = currentValue.End(); it != currentValue.Begin(); --it) { auto entry = (it - 1); @@ -624,22 +614,22 @@ namespace AZ::Dom::Json } break; case rapidjson::kStringType: - result = visitor->String( + result = visitor.String( AZStd::string_view(currentValue.GetString(), static_cast(currentValue.GetStringLength())), lifetime); break; case rapidjson::kNumberType: if (currentValue.IsFloat() || currentValue.IsDouble()) { - result = visitor->Double(currentValue.GetDouble()); + result = visitor.Double(currentValue.GetDouble()); } else if (currentValue.IsInt64() || currentValue.IsInt()) { - result = visitor->Int64(currentValue.GetInt64()); + result = visitor.Int64(currentValue.GetInt64()); } else { - result = visitor->Uint64(currentValue.GetUint64()); + result = visitor.Uint64(currentValue.GetUint64()); } break; default: @@ -648,23 +638,23 @@ namespace AZ::Dom::Json } else if constexpr (AZStd::is_same_v) { - result = visitor->EndArray(entryCountStack.top()); + result = visitor.EndArray(entryCountStack.top()); entryCountStack.pop(); } else if constexpr (AZStd::is_same_v) { - result = visitor->EndObject(entryCountStack.top()); + result = visitor.EndObject(entryCountStack.top()); entryCountStack.pop(); } else if constexpr (AZStd::is_same_v) { - if (visitor->SupportsRawKeys()) + if (visitor.SupportsRawKeys()) { - visitor->RawKey(arg, lifetime); + visitor.RawKey(arg, lifetime); } else { - visitor->Key(AZ::Name(arg)); + visitor.Key(AZ::Name(arg)); } } }, diff --git a/Code/Framework/AzCore/AzCore/DOM/Backends/JSON/JsonSerializationUtils.h b/Code/Framework/AzCore/AzCore/DOM/Backends/JSON/JsonSerializationUtils.h index 1f9b58ac51..007134227e 100644 --- a/Code/Framework/AzCore/AzCore/DOM/Backends/JSON/JsonSerializationUtils.h +++ b/Code/Framework/AzCore/AzCore/DOM/Backends/JSON/JsonSerializationUtils.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -25,36 +26,84 @@ namespace AZ::Dom::Json PrettyPrintedJson, //!< Formats JSON in a pretty printed form, focusing on legibility to readers. }; + //! Visitor that feeds into a rapidjson::Value + class RapidJsonValueWriter final : public Visitor + { + public: + RapidJsonValueWriter(rapidjson::Value& outputValue, rapidjson::Value::AllocatorType& allocator); + + VisitorFlags GetVisitorFlags() const override; + Result Null() override; + Result Bool(bool value) override; + Result Int64(AZ::s64 value) override; + Result Uint64(AZ::u64 value) override; + Result Double(double value) override; + + Result String(AZStd::string_view value, Lifetime lifetime) override; + Result StartObject() override; + Result EndObject(AZ::u64 attributeCount) override; + Result Key(AZ::Name key) override; + Result RawKey(AZStd::string_view key, Lifetime lifetime) override; + Result StartArray() override; + Result EndArray(AZ::u64 elementCount) override; + + private: + Result FinishWrite(); + rapidjson::Value& CurrentValue(); + + struct ValueInfo + { + ValueInfo(bool isObject, rapidjson::Value& container); + + rapidjson::Value m_key; + rapidjson::Value m_value; + rapidjson::Value& m_container; + AZ::u64 m_entryCount = 0; + bool m_isObject; + }; + + rapidjson::Value& m_result; + rapidjson::Value::AllocatorType& m_allocator; + AZStd::deque m_entryStack; + }; + //! Creates a Visitor that will write serialized JSON to the specified stream. //! \param stream The stream the visitor will write to. //! \param format The format to write in. //! \return A Visitor that will write to stream when visited. AZStd::unique_ptr GetJsonStreamWriter( - AZ::IO::GenericStream* stream, OutputFormatting format = OutputFormatting::PrettyPrintedJson); + AZ::IO::GenericStream& stream, OutputFormatting format = OutputFormatting::PrettyPrintedJson); //! Reads serialized JSON from a string and applies it to a visitor. //! \param buffer The UTF-8 serialized JSON to read. //! \param lifetime Specifies the lifetime of the specified buffer. If the string specified by buffer might be deallocated, //! ensure specify Lifetime::Temporary is specified. //! \param visitor The visitor to visit with the JSON buffer's contents. //! \return The aggregate result specifying whether the visitor operations were successful. - Visitor::Result VisitSerializedJson(AZStd::string_view buffer, Lifetime lifetime, Visitor* visitor); + Visitor::Result VisitSerializedJson(AZStd::string_view buffer, Lifetime lifetime, Visitor& visitor); //! Reads serialized JSON from a string in-place and applies it to a visitor. //! \param buffer The UTF-8 serialized JSON to read. This buffer will be modified as part of the deserialization process to //! apply null terminators. //! \param visitor The visitor to visit with the JSON buffer's contents. The strings provided to the visitor will only //! be valid for the lifetime of buffer. //! \return The aggregate result specifying whether the visitor operations were successful. - Visitor::Result VisitSerializedJsonInPlace(AZStd::string& buffer, Visitor* visitor); + Visitor::Result VisitSerializedJsonInPlace(AZStd::string& buffer, Visitor& visitor); //! Takes a visitor specified by a callback and produces a rapidjson::Document. //! \param writeCallback A callback specifying a visitor to accept to build the resulting document. //! \return An outcome with either the rapidjson::Document or an error message. AZ::Outcome WriteToRapidJsonDocument(Backend::WriteCallback writeCallback); + //! Takes a visitor specified by a callback and reads them into a rapidjson::Value. + //! \param value The value to read into, its contents will be overridden. + //! \param allocator The allocator to use when performing rapidjson allocations (generally provded by the rapidjson::Document). + //! \param writeCallback A callback specifying a visitor to accept to build the resulting document. + //! \return An outcome with either the rapidjson::Document or an error message. + Visitor::Result WriteToRapidJsonValue( + rapidjson::Value& value, rapidjson::Value::AllocatorType& allocator, Backend::WriteCallback writeCallback); //! Accepts a visitor with the contents of a rapidjson::Value. //! \param value The rapidjson::Value to apply to visitor. //! \param visitor The visitor to receive the contents of value. //! \param lifetime The lifetime to specify for visiting strings. If the rapidjson::Value might be destroyed or changed //! before the visitor is finished using these values, Lifetime::Temporary should be specified. //! \return The aggregate result specifying whether the visitor operations were successful. - Visitor::Result VisitRapidJsonValue(const rapidjson::Value& value, Visitor* visitor, Lifetime lifetime); + Visitor::Result VisitRapidJsonValue(const rapidjson::Value& value, Visitor& visitor, Lifetime lifetime); } // namespace AZ::Dom::Json diff --git a/Code/Framework/AzCore/AzCore/DOM/DomBackend.cpp b/Code/Framework/AzCore/AzCore/DOM/DomBackend.cpp index 925897c299..258975eff3 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomBackend.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomBackend.cpp @@ -10,11 +10,11 @@ #include #include -#include "DomBackend.h" +#include namespace AZ::Dom { - Visitor::Result Backend::ReadFromStream(AZ::IO::GenericStream* stream, Visitor* visitor, size_t maxSize) + Visitor::Result Backend::ReadFromStream(AZ::IO::GenericStream* stream, Visitor& visitor, size_t maxSize) { size_t length = stream->GetLength(); if (length > maxSize) @@ -27,21 +27,21 @@ namespace AZ::Dom return ReadFromString(buffer, Lifetime::Temporary, visitor); } - Visitor::Result Backend::ReadFromStringInPlace(AZStd::string& buffer, Visitor* visitor) + Visitor::Result Backend::ReadFromStringInPlace(AZStd::string& buffer, Visitor& visitor) { return ReadFromString(buffer, Lifetime::Persistent, visitor); } - Visitor::Result Backend::WriteToStream(AZ::IO::GenericStream* stream, WriteCallback callback) + Visitor::Result Backend::WriteToStream(AZ::IO::GenericStream& stream, WriteCallback callback) { AZStd::unique_ptr writer = CreateStreamWriter(stream); - return callback(writer.get()); + return callback(*writer.get()); } Visitor::Result Backend::WriteToString(AZStd::string& buffer, WriteCallback callback) { AZ::IO::ByteContainerStream stream{&buffer}; - AZStd::unique_ptr writer = CreateStreamWriter(&stream); - return callback(writer.get()); + AZStd::unique_ptr writer = CreateStreamWriter(stream); + return callback(*writer.get()); } } diff --git a/Code/Framework/AzCore/AzCore/DOM/DomBackend.h b/Code/Framework/AzCore/AzCore/DOM/DomBackend.h index d11335262e..d1eee5d676 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomBackend.h +++ b/Code/Framework/AzCore/AzCore/DOM/DomBackend.h @@ -25,24 +25,24 @@ namespace AZ::Dom //! Attempt to read this format from the given stream into the target Visitor. //! The base implementation reads the stream into memory and calls ReadFromString. virtual Visitor::Result ReadFromStream( - AZ::IO::GenericStream* stream, Visitor* visitor, size_t maxSize = AZStd::numeric_limits::max()); + AZ::IO::GenericStream* stream, Visitor& visitor, size_t maxSize = AZStd::numeric_limits::max()); //! Attempt to read this format from a mutable string into the target Visitor. This enables some backends to //! parse without making additional string allocations. //! This string may be modified and read in place without being copied, so when calling this please ensure that: //! - The string won't be deallocated until the visitor no longer needs the values and //! - The string is safe to modify in place. //! The base implementation simply calls ReadFromString. - virtual Visitor::Result ReadFromStringInPlace(AZStd::string& buffer, Visitor* visitor); + virtual Visitor::Result ReadFromStringInPlace(AZStd::string& buffer, Visitor& visitor); //! Attempt to read this format from an immutable buffer in memory into the target Visitor. - virtual Visitor::Result ReadFromString(AZStd::string_view buffer, Lifetime lifetime, Visitor* visitor) = 0; + virtual Visitor::Result ReadFromString(AZStd::string_view buffer, Lifetime lifetime, Visitor& visitor) = 0; //! Acquire a visitor interface for writing to the target output file. - virtual AZStd::unique_ptr CreateStreamWriter(AZ::IO::GenericStream* stream) = 0; + virtual AZStd::unique_ptr CreateStreamWriter(AZ::IO::GenericStream& stream) = 0; //! A callback that accepts a Visitor, making DOM calls to inform the serializer, and returns an //! aggregate error code to indicate whether or not the operation succeeded. - using WriteCallback = AZStd::function; + using WriteCallback = AZStd::function; //! Attempt to write a value to a stream using a write callback. - Visitor::Result WriteToStream(AZ::IO::GenericStream* stream, WriteCallback callback); + Visitor::Result WriteToStream(AZ::IO::GenericStream& stream, WriteCallback callback); //! Attempt to write a value to a string using a write callback. Visitor::Result WriteToString(AZStd::string& buffer, WriteCallback callback); }; diff --git a/Code/Framework/AzCore/Tests/DOM/DomJsonBenchmarks.cpp b/Code/Framework/AzCore/Tests/DOM/DomJsonBenchmarks.cpp index 4596316213..ea72092001 100644 --- a/Code/Framework/AzCore/Tests/DOM/DomJsonBenchmarks.cpp +++ b/Code/Framework/AzCore/Tests/DOM/DomJsonBenchmarks.cpp @@ -130,7 +130,7 @@ namespace Benchmark state.ResumeTiming(); auto result = AZ::Dom::Json::WriteToRapidJsonDocument( - [&](AZ::Dom::Visitor* visitor) + [&](AZ::Dom::Visitor& visitor) { return backend.ReadFromStringInPlace(payloadCopy, visitor); }); @@ -150,7 +150,7 @@ namespace Benchmark for (auto _ : state) { auto result = AZ::Dom::Json::WriteToRapidJsonDocument( - [&](AZ::Dom::Visitor* visitor) + [&](AZ::Dom::Visitor& visitor) { return backend.ReadFromString(serializedPayload, AZ::Dom::Lifetime::Temporary, visitor); }); diff --git a/Code/Framework/AzCore/Tests/DOM/DomJsonTests.cpp b/Code/Framework/AzCore/Tests/DOM/DomJsonTests.cpp index 25deeb8a82..cde5e2eb3f 100644 --- a/Code/Framework/AzCore/Tests/DOM/DomJsonTests.cpp +++ b/Code/Framework/AzCore/Tests/DOM/DomJsonTests.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -31,78 +32,6 @@ namespace AZ::Dom::Tests UnitTest::AllocatorsFixture::TearDown(); } - static bool DeepCompare(const rapidjson::Value& lhs, const rapidjson::Value& rhs) - { - if (lhs.GetType() != rhs.GetType()) - { - return false; - } - - switch (lhs.GetType()) - { - case rapidjson::kNullType: - return true; - case rapidjson::kFalseType: - return true; - case rapidjson::kTrueType: - return true; - case rapidjson::kObjectType: - { - if (lhs.MemberCount() != rhs.MemberCount()) - { - return false; - } - - auto lhsIt = lhs.MemberBegin(); - auto rhsIt = rhs.MemberBegin(); - while (lhsIt != lhs.MemberEnd()) - { - if (lhsIt->name != rhsIt->name) - { - return false; - } - - if (!DeepCompare(lhsIt->value, rhsIt->value)) - { - return false; - } - - ++lhsIt; - ++rhsIt; - } - return true; - } - case rapidjson::kArrayType: - { - if (lhs.Size() != rhs.Size()) - { - return false; - } - - auto lhsIt = lhs.Begin(); - auto rhsIt = rhs.Begin(); - while (lhsIt != lhs.End()) - { - if (!DeepCompare(*lhsIt, *rhsIt)) - { - return false; - } - - ++lhsIt; - ++rhsIt; - } - return true; - } - case rapidjson::kStringType: - return lhs == rhs; - case rapidjson::kNumberType: - return lhs == rhs; - } - - AZ_Assert(false, "Unexpected JSON value type"); - return false; - } - rapidjson::Value CreateString(const AZStd::string& text) { rapidjson::Value key; @@ -110,7 +39,7 @@ namespace AZ::Dom::Tests return key; } - template + template void AddValue(const AZStd::string& key, T value) { m_document->AddMember(CreateString(key), rapidjson::Value(value), m_document->GetAllocator()); @@ -125,7 +54,7 @@ namespace AZ::Dom::Tests AZStd::string canonicalSerializedDocument; AZ::JsonSerializationUtils::WriteJsonString(*m_document, canonicalSerializedDocument); - auto visitDocumentFn = [this](AZ::Dom::Visitor* visitor) + auto visitDocumentFn = [this](AZ::Dom::Visitor& visitor) { return Json::VisitRapidJsonValue(*m_document, visitor, Lifetime::Persistent); }; @@ -134,7 +63,7 @@ namespace AZ::Dom::Tests { auto result = Json::WriteToRapidJsonDocument(visitDocumentFn); EXPECT_TRUE(result.IsSuccess()); - EXPECT_TRUE(DeepCompare(*m_document, result.GetValue())); + EXPECT_EQ(AZ::JsonSerialization::Compare(*m_document, result.GetValue()), AZ::JsonSerializerCompareResult::Equal); } // Document -> string @@ -149,13 +78,13 @@ namespace AZ::Dom::Tests // string -> Document { auto result = Json::WriteToRapidJsonDocument( - [&canonicalSerializedDocument](AZ::Dom::Visitor* visitor) + [&canonicalSerializedDocument](AZ::Dom::Visitor& visitor) { JsonBackend backend; return backend.ReadFromString(canonicalSerializedDocument, AZ::Dom::Lifetime::Temporary, visitor); }); EXPECT_TRUE(result.IsSuccess()); - EXPECT_TRUE(DeepCompare(*m_document, result.GetValue())); + EXPECT_EQ(AZ::JsonSerialization::Compare(*m_document, result.GetValue()), JsonSerializerCompareResult::Equal); } // string -> string @@ -164,7 +93,7 @@ namespace AZ::Dom::Tests JsonBackend backend; auto result = backend.WriteToString( serializedDocument, - [&backend, &canonicalSerializedDocument](AZ::Dom::Visitor* visitor) + [&backend, &canonicalSerializedDocument](AZ::Dom::Visitor& visitor) { return backend.ReadFromString(canonicalSerializedDocument, AZ::Dom::Lifetime::Temporary, visitor); }); @@ -282,7 +211,8 @@ namespace AZ::Dom::Tests m_document->SetObject(); m_document->AddMember(CreateString("empty_string"), CreateString(""), m_document->GetAllocator()); m_document->AddMember(CreateString("short_string"), CreateString("test"), m_document->GetAllocator()); - m_document->AddMember(CreateString("long_string"), CreateString("abcdefghijklmnopqrstuvwxyz0123456789"), m_document->GetAllocator()); + m_document->AddMember( + CreateString("long_string"), CreateString("abcdefghijklmnopqrstuvwxyz0123456789"), m_document->GetAllocator()); PerformSerializationChecks(); } } // namespace AZ::Dom::Tests