Insert/emplace with hint.

This commit is contained in:
Daniel James
2016-08-17 12:08:15 +01:00
parent e58370b4ff
commit cae72eec2f
8 changed files with 307 additions and 56 deletions

View File

@ -278,5 +278,6 @@ C++11 support has resulted in some breaking changes:
for `unordered_multiset` and `unordered_multimap`. Might be a little slower.
* Stop using return value SFINAE which some older compilers have issues
with.
* Check hint iterator in `insert`/`emplace_hint`.
[endsect]

View File

@ -321,7 +321,10 @@ namespace boost { namespace unordered { namespace detail {
// Emplace/Insert
static inline void add_after_node(
// Add node 'n' to the group containing 'pos'.
// If 'pos' is the first node in group, add to the end of the group,
// otherwise add before 'pos'.
static inline void add_to_node_group(
node_pointer n,
node_pointer pos)
{
@ -338,7 +341,7 @@ namespace boost { namespace unordered { namespace detail {
{
n->hash_ = key_hash;
if (pos.node_) {
this->add_after_node(n, pos.node_);
this->add_to_node_group(n, pos.node_);
if (n->next_) {
std::size_t next_bucket = this->hash_to_bucket(
static_cast<node_pointer>(n->next_)->hash_);
@ -375,6 +378,24 @@ namespace boost { namespace unordered { namespace detail {
return iterator(n);
}
inline iterator add_using_hint(
node_pointer n,
node_pointer hint)
{
n->hash_ = hint->hash_;
this->add_to_node_group(n, hint);
if (n->next_ != hint && n->next_) {
std::size_t next_bucket = this->hash_to_bucket(
static_cast<node_pointer>(n->next_)->hash_);
if (next_bucket != this->hash_to_bucket(n->hash_)) {
this->get_bucket(next_bucket)->next_ = n;
}
}
++this->size_;
return iterator(n);
}
#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
# if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
iterator emplace(boost::unordered::detail::emplace_args1<
@ -383,6 +404,13 @@ namespace boost { namespace unordered { namespace detail {
BOOST_ASSERT(false);
return iterator();
}
iterator emplace_hint(c_iterator, boost::unordered::detail::emplace_args1<
boost::unordered::detail::please_ignore_this_overload> const&)
{
BOOST_ASSERT(false);
return iterator();
}
# else
iterator emplace(
boost::unordered::detail::please_ignore_this_overload const&)
@ -390,6 +418,13 @@ namespace boost { namespace unordered { namespace detail {
BOOST_ASSERT(false);
return iterator();
}
iterator emplace_hint(c_iterator,
boost::unordered::detail::please_ignore_this_overload const&)
{
BOOST_ASSERT(false);
return iterator();
}
# endif
#endif
@ -401,6 +436,14 @@ namespace boost { namespace unordered { namespace detail {
this->node_alloc(), BOOST_UNORDERED_EMPLACE_FORWARD)));
}
template <BOOST_UNORDERED_EMPLACE_TEMPLATE>
iterator emplace_hint(c_iterator hint, BOOST_UNORDERED_EMPLACE_ARGS)
{
return iterator(emplace_hint_impl(hint,
boost::unordered::detail::func::construct_value_generic(
this->node_alloc(), BOOST_UNORDERED_EMPLACE_FORWARD)));
}
iterator emplace_impl(node_pointer n)
{
node_tmp a(n, this->node_alloc());
@ -411,6 +454,22 @@ namespace boost { namespace unordered { namespace detail {
return this->add_node(a.release(), key_hash, position);
}
iterator emplace_hint_impl(c_iterator hint, node_pointer n)
{
node_tmp a(n, this->node_alloc());
key_type const& k = this->get_key(a.node_->value());
if (hint.node_ && this->key_eq()(k, this->get_key(*hint))) {
this->reserve_for_insert(this->size_ + 1);
return this->add_using_hint(a.release(), hint.node_);
}
else {
std::size_t key_hash = this->hash(k);
iterator position = this->find_node(key_hash, k);
this->reserve_for_insert(this->size_ + 1);
return this->add_node(a.release(), key_hash, position);
}
}
void emplace_impl_no_rehash(node_pointer n)
{
node_tmp a(n, this->node_alloc());

View File

@ -316,6 +316,14 @@ namespace boost { namespace unordered { namespace detail {
BOOST_ASSERT(false);
return emplace_return(this->begin(), false);
}
iterator emplace_hint(c_iterator,
boost::unordered::detail::emplace_args1<
boost::unordered::detail::please_ignore_this_overload> const&)
{
BOOST_ASSERT(false);
return this->begin();
}
# else
emplace_return emplace(
boost::unordered::detail::please_ignore_this_overload const&)
@ -323,6 +331,13 @@ namespace boost { namespace unordered { namespace detail {
BOOST_ASSERT(false);
return emplace_return(this->begin(), false);
}
iterator emplace_hint(c_iterator,
boost::unordered::detail::please_ignore_this_overload const&)
{
BOOST_ASSERT(false);
return this->begin();
}
# endif
#endif
@ -340,6 +355,21 @@ namespace boost { namespace unordered { namespace detail {
#endif
}
template <BOOST_UNORDERED_EMPLACE_TEMPLATE>
iterator emplace_hint(c_iterator hint,
BOOST_UNORDERED_EMPLACE_ARGS)
{
#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
return emplace_hint_impl(hint,
extractor::extract(BOOST_UNORDERED_EMPLACE_FORWARD),
BOOST_UNORDERED_EMPLACE_FORWARD);
#else
return emplace_hint_impl(hint,
extractor::extract(args.a0, args.a1),
BOOST_UNORDERED_EMPLACE_FORWARD);
#endif
}
#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
template <typename A0>
emplace_return emplace(
@ -347,8 +377,27 @@ namespace boost { namespace unordered { namespace detail {
{
return emplace_impl(extractor::extract(args.a0), args);
}
template <typename A0>
iterator emplace_hint(c_iterator hint,
boost::unordered::detail::emplace_args1<A0> const& args)
{
return emplace_hint_impl(hint, extractor::extract(args.a0), args);
}
#endif
template <BOOST_UNORDERED_EMPLACE_TEMPLATE>
iterator emplace_hint_impl(c_iterator hint, key_type const& k,
BOOST_UNORDERED_EMPLACE_ARGS)
{
if (hint.node_ && this->key_eq()(k, this->get_key(*hint))) {
return iterator(hint.node_);
}
else {
return emplace_impl(k, BOOST_UNORDERED_EMPLACE_FORWARD).first;
}
}
template <BOOST_UNORDERED_EMPLACE_TEMPLATE>
emplace_return emplace_impl(key_type const& k,
BOOST_UNORDERED_EMPLACE_ARGS)
@ -368,11 +417,31 @@ namespace boost { namespace unordered { namespace detail {
}
}
template <BOOST_UNORDERED_EMPLACE_TEMPLATE>
iterator emplace_hint_impl(c_iterator hint, no_key,
BOOST_UNORDERED_EMPLACE_ARGS)
{
node_tmp b(
boost::unordered::detail::func::construct_value_generic(
this->node_alloc(), BOOST_UNORDERED_EMPLACE_FORWARD),
this->node_alloc());
key_type const& k = this->get_key(b.node_->value());
if (hint.node_ && this->key_eq()(k, this->get_key(*hint))) {
return iterator(hint.node_);
}
std::size_t key_hash = this->hash(k);
iterator pos = this->find_node(key_hash, k);
if (pos.node_) {
return pos;
}
else {
return this->resize_and_add_node(b.release(), key_hash);
}
}
template <BOOST_UNORDERED_EMPLACE_TEMPLATE>
emplace_return emplace_impl(no_key, BOOST_UNORDERED_EMPLACE_ARGS)
{
// Don't have a key, so construct the node first in order
// to be able to lookup the position.
node_tmp b(
boost::unordered::detail::func::construct_value_generic(
this->node_alloc(), BOOST_UNORDERED_EMPLACE_FORWARD),

View File

@ -240,9 +240,9 @@ namespace unordered
}
template <class... Args>
iterator emplace_hint(const_iterator, BOOST_FWD_REF(Args)... args)
iterator emplace_hint(const_iterator hint, BOOST_FWD_REF(Args)... args)
{
return table_.emplace(boost::forward<Args>(args)...).first;
return table_.emplace_hint(hint, boost::forward<Args>(args)...);
}
#else
@ -281,12 +281,12 @@ namespace unordered
}
template <typename A0>
iterator emplace_hint(const_iterator, BOOST_FWD_REF(A0) a0)
iterator emplace_hint(const_iterator hint, BOOST_FWD_REF(A0) a0)
{
return table_.emplace(
return table_.emplace_hint(hint,
boost::unordered::detail::create_emplace_args(
boost::forward<A0>(a0))
).first;
);
}
template <typename A0, typename A1>
@ -302,15 +302,15 @@ namespace unordered
}
template <typename A0, typename A1>
iterator emplace_hint(const_iterator,
iterator emplace_hint(const_iterator hint,
BOOST_FWD_REF(A0) a0,
BOOST_FWD_REF(A1) a1)
{
return table_.emplace(
return table_.emplace_hint(hint,
boost::unordered::detail::create_emplace_args(
boost::forward<A0>(a0),
boost::forward<A1>(a1))
).first;
);
}
template <typename A0, typename A1, typename A2>
@ -328,17 +328,17 @@ namespace unordered
}
template <typename A0, typename A1, typename A2>
iterator emplace_hint(const_iterator,
iterator emplace_hint(const_iterator hint,
BOOST_FWD_REF(A0) a0,
BOOST_FWD_REF(A1) a1,
BOOST_FWD_REF(A2) a2)
{
return table_.emplace(
return table_.emplace_hint(hint,
boost::unordered::detail::create_emplace_args(
boost::forward<A0>(a0),
boost::forward<A1>(a1),
boost::forward<A2>(a2))
).first;
);
}
#define BOOST_UNORDERED_EMPLACE(z, n, _) \
@ -360,15 +360,15 @@ namespace unordered
BOOST_PP_ENUM_PARAMS_Z(z, n, typename A) \
> \
iterator emplace_hint( \
const_iterator, \
const_iterator hint, \
BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_FWD_PARAM, a) \
) \
{ \
return table_.emplace( \
return table_.emplace_hint(hint, \
boost::unordered::detail::create_emplace_args( \
BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_CALL_FORWARD, \
a) \
)).first; \
)); \
}
BOOST_PP_REPEAT_FROM_TO(4, BOOST_UNORDERED_EMPLACE_LIMIT,
@ -724,9 +724,9 @@ namespace unordered
}
template <class... Args>
iterator emplace_hint(const_iterator, BOOST_FWD_REF(Args)... args)
iterator emplace_hint(const_iterator hint, BOOST_FWD_REF(Args)... args)
{
return table_.emplace(boost::forward<Args>(args)...);
return table_.emplace_hint(hint, boost::forward<Args>(args)...);
}
#else
@ -765,9 +765,9 @@ namespace unordered
}
template <typename A0>
iterator emplace_hint(const_iterator, BOOST_FWD_REF(A0) a0)
iterator emplace_hint(const_iterator hint, BOOST_FWD_REF(A0) a0)
{
return table_.emplace(
return table_.emplace_hint(hint,
boost::unordered::detail::create_emplace_args(
boost::forward<A0>(a0))
);
@ -786,11 +786,11 @@ namespace unordered
}
template <typename A0, typename A1>
iterator emplace_hint(const_iterator,
iterator emplace_hint(const_iterator hint,
BOOST_FWD_REF(A0) a0,
BOOST_FWD_REF(A1) a1)
{
return table_.emplace(
return table_.emplace_hint(hint,
boost::unordered::detail::create_emplace_args(
boost::forward<A0>(a0),
boost::forward<A1>(a1))
@ -812,12 +812,12 @@ namespace unordered
}
template <typename A0, typename A1, typename A2>
iterator emplace_hint(const_iterator,
iterator emplace_hint(const_iterator hint,
BOOST_FWD_REF(A0) a0,
BOOST_FWD_REF(A1) a1,
BOOST_FWD_REF(A2) a2)
{
return table_.emplace(
return table_.emplace_hint(hint,
boost::unordered::detail::create_emplace_args(
boost::forward<A0>(a0),
boost::forward<A1>(a1),
@ -844,11 +844,11 @@ namespace unordered
BOOST_PP_ENUM_PARAMS_Z(z, n, typename A) \
> \
iterator emplace_hint( \
const_iterator, \
const_iterator hint, \
BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_FWD_PARAM, a) \
) \
{ \
return table_.emplace( \
return table_.emplace_hint(hint, \
boost::unordered::detail::create_emplace_args( \
BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_CALL_FORWARD, \
a) \

View File

@ -237,9 +237,9 @@ namespace unordered
}
template <class... Args>
iterator emplace_hint(const_iterator, BOOST_FWD_REF(Args)... args)
iterator emplace_hint(const_iterator hint, BOOST_FWD_REF(Args)... args)
{
return table_.emplace(boost::forward<Args>(args)...).first;
return table_.emplace_hint(hint, boost::forward<Args>(args)...);
}
#else
@ -278,12 +278,12 @@ namespace unordered
}
template <typename A0>
iterator emplace_hint(const_iterator, BOOST_FWD_REF(A0) a0)
iterator emplace_hint(const_iterator hint, BOOST_FWD_REF(A0) a0)
{
return table_.emplace(
return table_.emplace_hint(hint,
boost::unordered::detail::create_emplace_args(
boost::forward<A0>(a0))
).first;
);
}
template <typename A0, typename A1>
@ -299,15 +299,15 @@ namespace unordered
}
template <typename A0, typename A1>
iterator emplace_hint(const_iterator,
iterator emplace_hint(const_iterator hint,
BOOST_FWD_REF(A0) a0,
BOOST_FWD_REF(A1) a1)
{
return table_.emplace(
return table_.emplace_hint(hint,
boost::unordered::detail::create_emplace_args(
boost::forward<A0>(a0),
boost::forward<A1>(a1))
).first;
);
}
template <typename A0, typename A1, typename A2>
@ -325,17 +325,17 @@ namespace unordered
}
template <typename A0, typename A1, typename A2>
iterator emplace_hint(const_iterator,
iterator emplace_hint(const_iterator hint,
BOOST_FWD_REF(A0) a0,
BOOST_FWD_REF(A1) a1,
BOOST_FWD_REF(A2) a2)
{
return table_.emplace(
return table_.emplace_hint(hint,
boost::unordered::detail::create_emplace_args(
boost::forward<A0>(a0),
boost::forward<A1>(a1),
boost::forward<A2>(a2))
).first;
);
}
#define BOOST_UNORDERED_EMPLACE(z, n, _) \
@ -357,15 +357,15 @@ namespace unordered
BOOST_PP_ENUM_PARAMS_Z(z, n, typename A) \
> \
iterator emplace_hint( \
const_iterator, \
const_iterator hint, \
BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_FWD_PARAM, a) \
) \
{ \
return table_.emplace( \
return table_.emplace_hint(hint, \
boost::unordered::detail::create_emplace_args( \
BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_CALL_FORWARD, \
a) \
)).first; \
)); \
}
BOOST_PP_REPEAT_FROM_TO(4, BOOST_UNORDERED_EMPLACE_LIMIT,
@ -707,9 +707,9 @@ namespace unordered
}
template <class... Args>
iterator emplace_hint(const_iterator, BOOST_FWD_REF(Args)... args)
iterator emplace_hint(const_iterator hint, BOOST_FWD_REF(Args)... args)
{
return table_.emplace(boost::forward<Args>(args)...);
return table_.emplace_hint(hint, boost::forward<Args>(args)...);
}
#else
@ -748,9 +748,9 @@ namespace unordered
}
template <typename A0>
iterator emplace_hint(const_iterator, BOOST_FWD_REF(A0) a0)
iterator emplace_hint(const_iterator hint, BOOST_FWD_REF(A0) a0)
{
return table_.emplace(
return table_.emplace_hint(hint,
boost::unordered::detail::create_emplace_args(
boost::forward<A0>(a0))
);
@ -769,11 +769,11 @@ namespace unordered
}
template <typename A0, typename A1>
iterator emplace_hint(const_iterator,
iterator emplace_hint(const_iterator hint,
BOOST_FWD_REF(A0) a0,
BOOST_FWD_REF(A1) a1)
{
return table_.emplace(
return table_.emplace_hint(hint,
boost::unordered::detail::create_emplace_args(
boost::forward<A0>(a0),
boost::forward<A1>(a1))
@ -795,12 +795,12 @@ namespace unordered
}
template <typename A0, typename A1, typename A2>
iterator emplace_hint(const_iterator,
iterator emplace_hint(const_iterator hint,
BOOST_FWD_REF(A0) a0,
BOOST_FWD_REF(A1) a1,
BOOST_FWD_REF(A2) a2)
{
return table_.emplace(
return table_.emplace_hint(hint,
boost::unordered::detail::create_emplace_args(
boost::forward<A0>(a0),
boost::forward<A1>(a1),
@ -827,11 +827,11 @@ namespace unordered
BOOST_PP_ENUM_PARAMS_Z(z, n, typename A) \
> \
iterator emplace_hint( \
const_iterator, \
const_iterator hint, \
BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_FWD_PARAM, a) \
) \
{ \
return table_.emplace( \
return table_.emplace_hint(hint, \
boost::unordered::detail::create_emplace_args( \
BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_CALL_FORWARD, \
a) \

View File

@ -37,6 +37,7 @@ test-suite unordered
[ run unordered/assign_tests.cpp ]
[ run unordered/insert_tests.cpp ]
[ run unordered/insert_stable_tests.cpp ]
[ run unordered/insert_hint_tests.cpp ]
[ run unordered/unnecessary_copy_tests.cpp ]
[ run unordered/erase_tests.cpp ]
[ run unordered/erase_equiv_tests.cpp ]

View File

@ -0,0 +1,118 @@
// Copyright 2016 Daniel James.
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#include "../helpers/prefix.hpp"
#include <boost/unordered_set.hpp>
#include <boost/unordered_map.hpp>
#include "../helpers/postfix.hpp"
#include "../helpers/test.hpp"
#include "../helpers/invariants.hpp"
#include <map>
#include <set>
#include <iostream>
namespace insert_hint
{
UNORDERED_AUTO_TEST(insert_hint_empty) {
typedef boost::unordered_multiset<int> container;
container x;
x.insert(x.cbegin(), 10);
BOOST_TEST_EQ(x.size(), 1);
BOOST_TEST_EQ(x.count(10), 1);
test::check_equivalent_keys(x);
}
UNORDERED_AUTO_TEST(insert_hint_empty2) {
typedef boost::unordered_multimap<std::string, int> container;
container x;
x.emplace_hint(x.cbegin(), "hello", 50);
BOOST_TEST_EQ(x.size(), 1);
BOOST_TEST_EQ(x.count("hello"), 1);
BOOST_TEST_EQ(x.find("hello")->second, 50);
test::check_equivalent_keys(x);
}
UNORDERED_AUTO_TEST(insert_hint_single) {
typedef boost::unordered_multiset<std::string> container;
container x;
x.insert("equal");
x.insert(x.cbegin(), "equal");
BOOST_TEST_EQ(x.size(), 2);
BOOST_TEST_EQ(x.count("equal"), 2);
test::check_equivalent_keys(x);
}
UNORDERED_AUTO_TEST(insert_hint_single2) {
typedef boost::unordered_multimap<int, std::string> container;
container x;
x.emplace(10, "one");
x.emplace_hint(x.cbegin(), 10, "two");
BOOST_TEST_EQ(x.size(), 2);
BOOST_TEST_EQ(x.count(10), 2);
container::iterator it = x.find(10);
std::string v0 = (it++)->second;
std::string v1 = (it++)->second;
BOOST_TEST(v0 == "one" || v0 == "two");
BOOST_TEST(v1 == "one" || v1 == "two");
BOOST_TEST(v0 != v1);
test::check_equivalent_keys(x);
}
UNORDERED_AUTO_TEST(insert_hint_multiple) {
for (unsigned int size = 0; size < 10; ++size) {
for (unsigned int offset = 0; offset <= size; ++offset) {
typedef boost::unordered_multiset<std::string> container;
container x;
for (unsigned int i = 0; i < size; ++i) { x.insert("multiple"); }
BOOST_TEST_EQ(x.size(), size);
container::const_iterator position = x.cbegin();
for (unsigned int i = 0; i < offset; ++i) { ++position; }
x.insert(position, "multiple");
BOOST_TEST_EQ(x.size(), size + 1);
BOOST_TEST_EQ(x.count("multiple"), size + 1);
test::check_equivalent_keys(x);
}
}
}
UNORDERED_AUTO_TEST(insert_hint_unique) {
typedef boost::unordered_set<int> container;
container x;
x.insert(x.cbegin(), 10);
BOOST_TEST_EQ(x.size(), 1);
BOOST_TEST_EQ(x.count(10), 1);
test::check_equivalent_keys(x);
}
UNORDERED_AUTO_TEST(insert_hint_unique_single) {
typedef boost::unordered_set<int> container;
container x;
x.insert(10);
x.insert(x.cbegin(), 10);
BOOST_TEST_EQ(x.size(), 1);
BOOST_TEST(x.count(10) == 1);
test::check_equivalent_keys(x);
x.insert(x.cbegin(), 20);
BOOST_TEST(x.size() == 2);
BOOST_TEST(x.count(10) == 1);
BOOST_TEST(x.count(20) == 1);
test::check_equivalent_keys(x);
}
}
RUN_TESTS()

View File

@ -49,6 +49,8 @@ UNORDERED_AUTO_TEST(stable_insert_test1) {
x.insert(insert_stable::member(1,2));
x.insert(insert_stable::member(1,3));
BOOST_TEST(x.count(insert_stable::member(1,4)) == 3);
boost::unordered_multiset<insert_stable::member>::const_iterator
it = x.begin(), end = x.end();
BOOST_TEST(it != end);
@ -66,10 +68,11 @@ UNORDERED_AUTO_TEST(stable_insert_test2) {
boost::unordered_multimap<insert_stable::member, int>::const_iterator
iterator;
iterator it
= x.insert(x.end(), std::make_pair(insert_stable::member(1,1), 1));
it = x.insert(it, std::make_pair(insert_stable::member(1,2), 2));
it = x.insert(it, std::make_pair(insert_stable::member(1,3), 3));
iterator it = x.emplace(insert_stable::member(1,1), 1);
it = x.emplace(insert_stable::member(1,2), 2);
it = x.emplace(insert_stable::member(1,3), 3);
BOOST_TEST(x.count(insert_stable::member(1,4)) == 3);
it = x.begin();
iterator end = x.end();