Fixed unordered container equality tests to match STL

This commit is contained in:
John Wellbelove 2022-09-02 20:40:17 +01:00
parent 60204d95b6
commit 7c5a6e49a1
8 changed files with 396 additions and 103 deletions

View File

@ -142,11 +142,11 @@ namespace etl
typedef const value_type* const_pointer;
typedef size_t size_type;
typedef const TKey& key_parameter_t;
typedef etl::forward_link<0> link_t; // Default link.
// The nodes that store the elements.
// The nodes that store the elements.
struct node_t : public link_t
{
@ -158,6 +158,17 @@ namespace etl
value_type key_value_pair;
};
friend bool operator ==(const node_t& lhs, const node_t& rhs)
{
return (lhs.key_value_pair.first == rhs.key_value_pair.first) &&
(lhs.key_value_pair.second == rhs.key_value_pair.second);
}
friend bool operator !=(const node_t& lhs, const node_t& rhs)
{
return !(lhs == rhs);
}
protected:
typedef etl::intrusive_forward_list<node_t, link_t> bucket_t;
@ -167,7 +178,7 @@ namespace etl
// Local iterators iterate over one bucket.
typedef typename bucket_t::iterator local_iterator;
typedef typename bucket_t::const_iterator local_const_iterator;
typedef typename bucket_t::const_iterator const_local_iterator;
//*********************************************************************
class iterator : public etl::iterator<ETL_OR_STD::forward_iterator_tag, T>
@ -505,7 +516,7 @@ namespace etl
/// Returns a const_iterator to the beginning of the unordered_map bucket.
///\return A const iterator to the beginning of the unordered_map bucket.
//*********************************************************************
local_const_iterator begin(size_t i) const
const_local_iterator begin(size_t i) const
{
return pbuckets[i].cbegin();
}
@ -514,7 +525,7 @@ namespace etl
/// Returns a const_iterator to the beginning of the unordered_map bucket.
///\return A const iterator to the beginning of the unordered_map bucket.
//*********************************************************************
local_const_iterator cbegin(size_t i) const
const_local_iterator cbegin(size_t i) const
{
return pbuckets[i].cbegin();
}
@ -559,7 +570,7 @@ namespace etl
/// Returns a const_iterator to the end of the unordered_map bucket.
///\return A const iterator to the end of the unordered_map bucket.
//*********************************************************************
local_const_iterator end(size_t i) const
const_local_iterator end(size_t i) const
{
return pbuckets[i].cend();
}
@ -568,7 +579,7 @@ namespace etl
/// Returns a const_iterator to the end of the unordered_map bucket.
///\return A const iterator to the end of the unordered_map bucket.
//*********************************************************************
local_const_iterator cend(size_t i) const
const_local_iterator cend(size_t i) const
{
return pbuckets[i].cend();
}
@ -1501,7 +1512,21 @@ namespace etl
template <typename TKey, typename T, typename TKeyCompare>
bool operator ==(const etl::iunordered_map<TKey, T, TKeyCompare>& lhs, const etl::iunordered_map<TKey, T, TKeyCompare>& rhs)
{
return (lhs.size() == rhs.size()) && etl::equal(lhs.begin(), lhs.end(), rhs.begin());
const bool sizes_match = (lhs.size() == rhs.size());
bool elements_match = true;
if (sizes_match)
{
for (size_t i = 0; (i < lhs.bucket_count()) && elements_match; ++i)
{
if (!etl::is_permutation(lhs.begin(i), lhs.end(i), rhs.begin(i)))
{
elements_match = false;
}
}
}
return (sizes_match && elements_match);
}
//***************************************************************************

View File

@ -146,7 +146,9 @@ namespace etl
typedef etl::forward_link<0> link_t; // Default link.
struct node_t : public link_t // The nodes that store the elements.
//*********************************************************************
// The nodes that store the elements.
struct node_t : public link_t
{
node_t(const_reference key_value_pair_)
: key_value_pair(key_value_pair_)
@ -156,6 +158,17 @@ namespace etl
value_type key_value_pair;
};
friend bool operator ==(const node_t& lhs, const node_t& rhs)
{
return (lhs.key_value_pair.first == rhs.key_value_pair.first) &&
(lhs.key_value_pair.second == rhs.key_value_pair.second);
}
friend bool operator !=(const node_t& lhs, const node_t& rhs)
{
return !(lhs == rhs);
}
protected:
typedef etl::intrusive_forward_list<node_t, link_t> bucket_t;
@ -165,7 +178,7 @@ namespace etl
// Local iterators iterate over one bucket.
typedef typename bucket_t::iterator local_iterator;
typedef typename bucket_t::const_iterator local_const_iterator;
typedef typename bucket_t::const_iterator const_local_iterator;
//*********************************************************************
class iterator : public etl::iterator<ETL_OR_STD::forward_iterator_tag, T>
@ -504,7 +517,7 @@ namespace etl
/// Returns a const_iterator to the beginning of the unordered_multimap bucket.
///\return A const iterator to the beginning of the unordered_multimap bucket.
//*********************************************************************
local_const_iterator begin(size_t i) const
const_local_iterator begin(size_t i) const
{
return pbuckets[i].cbegin();
}
@ -513,7 +526,7 @@ namespace etl
/// Returns a const_iterator to the beginning of the unordered_multimap bucket.
///\return A const iterator to the beginning of the unordered_multimap bucket.
//*********************************************************************
local_const_iterator cbegin(size_t i) const
const_local_iterator cbegin(size_t i) const
{
return pbuckets[i].cbegin();
}
@ -558,7 +571,7 @@ namespace etl
/// Returns a const_iterator to the end of the unordered_multimap bucket.
///\return A const iterator to the end of the unordered_multimap bucket.
//*********************************************************************
local_const_iterator end(size_t i) const
const_local_iterator end(size_t i) const
{
return pbuckets[i].cend();
}
@ -567,7 +580,7 @@ namespace etl
/// Returns a const_iterator to the end of the unordered_multimap bucket.
///\return A const iterator to the end of the unordered_multimap bucket.
//*********************************************************************
local_const_iterator cend(size_t i) const
const_local_iterator cend(size_t i) const
{
return pbuckets[i].cend();
}
@ -1407,7 +1420,21 @@ namespace etl
template <typename TKey, typename TMapped, typename TKeyCompare>
bool operator ==(const etl::iunordered_multimap<TKey, TMapped, TKeyCompare>& lhs, const etl::iunordered_multimap<TKey, TMapped, TKeyCompare>& rhs)
{
return (lhs.size() == rhs.size()) && etl::equal(lhs.begin(), lhs.end(), rhs.begin());
const bool sizes_match = (lhs.size() == rhs.size());
bool elements_match = true;
if (sizes_match)
{
for (size_t i = 0; (i < lhs.bucket_count()) && elements_match; ++i)
{
if (!etl::is_permutation(lhs.begin(i), lhs.end(i), rhs.begin(i)))
{
elements_match = false;
}
}
}
return (sizes_match && elements_match);
}
//***************************************************************************

View File

@ -143,6 +143,7 @@ namespace etl
typedef etl::forward_link<0> link_t;
//*********************************************************************
// The nodes that store the elements.
struct node_t : public link_t
{
@ -154,6 +155,16 @@ namespace etl
value_type key;
};
friend bool operator ==(const node_t& lhs, const node_t& rhs)
{
return (lhs.key == rhs.key);
}
friend bool operator !=(const node_t& lhs, const node_t& rhs)
{
return !(lhs == rhs);
}
protected:
typedef etl::intrusive_forward_list<node_t, link_t> bucket_t;
@ -163,7 +174,7 @@ namespace etl
// Local iterators iterate over one bucket.
typedef typename bucket_t::iterator local_iterator;
typedef typename bucket_t::const_iterator local_const_iterator;
typedef typename bucket_t::const_iterator const_local_iterator;
//*********************************************************************
class iterator : public etl::iterator<ETL_OR_STD::forward_iterator_tag, TKey>
@ -500,7 +511,7 @@ namespace etl
/// Returns a const_iterator to the beginning of the unordered_multiset bucket.
///\return A const iterator to the beginning of the unordered_multiset bucket.
//*********************************************************************
local_const_iterator begin(size_t i) const
const_local_iterator begin(size_t i) const
{
return pbuckets[i].cbegin();
}
@ -509,7 +520,7 @@ namespace etl
/// Returns a const_iterator to the beginning of the unordered_multiset bucket.
///\return A const iterator to the beginning of the unordered_multiset bucket.
//*********************************************************************
local_const_iterator cbegin(size_t i) const
const_local_iterator cbegin(size_t i) const
{
return pbuckets[i].cbegin();
}
@ -554,7 +565,7 @@ namespace etl
/// Returns a const_iterator to the end of the unordered_multiset bucket.
///\return A const iterator to the end of the unordered_multiset bucket.
//*********************************************************************
local_const_iterator end(size_t i) const
const_local_iterator end(size_t i) const
{
return pbuckets[i].cend();
}
@ -563,7 +574,7 @@ namespace etl
/// Returns a const_iterator to the end of the unordered_multiset bucket.
///\return A const iterator to the end of the unordered_multiset bucket.
//*********************************************************************
local_const_iterator cend(size_t i) const
const_local_iterator cend(size_t i) const
{
return pbuckets[i].cend();
}
@ -1390,7 +1401,21 @@ namespace etl
template <typename TKey, typename TMapped, typename TKeyCompare>
bool operator ==(const etl::iunordered_multiset<TKey, TMapped, TKeyCompare>& lhs, const etl::iunordered_multiset<TKey, TMapped, TKeyCompare>& rhs)
{
return (lhs.size() == rhs.size()) && etl::equal(lhs.begin(), lhs.end(), rhs.begin());
const bool sizes_match = (lhs.size() == rhs.size());
bool elements_match = true;
if (sizes_match)
{
for (size_t i = 0; (i < lhs.bucket_count()) && elements_match; ++i)
{
if (!etl::is_permutation(lhs.begin(i), lhs.end(i), rhs.begin(i)))
{
elements_match = false;
}
}
}
return (sizes_match && elements_match);
}
//***************************************************************************

View File

@ -144,6 +144,7 @@ namespace etl
typedef etl::forward_link<0> link_t;
//*********************************************************************
// The nodes that store the elements.
struct node_t : public link_t
{
@ -155,6 +156,16 @@ namespace etl
value_type key;
};
friend bool operator ==(const node_t& lhs, const node_t& rhs)
{
return (lhs.key == rhs.key);
}
friend bool operator !=(const node_t& lhs, const node_t& rhs)
{
return !(lhs == rhs);
}
protected:
typedef etl::intrusive_forward_list<node_t, link_t> bucket_t;
@ -164,7 +175,7 @@ namespace etl
// Local iterators iterate over one bucket.
typedef typename bucket_t::iterator local_iterator;
typedef typename bucket_t::const_iterator local_const_iterator;
typedef typename bucket_t::const_iterator const_local_iterator;
//*********************************************************************
class iterator : public etl::iterator<ETL_OR_STD::forward_iterator_tag, TKey>
@ -501,7 +512,7 @@ namespace etl
/// Returns a const_iterator to the beginning of the unordered_set bucket.
///\return A const iterator to the beginning of the unordered_set bucket.
//*********************************************************************
local_const_iterator begin(size_t i) const
const_local_iterator begin(size_t i) const
{
return pbuckets[i].cbegin();
}
@ -510,7 +521,7 @@ namespace etl
/// Returns a const_iterator to the beginning of the unordered_set bucket.
///\return A const iterator to the beginning of the unordered_set bucket.
//*********************************************************************
local_const_iterator cbegin(size_t i) const
const_local_iterator cbegin(size_t i) const
{
return pbuckets[i].cbegin();
}
@ -555,7 +566,7 @@ namespace etl
/// Returns a const_iterator to the end of the unordered_set bucket.
///\return A const iterator to the end of the unordered_set bucket.
//*********************************************************************
local_const_iterator end(size_t i) const
const_local_iterator end(size_t i) const
{
return pbuckets[i].cend();
}
@ -564,7 +575,7 @@ namespace etl
/// Returns a const_iterator to the end of the unordered_set bucket.
///\return A const iterator to the end of the unordered_set bucket.
//*********************************************************************
local_const_iterator cend(size_t i) const
const_local_iterator cend(size_t i) const
{
return pbuckets[i].cend();
}
@ -1303,40 +1314,40 @@ namespace etl
//*********************************************************************
void adjust_first_last_markers_after_erase(bucket_t* pcurrent)
{
if (empty())
if (empty())
{
first = pbuckets;
last = pbuckets;
}
else
{
if (pcurrent == first)
{
// We erased the first so, we need to search again from where we erased.
while (first->empty())
{
++first;
}
}
else if (pcurrent == last)
{
// We erased the last, so we need to search again. Start from the first, go no further than the current last.
bucket_t* pcurrent = first;
bucket_t* pend = last;
last = first;
while (pcurrent != pend)
{
if (!pcurrent->empty())
{
first = pbuckets;
last = pbuckets;
last = pcurrent;
}
else
{
if (pcurrent == first)
{
// We erased the first so, we need to search again from where we erased.
while (first->empty())
{
++first;
}
}
else if (pcurrent == last)
{
// We erased the last, so we need to search again. Start from the first, go no further than the current last.
bucket_t* pcurrent = first;
bucket_t* pend = last;
last = first;
while (pcurrent != pend)
{
if (!pcurrent->empty())
{
last = pcurrent;
}
++pcurrent;
}
}
}
++pcurrent;
}
}
}
}
// Disable copy construction.
@ -1364,9 +1375,9 @@ namespace etl
/// For library debugging purposes only.
ETL_DECLARE_DEBUG_COUNT
//*************************************************************************
/// Destructor.
//*************************************************************************
//*************************************************************************
/// Destructor.
//*************************************************************************
#if defined(ETL_POLYMORPHIC_UNORDERED_SET) || defined(ETL_POLYMORPHIC_CONTAINERS)
public:
virtual ~iunordered_set()
@ -1384,20 +1395,34 @@ namespace etl
/// Equal operator.
///\param lhs Reference to the first unordered_set.
///\param rhs Reference to the second unordered_set.
///\return <b>true</b> if the arrays are equal, otherwise <b>false</b>
///\return <b>true</b> if the sets are equal, otherwise <b>false</b>
///\ingroup unordered_set
//***************************************************************************
template <typename TKey, typename TMapped, typename TKeyCompare>
bool operator ==(const etl::iunordered_set<TKey, TMapped, TKeyCompare>& lhs, const etl::iunordered_set<TKey, TMapped, TKeyCompare>& rhs)
{
return (lhs.size() == rhs.size()) && etl::equal(lhs.begin(), lhs.end(), rhs.begin());
const bool sizes_match = (lhs.size() == rhs.size());
bool elements_match = true;
if (sizes_match)
{
for (size_t i = 0; (i < lhs.bucket_count()) && elements_match; ++i)
{
if (!etl::is_permutation(lhs.begin(i), lhs.end(i), rhs.begin(i)))
{
elements_match = false;
}
}
}
return (sizes_match && elements_match);
}
//***************************************************************************
/// Not equal operator.
///\param lhs Reference to the first unordered_set.
///\param rhs Reference to the second unordered_set.
///\return <b>true</b> if the arrays are not equal, otherwise <b>false</b>
///\return <b>true</b> if the sets are not equal, otherwise <b>false</b>
///\ingroup unordered_set
//***************************************************************************
template <typename TKey, typename TMapped, typename TKeyCompare>

View File

@ -960,6 +960,75 @@ namespace
CHECK_CLOSE(2.0, data.load_factor(), 0.01);
}
//*************************************************************************
TEST(test_equality_comparison_fails_when_hash_collisions_occur_582)
{
struct bad_hash
{
// Force hash collisions
size_t operator()(int key) const
{
return key % 4;
}
};
using etl_map = etl::unordered_map<int, std::string, 20, 20, bad_hash>;
using stl_map = std::unordered_map<int, std::string, bad_hash>;
std::vector<etl_map::value_type> random_keys1 =
{
{17, "17"}, {14, "14"}, { 3, "3"}, { 7, "7"}, { 2, "2"},
{ 6, "6"}, { 9, "9"}, { 3, "3"}, {18, "18"}, {10, "10"},
{ 8, "8"}, {11, "11"}, { 4, "4"}, { 1, "1"}, {12, "12"},
{15, "15"}, {16, "16"}, { 0, "0"}, { 5, "5"}, {19, "19"}
};
std::vector<etl_map::value_type> random_keys2 =
{
{ 3, "3"}, { 6, "6"}, { 5, "5"}, {17, "17"}, { 2, "2"},
{ 7, "7"}, { 3, "3"}, {19, "19"}, { 8, "8"}, {15, "15"},
{14, "14"}, { 0, "0"}, {18, "18"}, { 4, "4"}, {10, "10"},
{ 9, "9"}, {16, "16"}, {11, "11"}, {12, "12"}, { 1, "1"}
};
// Check that the input data is valid.
CHECK_EQUAL(random_keys1.size(), random_keys2.size());
CHECK(std::is_permutation(random_keys1.begin(), random_keys1.end(), random_keys2.begin()));
//***************************************************
// Fill ETL
etl_map etlmap1;
etl_map etlmap2;
for (auto i : random_keys1)
{
etlmap1.insert(i);
}
for (auto i : random_keys2)
{
etlmap2.insert(i);
}
//***************************************************
// Fill STD
stl_map stdmap1;
stl_map stdmap2;
for (auto i : random_keys1)
{
stdmap1.insert(i);
}
for (auto i : random_keys2)
{
stdmap2.insert(i);
}
//***************************************************
CHECK_EQUAL((stdmap1 == stdmap2), (etlmap1 == etlmap2));
}
//*************************************************************************
TEST_FIXTURE(SetupFixture, test_insert_and_erase_bug)
{
@ -1065,11 +1134,11 @@ namespace
CustomHashFunction chf(1);
CustomKeyEq ceq(2);
etl::unordered_map<uint32_t, uint32_t, 5, 5, CustomHashFunction, CustomKeyEq> set1(chf, ceq);
etl::unordered_map<uint32_t, uint32_t, 5, 5, CustomHashFunction, CustomKeyEq> set2(set1);
etl::unordered_map<uint32_t, uint32_t, 5, 5, CustomHashFunction, CustomKeyEq> map1(chf, ceq);
etl::unordered_map<uint32_t, uint32_t, 5, 5, CustomHashFunction, CustomKeyEq> map2(map1);
CHECK_EQUAL(chf.id, set2.hash_function().id);
CHECK_EQUAL(ceq.id, set2.key_eq().id);
CHECK_EQUAL(chf.id, map2.hash_function().id);
CHECK_EQUAL(ceq.id, map2.key_eq().id);
}
//*************************************************************************
@ -1081,13 +1150,13 @@ namespace
CustomHashFunction chf3(3);
CustomKeyEq ceq4(4);
etl::unordered_map<uint32_t, uint32_t, 5, 5, CustomHashFunction, CustomKeyEq> set1(chf1, ceq2);
etl::unordered_map<uint32_t, uint32_t, 5, 5, CustomHashFunction, CustomKeyEq> set2(chf3, ceq4);
etl::unordered_map<uint32_t, uint32_t, 5, 5, CustomHashFunction, CustomKeyEq> map1(chf1, ceq2);
etl::unordered_map<uint32_t, uint32_t, 5, 5, CustomHashFunction, CustomKeyEq> map2(chf3, ceq4);
set2.operator=(set1);
map2.operator=(map1);
CHECK_EQUAL(chf1.id, set2.hash_function().id);
CHECK_EQUAL(ceq2.id, set2.key_eq().id);
CHECK_EQUAL(chf1.id, map2.hash_function().id);
CHECK_EQUAL(ceq2.id, map2.key_eq().id);
}
//*************************************************************************
@ -1106,10 +1175,10 @@ namespace
value_type{5, 55}
};
etl::unordered_map<uint32_t, uint32_t, 5, 5, CustomHashFunction, CustomKeyEq> set1(data.begin(), data.end(), chf1, ceq2);
etl::unordered_map<uint32_t, uint32_t, 5, 5, CustomHashFunction, CustomKeyEq> map1(data.begin(), data.end(), chf1, ceq2);
CHECK_EQUAL(chf1.id, set1.hash_function().id);
CHECK_EQUAL(ceq2.id, set1.key_eq().id);
CHECK_EQUAL(chf1.id, map1.hash_function().id);
CHECK_EQUAL(ceq2.id, map1.key_eq().id);
}
//*************************************************************************
@ -1120,10 +1189,10 @@ namespace
using value_type = etl::unordered_map<uint32_t, uint32_t, 5, 5, CustomHashFunction, CustomKeyEq>::value_type;
etl::unordered_map<uint32_t, uint32_t, 5, 5, CustomHashFunction, CustomKeyEq> set1({ value_type{1, 11}, value_type{2, 22}, value_type{3, 33}, value_type{4, 44}, value_type{5, 55} }, chf1, ceq2);
etl::unordered_map<uint32_t, uint32_t, 5, 5, CustomHashFunction, CustomKeyEq> map1({ value_type{1, 11}, value_type{2, 22}, value_type{3, 33}, value_type{4, 44}, value_type{5, 55} }, chf1, ceq2);
CHECK_EQUAL(chf1.id, set1.hash_function().id);
CHECK_EQUAL(ceq2.id, set1.key_eq().id);
CHECK_EQUAL(chf1.id, map1.hash_function().id);
CHECK_EQUAL(ceq2.id, map1.key_eq().id);
}
};
}

View File

@ -839,6 +839,75 @@ namespace
CHECK_CLOSE(2.0, data.load_factor(), 0.01);
}
//*************************************************************************
TEST(test_equality_comparison_fails_when_hash_collisions_occur_582)
{
struct bad_hash
{
// Force hash collisions
size_t operator()(int key) const
{
return key % 4;
}
};
using etl_map = etl::unordered_multimap<int, std::string, 20, 20, bad_hash>;
using stl_map = std::unordered_multimap<int, std::string, bad_hash>;
std::vector<etl_map::value_type> random_keys1 =
{
{17, "17"}, {14, "14"}, { 3, "3"}, { 7, "7"}, { 2, "2"},
{ 6, "6"}, { 9, "9"}, { 3, "3"}, {18, "18"}, {10, "10"},
{ 8, "8"}, {11, "11"}, { 4, "4"}, { 1, "1"}, {12, "12"},
{15, "15"}, {16, "16"}, { 0, "0"}, { 5, "5"}, {19, "19"}
};
std::vector<etl_map::value_type> random_keys2 =
{
{ 3, "3"}, { 6, "6"}, { 5, "5"}, {17, "17"}, { 2, "2"},
{ 7, "7"}, { 3, "3"}, {19, "19"}, { 8, "8"}, {15, "15"},
{14, "14"}, { 0, "0"}, {18, "18"}, { 4, "4"}, {10, "10"},
{ 9, "9"}, {16, "16"}, {11, "11"}, {12, "12"}, { 1, "1"}
};
// Check that the input data is valid.
CHECK_EQUAL(random_keys1.size(), random_keys2.size());
CHECK(std::is_permutation(random_keys1.begin(), random_keys1.end(), random_keys2.begin()));
//***************************************************
// Fill ETL
etl_map etlmap1;
etl_map etlmap2;
for (auto i : random_keys1)
{
etlmap1.insert(i);
}
for (auto i : random_keys2)
{
etlmap2.insert(i);
}
//***************************************************
// Fill STD
stl_map stdmap1;
stl_map stdmap2;
for (auto i : random_keys1)
{
stdmap1.insert(i);
}
for (auto i : random_keys2)
{
stdmap2.insert(i);
}
//***************************************************
CHECK_EQUAL((stdmap1 == stdmap2), (etlmap1 == etlmap2));
}
//*************************************************************************
TEST_FIXTURE(SetupFixture, test_insert_and_erase_bug)
{
@ -875,11 +944,11 @@ namespace
CustomHashFunction chf(1);
CustomKeyEq ceq(2);
etl::unordered_multimap<uint32_t, uint32_t, 5, 5, CustomHashFunction, CustomKeyEq> set1(chf, ceq);
etl::unordered_multimap<uint32_t, uint32_t, 5, 5, CustomHashFunction, CustomKeyEq> set2(set1);
etl::unordered_multimap<uint32_t, uint32_t, 5, 5, CustomHashFunction, CustomKeyEq> map1(chf, ceq);
etl::unordered_multimap<uint32_t, uint32_t, 5, 5, CustomHashFunction, CustomKeyEq> map2(map1);
CHECK_EQUAL(chf.id, set2.hash_function().id);
CHECK_EQUAL(ceq.id, set2.key_eq().id);
CHECK_EQUAL(chf.id, map2.hash_function().id);
CHECK_EQUAL(ceq.id, map2.key_eq().id);
}
//*************************************************************************
@ -891,13 +960,13 @@ namespace
CustomHashFunction chf3(3);
CustomKeyEq ceq4(4);
etl::unordered_multimap<uint32_t, uint32_t, 5, 5, CustomHashFunction, CustomKeyEq> set1(chf1, ceq2);
etl::unordered_multimap<uint32_t, uint32_t, 5, 5, CustomHashFunction, CustomKeyEq> set2(chf3, ceq4);
etl::unordered_multimap<uint32_t, uint32_t, 5, 5, CustomHashFunction, CustomKeyEq> map1(chf1, ceq2);
etl::unordered_multimap<uint32_t, uint32_t, 5, 5, CustomHashFunction, CustomKeyEq> map2(chf3, ceq4);
set2.operator=(set1);
map2.operator=(map1);
CHECK_EQUAL(chf1.id, set2.hash_function().id);
CHECK_EQUAL(ceq2.id, set2.key_eq().id);
CHECK_EQUAL(chf1.id, map2.hash_function().id);
CHECK_EQUAL(ceq2.id, map2.key_eq().id);
}
//*************************************************************************
@ -916,10 +985,10 @@ namespace
value_type{5, 55}
};
etl::unordered_multimap<uint32_t, uint32_t, 5, 5, CustomHashFunction, CustomKeyEq> set1(data.begin(), data.end(), chf1, ceq2);
etl::unordered_multimap<uint32_t, uint32_t, 5, 5, CustomHashFunction, CustomKeyEq> map1(data.begin(), data.end(), chf1, ceq2);
CHECK_EQUAL(chf1.id, set1.hash_function().id);
CHECK_EQUAL(ceq2.id, set1.key_eq().id);
CHECK_EQUAL(chf1.id, map1.hash_function().id);
CHECK_EQUAL(ceq2.id, map1.key_eq().id);
}
//*************************************************************************
@ -930,10 +999,10 @@ namespace
using value_type = etl::unordered_multimap<uint32_t, uint32_t, 5, 5, CustomHashFunction, CustomKeyEq>::value_type;
etl::unordered_multimap<uint32_t, uint32_t, 5, 5, CustomHashFunction, CustomKeyEq> set1({ value_type{1, 11}, value_type{2, 22}, value_type{3, 33}, value_type{4, 44}, value_type{5, 55} }, chf1, ceq2);
etl::unordered_multimap<uint32_t, uint32_t, 5, 5, CustomHashFunction, CustomKeyEq> map1({ value_type{1, 11}, value_type{2, 22}, value_type{3, 33}, value_type{4, 44}, value_type{5, 55} }, chf1, ceq2);
CHECK_EQUAL(chf1.id, set1.hash_function().id);
CHECK_EQUAL(ceq2.id, set1.key_eq().id);
CHECK_EQUAL(chf1.id, map1.hash_function().id);
CHECK_EQUAL(ceq2.id, map1.key_eq().id);
}
};
}

View File

@ -36,6 +36,7 @@ SOFTWARE.
#include <string>
#include <vector>
#include <numeric>
#include <unordered_set>
#include "data.h"
@ -770,6 +771,59 @@ namespace
CHECK_CLOSE(2.0, data.load_factor(), 0.01);
}
//*************************************************************************
TEST(test_equality_comparison_fails_when_hash_collisions_occur_582)
{
struct bad_hash
{
// Force hash collisions
size_t operator()(int key) const
{
return key % 4;
}
};
std::vector<int> random_keys1 = { 17, 14, 3, 7, 2, 6, 9, 3, 18, 10, 8, 11, 4, 1, 12, 15, 16, 0, 5, 19 };
std::vector<int> random_keys2 = { 3, 6, 5, 17, 2, 7, 3, 19, 8, 15, 14, 0, 18, 4, 10, 9, 16, 11, 12, 1 };
// Check that the input data is valid.
CHECK_EQUAL(random_keys1.size(), random_keys2.size());
CHECK(std::is_permutation(random_keys1.begin(), random_keys1.end(), random_keys2.begin()));
//***************************************************
// Fill ETL
etl::unordered_multiset<int, 20, 20, bad_hash> etlset1;
etl::unordered_multiset<int, 20, 20, bad_hash> etlset2;
for (auto i : random_keys1)
{
etlset1.insert(i);
}
for (auto i : random_keys2)
{
etlset2.insert(i);
}
//***************************************************
// Fill STD
std::unordered_multiset<int, bad_hash> stdset1;
std::unordered_multiset<int, bad_hash> stdset2;
for (auto i : random_keys1)
{
stdset1.insert(i);
}
for (auto i : random_keys2)
{
stdset2.insert(i);
}
//***************************************************
CHECK_EQUAL((stdset1 == stdset2), (etlset1 == etlset2));
}
//*************************************************************************
TEST(test_copying_of_hash_and_key_compare_with_copy_construct)
{

View File

@ -713,31 +713,31 @@ namespace
{
struct bad_hash
{
// Often has hash collisions
// Force hash collisions
size_t operator()(int key) const
{
return key % 4;
}
};
std::vector<int> keys1 = { 17, 14, 3, 7, 2, 6, 9, 3, 18, 10, 8, 11, 4, 1, 12, 15, 16, 0, 5, 19 };
std::vector<int> keys2 = { 3, 6, 5, 17, 2, 7, 3, 19, 8, 15, 14, 0, 18, 4, 10, 9, 16, 11, 12, 1 };
std::vector<int> random_keys1 = { 17, 14, 3, 7, 2, 6, 9, 3, 18, 10, 8, 11, 4, 1, 12, 15, 16, 0, 5, 19 };
std::vector<int> random_keys2 = { 3, 6, 5, 17, 2, 7, 3, 19, 8, 15, 14, 0, 18, 4, 10, 9, 16, 11, 12, 1 };
// Check that the input data is valid.
CHECK_EQUAL(keys1.size(), keys2.size());
CHECK(std::is_permutation(keys1.begin(), keys1.end(), keys2.begin()));
CHECK_EQUAL(random_keys1.size(), random_keys2.size());
CHECK(std::is_permutation(random_keys1.begin(), random_keys1.end(), random_keys2.begin()));
//***************************************************
// Fill ETL
etl::unordered_set<int, 20, 20, bad_hash> etlset1;
etl::unordered_set<int, 20, 20, bad_hash> etlset2;
for (auto i : keys1)
for (auto i : random_keys1)
{
etlset1.insert(i);
}
for (auto i : keys2)
for (auto i : random_keys2)
{
etlset2.insert(i);
}
@ -747,19 +747,18 @@ namespace
std::unordered_set<int, bad_hash> stdset1;
std::unordered_set<int, bad_hash> stdset2;
for (auto i : keys1)
for (auto i : random_keys1)
{
stdset1.insert(i);
}
for (auto i : keys2)
for (auto i : random_keys2)
{
stdset2.insert(i);
}
//***************************************************
CHECK(etlset1 == etlset2);
CHECK(stdset1 == stdset2);
CHECK_EQUAL((stdset1 == stdset2), (etlset1 == etlset2));
}
//*************************************************************************