Move deep comparison / copy to utils
Signed-off-by: Nicholas Van Sickle <nvsickle@amazon.com>
This commit is contained in:
@@ -33,4 +33,152 @@ namespace AZ::Dom::Utils
|
||||
}
|
||||
return AZ::Success(AZStd::move(value));
|
||||
}
|
||||
|
||||
bool DeepCompareIsEqual(const Value& lhs, const Value& rhs)
|
||||
{
|
||||
const Value::ValueType& lhsValue = lhs.GetInternalValue();
|
||||
const Value::ValueType& rhsValue = rhs.GetInternalValue();
|
||||
|
||||
if (lhs.IsString() && rhs.IsString())
|
||||
{
|
||||
// If we both hold the same ref counted string we don't need to do a full comparison
|
||||
if (AZStd::holds_alternative<Value::SharedStringType>(lhsValue) && lhsValue == rhsValue)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return lhs.GetString() == rhs.GetString();
|
||||
}
|
||||
|
||||
return AZStd::visit(
|
||||
[&](auto&& ourValue) -> bool
|
||||
{
|
||||
using Alternative = AZStd::decay_t<decltype(ourValue)>;
|
||||
|
||||
if constexpr (AZStd::is_same_v<Alternative, ObjectPtr>)
|
||||
{
|
||||
if (!rhs.IsObject())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auto&& theirValue = AZStd::get<AZStd::remove_cvref_t<decltype(ourValue)>>(rhsValue);
|
||||
if (ourValue == theirValue)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
const Object::ContainerType& ourValues = ourValue->GetValues();
|
||||
const Object::ContainerType& theirValues = theirValue->GetValues();
|
||||
|
||||
if (ourValues.size() != theirValues.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < ourValues.size(); ++i)
|
||||
{
|
||||
const Object::EntryType& lhsChild = ourValues[i];
|
||||
const Object::EntryType& rhsChild = theirValues[i];
|
||||
if (lhsChild.first != rhsChild.first || !DeepCompareIsEqual(lhsChild.second, rhsChild.second))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Alternative, ArrayPtr>)
|
||||
{
|
||||
if (!rhs.IsArray())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auto&& theirValue = AZStd::get<AZStd::remove_cvref_t<decltype(ourValue)>>(rhsValue);
|
||||
if (ourValue == theirValue)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
const Array::ContainerType& ourValues = ourValue->GetValues();
|
||||
const Array::ContainerType& theirValues = theirValue->GetValues();
|
||||
|
||||
if (ourValues.size() != theirValues.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < ourValues.size(); ++i)
|
||||
{
|
||||
const Value& lhsChild = ourValues[i];
|
||||
const Value& rhsChild = theirValues[i];
|
||||
if (!DeepCompareIsEqual(lhsChild, rhsChild))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Alternative, NodePtr>)
|
||||
{
|
||||
if (!rhs.IsNode())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auto&& theirValue = AZStd::get<AZStd::remove_cvref_t<decltype(ourValue)>>(rhsValue);
|
||||
if (ourValue == theirValue)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
const Node& ourNode = *ourValue;
|
||||
const Node& theirNode = *theirValue;
|
||||
|
||||
const Object::ContainerType& ourProperties = ourNode.GetProperties();
|
||||
const Object::ContainerType& theirProperties = theirNode.GetProperties();
|
||||
|
||||
if (ourProperties.size() != theirProperties.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < ourProperties.size(); ++i)
|
||||
{
|
||||
const Object::EntryType& lhsChild = ourProperties[i];
|
||||
const Object::EntryType& rhsChild = theirProperties[i];
|
||||
if (lhsChild.first != rhsChild.first || !DeepCompareIsEqual(lhsChild.second, rhsChild.second))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const Array::ContainerType& ourChildren = ourNode.GetChildren();
|
||||
const Array::ContainerType& theirChildren = theirNode.GetChildren();
|
||||
|
||||
for (size_t i = 0; i < ourChildren.size(); ++i)
|
||||
{
|
||||
const Value& lhsChild = ourChildren[i];
|
||||
const Value& rhsChild = theirChildren[i];
|
||||
if (!DeepCompareIsEqual(lhsChild, rhsChild))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return lhs == rhs;
|
||||
}
|
||||
},
|
||||
lhsValue);
|
||||
}
|
||||
|
||||
Value DeepCopy(const Value& value, bool copyStrings)
|
||||
{
|
||||
Value copiedValue;
|
||||
AZStd::unique_ptr<Visitor> writer = copiedValue.GetWriteHandler();
|
||||
value.Accept(*writer, copyStrings);
|
||||
return copiedValue;
|
||||
}
|
||||
} // namespace AZ::Dom::Utils
|
||||
|
||||
@@ -17,4 +17,7 @@ namespace AZ::Dom::Utils
|
||||
Visitor::Result ReadFromStringInPlace(Backend& backend, AZStd::string& string, Visitor& visitor);
|
||||
|
||||
AZ::Outcome<Value, AZStd::string> WriteToValue(const Backend::WriteCallback& writeCallback);
|
||||
|
||||
bool DeepCompareIsEqual(const Value& lhs, const Value& rhs);
|
||||
Value DeepCopy(const Value& value, bool copyStrings = true);
|
||||
} // namespace AZ::Dom::Utils
|
||||
|
||||
@@ -67,6 +67,16 @@ namespace AZ::Dom
|
||||
return Internal::ExtractTypeArgs<Value::ValueType>::GetTypeIndex<T>();
|
||||
}
|
||||
|
||||
const Array::ContainerType& Array::GetValues() const
|
||||
{
|
||||
return m_values;
|
||||
}
|
||||
|
||||
const Object::ContainerType& Object::GetValues() const
|
||||
{
|
||||
return m_values;
|
||||
}
|
||||
|
||||
Node::Node(AZ::Name name)
|
||||
: m_name(AZStd::move(name))
|
||||
{
|
||||
@@ -130,8 +140,8 @@ namespace AZ::Dom
|
||||
}
|
||||
}
|
||||
|
||||
Value::Value(const AZStd::any& value)
|
||||
: m_value(AZStd::allocate_shared<AZStd::any>(StdValueAllocator(), value))
|
||||
Value::Value(AZStd::any opaqueValue)
|
||||
: m_value(AZStd::allocate_shared<AZStd::any>(StdValueAllocator(), AZStd::move(opaqueValue)))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -660,7 +670,9 @@ namespace AZ::Dom
|
||||
Value& Value::AddMember(KeyType name, const Value& value)
|
||||
{
|
||||
Object::ContainerType& object = GetObjectInternal();
|
||||
object.reserve((object.size() / Object::ReserveIncrement + 1) * Object::ReserveIncrement);
|
||||
// Reserve in ReserveIncremenet chunks instead of the default vector doubling strategy
|
||||
// Profiling has found that this is an aggregate performance gain for typical workflows
|
||||
object.reserve(AZ_SIZE_ALIGN_UP(object.size() + 1, Object::ReserveIncrement));
|
||||
if (auto memberIt = FindMutableMember(name); memberIt != object.end())
|
||||
{
|
||||
memberIt->second = value;
|
||||
@@ -833,7 +845,9 @@ namespace AZ::Dom
|
||||
Value& Value::ArrayPushBack(Value value)
|
||||
{
|
||||
Array::ContainerType& array = GetArrayInternal();
|
||||
array.reserve((array.size() / Array::ReserveIncrement + 1) * Array::ReserveIncrement);
|
||||
// Reserve in ReserveIncremenet chunks instead of the default vector doubling strategy
|
||||
// Profiling has found that this is an aggregate performance gain for typical workflows
|
||||
array.reserve(AZ_SIZE_ALIGN_UP(array.size() + 1, Array::ReserveIncrement));
|
||||
array.push_back(AZStd::move(value));
|
||||
return *this;
|
||||
}
|
||||
@@ -1231,137 +1245,8 @@ namespace AZ::Dom
|
||||
return AZStd::make_unique<ValueWriter>(*this);
|
||||
}
|
||||
|
||||
bool Value::DeepCompareIsEqual(const Value& other) const
|
||||
const Value::ValueType& Value::GetInternalValue() const
|
||||
{
|
||||
if (IsString() && other.IsString())
|
||||
{
|
||||
// If we both hold the same ref counted string we don't need to do a full comparison
|
||||
if (AZStd::holds_alternative<SharedStringType>(m_value) && m_value == other.m_value)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return GetString() == other.GetString();
|
||||
}
|
||||
|
||||
if (m_value.index() != other.m_value.index())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return AZStd::visit(
|
||||
[&](auto&& ourValue) -> bool
|
||||
{
|
||||
using Alternative = AZStd::decay_t<decltype(ourValue)>;
|
||||
auto&& theirValue = AZStd::get<AZStd::remove_cvref_t<decltype(ourValue)>>(other.m_value);
|
||||
|
||||
if constexpr (AZStd::is_same_v<Alternative, AZStd::monostate>)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Alternative, ObjectPtr>)
|
||||
{
|
||||
if (ourValue == theirValue)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ourValue->m_values.size() != theirValue->m_values.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < ourValue->m_values.size(); ++i)
|
||||
{
|
||||
const Object::EntryType& lhs = ourValue->m_values[i];
|
||||
const Object::EntryType& rhs = theirValue->m_values[i];
|
||||
if (lhs.first != rhs.first || !lhs.second.DeepCompareIsEqual(rhs.second))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Alternative, ArrayPtr>)
|
||||
{
|
||||
if (ourValue == theirValue)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ourValue->m_values.size() != theirValue->m_values.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < ourValue->m_values.size(); ++i)
|
||||
{
|
||||
const Value& lhs = ourValue->m_values[i];
|
||||
const Value& rhs = theirValue->m_values[i];
|
||||
if (!lhs.DeepCompareIsEqual(rhs))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Alternative, NodePtr>)
|
||||
{
|
||||
if (ourValue == theirValue)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
const Node& ourNode = *ourValue;
|
||||
const Node& theirNode = *theirValue;
|
||||
|
||||
const Object::ContainerType& ourProperties = ourNode.GetProperties();
|
||||
const Object::ContainerType& theirProperties = theirNode.GetProperties();
|
||||
|
||||
if (ourProperties.size() != theirProperties.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < ourProperties.size(); ++i)
|
||||
{
|
||||
const Object::EntryType& lhs = ourProperties[i];
|
||||
const Object::EntryType& rhs = theirProperties[i];
|
||||
if (lhs.first != rhs.first || !lhs.second.DeepCompareIsEqual(rhs.second))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const Array::ContainerType& ourChildren = ourNode.GetChildren();
|
||||
const Array::ContainerType& theirChildren = theirNode.GetChildren();
|
||||
|
||||
for (size_t i = 0; i < ourChildren.size(); ++i)
|
||||
{
|
||||
const Value& lhs = ourChildren[i];
|
||||
const Value& rhs = theirChildren[i];
|
||||
if (!lhs.DeepCompareIsEqual(rhs))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ourValue == theirValue;
|
||||
}
|
||||
},
|
||||
m_value);
|
||||
}
|
||||
|
||||
Value Value::DeepCopy(bool copyStrings) const
|
||||
{
|
||||
Value newValue;
|
||||
AZStd::unique_ptr<Visitor> writer = newValue.GetWriteHandler();
|
||||
Accept(*writer, copyStrings);
|
||||
return newValue;
|
||||
return m_value;
|
||||
}
|
||||
} // namespace AZ::Dom
|
||||
|
||||
@@ -67,6 +67,9 @@ namespace AZ::Dom
|
||||
using Iterator = ContainerType::iterator;
|
||||
using ConstIterator = ContainerType::const_iterator;
|
||||
static constexpr const size_t ReserveIncrement = 4;
|
||||
static_assert((ReserveIncrement & (ReserveIncrement - 1)) == 0, "ReserveIncremenet must be a power of 2");
|
||||
|
||||
const ContainerType& GetValues() const;
|
||||
|
||||
private:
|
||||
ContainerType m_values;
|
||||
@@ -86,6 +89,9 @@ namespace AZ::Dom
|
||||
using Iterator = ContainerType::iterator;
|
||||
using ConstIterator = ContainerType::const_iterator;
|
||||
static constexpr const size_t ReserveIncrement = 8;
|
||||
static_assert((ReserveIncrement & (ReserveIncrement - 1)) == 0, "ReserveIncremenet must be a power of 2");
|
||||
|
||||
const ContainerType& GetValues() const;
|
||||
|
||||
private:
|
||||
ContainerType m_values;
|
||||
@@ -374,8 +380,9 @@ namespace AZ::Dom
|
||||
Visitor::Result Accept(Visitor& visitor, bool copyStrings) const;
|
||||
AZStd::unique_ptr<Visitor> GetWriteHandler();
|
||||
|
||||
bool DeepCompareIsEqual(const Value& other) const;
|
||||
Value DeepCopy(bool copyStrings = true) const;
|
||||
//! Gets the internal value of this Value. Note that this value's types may not correspond one-to-one with the Type enumeration,
|
||||
//! as internally the same type might have different storage mechanisms. Where possible, prefer using the typed API.
|
||||
const ValueType& GetInternalValue() const;
|
||||
|
||||
private:
|
||||
const Node& GetNodeInternal() const;
|
||||
@@ -385,7 +392,7 @@ namespace AZ::Dom
|
||||
const Array::ContainerType& GetArrayInternal() const;
|
||||
Array::ContainerType& GetArrayInternal();
|
||||
|
||||
explicit Value(const AZStd::any& opaqueValue);
|
||||
explicit Value(AZStd::any opaqueValue);
|
||||
|
||||
static_assert(
|
||||
sizeof(ValueType) == sizeof(ShortStringType) + sizeof(size_t), "ValueType should have no members larger than ShortStringType");
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
#include <AzCore/DOM/DomValue.h>
|
||||
#include <AzCore/DOM/DomUtils.h>
|
||||
#include <AzCore/Name/NameDictionary.h>
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
#include <cinttypes>
|
||||
@@ -171,7 +172,7 @@ namespace AZ::Dom::Benchmark
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
Value copy = original.DeepCopy();
|
||||
Value copy = Utils::DeepCopy(original);
|
||||
TakeAndDiscardWithoutTimingDtor(AZStd::move(copy), state);
|
||||
}
|
||||
|
||||
|
||||
@@ -41,10 +41,10 @@ namespace AZ::Dom::Tests
|
||||
{
|
||||
Value shallowCopy = m_value;
|
||||
EXPECT_EQ(m_value, shallowCopy);
|
||||
EXPECT_TRUE(m_value.DeepCompareIsEqual(shallowCopy));
|
||||
EXPECT_TRUE(Utils::DeepCompareIsEqual(m_value, shallowCopy));
|
||||
|
||||
Value deepCopy = m_value.DeepCopy();
|
||||
EXPECT_TRUE(m_value.DeepCompareIsEqual(deepCopy));
|
||||
Value deepCopy = Utils::DeepCopy(m_value);
|
||||
EXPECT_TRUE(Utils::DeepCompareIsEqual(m_value, deepCopy));
|
||||
}
|
||||
|
||||
Value m_value;
|
||||
|
||||
Reference in New Issue
Block a user