Address review feedback

Signed-off-by: Nicholas Van Sickle <nvsickle@amazon.com>
monroegm-disable-blank-issue-2
Nicholas Van Sickle 4 years ago
parent e06b8782cc
commit 73f166241e

@ -8,8 +8,8 @@
#include <AzCore/DOM/DomPatch.h> #include <AzCore/DOM/DomPatch.h>
#include <AzCore/DOM/DomUtils.h> #include <AzCore/DOM/DomUtils.h>
#include <AzCore/std/containers/unordered_set.h>
#include <AzCore/std/containers/queue.h> #include <AzCore/std/containers/queue.h>
#include <AzCore/std/containers/unordered_set.h>
namespace AZ::Dom namespace AZ::Dom
{ {
@ -257,7 +257,8 @@ namespace AZ::Dom
return AZ::Failure(sourceLoad.TakeError()); return AZ::Failure(sourceLoad.TakeError());
} }
return AZ::Success(PatchOperation::CopyOperation(Path(destLoad.GetValue().GetString()), Path(sourceLoad.GetValue().GetString()))); return AZ::Success(
PatchOperation::CopyOperation(Path(destLoad.GetValue().GetString()), Path(sourceLoad.GetValue().GetString())));
} }
else if (op == "move") else if (op == "move")
{ {
@ -272,7 +273,8 @@ namespace AZ::Dom
return AZ::Failure(sourceLoad.TakeError()); return AZ::Failure(sourceLoad.TakeError());
} }
return AZ::Success(PatchOperation::MoveOperation(Path(destLoad.GetValue().GetString()), Path(sourceLoad.GetValue().GetString()))); return AZ::Success(
PatchOperation::MoveOperation(Path(destLoad.GetValue().GetString()), Path(sourceLoad.GetValue().GetString())));
} }
else if (op == "test") else if (op == "test")
{ {
@ -319,8 +321,9 @@ namespace AZ::Dom
const Value* existingValue = stateBeforeApplication.FindChild(m_domPath); const Value* existingValue = stateBeforeApplication.FindChild(m_domPath);
if (existingValue == nullptr) if (existingValue == nullptr)
{ {
return AZ::Failure( AZStd::string errorMessage = "Unable to invert DOM remove patch, source path not found: ";
AZStd::string::format("Unable to invert DOM remove patch, source path not found: %s", m_domPath.ToString().data())); m_domPath.AppendToString(errorMessage);
return AZ::Failure(AZStd::move(errorMessage));
} }
return AZ::Success(PatchOperation::AddOperation(m_domPath, *existingValue)); return AZ::Success(PatchOperation::AddOperation(m_domPath, *existingValue));
} }
@ -330,8 +333,9 @@ namespace AZ::Dom
const Value* existingValue = stateBeforeApplication.FindChild(m_domPath); const Value* existingValue = stateBeforeApplication.FindChild(m_domPath);
if (existingValue == nullptr) if (existingValue == nullptr)
{ {
return AZ::Failure(AZStd::string::format( AZStd::string errorMessage = "Unable to invert DOM replace patch, source path not found: ";
"Unable to invert DOM replace patch, source path not found: %s", m_domPath.ToString().data())); m_domPath.AppendToString(errorMessage);
return AZ::Failure(AZStd::move(errorMessage));
} }
return AZ::Success(PatchOperation::ReplaceOperation(m_domPath, *existingValue)); return AZ::Success(PatchOperation::ReplaceOperation(m_domPath, *existingValue));
} }
@ -341,8 +345,9 @@ namespace AZ::Dom
const Value* existingValue = stateBeforeApplication.FindChild(m_domPath); const Value* existingValue = stateBeforeApplication.FindChild(m_domPath);
if (existingValue == nullptr) if (existingValue == nullptr)
{ {
return AZ::Failure( AZStd::string errorMessage = "Unable to invert DOM copy patch, source path not found: ";
AZStd::string::format("Unable to invert DOM copy patch, source path not found: %s", m_domPath.ToString().data())); m_domPath.AppendToString(errorMessage);
return AZ::Failure(AZStd::move(errorMessage));
} }
return AZ::Success(PatchOperation::ReplaceOperation(m_domPath, *existingValue)); return AZ::Success(PatchOperation::ReplaceOperation(m_domPath, *existingValue));
} }
@ -367,8 +372,9 @@ namespace AZ::Dom
const Value* existingValue = stateBeforeApplication.FindChild(commonAncestor); const Value* existingValue = stateBeforeApplication.FindChild(commonAncestor);
if (existingValue == nullptr) if (existingValue == nullptr)
{ {
return AZ::Failure(AZStd::string::format( AZStd::string errorMessage = "Unable to invert DOM copy patch, common ancestor path not found: ";
"Unable to invert DOM move patch, common ancestor path not found: %s", commonAncestor.ToString().data())); commonAncestor.AppendToString(errorMessage);
return AZ::Failure(AZStd::move(errorMessage));
} }
return AZ::Success(PatchOperation::ReplaceOperation(commonAncestor, *existingValue)); return AZ::Success(PatchOperation::ReplaceOperation(commonAncestor, *existingValue));
} }
@ -383,10 +389,10 @@ namespace AZ::Dom
} }
AZ::Outcome<PatchOperation::PathContext, AZStd::string> PatchOperation::LookupPath( AZ::Outcome<PatchOperation::PathContext, AZStd::string> PatchOperation::LookupPath(
Value& rootElement, const Path& path, AZ::u8 existenceCheckFlags) Value& rootElement, const Path& path, ExistenceCheckFlags flags)
{ {
const bool verifyFullPath = existenceCheckFlags & VerifyFullPath; const bool verifyFullPath = (flags & ExistenceCheckFlags::VerifyFullPath) != ExistenceCheckFlags::DefaultExistenceCheck;
const bool allowEndOfArray = existenceCheckFlags & AllowEndOfArray; const bool allowEndOfArray = (flags & ExistenceCheckFlags::AllowEndOfArray) != ExistenceCheckFlags::DefaultExistenceCheck;
Path target = path; Path target = path;
if (target.Size() == 0) if (target.Size() == 0)
@ -414,7 +420,9 @@ namespace AZ::Dom
Value* targetValue = rootElement.FindMutableChild(target); Value* targetValue = rootElement.FindMutableChild(target);
if (targetValue == nullptr) if (targetValue == nullptr)
{ {
return AZ::Failure(AZStd::string::format("Path not found (%s)", target.ToString().data())); AZStd::string errorMessage = "Path not found: ";
target.AppendToString(errorMessage);
return AZ::Failure(AZStd::move(errorMessage));
} }
if (destinationIndex.IsIndex() || destinationIndex.IsEndOfArray()) if (destinationIndex.IsIndex() || destinationIndex.IsEndOfArray())
@ -450,7 +458,7 @@ namespace AZ::Dom
PatchOperation::PatchOutcome PatchOperation::ApplyAdd(Value& rootElement) const PatchOperation::PatchOutcome PatchOperation::ApplyAdd(Value& rootElement) const
{ {
auto pathLookup = LookupPath(rootElement, m_domPath, AllowEndOfArray); auto pathLookup = LookupPath(rootElement, m_domPath, ExistenceCheckFlags::AllowEndOfArray);
if (!pathLookup.IsSuccess()) if (!pathLookup.IsSuccess())
{ {
return AZ::Failure(pathLookup.TakeError()); return AZ::Failure(pathLookup.TakeError());
@ -481,7 +489,7 @@ namespace AZ::Dom
PatchOperation::PatchOutcome PatchOperation::ApplyRemove(Value& rootElement) const PatchOperation::PatchOutcome PatchOperation::ApplyRemove(Value& rootElement) const
{ {
auto pathLookup = LookupPath(rootElement, m_domPath, VerifyFullPath | AllowEndOfArray); auto pathLookup = LookupPath(rootElement, m_domPath, ExistenceCheckFlags::VerifyFullPath | ExistenceCheckFlags::AllowEndOfArray);
if (!pathLookup.IsSuccess()) if (!pathLookup.IsSuccess())
{ {
return AZ::Failure(pathLookup.TakeError()); return AZ::Failure(pathLookup.TakeError());
@ -505,7 +513,7 @@ namespace AZ::Dom
PatchOperation::PatchOutcome PatchOperation::ApplyReplace(Value& rootElement) const PatchOperation::PatchOutcome PatchOperation::ApplyReplace(Value& rootElement) const
{ {
auto pathLookup = LookupPath(rootElement, m_domPath, VerifyFullPath); auto pathLookup = LookupPath(rootElement, m_domPath, ExistenceCheckFlags::VerifyFullPath);
if (!pathLookup.IsSuccess()) if (!pathLookup.IsSuccess())
{ {
return AZ::Failure(pathLookup.TakeError()); return AZ::Failure(pathLookup.TakeError());
@ -517,13 +525,13 @@ namespace AZ::Dom
PatchOperation::PatchOutcome PatchOperation::ApplyCopy(Value& rootElement) const PatchOperation::PatchOutcome PatchOperation::ApplyCopy(Value& rootElement) const
{ {
auto sourceLookup = LookupPath(rootElement, GetSourcePath(), VerifyFullPath); auto sourceLookup = LookupPath(rootElement, GetSourcePath(), ExistenceCheckFlags::VerifyFullPath);
if (!sourceLookup.IsSuccess()) if (!sourceLookup.IsSuccess())
{ {
return AZ::Failure(sourceLookup.TakeError()); return AZ::Failure(sourceLookup.TakeError());
} }
auto destLookup = LookupPath(rootElement, m_domPath, AllowEndOfArray); auto destLookup = LookupPath(rootElement, m_domPath, ExistenceCheckFlags::AllowEndOfArray);
if (!destLookup.IsSuccess()) if (!destLookup.IsSuccess())
{ {
return AZ::Failure(destLookup.TakeError()); return AZ::Failure(destLookup.TakeError());
@ -535,13 +543,13 @@ namespace AZ::Dom
PatchOperation::PatchOutcome PatchOperation::ApplyMove(Value& rootElement) const PatchOperation::PatchOutcome PatchOperation::ApplyMove(Value& rootElement) const
{ {
auto sourceLookup = LookupPath(rootElement, GetSourcePath(), VerifyFullPath); auto sourceLookup = LookupPath(rootElement, GetSourcePath(), ExistenceCheckFlags::VerifyFullPath);
if (!sourceLookup.IsSuccess()) if (!sourceLookup.IsSuccess())
{ {
return AZ::Failure(sourceLookup.TakeError()); return AZ::Failure(sourceLookup.TakeError());
} }
auto destLookup = LookupPath(rootElement, m_domPath, AllowEndOfArray); auto destLookup = LookupPath(rootElement, m_domPath, ExistenceCheckFlags::AllowEndOfArray);
if (!destLookup.IsSuccess()) if (!destLookup.IsSuccess())
{ {
return AZ::Failure(destLookup.TakeError()); return AZ::Failure(destLookup.TakeError());
@ -568,7 +576,7 @@ namespace AZ::Dom
PatchOperation::PatchOutcome PatchOperation::ApplyTest(Value& rootElement) const PatchOperation::PatchOutcome PatchOperation::ApplyTest(Value& rootElement) const
{ {
auto pathLookup = LookupPath(rootElement, m_domPath, VerifyFullPath); auto pathLookup = LookupPath(rootElement, m_domPath, ExistenceCheckFlags::VerifyFullPath);
if (!pathLookup.IsSuccess()) if (!pathLookup.IsSuccess())
{ {
return AZ::Failure(pathLookup.TakeError()); return AZ::Failure(pathLookup.TakeError());
@ -670,32 +678,32 @@ namespace AZ::Dom
return m_operations[index]; return m_operations[index];
} }
Patch::OperationsContainer::iterator Patch::begin() auto Patch::begin() -> OperationsContainer::iterator
{ {
return m_operations.begin(); return m_operations.begin();
} }
Patch::OperationsContainer::iterator Patch::end() auto Patch::end() -> OperationsContainer::iterator
{ {
return m_operations.end(); return m_operations.end();
} }
Patch::OperationsContainer::const_iterator Patch::begin() const auto Patch::begin() const -> OperationsContainer::const_iterator
{ {
return m_operations.cbegin(); return m_operations.cbegin();
} }
Patch::OperationsContainer::const_iterator Patch::end() const auto Patch::end() const -> OperationsContainer::const_iterator
{ {
return m_operations.cend(); return m_operations.cend();
} }
Patch::OperationsContainer::const_iterator Patch::cbegin() const auto Patch::cbegin() const -> OperationsContainer::const_iterator
{ {
return m_operations.cbegin(); return m_operations.cbegin();
} }
Patch::OperationsContainer::const_iterator Patch::cend() const auto Patch::cend() const -> OperationsContainer::const_iterator
{ {
return m_operations.cend(); return m_operations.cend();
} }
@ -794,7 +802,8 @@ namespace AZ::Dom
return PatchOperation(AZStd::move(testPath), PatchOperation::Type::Test, AZStd::move(value)); return PatchOperation(AZStd::move(testPath), PatchOperation::Type::Test, AZStd::move(value));
} }
PatchInfo GenerateHierarchicalDeltaPatch(const Value& beforeState, const Value& afterState) PatchInfo GenerateHierarchicalDeltaPatch(
const Value& beforeState, const Value& afterState, const DeltaPatchGenerationParameters& params)
{ {
PatchInfo patches; PatchInfo patches;
@ -804,7 +813,7 @@ namespace AZ::Dom
patches.m_inversePatches.PushFront(AZStd::move(inverse)); patches.m_inversePatches.PushFront(AZStd::move(inverse));
}; };
AZStd::function<void(const Path&, const Value&, const Value&)> CompareValues; AZStd::function<void(const Path&, const Value&, const Value&)> compareValues;
struct PendingComparison struct PendingComparison
{ {
@ -822,7 +831,7 @@ namespace AZ::Dom
AZStd::queue<PendingComparison> entriesToCompare; AZStd::queue<PendingComparison> entriesToCompare;
AZStd::unordered_set<AZ::Name::Hash> desiredKeys; AZStd::unordered_set<AZ::Name::Hash> desiredKeys;
auto CompareObjects = [&](const Path& path, const Value& before, const Value& after) auto compareObjects = [&](const Path& path, const Value& before, const Value& after)
{ {
desiredKeys.clear(); desiredKeys.clear();
Path subPath = path; Path subPath = path;
@ -853,22 +862,22 @@ namespace AZ::Dom
} }
}; };
auto CompareArrays = [&](const Path& path, const Value& before, const Value& after) auto compareArrays = [&](const Path& path, const Value& before, const Value& after)
{ {
const size_t beforeSize = before.ArraySize(); const size_t beforeSize = before.ArraySize();
const size_t afterSize = after.ArraySize(); const size_t afterSize = after.ArraySize();
// If more than replaceThreshold values differ, do a replace operation instead // If more than replaceThreshold values differ, do a replace operation instead
constexpr size_t replaceThreshold = 3; if (params.m_replaceThreshold != DeltaPatchGenerationParameters::NoReplace)
size_t changedValueCount = 0;
for (size_t i = 0; i < afterSize; ++i)
{ {
if (i < beforeSize) size_t changedValueCount = 0;
const size_t entriesToEnumerate = AZStd::min(beforeSize, afterSize);
for (size_t i = 0; i < entriesToEnumerate; ++i)
{ {
if (before[i] != after[i]) if (before[i] != after[i])
{ {
++changedValueCount; ++changedValueCount;
if (changedValueCount >= replaceThreshold) if (changedValueCount >= params.m_replaceThreshold)
{ {
AddPatch(PatchOperation::ReplaceOperation(path, after), PatchOperation::ReplaceOperation(path, before)); AddPatch(PatchOperation::ReplaceOperation(path, after), PatchOperation::ReplaceOperation(path, before));
return; return;
@ -904,7 +913,7 @@ namespace AZ::Dom
} }
}; };
auto CompareNodes = [&](const Path& path, const Value& before, const Value& after) auto compareNodes = [&](const Path& path, const Value& before, const Value& after)
{ {
if (before.GetNodeName() != after.GetNodeName()) if (before.GetNodeName() != after.GetNodeName())
{ {
@ -912,12 +921,12 @@ namespace AZ::Dom
} }
else else
{ {
CompareObjects(path, before, after); compareObjects(path, before, after);
CompareArrays(path, before, after); compareArrays(path, before, after);
} }
}; };
CompareValues = [&](const Path& path, const Value& before, const Value& after) compareValues = [&](const Path& path, const Value& before, const Value& after)
{ {
if (before.GetType() != after.GetType()) if (before.GetType() != after.GetType())
{ {
@ -931,15 +940,15 @@ namespace AZ::Dom
} }
else if (before.IsObject()) else if (before.IsObject())
{ {
CompareObjects(path, before, after); compareObjects(path, before, after);
} }
else if (before.IsArray()) else if (before.IsArray())
{ {
CompareArrays(path, before, after); compareArrays(path, before, after);
} }
else if (before.IsNode()) else if (before.IsNode())
{ {
CompareNodes(path, before, after); compareNodes(path, before, after);
} }
else else
{ {
@ -951,7 +960,7 @@ namespace AZ::Dom
while (!entriesToCompare.empty()) while (!entriesToCompare.empty())
{ {
PendingComparison& comparison = entriesToCompare.front(); PendingComparison& comparison = entriesToCompare.front();
CompareValues(comparison.m_path, comparison.m_before, comparison.m_after); compareValues(comparison.m_path, comparison.m_before, comparison.m_after);
entriesToCompare.pop(); entriesToCompare.pop();
} }
return patches; return patches;

@ -35,9 +35,9 @@ namespace AZ::Dom
PatchOperation(const PatchOperation&) = default; PatchOperation(const PatchOperation&) = default;
PatchOperation(PatchOperation&&) = default; PatchOperation(PatchOperation&&) = default;
explicit PatchOperation(Path destinationPath, Type type, Value value); PatchOperation(Path destionationPath, Type type, Value value);
explicit PatchOperation(Path destionationPath, Type type, Path sourcePath); PatchOperation(Path destionationPath, Type type, Path sourcePath);
explicit PatchOperation(Path path, Type type); PatchOperation(Path path, Type type);
static PatchOperation AddOperation(Path destinationPath, Value value); static PatchOperation AddOperation(Path destinationPath, Value value);
static PatchOperation RemoveOperation(Path pathToRemove); static PatchOperation RemoveOperation(Path pathToRemove);
@ -72,6 +72,13 @@ namespace AZ::Dom
AZ::Outcome<PatchOperation, AZStd::string> GetInverse(Value stateBeforeApplication) const; AZ::Outcome<PatchOperation, AZStd::string> GetInverse(Value stateBeforeApplication) const;
enum class ExistenceCheckFlags : AZ::u8
{
DefaultExistenceCheck = 0x0,
VerifyFullPath = 0x1,
AllowEndOfArray = 0x2,
};
private: private:
struct PathContext struct PathContext
{ {
@ -79,12 +86,8 @@ namespace AZ::Dom
PathEntry m_key; PathEntry m_key;
}; };
static constexpr AZ::u8 DefaultExistenceCheck = 0x0;
static constexpr AZ::u8 VerifyFullPath = 0x1;
static constexpr AZ::u8 AllowEndOfArray = 0x2;
static AZ::Outcome<PathContext, AZStd::string> LookupPath( static AZ::Outcome<PathContext, AZStd::string> LookupPath(
Value& rootElement, const Path& path, AZ::u8 existenceCheckFlags = DefaultExistenceCheck); Value& rootElement, const Path& path, ExistenceCheckFlags existenceCheckFlags = ExistenceCheckFlags::DefaultExistenceCheck);
PatchOutcome ApplyAdd(Value& rootElement) const; PatchOutcome ApplyAdd(Value& rootElement) const;
PatchOutcome ApplyRemove(Value& rootElement) const; PatchOutcome ApplyRemove(Value& rootElement) const;
@ -93,11 +96,13 @@ namespace AZ::Dom
PatchOutcome ApplyMove(Value& rootElement) const; PatchOutcome ApplyMove(Value& rootElement) const;
PatchOutcome ApplyTest(Value& rootElement) const; PatchOutcome ApplyTest(Value& rootElement) const;
AZStd::variant<AZStd::monostate, Value, Path> m_value;
Path m_domPath; Path m_domPath;
Type m_type; Type m_type;
AZStd::variant<AZStd::monostate, Value, Path> m_value;
}; };
AZ_DEFINE_ENUM_BITWISE_OPERATORS(PatchOperation::ExistenceCheckFlags);
class Patch; class Patch;
//! The current state of a Patch application operation. //! The current state of a Patch application operation.
@ -184,8 +189,18 @@ namespace AZ::Dom
Patch m_inversePatches; Patch m_inversePatches;
}; };
//! Parameters for GenerateHierarchicalDeltaPatch.
struct DeltaPatchGenerationParameters
{
static constexpr size_t NoReplace = AZStd::numeric_limits<size_t>::max();
//! The threshold of changed values in a node or array which, if exceeded, will cause the generation to create an
//! entire "replace" oepration instead. If set to NoReplace, no replacement will occur.
size_t m_replaceThreshold = 3;
};
//! Generates a set of patches such that m_forwardPatches.Apply(beforeState) shall produce a document equivalent to afterState, and //! Generates a set of patches such that m_forwardPatches.Apply(beforeState) shall produce a document equivalent to afterState, and
//! a subsequent m_inversePatches.Apply(beforeState) shall produce the original document. This patch generation strategy does a //! a subsequent m_inversePatches.Apply(beforeState) shall produce the original document. This patch generation strategy does a
//! hierarchical comparison and is not guaranteed to create the minimal set of patches required to transform between the two states. //! hierarchical comparison and is not guaranteed to create the minimal set of patches required to transform between the two states.
PatchInfo GenerateHierarchicalDeltaPatch(const Value& beforeState, const Value& afterState); PatchInfo GenerateHierarchicalDeltaPatch(const Value& beforeState, const Value& afterState, const DeltaPatchGenerationParameters& params = {});
} // namespace AZ::Dom } // namespace AZ::Dom

@ -54,19 +54,19 @@ namespace AZ::Dom
bool PathEntry::operator==(size_t value) const bool PathEntry::operator==(size_t value) const
{ {
const size_t* internalValue = AZStd::get_if<size_t>(&m_value); const size_t* internalValue = AZStd::get_if<size_t>(&m_value);
return internalValue == nullptr ? false : (*internalValue) == value; return internalValue != nullptr && *internalValue == value;
} }
bool PathEntry::operator==(const AZ::Name& key) const bool PathEntry::operator==(const AZ::Name& key) const
{ {
const AZ::Name* internalValue = AZStd::get_if<AZ::Name>(&m_value); const AZ::Name* internalValue = AZStd::get_if<AZ::Name>(&m_value);
return internalValue == nullptr ? false : (*internalValue) == key; return internalValue != nullptr && *internalValue == key;
} }
bool PathEntry::operator==(AZStd::string_view key) const bool PathEntry::operator==(AZStd::string_view key) const
{ {
const AZ::Name* internalValue = AZStd::get_if<AZ::Name>(&m_value); const AZ::Name* internalValue = AZStd::get_if<AZ::Name>(&m_value);
return internalValue == nullptr ? false : (*internalValue) == AZ::Name(key); return internalValue != nullptr && *internalValue == AZ::Name(key);
} }
bool PathEntry::operator!=(const PathEntry& other) const bool PathEntry::operator!=(const PathEntry& other) const
@ -323,13 +323,13 @@ namespace AZ::Dom
return size; return size;
} }
void Path::FormatString(char* stringBuffer, size_t bufferSize) const size_t Path::FormatString(char* stringBuffer, size_t bufferSize) const
{ {
size_t bufferIndex = 0; size_t bufferIndex = 0;
auto putChar = [&](char c) auto putChar = [&](char c)
{ {
if (bufferIndex == bufferSize) if (bufferIndex >= bufferSize)
{ {
return; return;
} }
@ -360,6 +360,11 @@ namespace AZ::Dom
for (const PathEntry& entry : m_entries) for (const PathEntry& entry : m_entries)
{ {
if (bufferIndex >= bufferSize)
{
return bufferIndex;
}
putChar(PathSeparator); putChar(PathSeparator);
if (entry.IsEndOfArray()) if (entry.IsEndOfArray())
{ {
@ -375,7 +380,10 @@ namespace AZ::Dom
} }
} }
size_t bytesWritten = bufferIndex;
putChar('\0'); putChar('\0');
return bytesWritten;
} }
AZStd::string Path::ToString() const AZStd::string Path::ToString() const

@ -128,10 +128,19 @@ namespace AZ::Dom
size_t GetStringLength() const; size_t GetStringLength() const;
//! Formats a JSON-pointer style path string into the target buffer. //! Formats a JSON-pointer style path string into the target buffer.
//! This operation will fail if bufferSize < GetStringLength() + 1 //! This operation will fail if bufferSize < GetStringLength() + 1
void FormatString(char* stringBuffer, size_t bufferSize) const; //! \return The number of bytes written, excepting the null terminator.
size_t FormatString(char* stringBuffer, size_t bufferSize) const;
//! Returns a JSON-pointer style path string for this path. //! Returns a JSON-pointer style path string for this path.
AZStd::string ToString() const; AZStd::string ToString() const;
void AppendToString(AZStd::string& output) const; void AppendToString(AZStd::string& output) const;
template <class T>
void AppendToString(T& output) const
{
const size_t startIndex = output.length();
output.resize_no_construct(startIndex + FormatString(output.data() + startIndex, output.capacity() - startIndex));
}
//! Reads a JSON-pointer style path from pathString and replaces this path's contents. //! Reads a JSON-pointer style path from pathString and replaces this path's contents.
//! Paths are accepted in the following forms: //! Paths are accepted in the following forms:
//! "/path/to/foo/0" //! "/path/to/foo/0"

@ -96,4 +96,25 @@ namespace AZ::Dom::Benchmark
state.SetItemsProcessed(3 * state.iterations()); state.SetItemsProcessed(3 * state.iterations());
} }
BENCHMARK_REGISTER_F(DomPathBenchmark, DomPathEntry_IsEndOfArray); BENCHMARK_REGISTER_F(DomPathBenchmark, DomPathEntry_IsEndOfArray);
BENCHMARK_DEFINE_F(DomPathBenchmark, DomPathEntry_Comparison)(benchmark::State& state)
{
PathEntry name("name");
PathEntry index(0);
PathEntry endOfArray;
endOfArray.SetEndOfArray();
for (auto _ : state)
{
name == name;
name == index;
name == endOfArray;
index == index;
index == endOfArray;
endOfArray == endOfArray;
}
state.SetItemsProcessed(6 * state.iterations());
}
BENCHMARK_REGISTER_F(DomPathBenchmark, DomPathEntry_Comparison);
} }

@ -174,4 +174,23 @@ namespace AZ::Dom::Tests
p.AppendToString(s); p.AppendToString(s);
EXPECT_EQ(s, "/foo/0/foo/0"); EXPECT_EQ(s, "/foo/0/foo/0");
} }
TEST_F(DomPathTests, MixedPath_AppendToFixedString)
{
Path p("/foo/0");
{
AZStd::fixed_string<7> s;
p.AppendToString(s);
EXPECT_EQ(s, "/foo/0");
}
{
AZStd::fixed_string<9> s;
p.AppendToString(s);
EXPECT_EQ(s, "/foo/0");
p.AppendToString(s);
EXPECT_EQ(s, "/foo/0/fo");
}
}
} // namespace AZ::Dom::Tests } // namespace AZ::Dom::Tests

Loading…
Cancel
Save