re PR libstdc++/83709 (Inserting duplicates into an unordered associative containers causes the container to invalidate iterators)
2018-01-09 François Dumont <fdumont@gcc.gnu.org> PR libstdc++/83709 * include/bits/hashtable_policy.h (__distance_fwd(_Iterator, _Iterator, input_iterator_tag)): Return 1 if __first != __last. (_Insert_base::_M_insert_range(_Ite, _Ite, _NodeGetter, true_type)): New. (_Insert_base::_M_insert_range(_Ite, _Ite, _NodeGetter, false_type)): Add false_type parameter. (_Insert_base::insert): Adapt. * include/bits/hashtable.h (_Hashtable::operator=(initializzr_list<>)): Adapt. (_Hashtable::_M_insert(_Arg&&, const _NodeGen&, true_type, size_t)): Add __n_elt parameter, defaulted to 1. (_Hashtable::_M_insert_unique_node): Likewise. Use it to call rehash policy _M_need_rehash. (_Hashtable::_M_merge_unique): Pass target number of elements to add to produce only 1 rehash if necessary. * testsuite/23_containers/unordered_map/insert/83709.cc: New. * testsuite/23_containers/unordered_set/insert/83709.cc: New. From-SVN: r256396
This commit is contained in:
parent
19d22f7c90
commit
0f1462579e
@ -1,3 +1,24 @@
|
||||
2018-01-09 François Dumont <fdumont@gcc.gnu.org>
|
||||
|
||||
PR libstdc++/83709
|
||||
* include/bits/hashtable_policy.h
|
||||
(__distance_fwd(_Iterator, _Iterator, input_iterator_tag)): Return 1 if
|
||||
__first != __last.
|
||||
(_Insert_base::_M_insert_range(_Ite, _Ite, _NodeGetter, true_type)): New.
|
||||
(_Insert_base::_M_insert_range(_Ite, _Ite, _NodeGetter, false_type)):
|
||||
Add false_type parameter.
|
||||
(_Insert_base::insert): Adapt.
|
||||
* include/bits/hashtable.h (_Hashtable::operator=(initializzr_list<>)):
|
||||
Adapt.
|
||||
(_Hashtable::_M_insert(_Arg&&, const _NodeGen&, true_type, size_t)):
|
||||
Add __n_elt parameter, defaulted to 1.
|
||||
(_Hashtable::_M_insert_unique_node): Likewise. Use it to call rehash
|
||||
policy _M_need_rehash.
|
||||
(_Hashtable::_M_merge_unique): Pass target number of elements to add to
|
||||
produce only 1 rehash if necessary.
|
||||
* testsuite/23_containers/unordered_map/insert/83709.cc: New.
|
||||
* testsuite/23_containers/unordered_set/insert/83709.cc: New.
|
||||
|
||||
2018-01-09 Jonathan Wakely <jwakely@redhat.com>
|
||||
|
||||
PR libstdc++/59253 (partial)
|
||||
|
@ -487,7 +487,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
__reuse_or_alloc_node_type __roan(_M_begin(), *this);
|
||||
_M_before_begin._M_nxt = nullptr;
|
||||
clear();
|
||||
this->_M_insert_range(__l.begin(), __l.end(), __roan);
|
||||
this->_M_insert_range(__l.begin(), __l.end(), __roan, __unique_keys());
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -675,7 +675,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
// deallocate it on exception.
|
||||
iterator
|
||||
_M_insert_unique_node(size_type __bkt, __hash_code __code,
|
||||
__node_type* __n);
|
||||
__node_type* __n, size_type __n_elt = 1);
|
||||
|
||||
// Insert node with hash code __code. Take ownership of the node,
|
||||
// deallocate it on exception.
|
||||
@ -704,12 +704,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
|
||||
template<typename _Arg, typename _NodeGenerator>
|
||||
std::pair<iterator, bool>
|
||||
_M_insert(_Arg&&, const _NodeGenerator&, std::true_type);
|
||||
_M_insert(_Arg&&, const _NodeGenerator&, true_type, size_type = 1);
|
||||
|
||||
template<typename _Arg, typename _NodeGenerator>
|
||||
iterator
|
||||
_M_insert(_Arg&& __arg, const _NodeGenerator& __node_gen,
|
||||
std::false_type __uk)
|
||||
false_type __uk)
|
||||
{
|
||||
return _M_insert(cend(), std::forward<_Arg>(__arg), __node_gen,
|
||||
__uk);
|
||||
@ -719,7 +719,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
template<typename _Arg, typename _NodeGenerator>
|
||||
iterator
|
||||
_M_insert(const_iterator, _Arg&& __arg,
|
||||
const _NodeGenerator& __node_gen, std::true_type __uk)
|
||||
const _NodeGenerator& __node_gen, true_type __uk)
|
||||
{
|
||||
return
|
||||
_M_insert(std::forward<_Arg>(__arg), __node_gen, __uk).first;
|
||||
@ -729,7 +729,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
template<typename _Arg, typename _NodeGenerator>
|
||||
iterator
|
||||
_M_insert(const_iterator, _Arg&&,
|
||||
const _NodeGenerator&, std::false_type);
|
||||
const _NodeGenerator&, false_type);
|
||||
|
||||
size_type
|
||||
_M_erase(std::true_type, const key_type&);
|
||||
@ -881,6 +881,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
node_type>, "Node types are compatible");
|
||||
__glibcxx_assert(get_allocator() == __src.get_allocator());
|
||||
|
||||
auto __n_elt = __src.size();
|
||||
for (auto __i = __src.begin(), __end = __src.end(); __i != __end;)
|
||||
{
|
||||
auto __pos = __i++;
|
||||
@ -890,9 +891,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
if (_M_find_node(__bkt, __k, __code) == nullptr)
|
||||
{
|
||||
auto __nh = __src.extract(__pos);
|
||||
_M_insert_unique_node(__bkt, __code, __nh._M_ptr);
|
||||
_M_insert_unique_node(__bkt, __code, __nh._M_ptr, __n_elt);
|
||||
__nh._M_ptr = nullptr;
|
||||
__n_elt = 1;
|
||||
}
|
||||
else if (__n_elt != 1)
|
||||
--__n_elt;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1713,12 +1717,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
|
||||
_H1, _H2, _Hash, _RehashPolicy, _Traits>::
|
||||
_M_insert_unique_node(size_type __bkt, __hash_code __code,
|
||||
__node_type* __node)
|
||||
__node_type* __node, size_type __n_elt)
|
||||
-> iterator
|
||||
{
|
||||
const __rehash_state& __saved_state = _M_rehash_policy._M_state();
|
||||
std::pair<bool, std::size_t> __do_rehash
|
||||
= _M_rehash_policy._M_need_rehash(_M_bucket_count, _M_element_count, 1);
|
||||
= _M_rehash_policy._M_need_rehash(_M_bucket_count, _M_element_count,
|
||||
__n_elt);
|
||||
|
||||
__try
|
||||
{
|
||||
@ -1816,7 +1821,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
auto
|
||||
_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
|
||||
_H1, _H2, _Hash, _RehashPolicy, _Traits>::
|
||||
_M_insert(_Arg&& __v, const _NodeGenerator& __node_gen, std::true_type)
|
||||
_M_insert(_Arg&& __v, const _NodeGenerator& __node_gen, true_type,
|
||||
size_type __n_elt)
|
||||
-> pair<iterator, bool>
|
||||
{
|
||||
const key_type& __k = this->_M_extract()(__v);
|
||||
@ -1828,7 +1834,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
return std::make_pair(iterator(__n), false);
|
||||
|
||||
__n = __node_gen(std::forward<_Arg>(__v));
|
||||
return std::make_pair(_M_insert_unique_node(__bkt, __code, __n), true);
|
||||
return { _M_insert_unique_node(__bkt, __code, __n, __n_elt), true };
|
||||
}
|
||||
|
||||
// Insert v unconditionally.
|
||||
@ -1841,7 +1847,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
|
||||
_H1, _H2, _Hash, _RehashPolicy, _Traits>::
|
||||
_M_insert(const_iterator __hint, _Arg&& __v,
|
||||
const _NodeGenerator& __node_gen, std::false_type)
|
||||
const _NodeGenerator& __node_gen, false_type)
|
||||
-> iterator
|
||||
{
|
||||
// First compute the hash code so that we don't do anything if it
|
||||
|
@ -58,12 +58,12 @@ namespace __detail
|
||||
struct _Hashtable_base;
|
||||
|
||||
// Helper function: return distance(first, last) for forward
|
||||
// iterators, or 0 for input iterators.
|
||||
// iterators, or 0/1 for input iterators.
|
||||
template<class _Iterator>
|
||||
inline typename std::iterator_traits<_Iterator>::difference_type
|
||||
__distance_fw(_Iterator __first, _Iterator __last,
|
||||
std::input_iterator_tag)
|
||||
{ return 0; }
|
||||
{ return __first != __last ? 1 : 0; }
|
||||
|
||||
template<class _Iterator>
|
||||
inline typename std::iterator_traits<_Iterator>::difference_type
|
||||
@ -74,10 +74,8 @@ namespace __detail
|
||||
template<class _Iterator>
|
||||
inline typename std::iterator_traits<_Iterator>::difference_type
|
||||
__distance_fw(_Iterator __first, _Iterator __last)
|
||||
{
|
||||
typedef typename std::iterator_traits<_Iterator>::iterator_category _Tag;
|
||||
return __distance_fw(__first, __last, _Tag());
|
||||
}
|
||||
{ return __distance_fw(__first, __last,
|
||||
std::__iterator_category(__first)); }
|
||||
|
||||
struct _Identity
|
||||
{
|
||||
@ -820,7 +818,12 @@ namespace __detail
|
||||
template<typename _InputIterator, typename _NodeGetter>
|
||||
void
|
||||
_M_insert_range(_InputIterator __first, _InputIterator __last,
|
||||
const _NodeGetter&);
|
||||
const _NodeGetter&, true_type);
|
||||
|
||||
template<typename _InputIterator, typename _NodeGetter>
|
||||
void
|
||||
_M_insert_range(_InputIterator __first, _InputIterator __last,
|
||||
const _NodeGetter&, false_type);
|
||||
|
||||
public:
|
||||
__ireturn_type
|
||||
@ -849,7 +852,7 @@ namespace __detail
|
||||
{
|
||||
__hashtable& __h = _M_conjure_hashtable();
|
||||
__node_gen_type __node_gen(__h);
|
||||
return _M_insert_range(__first, __last, __node_gen);
|
||||
return _M_insert_range(__first, __last, __node_gen, __unique_keys());
|
||||
}
|
||||
};
|
||||
|
||||
@ -862,13 +865,41 @@ namespace __detail
|
||||
_Insert_base<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash,
|
||||
_RehashPolicy, _Traits>::
|
||||
_M_insert_range(_InputIterator __first, _InputIterator __last,
|
||||
const _NodeGetter& __node_gen)
|
||||
const _NodeGetter& __node_gen, true_type)
|
||||
{
|
||||
size_type __n_elt = __detail::__distance_fw(__first, __last);
|
||||
if (__n_elt == 0)
|
||||
return;
|
||||
|
||||
__hashtable& __h = _M_conjure_hashtable();
|
||||
for (; __first != __last; ++__first)
|
||||
{
|
||||
if (__h._M_insert(*__first, __node_gen, __unique_keys(),
|
||||
__n_elt).second)
|
||||
__n_elt = 1;
|
||||
else if (__n_elt != 1)
|
||||
--__n_elt;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename _Key, typename _Value, typename _Alloc,
|
||||
typename _ExtractKey, typename _Equal,
|
||||
typename _H1, typename _H2, typename _Hash,
|
||||
typename _RehashPolicy, typename _Traits>
|
||||
template<typename _InputIterator, typename _NodeGetter>
|
||||
void
|
||||
_Insert_base<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash,
|
||||
_RehashPolicy, _Traits>::
|
||||
_M_insert_range(_InputIterator __first, _InputIterator __last,
|
||||
const _NodeGetter& __node_gen, false_type)
|
||||
{
|
||||
using __rehash_type = typename __hashtable::__rehash_type;
|
||||
using __rehash_state = typename __hashtable::__rehash_state;
|
||||
using pair_type = std::pair<bool, std::size_t>;
|
||||
|
||||
size_type __n_elt = __detail::__distance_fw(__first, __last);
|
||||
if (__n_elt == 0)
|
||||
return;
|
||||
|
||||
__hashtable& __h = _M_conjure_hashtable();
|
||||
__rehash_type& __rehash = __h._M_rehash_policy;
|
||||
|
@ -0,0 +1,43 @@
|
||||
// { dg-do run { target c++11 } }
|
||||
//
|
||||
// Copyright (C) 2018 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 <string>
|
||||
#include <testsuite_hooks.h>
|
||||
|
||||
void test01()
|
||||
{
|
||||
std::unordered_map<std::string, std::string> m
|
||||
{ {"E", "E" }, { "T", "T" } };
|
||||
|
||||
VERIFY( m.size() == 2 );
|
||||
|
||||
auto bcount = m.bucket_count();
|
||||
|
||||
m.insert({ {"E", "E" }, { "T", "T" } });
|
||||
|
||||
VERIFY( m.size() == 2 );
|
||||
VERIFY( m.bucket_count() == bcount );
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test01();
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
// { dg-do run { target c++11 } }
|
||||
//
|
||||
// Copyright (C) 2018 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 <string>
|
||||
#include <testsuite_hooks.h>
|
||||
|
||||
void test01()
|
||||
{
|
||||
std::unordered_set<std::string> s { "E", "T" };
|
||||
|
||||
VERIFY( s.size() == 2 );
|
||||
|
||||
auto bcount = s.bucket_count();
|
||||
|
||||
s.insert({"E", "T" });
|
||||
|
||||
VERIFY( s.size() == 2 );
|
||||
VERIFY( s.bucket_count() == bcount );
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test01();
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user