Compare commits

...

24 Commits

Author SHA1 Message Date
668abe4b0a Merge pull request #146 from cmazakas/feature/rehash-doc-updates
Rehash Doc Updates
2022-09-15 19:33:54 +02:00
5dcccfda3b Update docs for rehash/reserve for unordered_multimap 2022-09-15 07:59:53 -07:00
56b271850a Update docs for rehash/reserve for unordered_multiset 2022-09-15 07:59:53 -07:00
d338e94267 Update docs for rehash/reserve for unordered_set 2022-09-15 07:59:53 -07:00
42abfe3c7d Update docs for rehash/reserve for unordered_map 2022-09-15 07:59:53 -07:00
6ef6540378 Merge pull request #144 from cmazakas/feature/rehashing-conformity
Rehashing Conformity
2022-09-13 23:53:15 +02:00
9a9b8e0a7b Update rehashing implementation to better reflect STL conformance 2022-09-13 12:33:21 -07:00
5ad86f559f Clean up implementation of recalculate_max_load() to avoid std::floor/std::ceil 2022-09-13 12:33:21 -07:00
a8b0e19a33 Update rehashing tests to better stress test them for conformity with libc++ and other STL implementations 2022-09-13 12:33:21 -07:00
2b61fbb8df Merge pull request #145 from cmazakas/bugfix/noexcept-move-assign
Fix move assign noexcept violation/perf regression
2022-09-13 20:46:38 +02:00
dea6ce164c Remove erroneous unconditional call to reserve() from move_assign() when pocma is false 2022-09-12 13:16:45 -07:00
9ebb705e75 Update noexcept_tests to cover both values of POCMA and to also test move-assigning into larger and smaller hash tables 2022-09-12 13:16:45 -07:00
010dfa52e3 Merge pull request #143 from cmazakas/feature/no-alloc-default-construction
Update internal table to no longer allocate on default constructions and when the bucket count is 0
2022-08-31 09:21:13 -07:00
db9d9d1f77 Update implementation to support a default-constructible table and grouped_bucket_array 2022-08-30 15:20:31 -07:00
6f342bf119 Set default_bucket_count to zero 2022-08-30 15:20:31 -07:00
48765e82e0 Update tests to account for a default-constructed container having no buckets 2022-08-30 15:20:31 -07:00
f141cd1dea Merge pull request #141 from cmazakas/fix/appveyor-timeout
Split 64-bit mingw appveyor job into two to help prevent timeouts
2022-08-20 11:21:40 +03:00
6258856d2b Split 64-bit mingw appveyor job into two to help prevent timeouts 2022-08-19 13:31:02 -07:00
c93ea188f7 Merge pull request #142 from sdarwin/droneconfig
drone.jsonnet update
2022-08-19 23:15:26 +03:00
329eb419f5 drone.jsonnet update 2022-08-19 10:58:56 -06:00
e83c42ca26 Merge pull request #140 from boostorg/bugfix/valid-post-move
Fix use-after-move segfault
2022-08-19 00:54:07 +03:00
f9eae4153f Update code to be valid when the internal buckets_ data member is moved-from 2022-08-18 09:04:53 -07:00
7b41f4783f Update value type used by test allocators in move_tests so that the STL containers can be swapped in 2022-08-17 14:12:29 -07:00
7227cfc68a Add post-move tests to verify the container is valid after a move 2022-08-17 14:12:29 -07:00
19 changed files with 1377 additions and 99 deletions

View File

@ -85,7 +85,14 @@ environment:
- FLAVOR: mingw-w64, 64 bit
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
ADDPATH: C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin;
B2_CXXSTD: 03,11,14,17,2a
B2_CXXSTD: 03,11,14
B2_TOOLSET: gcc
B2_ADDRESS_MODEL: 64
- FLAVOR: mingw-w64, 64 bit
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
ADDPATH: C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin;
B2_CXXSTD: 17,2a
B2_TOOLSET: gcc
B2_ADDRESS_MODEL: 64

View File

@ -32,6 +32,7 @@ local linux_pipeline(name, image, environment, packages = "", sources = [], arch
commands:
[
'set -e',
'uname -a',
'wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -',
] +
(if sources != [] then [ ('apt-add-repository "' + source + '"') for source in sources ] else []) +
@ -63,6 +64,7 @@ local macos_pipeline(name, environment, xcode_version = "12.2", osx_version = "c
environment: environment + { "DEVELOPER_DIR": "/Applications/Xcode-" + xcode_version + ".app/Contents/Developer" },
commands:
[
'uname -a',
'export LIBRARY=' + library,
'./.drone/drone.sh',
]
@ -89,6 +91,7 @@ local windows_pipeline(name, image, environment, arch = "amd64") =
environment: environment,
commands:
[
'echo $env:DRONE_STAGE_MACHINE',
'cmd /C .drone\\\\drone.bat ' + library,
]
}
@ -168,8 +171,9 @@ local windows_pipeline(name, image, environment, arch = "amd64") =
),
macos_pipeline(
"MacOS 10.15 Xcode 12.2 ASAN",
"MacOS 12.4 Xcode 13.4.1 ASAN",
{ TOOLSET: 'clang', COMPILER: 'clang++', CXXSTD: '03,11,14,1z' } + asan,
xcode_version = "13.4.1", osx_version = "monterey",
),
windows_pipeline(

View File

@ -5,6 +5,7 @@
# https://www.boost.org/LICENSE_1_0.txt
set -ex
export PATH=~/.local/bin:/usr/local/bin:$PATH
DRONE_BUILD_DIR=$(pwd)

View File

@ -1566,7 +1566,9 @@ Effects:;; Changes the container's maximum load factor, using `z` as a hint.
void rehash(size_type n);
```
Changes the number of buckets so that there at least `n` buckets, and so that the load factor is less than the maximum load factor.
Changes the number of buckets so that there are at least `n` buckets, and so that the load factor is less than or equal to the maximum load factor. When applicable, this will either grow or shrink the `bucket_count()` associated with the container.
When `size() == 0`, `rehash(0)` will deallocate the underlying buckets array.
Invalidates iterators, and changes the order of elements. Pointers and references to elements are not invalidated.
@ -1580,6 +1582,10 @@ Throws:;; The function has no effect if an exception is thrown, unless it is thr
void reserve(size_type n);
```
Equivalent to `a.rehash(ceil(n / a.max_load_factor()))`, or `a.rehash(1)` if `n > 0` and `a.max_load_factor() == std::numeric_limits<float>::infinity()`.
Similar to `rehash`, this function can be used to grow or shrink the number of buckets in the container.
Invalidates iterators, and changes the order of elements. Pointers and references to elements are not invalidated.
[horizontal]

View File

@ -1345,7 +1345,9 @@ Effects:;; Changes the container's maximum load factor, using `z` as a hint.
void rehash(size_type n);
```
Changes the number of buckets so that there at least `n` buckets, and so that the load factor is less than the maximum load factor.
Changes the number of buckets so that there are at least `n` buckets, and so that the load factor is less than or equal to the maximum load factor. When applicable, this will either grow or shrink the `bucket_count()` associated with the container.
When `size() == 0`, `rehash(0)` will deallocate the underlying buckets array.
Invalidates iterators, and changes the order of elements. Pointers and references to elements are not invalidated.
@ -1359,6 +1361,10 @@ Throws:;; The function has no effect if an exception is thrown, unless it is thr
void reserve(size_type n);
```
Equivalent to `a.rehash(ceil(n / a.max_load_factor()))`, or `a.rehash(1)` if `n > 0` and `a.max_load_factor() == std::numeric_limits<float>::infinity()`.
Similar to `rehash`, this function can be used to grow or shrink the number of buckets in the container.
Invalidates iterators, and changes the order of elements. Pointers and references to elements are not invalidated.
[horizontal]

View File

@ -1306,7 +1306,9 @@ Effects:;; Changes the container's maximum load factor, using `z` as a hint.
void rehash(size_type n);
```
Changes the number of buckets so that there at least `n` buckets, and so that the load factor is less than the maximum load factor.
Changes the number of buckets so that there are at least `n` buckets, and so that the load factor is less than or equal to the maximum load factor. When applicable, this will either grow or shrink the `bucket_count()` associated with the container.
When `size() == 0`, `rehash(0)` will deallocate the underlying buckets array.
Invalidates iterators, and changes the order of elements. Pointers and references to elements are not invalidated.
@ -1320,11 +1322,16 @@ Throws:;; The function has no effect if an exception is thrown, unless it is thr
void reserve(size_type n);
```
Equivalent to `a.rehash(ceil(n / a.max_load_factor()))`, or `a.rehash(1)` if `n > 0` and `a.max_load_factor() == std::numeric_limits<float>::infinity()`.
Similar to `rehash`, this function can be used to grow or shrink the number of buckets in the container.
Invalidates iterators, and changes the order of elements. Pointers and references to elements are not invalidated.
[horizontal]
Throws:;; The function has no effect if an exception is thrown, unless it is thrown by the container's hash function or comparison function.
---
=== Equality Comparisons

View File

@ -1329,7 +1329,9 @@ Effects:;; Changes the container's maximum load factor, using `z` as a hint.
void rehash(size_type n);
```
Changes the number of buckets so that there at least `n` buckets, and so that the load factor is less than the maximum load factor.
Changes the number of buckets so that there are at least `n` buckets, and so that the load factor is less than or equal to the maximum load factor. When applicable, this will either grow or shrink the `bucket_count()` associated with the container.
When `size() == 0`, `rehash(0)` will deallocate the underlying buckets array.
Invalidates iterators, and changes the order of elements. Pointers and references to elements are not invalidated.
@ -1343,11 +1345,16 @@ Throws:;; The function has no effect if an exception is thrown, unless it is thr
void reserve(size_type n);
```
Equivalent to `a.rehash(ceil(n / a.max_load_factor()))`, or `a.rehash(1)` if `n > 0` and `a.max_load_factor() == std::numeric_limits<float>::infinity()`.
Similar to `rehash`, this function can be used to grow or shrink the number of buckets in the container.
Invalidates iterators, and changes the order of elements. Pointers and references to elements are not invalidated.
[horizontal]
Throws:;; The function has no effect if an exception is thrown, unless it is thrown by the container's hash function or comparison function.
=== Equality Comparisons
==== operator==

View File

@ -494,11 +494,33 @@ namespace boost {
group_pointer groups;
public:
static std::size_t bucket_count_for(std::size_t num_buckets)
{
if (num_buckets == 0) {
return 0;
}
return size_policy::size(size_policy::size_index(num_buckets));
}
grouped_bucket_array()
: empty_value<node_allocator_type>(
empty_init_t(), node_allocator_type()),
size_index_(0), size_(0), buckets(), groups()
{
}
grouped_bucket_array(size_type n, const Allocator& al)
: empty_value<node_allocator_type>(empty_init_t(), al),
size_index_(size_policy::size_index(n)),
size_(size_policy::size(size_index_)), buckets(), groups()
size_index_(0),
size_(0), buckets(), groups()
{
if (n == 0) {
return;
}
size_index_ = size_policy::size_index(n);
size_ = size_policy::size(size_index_);
bucket_allocator_type bucket_alloc = this->get_bucket_allocator();
group_allocator_type group_alloc = this->get_group_allocator();
@ -646,7 +668,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 +682,10 @@ namespace boost {
local_iterator begin(size_type n) const
{
if (size_ == 0) {
return this->end(n);
}
return local_iterator(
(buckets + static_cast<difference_type>(n))->next);
}
@ -670,12 +696,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<difference_type>(n),
groups + static_cast<difference_type>(n / N));
iterator pbg(buckets + static_cast<difference_type>(n),
groups + static_cast<difference_type>(n / N));
return pbg;
return pbg;
} else {
return this->end();
}
}
span<Bucket> raw()

View File

@ -200,7 +200,7 @@ namespace boost {
template <typename Types> struct table;
static const float minimum_max_load_factor = 1e-3f;
static const std::size_t default_bucket_count = 11;
static const std::size_t default_bucket_count = 0;
struct move_tag
{
@ -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;
}
@ -2069,13 +2071,18 @@ namespace boost {
void recalculate_max_load()
{
using namespace std;
// From 6.3.1/13:
// Only resize when size >= mlf_ * count
max_load_ = boost::unordered::detail::double_to_size(
floor(static_cast<double>(mlf_) *
static_cast<double>(buckets_.bucket_count())));
std::size_t const bc = buckets_.bucket_count();
// it's important we do the `bc == 0` check here because the `mlf_`
// can be specified to be infinity. The operation `n * INF` is `INF`
// for all `n > 0` but NaN for `n == 0`.
//
max_load_ =
bc == 0 ? 0
: boost::unordered::detail::double_to_size(
static_cast<double>(mlf_) * static_cast<double>(bc));
}
void max_load_factor(float z)
@ -2088,6 +2095,12 @@ namespace boost {
////////////////////////////////////////////////////////////////////////
// Constructors
table()
: functions(hasher(), key_equal()), size_(0), mlf_(1.0f),
max_load_(0)
{
}
table(std::size_t num_buckets, hasher const& hf, key_equal const& eq,
node_allocator_type const& a)
: functions(hf, eq), size_(0), mlf_(1.0f), max_load_(0),
@ -2359,7 +2372,6 @@ namespace boost {
template <typename UniqueType>
void move_assign(table& x, UniqueType is_unique, false_type)
{
reserve(x.size_);
if (node_alloc() == x.node_alloc()) {
move_assign_equal_alloc(x);
} else {
@ -2388,7 +2400,9 @@ namespace boost {
{
mlf_ = x.mlf_;
recalculate_max_load();
this->reserve_for_insert(x.size_);
if (x.size_ > 0) {
this->reserve_for_insert(x.size_);
}
this->clear_impl();
}
BOOST_CATCH(...)
@ -2420,11 +2434,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 +2470,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 +2486,13 @@ namespace boost {
template <class Key>
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;
@ -2503,6 +2524,17 @@ namespace boost {
new_buckets.insert_node(itnewb, p);
}
static std::size_t min_buckets(std::size_t num_elements, float mlf)
{
std::size_t num_buckets = static_cast<std::size_t>(
std::ceil(static_cast<float>(num_elements) / mlf));
if (num_buckets == 0 && num_elements > 0) { // mlf == inf
num_buckets = 1;
}
return num_buckets;
}
void rehash(std::size_t);
void reserve(std::size_t);
void reserve_for_insert(std::size_t);
@ -3411,22 +3443,18 @@ namespace boost {
template <typename Types>
inline void table<Types>::rehash(std::size_t num_buckets)
{
std::size_t bc = (std::max)(num_buckets,
static_cast<std::size_t>(1.0f + static_cast<float>(size_) / mlf_));
num_buckets = (std::max)(
min_buckets(size_, mlf_), buckets_.bucket_count_for(num_buckets));
if (bc <= buckets_.bucket_count()) {
return;
if (num_buckets != this->bucket_count()) {
this->rehash_impl(num_buckets);
}
this->rehash_impl(bc);
}
template <class Types>
inline void table<Types>::reserve(std::size_t num_elements)
{
std::size_t const num_buckets = static_cast<std::size_t>(
std::ceil(static_cast<float>(num_elements) / mlf_));
std::size_t num_buckets = min_buckets(num_elements, mlf_);
this->rehash(num_buckets);
}

View File

@ -1664,8 +1664,6 @@ namespace boost {
template <class K, class T, class H, class P, class A>
unordered_map<K, T, H, P, A>::unordered_map()
: table_(boost::unordered::detail::default_bucket_count, hasher(),
key_equal(), allocator_type())
{
}
@ -2069,6 +2067,10 @@ namespace boost {
template <class K, class T, class H, class P, class A>
float unordered_map<K, T, H, P, A>::load_factor() const BOOST_NOEXCEPT
{
if (table_.size_ == 0) {
return 0.0f;
}
BOOST_ASSERT(table_.bucket_count() != 0);
return static_cast<float>(table_.size_) /
static_cast<float>(table_.bucket_count());
@ -2143,8 +2145,6 @@ namespace boost {
template <class K, class T, class H, class P, class A>
unordered_multimap<K, T, H, P, A>::unordered_multimap()
: table_(boost::unordered::detail::default_bucket_count, hasher(),
key_equal(), allocator_type())
{
}
@ -2506,6 +2506,10 @@ namespace boost {
template <class K, class T, class H, class P, class A>
float unordered_multimap<K, T, H, P, A>::load_factor() const BOOST_NOEXCEPT
{
if (table_.size_ == 0) {
return 0.0f;
}
BOOST_ASSERT(table_.bucket_count() != 0);
return static_cast<float>(table_.size_) /
static_cast<float>(table_.bucket_count());

View File

@ -1266,8 +1266,6 @@ namespace boost {
////////////////////////////////////////////////////////////////////////////
template <class T, class H, class P, class A>
unordered_set<T, H, P, A>::unordered_set()
: table_(boost::unordered::detail::default_bucket_count, hasher(),
key_equal(), allocator_type())
{
}
@ -1586,6 +1584,10 @@ namespace boost {
template <class T, class H, class P, class A>
float unordered_set<T, H, P, A>::load_factor() const BOOST_NOEXCEPT
{
if (table_.size_ == 0) {
return 0.0f;
}
BOOST_ASSERT(table_.bucket_count() != 0);
return static_cast<float>(table_.size_) /
static_cast<float>(table_.bucket_count());
@ -1660,8 +1662,6 @@ namespace boost {
template <class T, class H, class P, class A>
unordered_multiset<T, H, P, A>::unordered_multiset()
: table_(boost::unordered::detail::default_bucket_count, hasher(),
key_equal(), allocator_type())
{
}
@ -1986,6 +1986,10 @@ namespace boost {
template <class T, class H, class P, class A>
float unordered_multiset<T, H, P, A>::load_factor() const BOOST_NOEXCEPT
{
if (table_.size_ == 0) {
return 0.0f;
}
BOOST_ASSERT(table_.bucket_count() != 0);
return static_cast<float>(table_.size_) /
static_cast<float>(table_.bucket_count());

View File

@ -95,7 +95,7 @@ void insert_rehash_exception_test(
T*, Inserter insert, test::random_generator gen)
{
for (int i = 0; i < 5; ++i) {
T x;
T x(1);
rehash_prep(x);
test::random_values<T> v2(5, gen);
@ -393,7 +393,7 @@ template <typename T>
void insert_range_rehash_exception_test(T*, test::random_generator gen)
{
for (int i = 0; i < 5; ++i) {
T x;
T x(1);
rehash_prep(x);
test::random_values<T> v2(5, gen);

View File

@ -45,6 +45,7 @@ namespace assign_tests {
BOOST_TEST(x.empty());
BOOST_TEST(test::equivalent(x.hash_function(), hf));
BOOST_TEST(test::equivalent(x.key_eq(), eq));
BOOST_TEST(test::detail::tracker.count_allocations == 0);
}
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "assign_tests1.2\n";
@ -94,6 +95,7 @@ namespace assign_tests {
BOOST_TEST(test::equivalent(x1.key_eq(), eq1));
BOOST_TEST(test::equivalent(x2.hash_function(), hf1));
BOOST_TEST(test::equivalent(x2.key_eq(), eq1));
BOOST_TEST(test::detail::tracker.count_allocations == 0);
test::check_container(x1, x2);
}

View File

@ -35,6 +35,7 @@ namespace constructor_tests {
T x(0, hf, eq);
BOOST_TEST(x.empty());
BOOST_TEST_EQ(x.bucket_count(), 0u);
BOOST_TEST(test::equivalent(x.hash_function(), hf));
BOOST_TEST(test::equivalent(x.key_eq(), eq));
BOOST_TEST(test::equivalent(x.get_allocator(), al));
@ -73,6 +74,7 @@ namespace constructor_tests {
T x;
BOOST_TEST(x.empty());
BOOST_TEST_EQ(x.bucket_count(), 0u);
BOOST_TEST(test::equivalent(x.hash_function(), hf));
BOOST_TEST(test::equivalent(x.key_eq(), eq));
BOOST_TEST(test::equivalent(x.get_allocator(), al));
@ -140,6 +142,7 @@ namespace constructor_tests {
T x(0, hf, eq, al);
BOOST_TEST(x.empty());
BOOST_TEST_EQ(x.bucket_count(), 0u);
BOOST_TEST(test::equivalent(x.hash_function(), hf));
BOOST_TEST(test::equivalent(x.key_eq(), eq));
BOOST_TEST(test::equivalent(x.get_allocator(), al));
@ -166,6 +169,7 @@ namespace constructor_tests {
T x(al);
BOOST_TEST(x.empty());
BOOST_TEST_EQ(x.bucket_count(), 0u);
BOOST_TEST(test::equivalent(x.hash_function(), hf));
BOOST_TEST(test::equivalent(x.key_eq(), eq));
BOOST_TEST(test::equivalent(x.get_allocator(), al));
@ -325,6 +329,7 @@ namespace constructor_tests {
T x(list);
BOOST_TEST(x.empty());
BOOST_TEST_EQ(x.bucket_count(), 0u);
BOOST_TEST(test::equivalent(x.hash_function(), hf));
BOOST_TEST(test::equivalent(x.key_eq(), eq));
BOOST_TEST(test::equivalent(x.get_allocator(), al));
@ -380,6 +385,104 @@ namespace constructor_tests {
#endif
}
template <class T>
void no_alloc_default_construct_test(T*, test::random_generator)
{
UNORDERED_SUB_TEST("Construct 1")
{
T x;
BOOST_TEST_EQ(x.bucket_count(), 0u);
BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u);
}
UNORDERED_SUB_TEST("Construct 2")
{
{
T x(0);
BOOST_TEST_EQ(x.bucket_count(), 0u);
BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u);
}
{
T x(1);
BOOST_TEST_GT(x.bucket_count(), 0u);
BOOST_TEST_GT(test::detail::tracker.count_allocations, 0u);
}
}
UNORDERED_SUB_TEST("Construct 3")
{
test::random_values<T> v;
T x(v.begin(), v.end());
BOOST_TEST_EQ(x.bucket_count(), 0u);
BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u);
}
UNORDERED_SUB_TEST("Construct 4")
{
{
test::random_values<T> v;
T x(v.begin(), v.end(), 0);
BOOST_TEST_EQ(x.bucket_count(), 0u);
BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u);
}
{
test::random_values<T> v;
T x(v.begin(), v.end(), 1);
BOOST_TEST_GT(x.bucket_count(), 0u);
BOOST_TEST_GT(test::detail::tracker.count_allocations, 0u);
}
}
UNORDERED_SUB_TEST("Construct 5")
{
typename T::allocator_type al;
{
T x(al);
BOOST_TEST_EQ(x.bucket_count(), 0u);
BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u);
}
}
UNORDERED_SUB_TEST("Construct 6")
{
typename T::allocator_type al;
T x(0, al);
BOOST_TEST_EQ(x.bucket_count(), 0u);
BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u);
}
#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST)
UNORDERED_SUB_TEST("Initializer list 1")
{
std::initializer_list<typename T::value_type> list;
T x(list);
BOOST_TEST_EQ(x.bucket_count(), 0u);
BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u);
}
UNORDERED_SUB_TEST("Initializer list 2")
{
{
std::initializer_list<typename T::value_type> list;
T x(list, 0);
BOOST_TEST_EQ(x.bucket_count(), 0u);
BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u);
}
{
std::initializer_list<typename T::value_type> list;
T x(list, 1);
BOOST_TEST_GT(x.bucket_count(), 0u);
BOOST_TEST_GT(test::detail::tracker.count_allocations, 0u);
}
}
#endif
}
template <class T>
void map_constructor_test(T*, test::random_generator const& generator)
{
@ -422,6 +525,10 @@ namespace constructor_tests {
((test_map_std_alloc)(test_map)(test_multimap))(
(default_generator)(generate_collisions)(limited_range)))
UNORDERED_TEST(no_alloc_default_construct_test,
((test_set)(test_multiset)(test_map)(test_multimap))(
(default_generator)(generate_collisions)(limited_range)))
#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST)
UNORDERED_AUTO_TEST (test_default_initializer_list) {

View File

@ -43,6 +43,23 @@ namespace copy_tests {
BOOST_TEST(x.max_load_factor() == y.max_load_factor());
BOOST_TEST(test::selected_count(y.get_allocator()) ==
(allocator_type::is_select_on_copy));
BOOST_TEST(test::detail::tracker.count_allocations == 0);
test::check_equivalent_keys(y);
}
{
test::check_instances check_;
T x(0);
T y(x);
BOOST_TEST(y.empty());
BOOST_TEST(test::equivalent(y.hash_function(), hf));
BOOST_TEST(test::equivalent(y.key_eq(), eq));
BOOST_TEST(test::equivalent(y.get_allocator(), al));
BOOST_TEST(x.max_load_factor() == y.max_load_factor());
BOOST_TEST(test::selected_count(y.get_allocator()) ==
(allocator_type::is_select_on_copy));
BOOST_TEST(test::detail::tracker.count_allocations == 0);
test::check_equivalent_keys(y);
}
@ -91,6 +108,22 @@ namespace copy_tests {
typedef typename T::allocator_type allocator_type;
{
test::check_instances check_;
T x(0, hf, eq, al);
T y(x);
BOOST_TEST(y.empty());
BOOST_TEST(test::equivalent(y.hash_function(), hf));
BOOST_TEST(test::equivalent(y.key_eq(), eq));
BOOST_TEST(test::equivalent(y.get_allocator(), al));
BOOST_TEST(x.max_load_factor() == y.max_load_factor());
BOOST_TEST(test::selected_count(y.get_allocator()) ==
(allocator_type::is_select_on_copy));
BOOST_TEST(test::detail::tracker.count_allocations == 0);
test::check_equivalent_keys(y);
}
{
test::check_instances check_;
@ -106,6 +139,21 @@ namespace copy_tests {
test::check_equivalent_keys(y);
}
{
test::check_instances check_;
T x(0, hf, eq, al);
T y(x, al2);
BOOST_TEST(y.empty());
BOOST_TEST(test::equivalent(y.hash_function(), hf));
BOOST_TEST(test::equivalent(y.key_eq(), eq));
BOOST_TEST(test::equivalent(y.get_allocator(), al2));
BOOST_TEST(x.max_load_factor() == y.max_load_factor());
BOOST_TEST(test::selected_count(y.get_allocator()) == 0);
BOOST_TEST(test::detail::tracker.count_allocations == 0);
test::check_equivalent_keys(y);
}
{
test::check_instances check_;
@ -120,6 +168,22 @@ namespace copy_tests {
test::check_equivalent_keys(y);
}
{
test::check_instances check_;
test::random_values<T> v;
T x(v.begin(), v.end(), 0, hf, eq, al);
T y(x);
test::unordered_equivalence_tester<T> equivalent(x);
BOOST_TEST(equivalent(y));
test::check_equivalent_keys(y);
BOOST_TEST(test::selected_count(y.get_allocator()) ==
(allocator_type::is_select_on_copy));
BOOST_TEST(test::equivalent(y.get_allocator(), al));
BOOST_TEST(test::detail::tracker.count_allocations == 0);
}
{
test::check_instances check_;
@ -135,6 +199,21 @@ namespace copy_tests {
BOOST_TEST(test::equivalent(y.get_allocator(), al));
}
{
test::check_instances check_;
test::random_values<T> v;
T x(v.begin(), v.end(), 0, hf, eq, al);
T y(x, al2);
test::unordered_equivalence_tester<T> equivalent(x);
BOOST_TEST(equivalent(y));
test::check_equivalent_keys(y);
BOOST_TEST(test::selected_count(y.get_allocator()) == 0);
BOOST_TEST(test::equivalent(y.get_allocator(), al2));
BOOST_TEST(test::detail::tracker.count_allocations == 0);
}
{
test::check_instances check_;

View File

@ -18,6 +18,9 @@
#include "../helpers/equivalent.hpp"
#include "../helpers/invariants.hpp"
#include <boost/core/ignore_unused.hpp>
#include <iterator>
#if defined(BOOST_MSVC)
#pragma warning(disable : 4127) // conditional expression is constant
#endif
@ -70,6 +73,10 @@ namespace move_tests {
BOOST_TEST(test::equivalent(y.get_allocator(), al));
BOOST_TEST(y.max_load_factor() == 1.0);
test::check_equivalent_keys(y);
#if defined(BOOST_UNORDERED_USE_MOVE) || \
!defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u);
#endif
}
{
@ -87,7 +94,7 @@ namespace move_tests {
}
template <class T>
void move_assign_tests1(T*, test::random_generator const& generator)
void move_assign_tests1(T* p, test::random_generator const& generator)
{
{
test::check_instances check_;
@ -98,6 +105,19 @@ namespace move_tests {
y = create(v, count);
#if BOOST_UNORDERED_TEST_MOVING && defined(BOOST_HAS_NRVO)
BOOST_TEST(count == test::global_object_count);
#endif
test::check_container(y, v);
test::check_equivalent_keys(y);
}
{
test::random_values<T> v;
T y;
y = empty(p);
#if defined(BOOST_UNORDERED_USE_MOVE) || \
!defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u);
#endif
test::check_container(y, v);
test::check_equivalent_keys(y);
@ -233,6 +253,32 @@ namespace move_tests {
#endif
}
{
test::random_values<T> v;
T y(0, hf, eq, al1);
T x(0, hf, eq, al2);
x.max_load_factor(0.25);
BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u);
y = boost::move(x);
#if defined(BOOST_UNORDERED_USE_MOVE) || \
!defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u);
#endif
test::check_container(y, v);
test::check_equivalent_keys(y);
BOOST_TEST(y.max_load_factor() == 0.25);
if (BOOST_UNORDERED_TEST_MOVING
? (bool)allocator_type::is_propagate_on_move
: (bool)allocator_type::is_propagate_on_assign) {
BOOST_TEST(test::equivalent(y.get_allocator(), al2));
} else {
BOOST_TEST(test::equivalent(y.get_allocator(), al1));
}
}
{
test::check_instances check_;
@ -298,17 +344,533 @@ namespace move_tests {
}
}
template <class T> T const& get_key(T const& t) { return t; }
template <class K, class V> K const& get_key(std::pair<K const, V> const& kv)
{
return kv.first;
}
template <class T> T const& get_value(T const& t) { return t; }
template <class K, class V> K const& get_value(std::pair<K const, V> const& kv)
{
return kv.second;
}
template <class T>
static void insert_range(T& y, test::random_values<T> const& v)
{
y.insert(v.begin(), v.end());
BOOST_TEST_EQ(y.size(),
static_cast<typename T::size_type>(std::distance(y.begin(), y.end())));
}
template <class T>
static void insert_single(T& y, test::random_values<T> const& v)
{
y.insert(*v.begin());
BOOST_TEST_EQ(y.size(),
static_cast<typename T::size_type>(std::distance(y.begin(), y.end())));
}
template <class T>
static void insert_single_hint(T& y, test::random_values<T> const& v)
{
y.insert(y.end(), *v.begin());
BOOST_TEST_EQ(y.size(),
static_cast<typename T::size_type>(std::distance(y.begin(), y.end())));
}
template <class T> struct insert_or_assign_invoker
{
void operator()(T&, test::random_values<T> const&) {}
};
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
struct insert_or_assign_invoker<
boost::unordered_map<Key, T, Hash, KeyEqual, Allocator> >
{
void operator()(boost::unordered_map<Key, T, Hash, KeyEqual, Allocator>& y,
test::random_values<
boost::unordered_map<Key, T, Hash, KeyEqual, Allocator> > const& v)
{
typedef typename boost::unordered_map<Key, T, Hash, KeyEqual,
Allocator>::size_type size_type;
y.insert_or_assign(get_key(*v.begin()), get_value(*v.begin()));
BOOST_TEST_EQ(
y.size(), static_cast<size_type>(std::distance(y.begin(), y.end())));
}
};
template <class T>
static void insert_or_assign(T& y, test::random_values<T> const& v)
{
insert_or_assign_invoker<T>()(y, v);
}
template <class T> struct insert_or_assign_hint_invoker
{
void operator()(T&, test::random_values<T> const&) {}
};
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
struct insert_or_assign_hint_invoker<
boost::unordered_map<Key, T, Hash, KeyEqual, Allocator> >
{
void operator()(boost::unordered_map<Key, T, Hash, KeyEqual, Allocator>& y,
test::random_values<
boost::unordered_map<Key, T, Hash, KeyEqual, Allocator> > const& v)
{
typedef typename boost::unordered_map<Key, T, Hash, KeyEqual,
Allocator>::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<size_type>(std::distance(y.begin(), y.end())));
}
};
template <class T>
static void insert_or_assign_hint(T& y, test::random_values<T> const& v)
{
insert_or_assign_hint_invoker<T>()(y, v);
}
template <class T> struct try_emplace_invoker
{
void operator()(T&, test::random_values<T> const&) {}
};
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
struct try_emplace_invoker<
boost::unordered_map<Key, T, Hash, KeyEqual, Allocator> >
{
void operator()(boost::unordered_map<Key, T, Hash, KeyEqual, Allocator>& y,
test::random_values<
boost::unordered_map<Key, T, Hash, KeyEqual, Allocator> > const& v)
{
typedef typename boost::unordered_map<Key, T, Hash, KeyEqual,
Allocator>::size_type size_type;
y.try_emplace(get_key(*v.begin()), get_value(*v.begin()));
BOOST_TEST_EQ(
y.size(), static_cast<size_type>(std::distance(y.begin(), y.end())));
}
};
template <class T>
static void try_emplace(T& y, test::random_values<T> const& v)
{
try_emplace_invoker<T>()(y, v);
}
template <class T> struct try_emplace_hint_invoker
{
void operator()(T&, test::random_values<T> const&) {}
};
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
struct try_emplace_hint_invoker<
boost::unordered_map<Key, T, Hash, KeyEqual, Allocator> >
{
void operator()(boost::unordered_map<Key, T, Hash, KeyEqual, Allocator>& y,
test::random_values<
boost::unordered_map<Key, T, Hash, KeyEqual, Allocator> > const& v)
{
typedef typename boost::unordered_map<Key, T, Hash, KeyEqual,
Allocator>::size_type size_type;
y.try_emplace(y.end(), get_key(*v.begin()), get_value(*v.begin()));
BOOST_TEST_EQ(
y.size(), static_cast<size_type>(std::distance(y.begin(), y.end())));
}
};
template <class T>
static void try_emplace_hint(T& y, test::random_values<T> const& v)
{
try_emplace_hint_invoker<T>()(y, v);
}
template <class T> struct at_invoker
{
void operator()(T&, test::random_values<T> const&) {}
};
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
struct at_invoker<boost::unordered_map<Key, T, Hash, KeyEqual, Allocator> >
{
void operator()(boost::unordered_map<Key, T, Hash, KeyEqual, Allocator>& y,
test::random_values<
boost::unordered_map<Key, T, Hash, KeyEqual, Allocator> > const& v)
{
BOOST_TRY { y.at(get_key(*v.begin())); }
BOOST_CATCH(...) {}
BOOST_CATCH_END
}
};
template <class T> static void at(T& y, test::random_values<T> const& v)
{
at_invoker<T>()(y, v);
}
template <class T> struct index_operator_invoker
{
void operator()(T&, test::random_values<T> const&) {}
};
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
struct index_operator_invoker<
boost::unordered_map<Key, T, Hash, KeyEqual, Allocator> >
{
void operator()(boost::unordered_map<Key, T, Hash, KeyEqual, Allocator>& y,
test::random_values<
boost::unordered_map<Key, T, Hash, KeyEqual, Allocator> > const& v)
{
typedef typename boost::unordered_map<Key, T, Hash, KeyEqual,
Allocator>::size_type size_type;
y[get_key(*v.begin())] = get_value(*v.begin());
BOOST_TEST_EQ(
y.size(), static_cast<size_type>(std::distance(y.begin(), y.end())));
}
};
template <class T>
static void index_operator(T& y, test::random_values<T> const& v)
{
index_operator_invoker<T>()(y, v);
}
template <class T> static void clear(T& y, test::random_values<T> const&)
{
y.clear();
BOOST_TEST(y.empty());
BOOST_TEST_EQ(y.size(),
static_cast<typename T::size_type>(std::distance(y.begin(), y.end())));
}
template <class T> static void capacity(T& y, test::random_values<T> 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 <class T> static void iterators(T& y, test::random_values<T> const&)
{
BOOST_TEST_EQ(y.size(),
static_cast<typename T::size_type>(std::distance(y.begin(), y.end())));
}
template <class T>
static void erase_range(T& y, test::random_values<T> const&)
{
y.erase(y.begin(), y.end());
BOOST_TEST(y.empty());
BOOST_TEST_EQ(y.size(),
static_cast<typename T::size_type>(std::distance(y.begin(), y.end())));
}
template <class T>
static void erase_key(T& y, test::random_values<T> const& v)
{
y.erase(get_key(*v.begin()));
BOOST_TEST_EQ(y.size(),
static_cast<typename T::size_type>(std::distance(y.begin(), y.end())));
}
template <class T> static void lookup(T& y, test::random_values<T> 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 <class T> static void reserve(T& y, test::random_values<T> const&)
{
y.reserve(1337);
BOOST_TEST_GT(y.bucket_count(), 1337u);
BOOST_TEST_EQ(y.size(),
static_cast<typename T::size_type>(std::distance(y.begin(), y.end())));
}
template <class T>
static void copy_assignment(T& y, test::random_values<T> const& v)
{
T x(v.begin(), v.end());
y = x;
BOOST_TEST_EQ(y.size(), x.size());
BOOST_TEST_EQ(y.size(),
static_cast<typename T::size_type>(std::distance(y.begin(), y.end())));
}
template <class T>
static void move_assignment(T& y, test::random_values<T> 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<typename T::size_type>(std::distance(y.begin(), y.end())));
}
template <class T> static void equal(T& y, test::random_values<T> const& v)
{
T x(v.begin(), v.end());
(void)(y == x);
BOOST_TEST_EQ(y.size(),
static_cast<typename T::size_type>(std::distance(y.begin(), y.end())));
}
template <class T> static void extract(T& y, test::random_values<T> const& v)
{
(void)y.extract(get_key(*v.begin()));
BOOST_TEST_EQ(y.size(),
static_cast<typename T::size_type>(std::distance(y.begin(), y.end())));
}
template <class T> static void merge(T& y, test::random_values<T> 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<typename T::size_type>(std::distance(y.begin(), y.end())));
}
template <class X> bool pred(X const&) { return true; }
template <class T>
static void erase_with_pred(T& y, test::random_values<T> const&)
{
erase_if(y, pred<typename T::value_type>);
BOOST_TEST_EQ(y.size(),
static_cast<typename T::size_type>(std::distance(y.begin(), y.end())));
}
template <class T>
static void container_swap(T& y, test::random_values<T> 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<typename T::size_type>(std::distance(y.begin(), y.end())));
}
template <class T> static void buckets(T& y, test::random_values<T> 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 <class T>
static void double_move_construct(T& y, test::random_values<T> const&)
{
T x = boost::move(y);
x.clear();
BOOST_TEST_EQ(y.size(),
static_cast<typename T::size_type>(std::distance(y.begin(), y.end())));
BOOST_TEST_EQ(x.size(),
static_cast<typename T::size_type>(std::distance(x.begin(), x.end())));
}
template <class T>
static void double_move_assign(T& y, test::random_values<T> const&)
{
T x;
x = boost::move(y);
x.clear();
BOOST_TEST_EQ(y.size(),
static_cast<typename T::size_type>(std::distance(y.begin(), y.end())));
BOOST_TEST_EQ(x.size(),
static_cast<typename T::size_type>(std::distance(x.begin(), x.end())));
}
template <class T>
static void post_move_tests(T* ptr, test::random_generator const& generator)
{
// clang-format off
void (*fps[])(T&, test::random_values<T> const&) = {
insert_range<T>,
insert_single<T>,
insert_single_hint<T>,
insert_or_assign<T>,
insert_or_assign_hint<T>,
try_emplace<T>,
try_emplace_hint<T>,
at<T>,
index_operator<T>,
clear<T>,
capacity<T>,
iterators<T>,
erase_range<T>,
erase_key<T>,
lookup<T>,
reserve<T>,
copy_assignment<T>,
move_assignment<T>,
equal<T>,
extract<T>,
merge<T>,
erase_with_pred<T>,
container_swap<T>,
buckets<T>,
double_move_construct<T>,
double_move_assign<T>
};
// 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<T> const v(1000, generator);
test::object_count count;
T y(create(v, count));
unsigned num_allocs = test::detail::tracker.count_allocations;
(void)num_allocs;
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());
BOOST_TEST_EQ(y.bucket_count(), 0u);
BOOST_TEST_EQ(test::detail::tracker.count_allocations, num_allocs);
#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<T> 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<T> v(1000, generator);
test::object_count count;
T y(create(v, count));
unsigned num_allocs = test::detail::tracker.count_allocations;
(void)num_allocs;
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());
BOOST_TEST_EQ(y.bucket_count(), 0u);
BOOST_TEST_EQ(test::detail::tracker.count_allocations, num_allocs);
#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<T> v(1000, generator);
test::object_count count;
T y(v.begin(), v.end(), 0, hf, eq, al1);
unsigned num_allocs = test::detail::tracker.count_allocations;
(void)num_allocs;
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());
BOOST_TEST_EQ(y.bucket_count(), 0u);
BOOST_TEST_EQ(test::detail::tracker.count_allocations, num_allocs);
#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::object, test::object, test::hash, test::equal_to,
std::allocator<test::object> >* test_map_std_alloc;
std::allocator<std::pair<test::object const, test::object> > >*
test_map_std_alloc;
boost::unordered_set<test::object, test::hash, test::equal_to,
test::allocator2<test::object> >* test_set;
boost::unordered_multiset<test::object, test::hash, test::equal_to,
test::allocator1<test::object> >* test_multiset;
boost::unordered_map<test::object, test::object, test::hash, test::equal_to,
test::allocator1<test::object> >* test_map;
test::allocator1<std::pair<test::object const, test::object> > >* test_map;
boost::unordered_multimap<test::object, test::object, test::hash,
test::equal_to, test::allocator2<test::object> >* test_multimap;
test::equal_to,
test::allocator2<std::pair<test::object const, test::object> > >*
test_multimap;
boost::unordered_set<test::object, test::hash, test::equal_to,
test::cxx11_allocator<test::object, test::propagate_move> >*
@ -317,11 +879,12 @@ namespace move_tests {
test::cxx11_allocator<test::object, test::propagate_move> >*
test_multiset_prop_move;
boost::unordered_map<test::object, test::object, test::hash, test::equal_to,
test::cxx11_allocator<test::object, test::propagate_move> >*
test_map_prop_move;
test::cxx11_allocator<std::pair<test::object const, test::object>,
test::propagate_move> >* test_map_prop_move;
boost::unordered_multimap<test::object, test::object, test::hash,
test::equal_to, test::cxx11_allocator<test::object, test::propagate_move> >*
test_multimap_prop_move;
test::equal_to,
test::cxx11_allocator<std::pair<test::object const, test::object>,
test::propagate_move> >* test_multimap_prop_move;
boost::unordered_set<test::object, test::hash, test::equal_to,
test::cxx11_allocator<test::object, test::no_propagate_move> >*
@ -330,12 +893,12 @@ namespace move_tests {
test::cxx11_allocator<test::object, test::no_propagate_move> >*
test_multiset_no_prop_move;
boost::unordered_map<test::object, test::object, test::hash, test::equal_to,
test::cxx11_allocator<test::object, test::no_propagate_move> >*
test_map_no_prop_move;
test::cxx11_allocator<std::pair<test::object const, test::object>,
test::no_propagate_move> >* test_map_no_prop_move;
boost::unordered_multimap<test::object, test::object, test::hash,
test::equal_to,
test::cxx11_allocator<test::object, test::no_propagate_move> >*
test_multimap_no_prop_move;
test::cxx11_allocator<std::pair<test::object const, test::object>,
test::no_propagate_move> >* test_multimap_no_prop_move;
using test::default_generator;
using test::generate_collisions;
@ -367,6 +930,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()

View File

@ -11,6 +11,7 @@
// clang-format on
#include "../helpers/test.hpp"
#include "../helpers/fwd.hpp"
#if defined(BOOST_MSVC)
#pragma warning(push)
@ -247,37 +248,65 @@ namespace noexcept_tests {
throwing_test_exception = false;
}
UNORDERED_AUTO_TEST (test_nothrow_move_assign_when_noexcept) {
typedef boost::unordered_set<int, hash_nothrow_move_assign,
equal_to_nothrow_move_assign>
throwing_set;
template <class T>
void test_nothrow_move_assign_when_noexcept(T*, test::random_generator const&)
{
{
if (have_is_nothrow_move_assign) {
BOOST_TEST(boost::is_nothrow_move_assignable<T>::value);
}
if (have_is_nothrow_move_assign) {
BOOST_TEST(boost::is_nothrow_move_assignable<throwing_set>::value);
throwing_test_exception = false;
T x1;
T x2;
x1.insert(10);
x1.insert(50);
for (int i = 0; i < 100; ++i) {
x2.insert(i);
}
try {
throwing_test_exception = true;
x2 = boost::move(x1);
BOOST_TEST(x2.size() == 2);
BOOST_TEST(*x2.begin() == 10 || *x2.begin() == 50);
BOOST_TEST(have_is_nothrow_move_assign);
} catch (test_exception) {
BOOST_TEST(!have_is_nothrow_move_assign);
}
throwing_test_exception = false;
}
throwing_test_exception = false;
{
if (have_is_nothrow_move_assign) {
BOOST_TEST(boost::is_nothrow_move_assignable<T>::value);
}
throwing_set x1;
throwing_set x2;
x1.insert(10);
x1.insert(50);
for (int i = 0; i < 100; ++i) {
x2.insert(i);
throwing_test_exception = false;
T x1;
T x2;
x1.insert(10);
x1.insert(50);
for (int i = 0; i < 100; ++i) {
x2.insert(i);
}
try {
throwing_test_exception = true;
x1 = boost::move(x2);
BOOST_TEST(x1.size() == 100);
BOOST_TEST(have_is_nothrow_move_assign);
} catch (test_exception) {
BOOST_TEST(!have_is_nothrow_move_assign);
}
throwing_test_exception = false;
}
try {
throwing_test_exception = true;
x2 = boost::move(x1);
BOOST_TEST(x2.size() == 2);
BOOST_TEST(*x2.begin() == 10 || *x2.begin() == 50);
BOOST_TEST(have_is_nothrow_move_assign);
} catch (test_exception) {
BOOST_TEST(!have_is_nothrow_move_assign);
}
throwing_test_exception = false;
}
UNORDERED_AUTO_TEST (test_nothrow_swap_when_noexcept) {
@ -318,4 +347,80 @@ namespace noexcept_tests {
#pragma warning(pop)
#endif
template <class T> class allocator1
{
BOOST_COPYABLE_AND_MOVABLE(allocator1)
allocator1 operator=(BOOST_COPY_ASSIGN_REF(allocator1));
allocator1 operator=(BOOST_RV_REF(allocator1));
public:
typedef T value_type;
allocator1() {}
allocator1(allocator1 const&) {}
template <class U> allocator1(allocator1<U> const&) {}
T* allocate(std::size_t n)
{
noexcept_tests::test_throw("Allocate");
return static_cast<T*>(::operator new(n * sizeof(T)));
}
void deallocate(T* p, std::size_t) { ::operator delete(p); }
friend bool operator==(allocator1 const&, allocator1 const&) { return true; }
friend bool operator!=(allocator1 const&, allocator1 const&) { return false; }
};
template <class T> class allocator2
{
BOOST_COPYABLE_AND_MOVABLE(allocator2)
allocator2 operator=(BOOST_COPY_ASSIGN_REF(allocator2));
public:
typedef T value_type;
typedef boost::true_type propagate_on_container_move_assignment;
allocator2() {}
allocator2(allocator2 const&) {}
template <class U> allocator2(allocator2<U> const&) {}
allocator2& operator=(BOOST_RV_REF(allocator2)) { return *this; }
T* allocate(std::size_t n)
{
noexcept_tests::test_throw("Allocate");
return static_cast<T*>(::operator new(n * sizeof(T)));
}
void deallocate(T* p, std::size_t) { ::operator delete(p); }
friend bool operator==(allocator2 const&, allocator2 const&) { return true; }
friend bool operator!=(allocator2 const&, allocator2 const&) { return false; }
};
UNORDERED_AUTO_TEST (prelim_allocator_checks) {
BOOST_TEST(boost::allocator_is_always_equal<allocator1<int> >::type::value);
BOOST_TEST(!boost::allocator_propagate_on_container_move_assignment<
allocator1<int> >::type::value);
BOOST_TEST(boost::allocator_is_always_equal<allocator2<int> >::type::value);
BOOST_TEST(boost::allocator_propagate_on_container_move_assignment<
allocator2<int> >::type::value);
}
boost::unordered_set<int, noexcept_tests::hash_nothrow_move_assign,
noexcept_tests::equal_to_nothrow_move_assign, allocator1<int> >*
throwing_set_alloc1;
boost::unordered_set<int, noexcept_tests::hash_nothrow_move_assign,
noexcept_tests::equal_to_nothrow_move_assign, allocator2<int> >*
throwing_set_alloc2;
using test::default_generator;
UNORDERED_TEST(test_nothrow_move_assign_when_noexcept,
((throwing_set_alloc1)(throwing_set_alloc2))((default_generator)))
RUN_TESTS()

View File

@ -81,6 +81,300 @@ namespace rehash_tests {
BOOST_TEST(postcondition(x, 0));
}
template <class X> void rehash_empty_tracking(X*, test::random_generator)
{
// valid for all load factors
float const max_load_factors[] = {
0.5f, 1.0f, 1e6f, std::numeric_limits<float>::infinity()};
std::size_t const max_load_factors_len =
sizeof(max_load_factors) / sizeof(*max_load_factors);
for (std::size_t i = 0; i < max_load_factors_len; ++i) {
X x;
BOOST_TEST_EQ(x.size(), 0u);
BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u);
x.max_load_factor(max_load_factors[i]);
{
BOOST_TEST_EQ(x.bucket_count(), 0u);
x.rehash(0);
BOOST_TEST_EQ(x.bucket_count(), 0u);
BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u);
}
{
BOOST_TEST_EQ(x.bucket_count(), 0u);
x.rehash(1000);
BOOST_TEST_GE(x.bucket_count(), 1000u);
BOOST_TEST_GT(test::detail::tracker.count_allocations, 0u);
x.rehash(0);
BOOST_TEST_EQ(x.bucket_count(), 0u);
BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u);
}
{
BOOST_TEST_EQ(x.bucket_count(), 0u);
x.rehash(1000);
BOOST_TEST_GE(x.bucket_count(), 1000u);
BOOST_TEST_GT(test::detail::tracker.count_allocations, 0u);
x.rehash(10);
BOOST_TEST_GE(x.bucket_count(), 10u);
BOOST_TEST_LT(x.bucket_count(), 1000u);
BOOST_TEST_GT(test::detail::tracker.count_allocations, 0u);
}
{
BOOST_TEST_GT(x.bucket_count(), 0u);
BOOST_TEST_LT(x.bucket_count(), 1000u);
x.rehash(1000);
BOOST_TEST_GE(x.bucket_count(), 1000u);
BOOST_TEST_GT(test::detail::tracker.count_allocations, 0u);
x.rehash(0);
BOOST_TEST_EQ(x.bucket_count(), 0u);
BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u);
}
}
for (std::size_t i = 0; i < max_load_factors_len; ++i) {
typedef typename X::size_type size_type;
X x;
BOOST_TEST_EQ(x.size(), 0u);
BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u);
float const mlf = max_load_factors[i];
x.max_load_factor(mlf);
{
BOOST_TEST_EQ(x.bucket_count(), 0u);
x.reserve(0);
BOOST_TEST_EQ(x.bucket_count(), 0u);
BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u);
}
{
BOOST_TEST_EQ(x.bucket_count(), 0u);
x.reserve(1000);
BOOST_TEST_GE(
x.bucket_count(), static_cast<size_type>(std::ceil(1000 / mlf)));
BOOST_TEST_GT(test::detail::tracker.count_allocations, 0u);
x.reserve(0);
BOOST_TEST_EQ(x.bucket_count(), 0u);
BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u);
}
{
BOOST_TEST_EQ(x.bucket_count(), 0u);
x.reserve(1000);
BOOST_TEST_GE(
x.bucket_count(), static_cast<size_type>(std::ceil(1000 / mlf)));
BOOST_TEST_GT(test::detail::tracker.count_allocations, 0u);
x.reserve(10);
BOOST_TEST_GE(
x.bucket_count(), static_cast<size_type>(std::ceil(10 / mlf)));
BOOST_TEST_LT(x.bucket_count(), 1000u);
BOOST_TEST_GT(test::detail::tracker.count_allocations, 0u);
}
{
BOOST_TEST_GT(x.bucket_count(), 0u);
BOOST_TEST_LT(x.bucket_count(), 1000u);
x.reserve(1000);
BOOST_TEST_GE(
x.bucket_count(), static_cast<size_type>(std::ceil(1000 / mlf)));
BOOST_TEST_GT(test::detail::tracker.count_allocations, 0u);
x.reserve(0);
BOOST_TEST_EQ(x.bucket_count(), 0u);
BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u);
}
}
}
template <class X>
void rehash_nonempty_tracking(X*, test::random_generator generator)
{
test::random_values<X> const v(1000, generator);
typedef typename X::size_type size_type;
float const max_load_factors[] = {0.5f, 1.0f, 1e2f};
size_type const max_load_factors_len =
sizeof(max_load_factors) / sizeof(*max_load_factors);
for (size_type i = 0; i < max_load_factors_len; ++i) {
float const mlf = max_load_factors[i];
X x(v.begin(), v.end());
BOOST_TEST_GT(x.size(), 0u);
BOOST_TEST_GT(test::detail::tracker.count_allocations, 0u);
x.max_load_factor(mlf);
size_type bucket_count = x.bucket_count();
{
BOOST_TEST_GT(x.bucket_count(), 0u);
x.rehash(0);
BOOST_TEST_GE(x.bucket_count(),
static_cast<size_type>(
std::floor(static_cast<float>(x.size()) / x.max_load_factor())));
BOOST_TEST_GT(test::detail::tracker.count_allocations, 0u);
bucket_count = x.bucket_count();
}
{
BOOST_TEST_GT(bucket_count, 0u);
x.rehash(2 * x.bucket_count());
BOOST_TEST_GT(x.bucket_count(), bucket_count);
bucket_count = x.bucket_count();
}
{
float const old_mlf = x.max_load_factor();
BOOST_TEST_GT(bucket_count, 0u);
x.rehash(bucket_count / 4);
BOOST_TEST_LT(x.bucket_count(), bucket_count);
x.max_load_factor(std::numeric_limits<float>::infinity());
x.rehash(0);
BOOST_TEST_GT(x.bucket_count(), 0u);
x.max_load_factor(old_mlf);
}
{
std::size_t const max_load =
static_cast<std::size_t>(static_cast<double>(x.max_load_factor()) *
static_cast<double>(x.bucket_count()));
while (x.size() < max_load) {
test::random_values<X> const t(max_load, generator);
typename test::random_values<X>::const_iterator pos = t.begin();
typename test::random_values<X>::const_iterator end = t.end();
for (; pos != end; ++pos) {
x.insert(*pos);
if (x.size() == max_load) {
break;
}
}
}
while (x.size() > max_load) {
x.erase(x.begin());
}
BOOST_TEST_EQ(x.size(), max_load);
bucket_count = x.bucket_count();
x.rehash(x.bucket_count());
BOOST_TEST_EQ(x.bucket_count(), bucket_count);
}
}
for (size_type i = 0; i < max_load_factors_len; ++i) {
X x(v.begin(), v.end());
BOOST_TEST_GT(x.size(), 0u);
BOOST_TEST_GT(test::detail::tracker.count_allocations, 0u);
float const mlf = max_load_factors[i];
x.max_load_factor(mlf);
size_type bucket_count = x.bucket_count();
{
BOOST_TEST_GT(x.bucket_count(), 0u);
x.reserve(0);
BOOST_TEST_GE(x.bucket_count(),
static_cast<size_type>(
std::floor(static_cast<float>(x.size()) / x.max_load_factor())));
BOOST_TEST_GT(test::detail::tracker.count_allocations, 0u);
bucket_count = x.bucket_count();
}
{
BOOST_TEST_GT(x.bucket_count(), 0u);
x.reserve(
2 * (static_cast<size_type>(
std::floor(static_cast<float>(x.size()) / x.max_load_factor()) +
std::floor(static_cast<float>(x.size()) * x.max_load_factor()))));
BOOST_TEST_GT(x.bucket_count(), bucket_count);
bucket_count = x.bucket_count();
BOOST_TEST_GT(bucket_count, 1u);
}
{
float const old_mlf = x.max_load_factor();
BOOST_TEST_GT(bucket_count, 4u);
x.reserve(bucket_count / 4);
BOOST_TEST_LT(x.bucket_count(), bucket_count);
x.max_load_factor(std::numeric_limits<float>::infinity());
x.reserve(0);
BOOST_TEST_GT(x.bucket_count(), 0u);
x.max_load_factor(old_mlf);
}
{
std::size_t const max_load =
static_cast<std::size_t>(static_cast<double>(x.max_load_factor()) *
static_cast<double>(x.bucket_count()));
while (x.size() < max_load) {
test::random_values<X> const t(max_load, generator);
typename test::random_values<X>::const_iterator pos = t.begin();
typename test::random_values<X>::const_iterator end = t.end();
for (; pos != end; ++pos) {
x.insert(*pos);
if (x.size() == max_load) {
break;
}
}
}
while (x.size() > max_load) {
x.erase(x.begin());
}
BOOST_TEST_EQ(x.size(), max_load);
bucket_count = x.bucket_count();
x.reserve(x.size());
BOOST_TEST_EQ(x.bucket_count(), bucket_count);
}
}
}
template <class X> void rehash_test1(X*, test::random_generator generator)
{
test::random_values<X> v(1000, generator);
@ -199,6 +493,18 @@ namespace rehash_tests {
test::allocator2<test::movable> >* test_map_ptr;
boost::unordered_multimap<int, int>* int_multimap_ptr;
boost::unordered_set<test::object, test::hash, test::equal_to,
test::allocator1<test::object> >* test_set_tracking;
boost::unordered_multiset<test::object, test::hash, test::equal_to,
test::allocator1<test::object> >* test_multiset_tracking;
boost::unordered_map<test::object, test::object, test::hash, test::equal_to,
test::allocator1<std::pair<test::object const, test::object> > >*
test_map_tracking;
boost::unordered_multimap<test::object, test::object, test::hash,
test::equal_to,
test::allocator1<std::pair<test::object const, test::object> > >*
test_multimap_tracking;
using test::default_generator;
using test::generate_collisions;
using test::limited_range;
@ -224,6 +530,12 @@ namespace rehash_tests {
UNORDERED_TEST(reserve_test2,
((int_set_ptr)(test_multiset_ptr)(test_map_ptr)(int_multimap_ptr))(
(default_generator)(generate_collisions)(limited_range)))
UNORDERED_TEST(rehash_empty_tracking,
((test_set_tracking)(test_multiset_tracking)(test_map_tracking)(test_multimap_tracking))(
(default_generator)(generate_collisions)(limited_range)))
UNORDERED_TEST(rehash_nonempty_tracking,
((test_set_tracking)(test_multiset_tracking)(test_map_tracking)(test_multimap_tracking))(
(default_generator)(generate_collisions)(limited_range)))
}
RUN_TESTS()

View File

@ -224,7 +224,7 @@ template <class C1, class C2> void scary_test()
typename C2::const_iterator cbegin(x.cbegin());
BOOST_TEST(cbegin == x.cend());
BOOST_ASSERT(x.bucket_count() > 0);
BOOST_TEST_EQ(x.bucket_count(), 0u);
typename C2::local_iterator lbegin(x.begin(0));
BOOST_TEST(lbegin == x.end(0));