diff --git a/include/boost/unordered/detail/implementation.hpp b/include/boost/unordered/detail/implementation.hpp index 4ee11748..48a0170d 100644 --- a/include/boost/unordered/detail/implementation.hpp +++ b/include/boost/unordered/detail/implementation.hpp @@ -3411,8 +3411,8 @@ namespace boost { template inline void table::rehash(std::size_t num_buckets) { - num_buckets = (std::max)( - min_buckets(size_, mlf_), buckets_.bucket_count_for(num_buckets)); + num_buckets = buckets_.bucket_count_for( + (std::max)(min_buckets(size_, mlf_), num_buckets)); if (num_buckets != this->bucket_count()) { this->rehash_impl(num_buckets); diff --git a/test/unordered/rehash_tests.cpp b/test/unordered/rehash_tests.cpp index d543cdda..0c5227a3 100644 --- a/test/unordered/rehash_tests.cpp +++ b/test/unordered/rehash_tests.cpp @@ -6,16 +6,48 @@ #include "../helpers/unordered.hpp" -#include "../helpers/test.hpp" -#include "../helpers/random_values.hpp" -#include "../helpers/tracker.hpp" #include "../helpers/metafunctions.hpp" +#include "../helpers/random_values.hpp" +#include "../helpers/test.hpp" +#include "../helpers/tracker.hpp" #include "../objects/test.hpp" namespace rehash_tests { test::seed_t initialize_seed(2974); + static int count_allocations; + template struct monotonic_allocator + { + typedef T value_type; + monotonic_allocator() {} + monotonic_allocator(monotonic_allocator const&) {} + + template monotonic_allocator(monotonic_allocator const&) {} + + friend bool operator==( + monotonic_allocator const&, monotonic_allocator const&) + { + return true; + } + + friend bool operator!=( + monotonic_allocator const&, monotonic_allocator const&) + { + return false; + } + + T* allocate(std::size_t n) + { + ++count_allocations; + return static_cast(::operator new(sizeof(T) * n)); + } + + void deallocate(T* p, std::size_t) { ::operator delete(p); } + }; + + void reset_counts() { count_allocations = 0; } + template bool postcondition(X const& x, typename X::size_type n) { return static_cast(x.bucket_count()) >= @@ -321,9 +353,10 @@ namespace rehash_tests { BOOST_TEST_GT(x.bucket_count(), 0u); x.reserve( - 2 * (static_cast( - std::floor(static_cast(x.size()) / x.max_load_factor()) + - std::floor(static_cast(x.size()) * x.max_load_factor())))); + 2 * + (static_cast( + std::floor(static_cast(x.size()) / x.max_load_factor()) + + std::floor(static_cast(x.size()) * x.max_load_factor())))); BOOST_TEST_GT(x.bucket_count(), bucket_count); @@ -376,6 +409,34 @@ namespace rehash_tests { } } + template void rehash_stability(X*, test::random_generator generator) + { + reset_counts(); + + typedef typename X::size_type size_type; + + size_type bucket_count = 100; + X x(bucket_count); + + size_type num_elems = x.bucket_count() - 1; + + test::random_values v(num_elems, generator); + test::ordered tracker; + tracker.insert_range(v.begin(), v.end()); + + typename test::random_values::iterator pos = v.begin(); + for (size_type i = 0; i < num_elems; ++i) { + x.insert(*pos); + ++pos; + } + + int const old_count = count_allocations; + x.rehash(0); + + BOOST_TEST_EQ(count_allocations, old_count); + tracker.compare(x); + } + template void rehash_test1(X*, test::random_generator generator) { test::random_values v(1000, generator); @@ -513,6 +574,13 @@ namespace rehash_tests { test::allocator1 > >* test_map_tracking; + boost::unordered_flat_set >* test_set_monotonic; + boost::unordered_flat_map > >* + test_map_monotonic; + UNORDERED_TEST(rehash_empty_test1, ((int_set_ptr)(test_map_ptr))) UNORDERED_TEST(rehash_empty_test2, ((int_set_ptr)(test_map_ptr))( @@ -534,9 +602,11 @@ namespace rehash_tests { UNORDERED_TEST(rehash_empty_tracking, ((test_set_tracking)(test_map_tracking))( (default_generator)(generate_collisions)(limited_range))) - UNORDERED_TEST(rehash_nonempty_tracking, - ((test_set_tracking)(test_map_tracking))( - (default_generator)(generate_collisions)(limited_range))) + UNORDERED_TEST( + rehash_nonempty_tracking, ((test_set_tracking)(test_map_tracking))( + (default_generator)(limited_range))) + UNORDERED_TEST(rehash_stability, ((test_set_monotonic)(test_map_monotonic))( + (default_generator)(limited_range))) #else boost::unordered_set* int_set_ptr; boost::unordered_multiset > >* test_multimap_tracking; + boost::unordered_set >* test_set_monotonic; + boost::unordered_multiset >* test_multiset_monotonic; + boost::unordered_map > >* + test_map_monotonic; + boost::unordered_multimap > >* + test_multimap_monotonic; + UNORDERED_TEST(rehash_empty_test1, ((int_set_ptr)(test_multiset_ptr)(test_map_ptr)(int_multimap_ptr))) UNORDERED_TEST(rehash_empty_test2, @@ -584,7 +666,10 @@ namespace rehash_tests { UNORDERED_TEST(rehash_nonempty_tracking, ((test_set_tracking)(test_multiset_tracking)(test_map_tracking)(test_multimap_tracking))( (default_generator)(generate_collisions)(limited_range))) + UNORDERED_TEST(rehash_stability, + ((test_set_monotonic)(test_multiset_monotonic)(test_map_monotonic)(test_multimap_monotonic))( + (default_generator)(limited_range))) #endif -} +} // namespace rehash_tests RUN_TESTS()