Hi

    Bug confirmed, limited to range insertion on unordered_set and unordered_map.

    I had to specialize _M_insert_range for those containers. Now this method maintains the theoretical number of elements to insert which is used only if an insertion takes place.

    I also took this oportunity to introduce a small change in __distance_fw to report 0 only if __first == __last and do nothing in this case.

    * 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_unique_node): Add __n_elt parameter, defaulted
    to 1.
    (_Hashtable::_M_insert(_Arg&&, const _NodeGen&, true_type, size_t)):
    Likewise.
    (_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.

Tested under Linux x86_64 normal and debug modes.

Ok to commit ?

François


diff --git a/libstdc++-v3/include/bits/hashtable.h b/libstdc++-v3/include/bits/hashtable.h
index e6108a6..1245d79 100644
--- a/libstdc++-v3/include/bits/hashtable.h
+++ b/libstdc++-v3/include/bits/hashtable.h
@@ -490,7 +490,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;
       }
 
@@ -678,7 +678,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.
@@ -707,12 +707,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);
@@ -722,7 +722,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;
@@ -732,7 +732,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&);
@@ -884,6 +884,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++;
@@ -893,9 +894,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;
 	    }
 	}
 
@@ -1721,12 +1725,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
 	{
@@ -1824,7 +1829,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);
@@ -1836,7 +1842,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.
@@ -1849,7 +1855,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
diff --git a/libstdc++-v3/include/bits/hashtable_policy.h b/libstdc++-v3/include/bits/hashtable_policy.h
index ab59b61..df6a0eb 100644
--- a/libstdc++-v3/include/bits/hashtable_policy.h
+++ b/libstdc++-v3/include/bits/hashtable_policy.h
@@ -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;
diff --git a/libstdc++-v3/testsuite/23_containers/unordered_map/insert/83709.cc b/libstdc++-v3/testsuite/23_containers/unordered_map/insert/83709.cc
new file mode 100644
index 0000000..16e4f03
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/unordered_map/insert/83709.cc
@@ -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;
+}
diff --git a/libstdc++-v3/testsuite/23_containers/unordered_set/insert/83709.cc b/libstdc++-v3/testsuite/23_containers/unordered_set/insert/83709.cc
new file mode 100644
index 0000000..0a5a520
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/unordered_set/insert/83709.cc
@@ -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;
+}

Reply via email to