This one is a refinement for multimap/multiset.
It allows to have share the same key if managed with ref counting like
the cow string.
libstdc++: [_Rb_tree] Limit allocations on equal insertions [PR 96088]
When inserting the same key several times prefer to insert the new
entry using the
current stored key_type instance if this copy is noexcept.
Otherwise create a new
key instance from input argument.
libstdc++-v3/ChangeLog:
PR libstdc++/96088
* include/bits/cow_string.h
(basic_string<>::basic_string(const basic_string&)):
Add noexcept qualification when allocator is always equal.
* include/bits/stl_tree.h
(_Rb_tree<>::_M_get_insert_equal_pos_tr): New.
(_Rb_tree<>::_M_emplace_equal_tr): New, use latter.
(_Rb_tree<>::_M_emplace_equal_aux): New, use latter.
(_Rb_tree<>::_M_emplace_equal<_Arg>(_Arg&&)): New, use latter.
* testsuite/23_containers/multimap/96088.cc (test01): Add
check on redundant
insertion.
(test02): Likewise.
* testsuite/23_containers/multiset/96088.cc (test01,
test02): Likewise.
François
diff --git a/libstdc++-v3/include/bits/cow_string.h b/libstdc++-v3/include/bits/cow_string.h
index ad9929c4ad3..cdfbe5e190b 100644
--- a/libstdc++-v3/include/bits/cow_string.h
+++ b/libstdc++-v3/include/bits/cow_string.h
@@ -541,6 +541,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
* @param __str Source string.
*/
basic_string(const basic_string& __str)
+ _GLIBCXX_NOEXCEPT_IF(_CharT_alloc_traits::_S_always_equal())
: _M_dataplus(__str._M_rep()->_M_grab(_Alloc(__str.get_allocator()),
__str.get_allocator()),
__str.get_allocator())
diff --git a/libstdc++-v3/include/bits/stl_tree.h b/libstdc++-v3/include/bits/stl_tree.h
index 21e9586b0a3..c3a4a97cfcf 100644
--- a/libstdc++-v3/include/bits/stl_tree.h
+++ b/libstdc++-v3/include/bits/stl_tree.h
@@ -968,6 +968,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _Kt>
pair<_Base_ptr, _Base_ptr>
_M_get_insert_unique_pos_tr(const _Kt& __k);
+
+ template<typename _Kt>
+ pair<_Base_ptr, _Base_ptr>
+ _M_get_insert_equal_pos_tr(const _Kt& __k);
#endif
pair<_Base_ptr, _Base_ptr>
@@ -1225,6 +1229,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
std::forward<_Arg>(__arg));
}
+ template<typename _Kt, typename _Arg>
+ iterator
+ _M_emplace_equal_kv(_Kt&&, _Arg&&);
+
+ template<typename _Arg>
+ iterator
+ _M_emplace_equal_aux(_Arg&& __arg)
+ {
+ return _M_emplace_equal_kv(
+ _S_forward_key(_KeyOfValue{}(std::forward<_Arg>(__arg))),
+ std::forward<_Arg>(__arg));
+ }
+
template<typename _Arg>
pair<iterator, bool>
_M_emplace_unique(_Arg&& __arg)
@@ -1237,6 +1254,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
pair<iterator, bool>
_M_emplace_unique(_Args&&... __args);
+ template<typename _Arg>
+ iterator
+ _M_emplace_equal(_Arg&& __arg)
+ {
+ using __to_value = _ConvertToValueType<_KeyOfValue, value_type>;
+ return _M_emplace_equal_aux(__to_value{}(std::forward<_Arg>(__arg)));
+ }
+
template<typename... _Args>
iterator
_M_emplace_equal(_Args&&... __args);
@@ -2355,6 +2380,26 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
return _Res(__x, __y);
return _Res(__j._M_node, 0);
}
+
+ template<typename _Key, typename _Val, typename _KeyOfValue,
+ typename _Compare, typename _Alloc>
+ template<typename _Kt>
+ auto
+ _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
+ _M_get_insert_equal_pos_tr(const _Kt& __k)
+ -> pair<_Base_ptr, _Base_ptr>
+ {
+ typedef pair<_Base_ptr, _Base_ptr> _Res;
+ _Link_type __x = _M_begin();
+ _Base_ptr __y = _M_end();
+ while (__x != 0)
+ {
+ __y = __x;
+ __x = _M_impl._M_key_compare(__k, _S_key(__x)) ?
+ _S_left(__x) : _S_right(__x);
+ }
+ return _Res(__x, __y);
+ }
#endif
template<typename _Key, typename _Val, typename _KeyOfValue,
@@ -2661,6 +2706,26 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
return { iterator(__res.first), false };
}
+ template<typename _Key, typename _Val, typename _KeyOfValue,
+ typename _Compare, typename _Alloc>
+ template<typename _Kt, typename _Arg>
+ auto
+ _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
+ _M_emplace_equal_kv(_Kt&& __k, _Arg&& __arg)
+ -> iterator
+ {
+ auto __res = _M_get_insert_equal_pos_tr(__k);
+ _Auto_node __z =
+ (!is_nothrow_copy_constructible<key_type>::value
+ || __res.second == _M_end()
+ || _M_impl._M_key_compare(__k, _S_key(__res.second)))
+ ? _S_build_node(*this, std::forward<_Kt>(__k),
+ std::forward<_Arg>(__arg), _KeyOfValue{})
+ : _S_build_node(*this, _S_key(__res.second),
+ std::forward<_Arg>(__arg), _KeyOfValue{});
+ return __z._M_insert(__res);
+ }
+
template<typename _Key, typename _Val, typename _KeyOfValue,
typename _Compare, typename _Alloc>
template<typename... _Args>
diff --git a/libstdc++-v3/testsuite/23_containers/multimap/96088.cc b/libstdc++-v3/testsuite/23_containers/multimap/96088.cc
index 919c5e59c71..1a778a0785d 100644
--- a/libstdc++-v3/testsuite/23_containers/multimap/96088.cc
+++ b/libstdc++-v3/testsuite/23_containers/multimap/96088.cc
@@ -1,4 +1,5 @@
// { dg-do run { target c++17 } }
+// { dg-add-options no_pch }
// { dg-require-effective-target std_allocator_new }
// Copyright (C) 2023 Free Software Foundation, Inc.
@@ -20,6 +21,9 @@
// libstdc++/96088
+#undef _GLIBCXX_USE_CXX11_ABI
+#define _GLIBCXX_USE_CXX11_ABI 0
+
#include <string_view>
#include <string>
#include <map>
@@ -42,6 +46,15 @@ test01()
VERIFY( __gnu_test::counter::count() == 2 );
VERIFY( __gnu_test::counter::get()._M_increments == 2 );
+
+ // Allocate a node and the std::string (unless COW).
+ constexpr std::size_t increments = _GLIBCXX_USE_CXX11_ABI ? 2 : 1;
+
+ foo.insert(lst.begin(), lst.end());
+ VERIFY( foo.size() == 2 );
+
+ VERIFY( __gnu_test::counter::count() == 2 + increments );
+ VERIFY( __gnu_test::counter::get()._M_increments == 2 + increments );
}
void
@@ -54,6 +67,15 @@ test02()
VERIFY( __gnu_test::counter::count() == 2 );
VERIFY( __gnu_test::counter::get()._M_increments == 2 );
+
+ // Allocate a node and the std::string (unless COW).
+ constexpr std::size_t increments = _GLIBCXX_USE_CXX11_ABI ? 2 : 1;
+
+ foo.insert(lst.begin(), lst.end());
+ VERIFY( foo.size() == 2 );
+
+ VERIFY( __gnu_test::counter::count() == 2 + increments );
+ VERIFY( __gnu_test::counter::get()._M_increments == 2 + 2 );
}
int