Support equality operator for Property::Map and Property::Array 41/318241/9
authorEunki, Hong <eunkiki.hong@samsung.com>
Tue, 14 Jan 2025 12:33:59 +0000 (21:33 +0900)
committerEunki, Hong <eunkiki.hong@samsung.com>
Wed, 15 Jan 2025 03:04:44 +0000 (12:04 +0900)
Change-Id: I8528b5cc46be1cd7c1ba2d6c44c0a39018997917
Signed-off-by: Eunki, Hong <eunkiki.hong@samsung.com>
automated-tests/src/dali/utc-Dali-PropertyArray.cpp
automated-tests/src/dali/utc-Dali-PropertyMap.cpp
dali/public-api/object/property-array.cpp
dali/public-api/object/property-map.cpp

index 05a87b7f6b74ac6c7d906203032ddebebbdb06bf..92c6595d34c0d903f1a226fadb51da44666d73f7 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -403,6 +403,10 @@ int UtcDaliPropertyArrayMovedArrayP1(void)
   DALI_TEST_EQUALS(0u, array1.Count(), TEST_LOCATION);
   DALI_TEST_EQUALS(0u, array1.Capacity(), TEST_LOCATION);
   DALI_TEST_EQUALS(true, array1.Empty(), TEST_LOCATION);
+
+  // DALI_TEST_EQUALS copy the array. We should use DALI_TEST_CHECK
+  DALI_TEST_CHECK(emptyArray == array1);
+  DALI_TEST_CHECK(array1 == emptyArray);
   array1.Clear();
 
   // Test reserve
@@ -654,8 +658,8 @@ int UtcDaliPropertyArrayEqualNonFloatType(void)
 
   array2.PushBack(1);
   array2.PushBack(false);
-  array2.PushBack(subArray1);
-  array2.PushBack(subMap1);
+  array2.PushBack(subArray2);
+  array2.PushBack(subMap2);
   array2.PushBack(4);
 
   DALI_TEST_CHECK(array1 == array2);
@@ -685,3 +689,71 @@ int UtcDaliPropertyArrayEqualNonFloatType(void)
 
   END_TEST;
 }
+
+int UtcDaliPropertyArrayEqualFloatType(void)
+{
+  tet_infoline("Check Property::Array equality even if some values need to consider epsilon");
+
+  Property::Array array1;
+  Property::Array subArray1;
+  Property::Map   subMap1;
+
+  subArray1.PushBack(2.0f);
+  subArray1.PushBack(3);
+
+  subMap1.Insert(0, "0");
+  subMap1.Insert("1", 1.0f);
+
+  array1.PushBack(1.0f);
+  array1.PushBack(false);
+  array1.PushBack(subArray1);
+  array1.PushBack(subMap1);
+  array1.PushBack(4);
+
+  tet_printf("Check self-equality return true\n");
+  DALI_TEST_CHECK(array1 == array1);
+  DALI_TEST_EQUALS(array1, array1, TEST_LOCATION);
+
+  tet_printf("Generate exactly same Property::Array with array1\n");
+
+  Property::Array array2;
+  Property::Array subArray2;
+  Property::Map   subMap2;
+
+  subArray2.PushBack(2.0f + Math::MACHINE_EPSILON_1);
+  subArray2.PushBack(3);
+
+  subMap2.Insert(0, "0");
+  subMap2.Insert("1", 1.0f - Math::MACHINE_EPSILON_1);
+
+  array2.PushBack(1.0f + Math::MACHINE_EPSILON_1);
+  DALI_TEST_CHECK(array1 != array2);
+
+  array2.PushBack(false);
+  DALI_TEST_CHECK(array1 != array2);
+
+  array2.PushBack(subArray2);
+  DALI_TEST_CHECK(array1 != array2);
+
+  array2.PushBack(subMap2);
+  DALI_TEST_CHECK(array1 != array2);
+
+  array2.PushBack(4);
+
+  DALI_TEST_CHECK(array1 == array2);
+  DALI_TEST_EQUALS(array1, array2, TEST_LOCATION);
+
+  // Hash value may not be equal!
+  DALI_TEST_NOT_EQUALS(array1.GetHash(), array2.GetHash(), Math::MACHINE_EPSILON_100, TEST_LOCATION);
+
+  array2.PushBack(5);
+
+  DALI_TEST_CHECK(array1 != array2);
+
+  array2.Resize(5);
+
+  DALI_TEST_CHECK(array1 == array2);
+  DALI_TEST_EQUALS(array1, array2, TEST_LOCATION);
+
+  END_TEST;
+}
index 11f179f7b94bf2c97dccd96a9c63fa8a937c1f18..a42528d1f2870675135b5b4eaf643c08e6d41e1a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -175,6 +175,10 @@ int UtcDaliPropertyMapMovedMapP1(void)
   DALI_TEST_EQUALS(emptyMap.GetHash(), map1.GetHash(), TEST_LOCATION);
   DALI_TEST_EQUALS(0u, map1.Count(), TEST_LOCATION);
   DALI_TEST_EQUALS(true, map1.Empty(), TEST_LOCATION);
+
+  // DALI_TEST_EQUALS copy the map. We should use DALI_TEST_CHECK
+  DALI_TEST_CHECK(emptyMap == map1);
+  DALI_TEST_CHECK(map1 == emptyMap);
   map1.Clear();
 
   DALI_TEST_EQUALS(false, map1.Remove(10), TEST_LOCATION);
@@ -1135,11 +1139,11 @@ int UtcDaliPropertyMapEqualNonFloatType(void)
   subMap2.Insert("1", 1);
   subMap2.Insert(0, "0");
 
-  map2.Insert(3, subArray1);
+  map2.Insert(3, subArray2);
   map2.Insert(2, false);
   map2.Insert(1, 1);
   map2.Insert("5", 4);
-  map2.Insert("4", subMap1);
+  map2.Insert("4", subMap2);
 
   DALI_TEST_CHECK(map1 == map2);
   DALI_TEST_EQUALS(map1, map2, TEST_LOCATION);
@@ -1167,3 +1171,74 @@ int UtcDaliPropertyMapEqualNonFloatType(void)
 
   END_TEST;
 }
+
+int UtcDaliPropertyMapEqualFloatType(void)
+{
+  tet_infoline("Check Property::Map equality even if some values need to consider epsilon");
+
+  Property::Map   map1;
+  Property::Array subArray1;
+  Property::Map   subMap1;
+
+  subArray1.PushBack(2.0f);
+  subArray1.PushBack(3);
+
+  subMap1.Insert(0, "0");
+  subMap1.Insert("1", 1.0f);
+
+  map1.Insert(1, 1.0f);
+  map1.Insert(2, false);
+  map1.Insert(3, subArray1);
+  map1.Insert("4", subMap1);
+  map1.Insert("5", 4);
+
+  tet_printf("Check self-equality return true\n");
+  DALI_TEST_CHECK(map1 == map1);
+  DALI_TEST_EQUALS(map1, map1, TEST_LOCATION);
+
+  tet_printf("Generate exactly same Property::Map with map1\n");
+
+  Property::Map   map2;
+  Property::Array subArray2;
+  Property::Map   subMap2;
+
+  subArray2.PushBack(2.0f + Math::MACHINE_EPSILON_1);
+  subArray2.PushBack(3);
+
+  subMap2.Insert("1", 1.0f - Math::MACHINE_EPSILON_1);
+  subMap2.Insert(0, "0");
+
+  map2.Insert(3, subArray2);
+  DALI_TEST_CHECK(map1 != map2);
+
+  map2.Insert(2, false);
+  DALI_TEST_CHECK(map1 != map2);
+
+  map2.Insert(1, 1.0f + Math::MACHINE_EPSILON_1);
+  DALI_TEST_CHECK(map1 != map2);
+
+  map2.Insert("5", 4);
+  DALI_TEST_CHECK(map1 != map2);
+
+  map2.Insert("4", subMap2);
+
+  DALI_TEST_CHECK(map1 == map2);
+  DALI_TEST_EQUALS(map1, map2, TEST_LOCATION);
+
+  // Hash value may not be equal!
+  DALI_TEST_NOT_EQUALS(map1.GetHash(), map2.GetHash(), Math::MACHINE_EPSILON_100, TEST_LOCATION);
+
+  map2.Insert("6", 8);
+  DALI_TEST_CHECK(map1 != map2);
+
+  map2.Remove(2);
+  DALI_TEST_CHECK(map1 != map2);
+
+  map2.Insert(2, false);
+  DALI_TEST_CHECK(map1 != map2);
+
+  map2.Remove("6");
+  DALI_TEST_CHECK(map1 == map2);
+
+  END_TEST;
+}
index 640500f4f8eb70c539142f7fa0080f7b63f65a3d..14953061cc4f5d9273b76b10a3b410b18444c629 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -219,8 +219,34 @@ Property::Array& Property::Array::operator=(Property::Array&& other) noexcept
 
 bool Property::Array::operator==(const Property::Array& rhs) const
 {
-  // TODO : Need to check epsilon for float comparison in future. For now, just compare hash value and count.
-  return Count() == rhs.Count() && GetHash() == rhs.GetHash();
+  if(DALI_UNLIKELY(this == &rhs))
+  {
+    // Fast out for self comparision
+    return true;
+  }
+
+  const auto lhsCount = Count();
+  if(lhsCount != rhs.Count())
+  {
+    return false;
+  }
+  if(DALI_UNLIKELY(mImpl == nullptr))
+  {
+    return rhs.Empty();
+  }
+  if(DALI_UNLIKELY(rhs.mImpl == nullptr))
+  {
+    return Empty();
+  }
+
+  for(SizeType i = 0u; i < lhsCount; ++i)
+  {
+    if(mImpl->mArray[i] != rhs.mImpl->mArray[i])
+    {
+      return false;
+    }
+  }
+  return true;
 }
 
 std::size_t Property::Array::GetHash() const
index ebbf2a0c650567ddcab9629163cf13cb0ca45b60..4f078c742a54ec5a9f8c32cc44edb43970bb3fe2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@
 // EXTERNAL INCLUDES
 #include <dali/integration-api/debug.h>
 #include <limits>
+#include <unordered_map>
 
 // INTERNAL INCLUDES
 #include <dali/internal/common/hash-utils.h>
@@ -559,8 +560,76 @@ Property::Map& Property::Map::operator=(Property::Map&& other) noexcept
 
 bool Property::Map::operator==(const Property::Map& rhs) const
 {
-  // TODO : Need to check epsilon for float comparison in future. For now, just compare hash value and count.
-  return Count() == rhs.Count() && GetHash() == rhs.GetHash();
+  if(DALI_UNLIKELY(this == &rhs))
+  {
+    // Fast out for self comparision
+    return true;
+  }
+
+  if(Count() != rhs.Count())
+  {
+    return false;
+  }
+  if(DALI_UNLIKELY(mImpl == nullptr))
+  {
+    return rhs.Empty();
+  }
+  if(DALI_UNLIKELY(rhs.mImpl == nullptr))
+  {
+    return Empty();
+  }
+
+  // TODO : Should we support duplication key comparision?
+  {
+    std::unordered_map<std::string_view, const Property::Value*> stringValueMap;
+    for(auto&& iter : mImpl->mStringValueContainer)
+    {
+      stringValueMap[std::string_view(iter.first)] = &iter.second;
+    }
+    for(auto&& iter : rhs.mImpl->mStringValueContainer)
+    {
+      auto mapIter = stringValueMap.find(std::string_view(iter.first));
+      if(mapIter == stringValueMap.end())
+      {
+        return false;
+      }
+      if(*(mapIter->second) != iter.second)
+      {
+        return false;
+      }
+      stringValueMap.erase(mapIter);
+    }
+    if(!stringValueMap.empty())
+    {
+      return false;
+    }
+  }
+  {
+    std::unordered_map<Property::Index, const Property::Value*> indexValueMap;
+    for(auto&& iter : mImpl->mIndexValueContainer)
+    {
+      indexValueMap[iter.first] = &iter.second;
+    }
+    for(auto&& iter : rhs.mImpl->mIndexValueContainer)
+    {
+      auto mapIter = indexValueMap.find(iter.first);
+      if(mapIter == indexValueMap.end())
+      {
+        return false;
+      }
+      if(*(mapIter->second) != iter.second)
+      {
+        return false;
+      }
+      indexValueMap.erase(mapIter);
+    }
+    if(!indexValueMap.empty())
+    {
+      return false;
+    }
+  }
+
+  return true;
 }
 
 std::size_t Property::Map::GetHash() const