Move deep comparison / copy to utils

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

@ -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
{
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
const Value::ValueType& Value::GetInternalValue() 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;

Loading…
Cancel
Save