Generic DOM: Add DomBackend abstraction and JSON support
This change adds a `DomBackend` interface and a `DomBackendRegistry` for backend discovery (currently not hooked up to anything) alongside a JSON backend implementation. The JSON backend comes with a small suite of unit and performance tests. The unit tests validate generic DOM conversion to and from serialized JSON and rapidjson::Document objects (the support for which lives in `JsonSerializationUtils.h`). The performance tests show a throughput decrease compared to directly using the rapidjson serializer when mirroring our current pattern of copying strings from the serialized JSON representation, but with the coming in-memory store we have the opportunity to keep the buffer in memory and deserialize in-situ using rapidjson's API, which is consistently at least 100MiB/s faster on my machine (Ryzen Threadripper 3970X). The first parameter is the nested object complexity (N*N, objects with N keys comprised of arrays with N values) and the second parameter is the base size of the strings within each entry of this object. ``` ---------------------------------------------------------------------------------------------------------------------- Benchmark Time CPU Iterations UserCounters... ---------------------------------------------------------------------------------------------------------------------- DomJsonBenchmark/DomDeserializeToDocumentInPlace/10/5 0.050 ms 0.050 ms 10000 bytes_per_second=355.816M/s DomJsonBenchmark/DomDeserializeToDocumentInPlace/10/50 0.057 ms 0.057 ms 11200 bytes_per_second=386.064M/s DomJsonBenchmark/DomDeserializeToDocumentInPlace/100/5 4.77 ms 4.88 ms 112 bytes_per_second=364.046M/s DomJsonBenchmark/DomDeserializeToDocumentInPlace/100/500 11.6 ms 11.7 ms 64 bytes_per_second=554.518M/s DomJsonBenchmark/DomDeserializeToDocumentWithCopies/10/5 0.084 ms 0.084 ms 7467 bytes_per_second=212.55M/s DomJsonBenchmark/DomDeserializeToDocumentWithCopies/10/50 0.099 ms 0.100 ms 6400 bytes_per_second=220.608M/s DomJsonBenchmark/DomDeserializeToDocumentWithCopies/100/5 8.22 ms 8.16 ms 90 bytes_per_second=217.847M/s DomJsonBenchmark/DomDeserializeToDocumentWithCopies/100/500 23.2 ms 22.9 ms 30 bytes_per_second=283.56M/s DomJsonBenchmark/JsonUtilsDeserializeToDocument/10/5 0.070 ms 0.070 ms 11200 bytes_per_second=255.049M/s DomJsonBenchmark/JsonUtilsDeserializeToDocument/10/50 0.086 ms 0.087 ms 8960 bytes_per_second=253.258M/s DomJsonBenchmark/JsonUtilsDeserializeToDocument/100/5 6.86 ms 6.84 ms 112 bytes_per_second=260.033M/s DomJsonBenchmark/JsonUtilsDeserializeToDocument/100/500 22.8 ms 22.9 ms 32 bytes_per_second=283.158M/s ``` For `AZ::DOM::Document`, the current plan is to offer helper methods that can load from a file path or string using a given backend that can take advantage of in-place parsing by internally storing the serialized buffer. Signed-off-by: Nicholas Van Sickle <nvsickle@amazon.com>
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/DOM/Backends/JSON/JsonBackend.h>
|
||||
|
||||
#include <AzCore/DOM/DomBackendRegistry.h>
|
||||
|
||||
namespace AZ::DOM
|
||||
{
|
||||
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)
|
||||
{
|
||||
return Json::VisitSerializedJson(buffer, lifetime, visitor);
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<Visitor> JsonBackend::CreateStreamWriter(AZ::IO::GenericStream* stream)
|
||||
{
|
||||
return Json::GetJsonStreamWriter(stream, Json::OutputFormatting::PrettyPrintedJson);
|
||||
}
|
||||
|
||||
void JsonBackend::Register()
|
||||
{
|
||||
if (auto backendRegistry = BackendRegistry::Get())
|
||||
{
|
||||
backendRegistry->RegisterBackend<JsonBackend>(kName, {kExtension});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/DOM/DomBackend.h>
|
||||
#include <AzCore/DOM/Backends/JSON/JsonSerializationUtils.h>
|
||||
|
||||
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<Visitor> CreateStreamWriter(AZ::IO::GenericStream* stream) override;
|
||||
|
||||
static constexpr const char* kName = "JSON";
|
||||
static constexpr const char* kExtension = ".json";
|
||||
static void Register();
|
||||
};
|
||||
} // namespace AZ::DOM
|
||||
@@ -0,0 +1,675 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/DOM/Backends/JSON/JsonSerializationUtils.h>
|
||||
|
||||
#include <AzCore/IO/ByteContainerStream.h>
|
||||
#include <AzCore/IO/TextStreamWriters.h>
|
||||
#include <AzCore/JSON/filewritestream.h>
|
||||
#include <AzCore/JSON/memorystream.h>
|
||||
#include <AzCore/JSON/prettywriter.h>
|
||||
#include <AzCore/JSON/rapidjson.h>
|
||||
#include <AzCore/JSON/reader.h>
|
||||
#include <AzCore/JSON/writer.h>
|
||||
#include <AzCore/std/containers/queue.h>
|
||||
#include <AzCore/std/containers/stack.h>
|
||||
#include <AzCore/std/containers/variant.h>
|
||||
#include <AzCore/std/optional.h>
|
||||
|
||||
namespace AZ::DOM::Json
|
||||
{
|
||||
//
|
||||
// class DocumentWriter
|
||||
//
|
||||
// Visitor that produces a rapidjson::Document
|
||||
class DocumentWriter final : public Visitor
|
||||
{
|
||||
public:
|
||||
VisitorFlags GetVisitorFlags() const override
|
||||
{
|
||||
return VisitorFlags::SupportsRawKeys | VisitorFlags::SupportsArrays | VisitorFlags::SupportsObjects;
|
||||
}
|
||||
|
||||
Result Null() override
|
||||
{
|
||||
CurrentValue().SetNull();
|
||||
return FinishWrite();
|
||||
}
|
||||
|
||||
Result Bool(bool value) override
|
||||
{
|
||||
CurrentValue().SetBool(value);
|
||||
return FinishWrite();
|
||||
}
|
||||
|
||||
Result Int64(AZ::s64 value) override
|
||||
{
|
||||
CurrentValue().SetInt64(value);
|
||||
return FinishWrite();
|
||||
}
|
||||
|
||||
Result Uint64(AZ::u64 value) override
|
||||
{
|
||||
CurrentValue().SetUint64(value);
|
||||
return FinishWrite();
|
||||
}
|
||||
|
||||
Result Double(double value) override
|
||||
{
|
||||
CurrentValue().SetDouble(value);
|
||||
return FinishWrite();
|
||||
}
|
||||
|
||||
Result String(AZStd::string_view value, Lifetime lifetime) override
|
||||
{
|
||||
if (lifetime == Lifetime::Temporary)
|
||||
{
|
||||
CurrentValue().SetString(value.data(), static_cast<rapidjson::SizeType>(value.length()), m_result.GetAllocator());
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentValue().SetString(value.data(), static_cast<rapidjson::SizeType>(value.size()));
|
||||
}
|
||||
return FinishWrite();
|
||||
}
|
||||
|
||||
Result StartObject() override
|
||||
{
|
||||
CurrentValue().SetObject();
|
||||
|
||||
const bool isObject = true;
|
||||
m_entryStack.emplace_front(isObject, CurrentValue());
|
||||
return VisitorSuccess();
|
||||
}
|
||||
|
||||
Result EndObject(AZ::u64 attributeCount) override
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
Result Key(AZ::Name key) override
|
||||
{
|
||||
return RawKey(key.GetStringView(), Lifetime::Persistent);
|
||||
}
|
||||
|
||||
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<rapidjson::SizeType>(key.size()));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_entryStack.front().m_key.SetString(key.data(), static_cast<rapidjson::SizeType>(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)
|
||||
{
|
||||
}
|
||||
|
||||
bool m_isObject;
|
||||
rapidjson::Value& m_container;
|
||||
rapidjson::Value m_value;
|
||||
AZ::u64 m_entryCount = 0;
|
||||
rapidjson::Value m_key;
|
||||
};
|
||||
|
||||
rapidjson::Document m_result;
|
||||
AZStd::deque<ValueInfo> m_entryStack;
|
||||
AZ::u64 m_entryCount = 0;
|
||||
};
|
||||
|
||||
//
|
||||
// class StreamWriter
|
||||
//
|
||||
// Visitor that writes to a rapidjson::Writer
|
||||
template<class TWriter>
|
||||
class StreamWriter : public Visitor
|
||||
{
|
||||
public:
|
||||
StreamWriter(AZ::IO::GenericStream* stream)
|
||||
: m_streamWriter(stream)
|
||||
, m_writer(TWriter(m_streamWriter))
|
||||
{
|
||||
}
|
||||
|
||||
VisitorFlags GetVisitorFlags() const override
|
||||
{
|
||||
return VisitorFlags::SupportsRawKeys | VisitorFlags::SupportsArrays | VisitorFlags::SupportsObjects;
|
||||
}
|
||||
|
||||
Result Null() override
|
||||
{
|
||||
return CheckWrite(m_writer.Null());
|
||||
}
|
||||
|
||||
Result Bool(bool value) override
|
||||
{
|
||||
return CheckWrite(m_writer.Bool(value));
|
||||
}
|
||||
|
||||
Result Int64(AZ::s64 value) override
|
||||
{
|
||||
return CheckWrite(m_writer.Int64(value));
|
||||
}
|
||||
|
||||
Result Uint64(AZ::u64 value) override
|
||||
{
|
||||
return CheckWrite(m_writer.Uint64(value));
|
||||
}
|
||||
|
||||
Result Double(double value) override
|
||||
{
|
||||
return CheckWrite(m_writer.Double(value));
|
||||
}
|
||||
|
||||
Result String(AZStd::string_view value, Lifetime lifetime) override
|
||||
{
|
||||
const bool shouldCopy = lifetime == Lifetime::Temporary;
|
||||
return CheckWrite(m_writer.String(value.data(), static_cast<rapidjson::SizeType>(value.size()), shouldCopy));
|
||||
}
|
||||
|
||||
Result StartObject() override
|
||||
{
|
||||
return CheckWrite(m_writer.StartObject());
|
||||
}
|
||||
|
||||
Result EndObject(AZ::u64 attributeCount) override
|
||||
{
|
||||
return CheckWrite(m_writer.EndObject(static_cast<rapidjson::SizeType>(attributeCount)));
|
||||
}
|
||||
|
||||
Result Key(AZ::Name key) override
|
||||
{
|
||||
return RawKey(key.GetStringView(), Lifetime::Persistent);
|
||||
}
|
||||
|
||||
Result RawKey(AZStd::string_view key, Lifetime lifetime) override
|
||||
{
|
||||
const bool shouldCopy = lifetime == Lifetime::Temporary;
|
||||
return CheckWrite(m_writer.Key(key.data(), static_cast<rapidjson::SizeType>(key.size()), shouldCopy));
|
||||
}
|
||||
|
||||
Result StartArray() override
|
||||
{
|
||||
return CheckWrite(m_writer.StartArray());
|
||||
}
|
||||
|
||||
Result EndArray(AZ::u64 elementCount) override
|
||||
{
|
||||
return CheckWrite(m_writer.EndArray(static_cast<rapidjson::SizeType>(elementCount)));
|
||||
}
|
||||
|
||||
private:
|
||||
Result CheckWrite(bool writeSucceeded)
|
||||
{
|
||||
if (!writeSucceeded)
|
||||
{
|
||||
return VisitorFailure(VisitorErrorCode::InternalError, "Failed to write JSON");
|
||||
}
|
||||
return VisitorSuccess();
|
||||
}
|
||||
|
||||
AZ::IO::RapidJSONStreamWriter m_streamWriter;
|
||||
TWriter m_writer;
|
||||
};
|
||||
|
||||
//
|
||||
// struct JsonReadHandler
|
||||
//
|
||||
// Handler for a rapidjson::Reader that translates reads into an AZ::DOM::Visitor
|
||||
struct JsonReadHandler : public rapidjson::BaseReaderHandler<rapidjson::UTF8<>, JsonReadHandler>
|
||||
{
|
||||
public:
|
||||
JsonReadHandler(Visitor* visitor, Lifetime stringLifetime)
|
||||
: m_visitor(visitor)
|
||||
, m_stringLifetime(stringLifetime)
|
||||
, m_outcome(AZ::Success())
|
||||
{
|
||||
}
|
||||
|
||||
bool Null()
|
||||
{
|
||||
return CheckResult(m_visitor->Null());
|
||||
}
|
||||
|
||||
bool Bool(bool b)
|
||||
{
|
||||
return CheckResult(m_visitor->Bool(b));
|
||||
}
|
||||
|
||||
bool Int(int i)
|
||||
{
|
||||
return CheckResult(m_visitor->Int64(static_cast<AZ::s64>(i)));
|
||||
}
|
||||
|
||||
bool Uint(unsigned i)
|
||||
{
|
||||
return CheckResult(m_visitor->Uint64(static_cast<AZ::u64>(i)));
|
||||
}
|
||||
|
||||
bool Int64(int64_t i)
|
||||
{
|
||||
return CheckResult(m_visitor->Int64(i));
|
||||
}
|
||||
|
||||
bool Uint64(uint64_t i)
|
||||
{
|
||||
return CheckResult(m_visitor->Uint64(i));
|
||||
}
|
||||
|
||||
bool Double(double d)
|
||||
{
|
||||
return CheckResult(m_visitor->Double(d));
|
||||
}
|
||||
|
||||
bool RawNumber([[maybe_unused]] const Ch* str, [[maybe_unused]] rapidjson::SizeType length, [[maybe_unused]] bool copy)
|
||||
{
|
||||
AZ_Assert(false, "Raw numbers are unsupported");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool String(const Ch* str, rapidjson::SizeType length, bool copy)
|
||||
{
|
||||
Lifetime lifetime = m_stringLifetime;
|
||||
if (!copy)
|
||||
{
|
||||
lifetime = Lifetime::Temporary;
|
||||
}
|
||||
return CheckResult(m_visitor->String(AZStd::string_view(str, length), lifetime));
|
||||
}
|
||||
|
||||
bool StartObject()
|
||||
{
|
||||
return CheckResult(m_visitor->StartObject());
|
||||
}
|
||||
|
||||
bool Key(const Ch* str, rapidjson::SizeType length, [[maybe_unused]] bool copy)
|
||||
{
|
||||
AZStd::string_view key = AZStd::string_view(str, length);
|
||||
if (!m_visitor->SupportsRawKeys())
|
||||
{
|
||||
m_visitor->Key(AZ::Name(key));
|
||||
}
|
||||
Lifetime lifetime = m_stringLifetime;
|
||||
if (!copy)
|
||||
{
|
||||
lifetime = Lifetime::Temporary;
|
||||
}
|
||||
return CheckResult(m_visitor->RawKey(key, lifetime));
|
||||
}
|
||||
|
||||
bool EndObject([[maybe_unused]] rapidjson::SizeType memberCount)
|
||||
{
|
||||
return CheckResult(m_visitor->EndObject(memberCount));
|
||||
}
|
||||
|
||||
bool StartArray()
|
||||
{
|
||||
return CheckResult(m_visitor->StartArray());
|
||||
}
|
||||
|
||||
bool EndArray([[maybe_unused]] rapidjson::SizeType elementCount)
|
||||
{
|
||||
return CheckResult(m_visitor->EndArray(elementCount));
|
||||
}
|
||||
|
||||
Visitor::Result&& TakeOutcome()
|
||||
{
|
||||
return AZStd::move(m_outcome);
|
||||
}
|
||||
|
||||
private:
|
||||
bool CheckResult(Visitor::Result result)
|
||||
{
|
||||
if (!result.IsSuccess())
|
||||
{
|
||||
m_outcome = AZStd::move(result);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Visitor::Result m_outcome;
|
||||
Visitor* m_visitor;
|
||||
Lifetime m_stringLifetime;
|
||||
};
|
||||
|
||||
//
|
||||
// struct AzStringStream
|
||||
//
|
||||
// rapidjson stream wrapper for AZStd::string suitable for in-situ parsing
|
||||
struct AzStringStream
|
||||
{
|
||||
using Ch = char;
|
||||
|
||||
AzStringStream(AZStd::string& buffer)
|
||||
{
|
||||
m_cursor = buffer.data();
|
||||
m_begin = m_cursor;
|
||||
}
|
||||
|
||||
char Peek() const
|
||||
{
|
||||
return *m_cursor;
|
||||
}
|
||||
|
||||
char Take()
|
||||
{
|
||||
return *m_cursor++;
|
||||
}
|
||||
|
||||
size_t Tell() const
|
||||
{
|
||||
return static_cast<size_t>(m_cursor - m_begin);
|
||||
}
|
||||
|
||||
char* PutBegin()
|
||||
{
|
||||
m_write = m_cursor;
|
||||
return m_cursor;
|
||||
}
|
||||
|
||||
void Put(char c)
|
||||
{
|
||||
(*m_write++) = c;
|
||||
}
|
||||
|
||||
void Flush()
|
||||
{
|
||||
}
|
||||
|
||||
size_t PutEnd(char* begin)
|
||||
{
|
||||
return m_write - begin;
|
||||
}
|
||||
|
||||
const char* Peek4() const
|
||||
{
|
||||
AZ_Assert(false, "Not implemented, encoding is hard-coded to UTF-8");
|
||||
}
|
||||
|
||||
char* m_cursor; //!< Current read position.
|
||||
char* m_write; //!< Current write position.
|
||||
const char* m_begin; //!< Head of string.
|
||||
};
|
||||
|
||||
//
|
||||
// Serialized JSON util functions
|
||||
//
|
||||
AZStd::unique_ptr<Visitor> GetJsonStreamWriter(AZ::IO::GenericStream* stream, OutputFormatting format)
|
||||
{
|
||||
if (format == OutputFormatting::MinifiedJson)
|
||||
{
|
||||
using WriterType = rapidjson::Writer<AZ::IO::RapidJSONStreamWriter>;
|
||||
return AZStd::make_unique<StreamWriter<WriterType>>(stream);
|
||||
}
|
||||
else
|
||||
{
|
||||
using WriterType = rapidjson::PrettyWriter<AZ::IO::RapidJSONStreamWriter>;
|
||||
return AZStd::make_unique<StreamWriter<WriterType>>(stream);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
constexpr int flags = rapidjson::kParseCommentsFlag;
|
||||
reader.Parse<flags>(stream, handler);
|
||||
return handler.TakeOutcome();
|
||||
}
|
||||
|
||||
Visitor::Result VisitSerializedJsonInPlace(AZStd::string& buffer, Visitor* visitor)
|
||||
{
|
||||
rapidjson::Reader reader;
|
||||
AzStringStream stream(buffer);
|
||||
JsonReadHandler handler(visitor, Lifetime::Persistent);
|
||||
|
||||
constexpr int flags = rapidjson::kParseCommentsFlag | rapidjson::kParseInsituFlag;
|
||||
reader.Parse<flags>(stream, handler);
|
||||
return handler.TakeOutcome();
|
||||
}
|
||||
|
||||
//
|
||||
// In-memory rapidjson util functions
|
||||
//
|
||||
AZ::Outcome<rapidjson::Document, AZStd::string> WriteToRapidJsonDocument(Backend::WriteCallback writeCallback)
|
||||
{
|
||||
DocumentWriter writer;
|
||||
auto result = writeCallback(&writer);
|
||||
if (!result.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(result.TakeError().FormatVisitorErrorMessage());
|
||||
}
|
||||
return AZ::Success(writer.TakeDocument());
|
||||
}
|
||||
|
||||
Visitor::Result VisitRapidJsonValue(const rapidjson::Value& value, Visitor* visitor, Lifetime lifetime)
|
||||
{
|
||||
enum class EndMarker
|
||||
{
|
||||
EndArray,
|
||||
EndObject
|
||||
};
|
||||
|
||||
// Processing stack consists of values comprised of one of a:
|
||||
// - rapidjson::Value to process
|
||||
// - EndMarker denoting the end of an array or object
|
||||
// - string denoting a key at the beginning of a key/value pair
|
||||
using Entry = AZStd::variant<const rapidjson::Value*, EndMarker, AZStd::string_view>;
|
||||
AZStd::stack<Entry> entryStack;
|
||||
AZStd::stack<u64> entryCountStack;
|
||||
entryStack.push(&value);
|
||||
|
||||
while (!entryStack.empty())
|
||||
{
|
||||
const auto currentEntry = entryStack.top();
|
||||
entryStack.pop();
|
||||
|
||||
if (AZStd::holds_alternative<EndMarker>(currentEntry))
|
||||
{
|
||||
EndMarker marker = AZStd::get<EndMarker>(currentEntry);
|
||||
if (marker == EndMarker::EndArray)
|
||||
{
|
||||
visitor->EndArray(entryCountStack.top());
|
||||
}
|
||||
else
|
||||
{
|
||||
visitor->EndObject(entryCountStack.top());
|
||||
}
|
||||
entryCountStack.pop();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (AZStd::holds_alternative<AZStd::string_view>(currentEntry))
|
||||
{
|
||||
AZStd::string_view key = AZStd::get<AZStd::string_view>(currentEntry);
|
||||
if (visitor->SupportsRawKeys())
|
||||
{
|
||||
visitor->RawKey(key, lifetime);
|
||||
}
|
||||
else
|
||||
{
|
||||
visitor->Key(AZ::Name(key));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const rapidjson::Value& currentValue = *AZStd::get<const rapidjson::Value*>(currentEntry);
|
||||
if (!entryCountStack.empty())
|
||||
{
|
||||
++entryCountStack.top();
|
||||
}
|
||||
|
||||
Visitor::Result result = AZ::Success();
|
||||
|
||||
switch (currentValue.GetType())
|
||||
{
|
||||
case rapidjson::kNullType:
|
||||
result = visitor->Null();
|
||||
break;
|
||||
case rapidjson::kFalseType:
|
||||
result = visitor->Bool(false);
|
||||
break;
|
||||
case rapidjson::kTrueType:
|
||||
result = visitor->Bool(true);
|
||||
break;
|
||||
case rapidjson::kObjectType:
|
||||
entryStack.push(EndMarker::EndObject);
|
||||
entryCountStack.push(0);
|
||||
result = visitor->StartObject();
|
||||
for (auto it = currentValue.MemberEnd(); it != currentValue.MemberBegin(); --it)
|
||||
{
|
||||
auto entry = (it - 1);
|
||||
const AZStd::string_view key(entry->name.GetString(), static_cast<size_t>(entry->name.GetStringLength()));
|
||||
entryStack.push(&entry->value);
|
||||
entryStack.push(key);
|
||||
}
|
||||
break;
|
||||
case rapidjson::kArrayType:
|
||||
entryStack.push(EndMarker::EndArray);
|
||||
entryCountStack.push(0);
|
||||
result = visitor->StartArray();
|
||||
for (auto it = currentValue.End(); it != currentValue.Begin(); --it)
|
||||
{
|
||||
auto entry = (it - 1);
|
||||
entryStack.push(entry);
|
||||
}
|
||||
break;
|
||||
case rapidjson::kStringType:
|
||||
result = visitor->String(
|
||||
AZStd::string_view(currentValue.GetString(), static_cast<size_t>(currentValue.GetStringLength())), lifetime);
|
||||
break;
|
||||
case rapidjson::kNumberType:
|
||||
if (currentValue.IsFloat() || currentValue.IsDouble())
|
||||
{
|
||||
result = visitor->Double(currentValue.GetDouble());
|
||||
}
|
||||
else if (currentValue.IsInt64() || currentValue.IsInt())
|
||||
{
|
||||
result = visitor->Int64(currentValue.GetInt64());
|
||||
}
|
||||
else
|
||||
{
|
||||
result = visitor->Uint64(currentValue.GetUint64());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return AZ::Failure(VisitorError(VisitorErrorCode::InvalidData, "Value with invalid type specified"));
|
||||
}
|
||||
|
||||
if (!result.IsSuccess())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return AZ::Success();
|
||||
}
|
||||
} // namespace AZ::DOM::Json
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/DOM/DomBackend.h>
|
||||
#include <AzCore/DOM/DomVisitor.h>
|
||||
#include <AzCore/IO/GenericStreams.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/JSON/document.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
|
||||
namespace AZ::DOM::Json
|
||||
{
|
||||
//! Specifies how JSON should be formatted when serialized.
|
||||
enum class OutputFormatting
|
||||
{
|
||||
MinifiedJson, //!< Formats JSON in compact minified form, focusing on minimizing output size.
|
||||
PrettyPrintedJson, //!< Formats JSON in a pretty printed form, focusing on legibility to readers.
|
||||
};
|
||||
|
||||
//! 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<Visitor> GetJsonStreamWriter(
|
||||
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);
|
||||
//! 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);
|
||||
|
||||
//! 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<rapidjson::Document, AZStd::string> WriteToRapidJsonDocument(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);
|
||||
} // namespace AZ::DOM::Json
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/DOM/DomBackend.h>
|
||||
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <AzCore/IO/ByteContainerStream.h>
|
||||
#include "DomBackend.h"
|
||||
|
||||
namespace AZ::DOM
|
||||
{
|
||||
Visitor::Result Backend::ReadFromPath(AZ::IO::PathView pathName, Visitor* visitor, size_t maxFileSize)
|
||||
{
|
||||
auto readResult = AZ::Utils::ReadFile(pathName.Native(), maxFileSize);
|
||||
if (!readResult.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(VisitorError(VisitorErrorCode::InternalError, readResult.TakeError()));
|
||||
}
|
||||
|
||||
AZStd::string fileContents = readResult.TakeValue();
|
||||
return ReadFromString(fileContents, Lifetime::Temporary, visitor);
|
||||
}
|
||||
|
||||
Visitor::Result Backend::ReadFromStream(AZ::IO::GenericStream* stream, Visitor* visitor, size_t maxSize)
|
||||
{
|
||||
size_t length = stream->GetLength();
|
||||
if (length > maxSize)
|
||||
{
|
||||
return AZ::Failure(VisitorError(VisitorErrorCode::InternalError, "Stream is too large."));
|
||||
}
|
||||
AZStd::string buffer;
|
||||
buffer.resize(maxSize);
|
||||
stream->Read(length, buffer.data());
|
||||
return ReadFromString(buffer, Lifetime::Temporary, 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)
|
||||
{
|
||||
AZStd::unique_ptr<Visitor> writer = CreateStreamWriter(stream);
|
||||
return callback(writer.get());
|
||||
}
|
||||
|
||||
Visitor::Result Backend::WriteToPath(AZ::IO::PathView pathName, WriteCallback callback)
|
||||
{
|
||||
AZStd::string buffer;
|
||||
auto serializeResult = WriteToString(buffer, callback);
|
||||
if (!serializeResult.IsSuccess())
|
||||
{
|
||||
return serializeResult;
|
||||
}
|
||||
|
||||
auto writeResult = AZ::Utils::WriteFile(buffer, pathName.Native());
|
||||
if (!writeResult.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(VisitorError(VisitorErrorCode::InternalError, writeResult.TakeError()));
|
||||
}
|
||||
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
Visitor::Result Backend::WriteToString(AZStd::string& buffer, WriteCallback callback)
|
||||
{
|
||||
AZ::IO::ByteContainerStream<AZStd::string> stream{&buffer};
|
||||
AZStd::unique_ptr<Visitor> writer = CreateStreamWriter(&stream);
|
||||
return callback(writer.get());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/DOM/DomVisitor.h>
|
||||
#include <AzCore/IO/GenericStreams.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
|
||||
namespace AZ::DOM
|
||||
{
|
||||
//! Backends are registered centrally and used to transition DOM formats to and from a textual format.
|
||||
class Backend
|
||||
{
|
||||
public:
|
||||
virtual ~Backend() = default;
|
||||
|
||||
//! Attempt to read this format from the given file into the target Visitor.
|
||||
Visitor::Result ReadFromPath(
|
||||
AZ::IO::PathView pathName, Visitor* visitor, size_t maxFileSize = AZStd::numeric_limits<size_t>::max());
|
||||
//! 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<size_t>::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);
|
||||
//! 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;
|
||||
|
||||
//! Acquire a visitor interface for writing to the target output file.
|
||||
virtual AZStd::unique_ptr<Visitor> 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<Visitor::Result(Visitor*)>;
|
||||
//! Attempt to write a value to a stream using a write callback.
|
||||
Visitor::Result WriteToStream(AZ::IO::GenericStream* stream, WriteCallback callback);
|
||||
//! Attempt to write a value to a file using a write callback.
|
||||
Visitor::Result WriteToPath(AZ::IO::PathView pathName, WriteCallback callback);
|
||||
//! Attempt to write a value to a string using a write callback.
|
||||
Visitor::Result WriteToString(AZStd::string& buffer, WriteCallback callback);
|
||||
};
|
||||
} // namespace AZ::DOM
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/DOM/DomBackendRegistry.h>
|
||||
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
|
||||
namespace AZ::DOM
|
||||
{
|
||||
BackendRegistry* BackendRegistry::s_instance = nullptr;
|
||||
|
||||
BackendRegistryInterface* BackendRegistry::Get()
|
||||
{
|
||||
return AZ::Interface<BackendRegistryInterface>::Get();
|
||||
}
|
||||
|
||||
void BackendRegistry::Create()
|
||||
{
|
||||
AZ_Assert(!s_instance, "Attempted to register BackendRegistry when it's already registered");
|
||||
s_instance = aznew BackendRegistry;
|
||||
AZ::Interface<BackendRegistryInterface>::Register(s_instance);
|
||||
}
|
||||
|
||||
void BackendRegistry::Destroy()
|
||||
{
|
||||
AZ_Assert(s_instance, "Attempted to unregister a non-existent BackendRegistry");
|
||||
AZ::Interface<BackendRegistryInterface>::Unregister(s_instance);
|
||||
delete s_instance;
|
||||
s_instance = nullptr;
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<Backend> BackendRegistry::GetBackendByName(AZStd::string_view name)
|
||||
{
|
||||
auto backendIterator = m_nameToBackend.find(name);
|
||||
if (backendIterator != m_nameToBackend.end())
|
||||
{
|
||||
return backendIterator->second();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<Backend> BackendRegistry::GetBackendForExtension(AZStd::string_view extension)
|
||||
{
|
||||
auto extensionIterator = m_extensionToName.find(extension);
|
||||
if (extensionIterator != m_extensionToName.end())
|
||||
{
|
||||
return GetBackendByName(extensionIterator->second);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void BackendRegistry::RegisterBackendInternal(
|
||||
BackendRegistryInterface::BackendFactory factory, AZStd::string name, AZStd::vector<AZStd::string> extensions)
|
||||
{
|
||||
AZ_Assert(m_nameToBackend.find(name) == m_nameToBackend.end(), "DOM Backend %s already registered", name.c_str());
|
||||
m_nameToBackend.insert({name, factory});
|
||||
for (AZStd::string& extension : extensions)
|
||||
{
|
||||
AZ_Assert(m_extensionToName.find(extension) == m_extensionToName.end(), "DOM Extensions already registered", extension.c_str());
|
||||
m_extensionToName.insert({AZStd::move(extension), name});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/DOM/DomBackendRegistryInterface.h>
|
||||
#include <AzCore/std/containers/unordered_map.h>
|
||||
|
||||
namespace AZ::DOM
|
||||
{
|
||||
//! DOM backend registry implementation.
|
||||
//! \see BackendRegistryInterface
|
||||
class BackendRegistry final : public BackendRegistryInterface
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(BackendRegistry, "{95533861-6201-4E45-BD03-097A20850C48}", BackendRegistryInterface);
|
||||
AZ_CLASS_ALLOCATOR(BackendRegistry, AZ::SystemAllocator, 0);
|
||||
|
||||
AZStd::unique_ptr<Backend> GetBackendByName(AZStd::string_view name) override;
|
||||
AZStd::unique_ptr<Backend> GetBackendForExtension(AZStd::string_view extension) override;
|
||||
|
||||
//! Convenience method, gets the current instance of the BackendRegistry via AZ::Interface.
|
||||
static BackendRegistryInterface* Get();
|
||||
//! Creates a singleton BackendRegistry and registers it via AZ::Interface.
|
||||
static void Create();
|
||||
//! Destroys the singleton BackendRegistry created by Create and unregisters it from AZ::Interface.
|
||||
static void Destroy();
|
||||
|
||||
protected:
|
||||
void RegisterBackendInternal(BackendFactory factory, AZStd::string name, AZStd::vector<AZStd::string> extensions) override;
|
||||
|
||||
private:
|
||||
using BackendFactory = AZStd::function<AZStd::unique_ptr<Backend>()>;
|
||||
AZStd::unordered_map<AZStd::string, BackendFactory> m_nameToBackend;
|
||||
AZStd::unordered_map<AZStd::string, AZStd::string> m_extensionToName;
|
||||
|
||||
static BackendRegistry* s_instance;
|
||||
};
|
||||
} // namespace AZ::DOM
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/DOM/DomBackend.h>
|
||||
|
||||
namespace AZ::DOM
|
||||
{
|
||||
//! Central interface for registering and looking up backend types by file extension.
|
||||
class BackendRegistryInterface
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(BackendRegistryInterface, "{B72A88AC-DF15-4F92-9489-ABCDEA3D94E6}")
|
||||
using BackendFactory = AZStd::function<AZStd::unique_ptr<Backend>()>;
|
||||
|
||||
virtual ~BackendRegistryInterface() = default;
|
||||
|
||||
//! Registers a factory for a given backend.
|
||||
//! \param name A unique name to identify this backend.
|
||||
//! \param extensions A set of file extensions this backend supports.
|
||||
//! Extensions should by the entire file suffix including any dots (.), for example:
|
||||
//! ".json" or ".tar.gz"
|
||||
template<class TBackend>
|
||||
void RegisterBackend(AZStd::string name, AZStd::vector<AZStd::string> extensions);
|
||||
|
||||
//! Looks up a DOM backend based on its name.
|
||||
virtual AZStd::unique_ptr<Backend> GetBackendByName(AZStd::string_view name) = 0;
|
||||
|
||||
//! Looks up a DOM backend based on a file extension.
|
||||
virtual AZStd::unique_ptr<Backend> GetBackendForExtension(AZStd::string_view extension) = 0;
|
||||
|
||||
protected:
|
||||
//! Registers a factory for a given backend, called by RegisterBackend.
|
||||
//! \param factory The factory function to create new backend instances.
|
||||
//! \param name The unique name to identify this backend.
|
||||
//! \param extensions A set of file extensions this backend supports.
|
||||
//! \see RegisterBackend
|
||||
virtual void RegisterBackendInternal(BackendFactory factory, AZStd::string name, AZStd::vector<AZStd::string> extensions) = 0;
|
||||
};
|
||||
|
||||
template<class TBackend>
|
||||
void BackendRegistryInterface::RegisterBackend(AZStd::string name, AZStd::vector<AZStd::string> extensions)
|
||||
{
|
||||
RegisterBackendInternal([](){
|
||||
return AZStd::make_unique<TBackend>();
|
||||
}, AZStd::move(name), AZStd::move(extensions));
|
||||
}
|
||||
} // namespace AZ::DOM
|
||||
@@ -17,7 +17,7 @@ namespace AZ::DOM
|
||||
{
|
||||
//
|
||||
// Lifetime enum
|
||||
//
|
||||
//
|
||||
//! Specifies the period in which a reference value will still be alive and safe to read.
|
||||
enum class Lifetime
|
||||
{
|
||||
@@ -30,7 +30,7 @@ namespace AZ::DOM
|
||||
|
||||
//
|
||||
// VisitorErrorCode enum
|
||||
//
|
||||
//
|
||||
//! Error code specifying the reason a Visitor operation failed.
|
||||
enum class VisitorErrorCode
|
||||
{
|
||||
@@ -75,7 +75,7 @@ namespace AZ::DOM
|
||||
};
|
||||
|
||||
//! A type alias for opaque DOM types that aren't meant to be serializable.
|
||||
//! /see VisitorInterface::OpaqueValue
|
||||
//! \see VisitorInterface::OpaqueValue
|
||||
using OpaqueType = AZStd::any;
|
||||
|
||||
//
|
||||
@@ -116,7 +116,7 @@ namespace AZ::DOM
|
||||
//! - \ref Double: 64 bit double precision float
|
||||
//! - \ref Null: sentinel "empty" type with no value representation
|
||||
//! - \ref String: UTF8 encoded string
|
||||
//! - \ref Object: an ordered container of key/value pairs where keys are AZ::Names and values may be any DOM type
|
||||
//! - \ref Object: an ordered container of key/value pairs where keys are \ref AZ::Name and values may be any DOM type
|
||||
//! (including Object)
|
||||
//! - \ref Array: an ordered container of values, in which values are any DOM value type (including Array)
|
||||
//! - \ref Node: a container
|
||||
@@ -144,17 +144,17 @@ namespace AZ::DOM
|
||||
//! Raw (\see VisitorFlags::SupportsRawValues) and opaque values (\see VisitorFlags::SupportsOpaqueValues)
|
||||
//! are disallowed by default, as their handling is intended to be implementation-specific.
|
||||
virtual VisitorFlags GetVisitorFlags() const;
|
||||
//! /see VisitorFlags::SupportsRawValues
|
||||
//! \see VisitorFlags::SupportsRawValues
|
||||
bool SupportsRawValues() const;
|
||||
//! /see VisitorFlags::SupportsRawKeys
|
||||
//! \see VisitorFlags::SupportsRawKeys
|
||||
bool SupportsRawKeys() const;
|
||||
//! /see VisitorFlags::SupportsObjects
|
||||
//! \see VisitorFlags::SupportsObjects
|
||||
bool SupportsObjects() const;
|
||||
//! /see VisitorFlags::SupportsArrays
|
||||
//! \see VisitorFlags::SupportsArrays
|
||||
bool SupportsArrays() const;
|
||||
//! /see VisitorFlags::SupportsNodes
|
||||
//! \see VisitorFlags::SupportsNodes
|
||||
bool SupportsNodes() const;
|
||||
//! /see VisitorFlags::SupportsOpaqueValues
|
||||
//! \see VisitorFlags::SupportsOpaqueValues
|
||||
bool SupportsOpaqueValues() const;
|
||||
|
||||
//! Operates on an empty null value.
|
||||
@@ -231,6 +231,15 @@ namespace AZ::DOM
|
||||
static Result VisitorFailure(VisitorErrorCode code, AZStd::string additionalInfo);
|
||||
//! Helper method, constructs a failure \ref Result with the specified error.
|
||||
static Result VisitorFailure(VisitorError error);
|
||||
|
||||
//! Helper method, constructs a failure \ref Result with the specified code and supplemental info specified by a format string
|
||||
//! and its arguments.
|
||||
template<class... TArgs>
|
||||
static Result FormatVisitorFailure(VisitorErrorCode code, TArgs... formatArgs)
|
||||
{
|
||||
return VisitorFailure(code, AZStd::string::format(formatArgs...));
|
||||
}
|
||||
|
||||
//! Helper method, constructs a success \ref Result.
|
||||
static Result VisitorSuccess();
|
||||
};
|
||||
|
||||
@@ -125,8 +125,18 @@ set(FILES
|
||||
Debug/TraceMessagesDrillerBus.h
|
||||
Debug/TraceReflection.cpp
|
||||
Debug/TraceReflection.h
|
||||
DOM/DomAdapter.h
|
||||
DOM/DomBackend.cpp
|
||||
DOM/DomBackend.h
|
||||
DOM/DomBackendRegistry.cpp
|
||||
DOM/DomBackendRegistry.h
|
||||
DOM/DomBackendRegistryInterface.h
|
||||
DOM/DomVisitor.cpp
|
||||
DOM/DomVisitor.h
|
||||
DOM/Backends/JSON/JsonBackend.cpp
|
||||
DOM/Backends/JSON/JsonBackend.h
|
||||
DOM/Backends/JSON/JsonSerializationUtils.cpp
|
||||
DOM/Backends/JSON/JsonSerializationUtils.h
|
||||
Driller/DefaultStringPool.h
|
||||
Driller/Driller.cpp
|
||||
Driller/Driller.h
|
||||
|
||||
Reference in New Issue
Block a user