re PR libstdc++/52476 ([DR 518] Unordered multimap reorders equivalent elements)
2012-03-15 François Dumont <fdumont@gcc.gnu.org> PR libstdc++/52476 * include/bits/hashtable.h (_Hashtable<>::_M_rehash_aux): Add. (_Hashtable<>::_M_rehash): Use the latter. * testsuite/23_containers/unordered_multimap/insert/52476.cc: New. * testsuite/23_containers/unordered_multiset/insert/52476.cc: New. From-SVN: r185476
This commit is contained in:
parent
d66823158e
commit
ac0ab963f2
@ -1,3 +1,11 @@
|
||||
2012-03-16 François Dumont <fdumont@gcc.gnu.org>
|
||||
|
||||
PR libstdc++/52476
|
||||
* include/bits/hashtable.h (_Hashtable<>::_M_rehash_aux): Add.
|
||||
(_Hashtable<>::_M_rehash): Use the latter.
|
||||
* testsuite/23_containers/unordered_multimap/insert/52476.cc: New.
|
||||
* testsuite/23_containers/unordered_multiset/insert/52476.cc: New.
|
||||
|
||||
2012-03-14 Rainer Orth <ro@CeBiTec.Uni-Bielefeld.DE>
|
||||
|
||||
* config/os/solaris/solaris2.8: Rename to ...
|
||||
|
@ -596,6 +596,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
// reserve, if present, comes from _Rehash_base.
|
||||
|
||||
private:
|
||||
// Helper rehash method used when keys are unique.
|
||||
void _M_rehash_aux(size_type __n, std::true_type);
|
||||
|
||||
// Helper rehash method used when keys can be non-unique.
|
||||
void _M_rehash_aux(size_type __n, std::false_type);
|
||||
|
||||
// Unconditionally change size of bucket array to n, restore hash policy
|
||||
// state to __state on exception.
|
||||
void _M_rehash(size_type __n, const _RehashPolicyState& __state);
|
||||
@ -1592,33 +1598,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
{
|
||||
__try
|
||||
{
|
||||
_Bucket* __new_buckets = _M_allocate_buckets(__n);
|
||||
_Node* __p = _M_begin();
|
||||
_M_before_begin._M_nxt = nullptr;
|
||||
std::size_t __cur_bbegin_bkt;
|
||||
while (__p)
|
||||
{
|
||||
_Node* __next = __p->_M_next();
|
||||
std::size_t __new_index = _HCBase::_M_bucket_index(__p, __n);
|
||||
if (!__new_buckets[__new_index])
|
||||
{
|
||||
__p->_M_nxt = _M_before_begin._M_nxt;
|
||||
_M_before_begin._M_nxt = __p;
|
||||
__new_buckets[__new_index] = &_M_before_begin;
|
||||
if (__p->_M_nxt)
|
||||
__new_buckets[__cur_bbegin_bkt] = __p;
|
||||
__cur_bbegin_bkt = __new_index;
|
||||
}
|
||||
else
|
||||
{
|
||||
__p->_M_nxt = __new_buckets[__new_index]->_M_nxt;
|
||||
__new_buckets[__new_index]->_M_nxt = __p;
|
||||
}
|
||||
__p = __next;
|
||||
}
|
||||
_M_deallocate_buckets(_M_buckets, _M_bucket_count);
|
||||
_M_bucket_count = __n;
|
||||
_M_buckets = __new_buckets;
|
||||
_M_rehash_aux(__n, integral_constant<bool, __uk>());
|
||||
}
|
||||
__catch(...)
|
||||
{
|
||||
@ -1629,6 +1609,136 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
}
|
||||
}
|
||||
|
||||
// Rehash when there is no equivalent elements.
|
||||
template<typename _Key, typename _Value,
|
||||
typename _Allocator, typename _ExtractKey, typename _Equal,
|
||||
typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
|
||||
bool __chc, bool __cit, bool __uk>
|
||||
void
|
||||
_Hashtable<_Key, _Value, _Allocator, _ExtractKey, _Equal,
|
||||
_H1, _H2, _Hash, _RehashPolicy, __chc, __cit, __uk>::
|
||||
_M_rehash_aux(size_type __n, std::true_type)
|
||||
{
|
||||
_Bucket* __new_buckets = _M_allocate_buckets(__n);
|
||||
_Node* __p = _M_begin();
|
||||
_M_before_begin._M_nxt = nullptr;
|
||||
std::size_t __bbegin_bkt;
|
||||
while (__p)
|
||||
{
|
||||
_Node* __next = __p->_M_next();
|
||||
std::size_t __bkt = _HCBase::_M_bucket_index(__p, __n);
|
||||
if (!__new_buckets[__bkt])
|
||||
{
|
||||
__p->_M_nxt = _M_before_begin._M_nxt;
|
||||
_M_before_begin._M_nxt = __p;
|
||||
__new_buckets[__bkt] = &_M_before_begin;
|
||||
if (__p->_M_nxt)
|
||||
__new_buckets[__bbegin_bkt] = __p;
|
||||
__bbegin_bkt = __bkt;
|
||||
}
|
||||
else
|
||||
{
|
||||
__p->_M_nxt = __new_buckets[__bkt]->_M_nxt;
|
||||
__new_buckets[__bkt]->_M_nxt = __p;
|
||||
}
|
||||
__p = __next;
|
||||
}
|
||||
_M_deallocate_buckets(_M_buckets, _M_bucket_count);
|
||||
_M_bucket_count = __n;
|
||||
_M_buckets = __new_buckets;
|
||||
}
|
||||
|
||||
// Rehash when there can be equivalent elements, preserve their relative
|
||||
// order.
|
||||
template<typename _Key, typename _Value,
|
||||
typename _Allocator, typename _ExtractKey, typename _Equal,
|
||||
typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
|
||||
bool __chc, bool __cit, bool __uk>
|
||||
void
|
||||
_Hashtable<_Key, _Value, _Allocator, _ExtractKey, _Equal,
|
||||
_H1, _H2, _Hash, _RehashPolicy, __chc, __cit, __uk>::
|
||||
_M_rehash_aux(size_type __n, std::false_type)
|
||||
{
|
||||
_Bucket* __new_buckets = _M_allocate_buckets(__n);
|
||||
|
||||
_Node* __p = _M_begin();
|
||||
_M_before_begin._M_nxt = nullptr;
|
||||
std::size_t __bbegin_bkt;
|
||||
std::size_t __prev_bkt;
|
||||
_Node* __prev_p = nullptr;
|
||||
bool __check_bucket = false;
|
||||
|
||||
while (__p)
|
||||
{
|
||||
bool __check_now = true;
|
||||
_Node* __next = __p->_M_next();
|
||||
std::size_t __bkt = _HCBase::_M_bucket_index(__p, __n);
|
||||
|
||||
if (!__new_buckets[__bkt])
|
||||
{
|
||||
__p->_M_nxt = _M_before_begin._M_nxt;
|
||||
_M_before_begin._M_nxt = __p;
|
||||
__new_buckets[__bkt] = &_M_before_begin;
|
||||
if (__p->_M_nxt)
|
||||
__new_buckets[__bbegin_bkt] = __p;
|
||||
__bbegin_bkt = __bkt;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (__prev_p && __prev_bkt == __bkt)
|
||||
{
|
||||
// Previous insert was already in this bucket, we insert after
|
||||
// the previously inserted one to preserve equivalent elements
|
||||
// relative order.
|
||||
__p->_M_nxt = __prev_p->_M_nxt;
|
||||
__prev_p->_M_nxt = __p;
|
||||
|
||||
// Inserting after a node in a bucket require to check that we
|
||||
// haven't change the bucket last node, in this case next
|
||||
// bucket containing its before begin node must be updated. We
|
||||
// schedule a check as soon as we move out of the sequence of
|
||||
// equivalent nodes to limit the number of checks.
|
||||
__check_bucket = true;
|
||||
__check_now = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
__p->_M_nxt = __new_buckets[__bkt]->_M_nxt;
|
||||
__new_buckets[__bkt]->_M_nxt = __p;
|
||||
}
|
||||
}
|
||||
|
||||
if (__check_now && __check_bucket)
|
||||
{
|
||||
// Check if we shall update the next bucket because of insertions
|
||||
// into __prev_bkt bucket.
|
||||
if (__prev_p->_M_nxt)
|
||||
{
|
||||
std::size_t __next_bkt
|
||||
= _HCBase::_M_bucket_index(__prev_p->_M_next(), __n);
|
||||
if (__next_bkt != __prev_bkt)
|
||||
__new_buckets[__next_bkt] = __prev_p;
|
||||
}
|
||||
__check_bucket = false;
|
||||
}
|
||||
__prev_p = __p;
|
||||
__prev_bkt = __bkt;
|
||||
__p = __next;
|
||||
}
|
||||
|
||||
if (__check_bucket && __prev_p->_M_nxt)
|
||||
{
|
||||
std::size_t __next_bkt
|
||||
= _HCBase::_M_bucket_index(__prev_p->_M_next(), __n);
|
||||
if (__next_bkt != __prev_bkt)
|
||||
__new_buckets[__next_bkt] = __prev_p;
|
||||
}
|
||||
|
||||
_M_deallocate_buckets(_M_buckets, _M_bucket_count);
|
||||
_M_bucket_count = __n;
|
||||
_M_buckets = __new_buckets;
|
||||
}
|
||||
|
||||
_GLIBCXX_END_NAMESPACE_VERSION
|
||||
} // namespace std
|
||||
|
||||
|
@ -0,0 +1,59 @@
|
||||
// { dg-options "-std=gnu++0x" }
|
||||
//
|
||||
// Copyright (C) 2012 Free Software Foundation, Inc.
|
||||
//
|
||||
// This file is part of the GNU ISO C++ Library. This library is free
|
||||
// software; you can redistribute it and/or modify it under the
|
||||
// terms of the GNU General Public License as published by the
|
||||
// Free Software Foundation; either version 3, or (at your option)
|
||||
// any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this library; see the file COPYING3. If not see
|
||||
// <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <testsuite_hooks.h>
|
||||
|
||||
void test01()
|
||||
{
|
||||
using namespace std;
|
||||
bool test __attribute__((unused)) = true;
|
||||
|
||||
unordered_multimap<int, int> mmap;
|
||||
vector<int> values;
|
||||
|
||||
size_t nb_bkts = mmap.bucket_count();
|
||||
int i = 0;
|
||||
for (;; ++i)
|
||||
{
|
||||
mmap.insert(make_pair(0, i));
|
||||
if (mmap.bucket_count() != nb_bkts)
|
||||
// Container got rehash
|
||||
break;
|
||||
values.clear();
|
||||
transform(mmap.begin(), mmap.end(), back_inserter(values),
|
||||
[](const pair<int, int>& p) { return p.second; });
|
||||
}
|
||||
|
||||
vector<int> rehash_values;
|
||||
transform(mmap.begin(), mmap.end(), back_inserter(rehash_values),
|
||||
[](const pair<int, int>& p) { return p.second; });
|
||||
// Remove the value that result in a rehash
|
||||
rehash_values.erase(remove(rehash_values.begin(), rehash_values.end(), i));
|
||||
|
||||
VERIFY( rehash_values == values );
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test01();
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
// { dg-options "-std=gnu++0x" }
|
||||
//
|
||||
// Copyright (C) 2012 Free Software Foundation, Inc.
|
||||
//
|
||||
// This file is part of the GNU ISO C++ Library. This library is free
|
||||
// software; you can redistribute it and/or modify it under the
|
||||
// terms of the GNU General Public License as published by the
|
||||
// Free Software Foundation; either version 3, or (at your option)
|
||||
// any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this library; see the file COPYING3. If not see
|
||||
// <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <testsuite_hooks.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
struct pair_hash
|
||||
{
|
||||
std::size_t
|
||||
operator()(const std::pair<int, int>& p) const noexcept
|
||||
{ return std::hash<int>()(p.first); }
|
||||
};
|
||||
|
||||
struct pair_equal_to
|
||||
{
|
||||
bool
|
||||
operator()(const std::pair<int, int>& x,
|
||||
const std::pair<int, int>& y) const noexcept
|
||||
{ return x.first == y.first; }
|
||||
};
|
||||
}
|
||||
|
||||
void test01()
|
||||
{
|
||||
using namespace std;
|
||||
bool test __attribute__((unused)) = true;
|
||||
|
||||
unordered_multiset<pair<int, int>, pair_hash, pair_equal_to> mset;
|
||||
vector<int> values;
|
||||
|
||||
size_t nb_bkts = mset.bucket_count();
|
||||
int i = 0;
|
||||
for (;; ++i)
|
||||
{
|
||||
mset.insert(make_pair(0, i));
|
||||
if (mset.bucket_count() != nb_bkts)
|
||||
// Container got rehash
|
||||
break;
|
||||
values.clear();
|
||||
transform(mset.begin(), mset.end(), back_inserter(values),
|
||||
[](const pair<int, int>& p) { return p.second; });
|
||||
}
|
||||
|
||||
vector<int> rehash_values;
|
||||
transform(mset.begin(), mset.end(), back_inserter(rehash_values),
|
||||
[](const pair<int, int>& p) { return p.second; });
|
||||
// Remove the value that result in a rehash
|
||||
rehash_values.erase(remove(rehash_values.begin(), rehash_values.end(), i));
|
||||
|
||||
VERIFY( rehash_values == values );
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test01();
|
||||
return 0;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user