diff --git a/include/boost/unordered/detail/fca.hpp b/include/boost/unordered/detail/fca.hpp index 19fafe73..a1d14d95 100644 --- a/include/boost/unordered/detail/fca.hpp +++ b/include/boost/unordered/detail/fca.hpp @@ -646,7 +646,7 @@ namespace boost { size_type bucket_count() const { return size_; } - iterator begin() const { return ++at(size_); } + iterator begin() const { return size_ == 0 ? end() : ++at(size_); } iterator end() const { @@ -660,6 +660,10 @@ namespace boost { local_iterator begin(size_type n) const { + if (size_ == 0) { + return this->end(n); + } + return local_iterator( (buckets + static_cast(n))->next); } @@ -670,12 +674,16 @@ namespace boost { iterator at(size_type n) const { - std::size_t const N = group::N; + if (size_ > 0) { + std::size_t const N = group::N; - iterator pbg(buckets + static_cast(n), - groups + static_cast(n / N)); + iterator pbg(buckets + static_cast(n), + groups + static_cast(n / N)); - return pbg; + return pbg; + } else { + return this->end(); + } } span raw() diff --git a/include/boost/unordered/detail/implementation.hpp b/include/boost/unordered/detail/implementation.hpp index 2cc27c5d..37323675 100644 --- a/include/boost/unordered/detail/implementation.hpp +++ b/include/boost/unordered/detail/implementation.hpp @@ -2054,12 +2054,14 @@ namespace boost { std::size_t bucket_size(std::size_t index) const { - bucket_iterator itb = buckets_.at(index); - node_pointer n = itb->next; std::size_t count = 0; - while (n) { - ++count; - n = n->next; + if (size_ > 0) { + bucket_iterator itb = buckets_.at(index); + node_pointer n = itb->next; + while (n) { + ++count; + n = n->next; + } } return count; } @@ -2420,11 +2422,14 @@ namespace boost { node_pointer find_node_impl( Key const& x, bucket_iterator itb) const { - key_equal const& pred = this->key_eq(); - node_pointer p = itb->next; - for (; p; p = p->next) { - if (pred(x, extractor::extract(p->value()))) { - break; + node_pointer p = node_pointer(); + if (itb != buckets_.end()) { + key_equal const& pred = this->key_eq(); + p = itb->next; + for (; p; p = p->next) { + if (pred(x, extractor::extract(p->value()))) { + break; + } } } return p; @@ -2453,11 +2458,13 @@ namespace boost { inline iterator transparent_find( Key const& k, Hash const& h, Pred const& pred) const { - std::size_t const key_hash = h(k); - bucket_iterator itb = buckets_.at(buckets_.position(key_hash)); - for (node_pointer p = itb->next; p; p = p->next) { - if (BOOST_LIKELY(pred(k, extractor::extract(p->value())))) { - return iterator(p, itb); + if (size_ > 0) { + std::size_t const key_hash = h(k); + bucket_iterator itb = buckets_.at(buckets_.position(key_hash)); + for (node_pointer p = itb->next; p; p = p->next) { + if (BOOST_LIKELY(pred(k, extractor::extract(p->value())))) { + return iterator(p, itb); + } } } @@ -2467,11 +2474,13 @@ namespace boost { template node_pointer* find_prev(Key const& key, bucket_iterator itb) { - key_equal pred = this->key_eq(); - for (node_pointer* pp = boost::addressof(itb->next); *pp; - pp = boost::addressof((*pp)->next)) { - if (pred(key, extractor::extract((*pp)->value()))) { - return pp; + if (size_ > 0) { + key_equal pred = this->key_eq(); + for (node_pointer* pp = boost::addressof(itb->next); *pp; + pp = boost::addressof((*pp)->next)) { + if (pred(key, extractor::extract((*pp)->value()))) { + return pp; + } } } typedef node_pointer* node_pointer_pointer; diff --git a/include/boost/unordered/unordered_map.hpp b/include/boost/unordered/unordered_map.hpp index 97908fb6..3e25a28a 100644 --- a/include/boost/unordered/unordered_map.hpp +++ b/include/boost/unordered/unordered_map.hpp @@ -2069,6 +2069,10 @@ namespace boost { template float unordered_map::load_factor() const BOOST_NOEXCEPT { + if (table_.size_ == 0) { + return 0.0f; + } + BOOST_ASSERT(table_.bucket_count() != 0); return static_cast(table_.size_) / static_cast(table_.bucket_count()); @@ -2506,6 +2510,10 @@ namespace boost { template float unordered_multimap::load_factor() const BOOST_NOEXCEPT { + if (table_.size_ == 0) { + return 0.0f; + } + BOOST_ASSERT(table_.bucket_count() != 0); return static_cast(table_.size_) / static_cast(table_.bucket_count()); diff --git a/include/boost/unordered/unordered_set.hpp b/include/boost/unordered/unordered_set.hpp index 8721a68a..82d323c6 100644 --- a/include/boost/unordered/unordered_set.hpp +++ b/include/boost/unordered/unordered_set.hpp @@ -1586,6 +1586,10 @@ namespace boost { template float unordered_set::load_factor() const BOOST_NOEXCEPT { + if (table_.size_ == 0) { + return 0.0f; + } + BOOST_ASSERT(table_.bucket_count() != 0); return static_cast(table_.size_) / static_cast(table_.bucket_count()); @@ -1986,6 +1990,10 @@ namespace boost { template float unordered_multiset::load_factor() const BOOST_NOEXCEPT { + if (table_.size_ == 0) { + return 0.0f; + } + BOOST_ASSERT(table_.bucket_count() != 0); return static_cast(table_.size_) / static_cast(table_.bucket_count()); diff --git a/test/unordered/move_tests.cpp b/test/unordered/move_tests.cpp index 82ba6011..f2afbf68 100644 --- a/test/unordered/move_tests.cpp +++ b/test/unordered/move_tests.cpp @@ -18,6 +18,9 @@ #include "../helpers/equivalent.hpp" #include "../helpers/invariants.hpp" +#include +#include + #if defined(BOOST_MSVC) #pragma warning(disable : 4127) // conditional expression is constant #endif @@ -298,17 +301,515 @@ namespace move_tests { } } + template T const& get_key(T const& t) { return t; } + + template K const& get_key(std::pair const& kv) + { + return kv.first; + } + + template T const& get_value(T const& t) { return t; } + + template K const& get_value(std::pair const& kv) + { + return kv.second; + } + + template + static void insert_range(T& y, test::random_values const& v) + { + y.insert(v.begin(), v.end()); + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + } + + template + static void insert_single(T& y, test::random_values const& v) + { + y.insert(*v.begin()); + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + } + + template + static void insert_single_hint(T& y, test::random_values const& v) + { + y.insert(y.end(), *v.begin()); + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + } + + template struct insert_or_assign_invoker + { + void operator()(T&, test::random_values const&) {} + }; + + template + struct insert_or_assign_invoker< + boost::unordered_map > + { + void operator()(boost::unordered_map& y, + test::random_values< + boost::unordered_map > const& v) + { + typedef typename boost::unordered_map::size_type size_type; + + y.insert_or_assign(get_key(*v.begin()), get_value(*v.begin())); + BOOST_TEST_EQ( + y.size(), static_cast(std::distance(y.begin(), y.end()))); + } + }; + + template + static void insert_or_assign(T& y, test::random_values const& v) + { + insert_or_assign_invoker()(y, v); + } + + template struct insert_or_assign_hint_invoker + { + void operator()(T&, test::random_values const&) {} + }; + + template + struct insert_or_assign_hint_invoker< + boost::unordered_map > + { + void operator()(boost::unordered_map& y, + test::random_values< + boost::unordered_map > const& v) + { + typedef typename boost::unordered_map::size_type size_type; + y.insert_or_assign(y.end(), get_key(*v.begin()), get_value(*v.begin())); + BOOST_TEST_EQ( + y.size(), static_cast(std::distance(y.begin(), y.end()))); + } + }; + + template + static void insert_or_assign_hint(T& y, test::random_values const& v) + { + insert_or_assign_hint_invoker()(y, v); + } + + template struct try_emplace_invoker + { + void operator()(T&, test::random_values const&) {} + }; + + template + struct try_emplace_invoker< + boost::unordered_map > + { + void operator()(boost::unordered_map& y, + test::random_values< + boost::unordered_map > const& v) + { + typedef typename boost::unordered_map::size_type size_type; + y.try_emplace(get_key(*v.begin()), get_value(*v.begin())); + BOOST_TEST_EQ( + y.size(), static_cast(std::distance(y.begin(), y.end()))); + } + }; + + template + static void try_emplace(T& y, test::random_values const& v) + { + try_emplace_invoker()(y, v); + } + + template struct try_emplace_hint_invoker + { + void operator()(T&, test::random_values const&) {} + }; + + template + struct try_emplace_hint_invoker< + boost::unordered_map > + { + void operator()(boost::unordered_map& y, + test::random_values< + boost::unordered_map > const& v) + { + typedef typename boost::unordered_map::size_type size_type; + y.try_emplace(y.end(), get_key(*v.begin()), get_value(*v.begin())); + BOOST_TEST_EQ( + y.size(), static_cast(std::distance(y.begin(), y.end()))); + } + }; + + template + static void try_emplace_hint(T& y, test::random_values const& v) + { + try_emplace_hint_invoker()(y, v); + } + + template struct at_invoker + { + void operator()(T&, test::random_values const&) {} + }; + + template + struct at_invoker > + { + void operator()(boost::unordered_map& y, + test::random_values< + boost::unordered_map > const& v) + { + BOOST_TRY { y.at(get_key(*v.begin())); } + BOOST_CATCH(...) {} + BOOST_CATCH_END + } + }; + + template static void at(T& y, test::random_values const& v) + { + at_invoker()(y, v); + } + + template struct index_operator_invoker + { + void operator()(T&, test::random_values const&) {} + }; + + template + struct index_operator_invoker< + boost::unordered_map > + { + void operator()(boost::unordered_map& y, + test::random_values< + boost::unordered_map > const& v) + { + typedef typename boost::unordered_map::size_type size_type; + y[get_key(*v.begin())] = get_value(*v.begin()); + BOOST_TEST_EQ( + y.size(), static_cast(std::distance(y.begin(), y.end()))); + } + }; + + template + static void index_operator(T& y, test::random_values const& v) + { + index_operator_invoker()(y, v); + } + + template static void clear(T& y, test::random_values const&) + { + y.clear(); + BOOST_TEST(y.empty()); + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + } + + template static void capacity(T& y, test::random_values const&) + { + (void)y.empty(); + (void)y.size(); + (void)y.max_size(); + (void)y.load_factor(); + (void)y.max_load_factor(); + (void)y.hash_function(); + (void)y.key_eq(); + (void)y.get_allocator(); + } + + template static void iterators(T& y, test::random_values const&) + { + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + } + + template + static void erase_range(T& y, test::random_values const&) + { + y.erase(y.begin(), y.end()); + BOOST_TEST(y.empty()); + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + } + + template + static void erase_key(T& y, test::random_values const& v) + { + y.erase(get_key(*v.begin())); + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + } + + template static void lookup(T& y, test::random_values const& v) + { + (void)y.count(get_key(*v.begin())); + (void)y.find(get_key(*v.begin())); + (void)y.contains(get_key(*v.begin())); + (void)y.equal_range(get_key(*v.begin())); + } + + template static void reserve(T& y, test::random_values const&) + { + y.reserve(1337); + BOOST_TEST_GT(y.bucket_count(), 1337u); + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + } + + template + static void copy_assignment(T& y, test::random_values const& v) + { + T x(v.begin(), v.end()); + y = x; + BOOST_TEST_EQ(y.size(), x.size()); + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + } + + template + static void move_assignment(T& y, test::random_values const& v) + { + T x(v.begin(), v.end()); + std::size_t const size = x.size(); + y = boost::move(x); + BOOST_TEST_GE(y.size(), size); + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + } + + template static void equal(T& y, test::random_values const& v) + { + T x(v.begin(), v.end()); + (void)(y == x); + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + } + + template static void extract(T& y, test::random_values const& v) + { + (void)y.extract(get_key(*v.begin())); + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + } + + template static void merge(T& y, test::random_values const& v) + { + T x(v.begin(), v.end()); + if (y.get_allocator() == x.get_allocator()) { + y.merge(x); + } + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + } + + template bool pred(X const&) { return true; } + + template + static void erase_with_pred(T& y, test::random_values const&) + { + erase_if(y, pred); + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + } + + template + static void container_swap(T& y, test::random_values const& v) + { + T x(v.begin(), v.end()); + if (boost::allocator_propagate_on_container_swap< + typename T::allocator_type>::type::value || + x.get_allocator() == y.get_allocator()) { + y.swap(x); + } + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + } + + template static void buckets(T& y, test::random_values const& v) + { + (void)y.begin(0); + (void)y.end(0); + (void)y.bucket_count(); + (void)y.max_bucket_count(); + (void)y.bucket_size(0); + (void)y.bucket(get_key(*v.begin())); + } + + template + static void double_move_construct(T& y, test::random_values const&) + { + T x = boost::move(y); + x.clear(); + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + BOOST_TEST_EQ(x.size(), + static_cast(std::distance(x.begin(), x.end()))); + } + + template + static void double_move_assign(T& y, test::random_values const&) + { + T x; + x = boost::move(y); + x.clear(); + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + BOOST_TEST_EQ(x.size(), + static_cast(std::distance(x.begin(), x.end()))); + } + + template + static void post_move_tests(T* ptr, test::random_generator const& generator) + { + // clang-format off + void (*fps[])(T&, test::random_values const&) = { + insert_range, + insert_single, + insert_single_hint, + insert_or_assign, + insert_or_assign_hint, + try_emplace, + try_emplace_hint, + at, + index_operator, + clear, + capacity, + iterators, + erase_range, + erase_key, + lookup, + reserve, + copy_assignment, + move_assignment, + equal, + extract, + merge, + erase_with_pred, + container_swap, + buckets, + double_move_construct, + double_move_assign + }; + // clang-format on + + std::size_t const len = (sizeof(fps) / sizeof(*(fps))); + + for (std::size_t i = 0; i < len; ++i) { + test::check_instances check_; + + test::random_values const v(1000, generator); + test::object_count count; + T y(create(v, count)); + T x(boost::move(y)); + +#if defined(BOOST_UNORDERED_USE_MOVE) || \ + !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + BOOST_TEST(y.empty()); + BOOST_TEST(y.begin() == y.end()); +#endif + + fps[i](y, v); + + test::check_container(x, v); + test::check_equivalent_keys(x); + } + + for (std::size_t i = 0; i < len; ++i) { + typename T::hasher hf(1); + typename T::key_equal eq(1); + typename T::allocator_type al1(1); + typename T::allocator_type al2(2); + + test::check_instances check_; + + test::random_values v(1000, generator); + test::object_count count; + T y(v.begin(), v.end(), 0, hf, eq, al1); + T x(boost::move(y), al2); + + BOOST_TEST_NOT(y.empty()); + BOOST_TEST(y.begin() != y.end()); + + fps[i](y, v); + + test::check_container(x, v); + test::check_equivalent_keys(x); + } + + for (std::size_t i = 0; i < len; ++i) { + test::check_instances check_; + + test::random_values v(1000, generator); + test::object_count count; + T y(create(v, count)); + T x(empty(ptr)); + x = boost::move(y); + +#if defined(BOOST_UNORDERED_USE_MOVE) || \ + !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + BOOST_TEST(y.empty()); + BOOST_TEST(y.begin() == y.end()); +#endif + + fps[i](y, v); + + test::check_container(x, v); + test::check_equivalent_keys(x); + } + + for (std::size_t i = 0; i < len; ++i) { + typename T::hasher hf(1); + typename T::key_equal eq(1); + typename T::allocator_type al1(1); + typename T::allocator_type al2(2); + + test::check_instances check_; + + test::random_values v(1000, generator); + test::object_count count; + T y(v.begin(), v.end(), 0, hf, eq, al1); + T x(al2); + x = boost::move(y); + + bool b = boost::allocator_propagate_on_container_move_assignment< + typename T::allocator_type>::type::value; + if (b) { + #if defined(BOOST_UNORDERED_USE_MOVE) || \ + !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + BOOST_TEST(y.empty()); + BOOST_TEST(y.begin() == y.end()); + #else + BOOST_TEST_NOT(y.empty()); + BOOST_TEST(y.begin() != y.end()); + +#endif + } else { + BOOST_TEST_NOT(y.empty()); + BOOST_TEST(y.begin() != y.end()); + } + + fps[i](y, v); + + test::check_container(x, v); + test::check_equivalent_keys(x); + } + } + boost::unordered_map >* test_map_std_alloc; + std::allocator > >* + test_map_std_alloc; boost::unordered_set >* test_set; boost::unordered_multiset >* test_multiset; boost::unordered_map >* test_map; + test::allocator1 > >* test_map; boost::unordered_multimap >* test_multimap; + test::equal_to, + test::allocator2 > >* + test_multimap; boost::unordered_set >* @@ -317,11 +818,12 @@ namespace move_tests { test::cxx11_allocator >* test_multiset_prop_move; boost::unordered_map >* - test_map_prop_move; + test::cxx11_allocator, + test::propagate_move> >* test_map_prop_move; boost::unordered_multimap >* - test_multimap_prop_move; + test::equal_to, + test::cxx11_allocator, + test::propagate_move> >* test_multimap_prop_move; boost::unordered_set >* @@ -330,12 +832,12 @@ namespace move_tests { test::cxx11_allocator >* test_multiset_no_prop_move; boost::unordered_map >* - test_map_no_prop_move; + test::cxx11_allocator, + test::no_propagate_move> >* test_map_no_prop_move; boost::unordered_multimap >* - test_multimap_no_prop_move; + test::cxx11_allocator, + test::no_propagate_move> >* test_multimap_no_prop_move; using test::default_generator; using test::generate_collisions; @@ -367,6 +869,12 @@ namespace move_tests { test_set_no_prop_move)(test_multiset_no_prop_move)(test_map_no_prop_move)( test_multimap_no_prop_move))( (default_generator)(generate_collisions)(limited_range))) + UNORDERED_TEST(post_move_tests, + ((test_set)(test_multiset)(test_map)(test_multimap)(test_set_prop_move)( + test_multiset_prop_move)(test_map_prop_move)(test_multimap_prop_move)( + test_set_no_prop_move)(test_multiset_no_prop_move)(test_map_no_prop_move)( + test_multimap_no_prop_move))( + (default_generator)(generate_collisions)(limited_range))) } RUN_TESTS()