diff options
| author | Stefan Boberg <[email protected]> | 2025-11-07 14:49:13 +0100 |
|---|---|---|
| committer | GitHub Enterprise <[email protected]> | 2025-11-07 14:49:13 +0100 |
| commit | 24e43a913f29ac3b314354e8ce5175f135bcc64f (patch) | |
| tree | ca442937ceeb63461012b33a4576e9835099f106 /thirdparty/robin-map/tests | |
| parent | get oplog attachments (#622) (diff) | |
| download | zen-24e43a913f29ac3b314354e8ce5175f135bcc64f.tar.xz zen-24e43a913f29ac3b314354e8ce5175f135bcc64f.zip | |
switch to xmake for package management (#611)
This change removes our dependency on vcpkg for package management, in favour of bringing some code in-tree in the `thirdparty` folder as well as using the xmake build-in package management feature. For the latter, all the package definitions are maintained in the zen repo itself, in the `repo` folder.
It should now also be easier to build the project as it will no longer depend on having the right version of vcpkg installed, which has been a common problem for new people coming in to the codebase. Now you should only need xmake to build.
* Bumps xmake requirement on github runners to 2.9.9 to resolve an issue where xmake on Windows invokes cmake with `v144` toolchain which does not exist
* BLAKE3 is now in-tree at `thirdparty/blake3`
* cpr is now in-tree at `thirdparty/cpr`
* cxxopts is now in-tree at `thirdparty/cxxopts`
* fmt is now in-tree at `thirdparty/fmt`
* robin-map is now in-tree at `thirdparty/robin-map`
* ryml is now in-tree at `thirdparty/ryml`
* sol2 is now in-tree at `thirdparty/sol2`
* spdlog is now in-tree at `thirdparty/spdlog`
* utfcpp is now in-tree at `thirdparty/utfcpp`
* xmake package repo definitions is in `repo`
* implemented support for sanitizers. ASAN is supported on windows, TSAN, UBSAN, MSAN etc are supported on Linux/MacOS though I have not yet tested it extensively on MacOS
* the zencore encryption implementation also now supports using mbedTLS which is used on MacOS, though for now we still use openssl on Linux
* crashpad
* bumps libcurl to 8.11.0 (from 8.8.0) which should address a rare build upload bug
Diffstat (limited to 'thirdparty/robin-map/tests')
| -rw-r--r-- | thirdparty/robin-map/tests/custom_allocator_tests.cpp | 138 | ||||
| -rw-r--r-- | thirdparty/robin-map/tests/main.cpp | 26 | ||||
| -rw-r--r-- | thirdparty/robin-map/tests/policy_tests.cpp | 97 | ||||
| -rw-r--r-- | thirdparty/robin-map/tests/robin_map_tests.cpp | 1462 | ||||
| -rw-r--r-- | thirdparty/robin-map/tests/robin_set_tests.cpp | 174 | ||||
| -rw-r--r-- | thirdparty/robin-map/tests/utils.h | 443 |
6 files changed, 2340 insertions, 0 deletions
diff --git a/thirdparty/robin-map/tests/custom_allocator_tests.cpp b/thirdparty/robin-map/tests/custom_allocator_tests.cpp new file mode 100644 index 000000000..4ee18ee6c --- /dev/null +++ b/thirdparty/robin-map/tests/custom_allocator_tests.cpp @@ -0,0 +1,138 @@ +/** + * MIT License + * + * Copyright (c) 2017 Thibaut Goetghebuer-Planchon <[email protected]> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include <tsl/robin_map.h> + +#include <boost/test/unit_test.hpp> +#include <cstddef> +#include <cstdlib> +#include <functional> +#include <limits> +#include <stdexcept> +#include <type_traits> +#include <utility> + +#include "utils.h" + +static std::size_t nb_custom_allocs = 0; + +template <typename T> +class custom_allocator { + public: + using value_type = T; + using pointer = T*; + using const_pointer = const T*; + using reference = T&; + using const_reference = const T&; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using propagate_on_container_move_assignment = std::true_type; + + template <typename U> + struct rebind { + using other = custom_allocator<U>; + }; + + custom_allocator() = default; + custom_allocator(const custom_allocator&) = default; + + template <typename U> + custom_allocator(const custom_allocator<U>&) {} + + pointer address(reference x) const noexcept { return &x; } + + const_pointer address(const_reference x) const noexcept { return &x; } + + pointer allocate(size_type n, const void* /*hint*/ = 0) { + nb_custom_allocs++; + + pointer ptr = static_cast<pointer>(std::malloc(n * sizeof(T))); + if (ptr == nullptr) { +#ifdef TSL_RH_NO_EXCEPTIONS + std::abort(); +#else + throw std::bad_alloc(); +#endif + } + + return ptr; + } + + void deallocate(T* p, size_type /*n*/) { std::free(p); } + + size_type max_size() const noexcept { + return std::numeric_limits<size_type>::max() / sizeof(value_type); + } + + template <typename U, typename... Args> + void construct(U* p, Args&&... args) { + ::new (static_cast<void*>(p)) U(std::forward<Args>(args)...); + } + + template <typename U> + void destroy(U* p) { + p->~U(); + } +}; + +template <class T, class U> +bool operator==(const custom_allocator<T>&, const custom_allocator<U>&) { + return true; +} + +template <class T, class U> +bool operator!=(const custom_allocator<T>&, const custom_allocator<U>&) { + return false; +} + +// TODO Avoid overloading new to check number of global new +// static std::size_t nb_global_new = 0; +// void* operator new(std::size_t sz) { +// nb_global_new++; +// return std::malloc(sz); +// } +// +// void operator delete(void* ptr) noexcept { +// std::free(ptr); +// } + +BOOST_AUTO_TEST_SUITE(test_custom_allocator) + +BOOST_AUTO_TEST_CASE(test_custom_allocator_1) { + // nb_global_new = 0; + nb_custom_allocs = 0; + + tsl::robin_map<int, int, std::hash<int>, std::equal_to<int>, + custom_allocator<std::pair<int, int>>> + map; + + const int nb_elements = 1000; + for (int i = 0; i < nb_elements; i++) { + map.insert({i, i * 2}); + } + + BOOST_CHECK_NE(nb_custom_allocs, 0); + // BOOST_CHECK_EQUAL(nb_global_new, 0); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/thirdparty/robin-map/tests/main.cpp b/thirdparty/robin-map/tests/main.cpp new file mode 100644 index 000000000..e33a020d8 --- /dev/null +++ b/thirdparty/robin-map/tests/main.cpp @@ -0,0 +1,26 @@ +/** + * MIT License + * + * Copyright (c) 2017 Thibaut Goetghebuer-Planchon <[email protected]> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#define BOOST_TEST_MODULE robin_map_tests + +#include <boost/test/unit_test.hpp>
\ No newline at end of file diff --git a/thirdparty/robin-map/tests/policy_tests.cpp b/thirdparty/robin-map/tests/policy_tests.cpp new file mode 100644 index 000000000..798a22d14 --- /dev/null +++ b/thirdparty/robin-map/tests/policy_tests.cpp @@ -0,0 +1,97 @@ +/** + * MIT License + * + * Copyright (c) 2017 Thibaut Goetghebuer-Planchon <[email protected]> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include <tsl/robin_growth_policy.h> + +#include <boost/mpl/list.hpp> +#include <boost/test/unit_test.hpp> +#include <cstddef> +#include <limits> +#include <ratio> +#include <stdexcept> + +#include "utils.h" + +BOOST_AUTO_TEST_SUITE(test_policy) + +using test_types = + boost::mpl::list<tsl::rh::power_of_two_growth_policy<2>, + tsl::rh::power_of_two_growth_policy<4>, + tsl::rh::prime_growth_policy, tsl::rh::mod_growth_policy<>, + tsl::rh::mod_growth_policy<std::ratio<7, 2>>>; + +BOOST_AUTO_TEST_CASE_TEMPLATE(test_policy, Policy, test_types) { + // Call next_bucket_count() on the policy until we reach its + // max_bucket_count() + std::size_t bucket_count = 0; + Policy policy(bucket_count); + + BOOST_CHECK_EQUAL(policy.bucket_for_hash(0), 0); + BOOST_CHECK_EQUAL(bucket_count, 0); + +#ifndef TSL_RH_NO_EXCEPTIONS + bool exception_thrown = false; + try { + while (true) { + const std::size_t previous_bucket_count = bucket_count; + + bucket_count = policy.next_bucket_count(); + policy = Policy(bucket_count); + + BOOST_CHECK_EQUAL(policy.bucket_for_hash(0), 0); + BOOST_CHECK(bucket_count > previous_bucket_count); + } + } catch (const std::length_error&) { + exception_thrown = true; + } + + BOOST_CHECK(exception_thrown); +#endif +} + +BOOST_AUTO_TEST_CASE_TEMPLATE(test_policy_min_bucket_count, Policy, + test_types) { + // Check policy when a bucket_count of 0 is asked. + std::size_t bucket_count = 0; + Policy policy(bucket_count); + + BOOST_CHECK_EQUAL(policy.bucket_for_hash(0), 0); +} + +BOOST_AUTO_TEST_CASE_TEMPLATE(test_policy_max_bucket_count, Policy, + test_types) { + // Test a bucket_count equals to the max_bucket_count limit and above + std::size_t bucket_count = 0; + Policy policy(bucket_count); + + bucket_count = policy.max_bucket_count(); + Policy policy2(bucket_count); + + bucket_count = std::numeric_limits<std::size_t>::max(); + TSL_RH_CHECK_THROW((Policy(bucket_count)), std::length_error); + + bucket_count = policy.max_bucket_count() + 1; + TSL_RH_CHECK_THROW((Policy(bucket_count)), std::length_error); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/thirdparty/robin-map/tests/robin_map_tests.cpp b/thirdparty/robin-map/tests/robin_map_tests.cpp new file mode 100644 index 000000000..68398e2a3 --- /dev/null +++ b/thirdparty/robin-map/tests/robin_map_tests.cpp @@ -0,0 +1,1462 @@ +/** + * MIT License + * + * Copyright (c) 2017 Thibaut Goetghebuer-Planchon <[email protected]> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include <tsl/robin_map.h> + +#include <boost/mpl/list.hpp> +#include <boost/test/unit_test.hpp> +#include <cstddef> +#include <cstdint> +#include <functional> +#include <iterator> +#include <memory> +#include <stdexcept> +#include <string> +#include <tuple> +#include <type_traits> +#include <utility> +#include <vector> + +#include "utils.h" + +BOOST_AUTO_TEST_SUITE(test_robin_map) + +using test_types = boost::mpl::list< + tsl::robin_map<std::int64_t, std::int64_t>, + tsl::robin_map<std::string, std::string>, + // Test with hash having a lot of collisions + tsl::robin_map<std::int64_t, std::int64_t, mod_hash<9>>, + tsl::robin_map<std::string, std::string, mod_hash<9>>, + tsl::robin_map<move_only_test, move_only_test, mod_hash<9>>, + tsl::robin_map<copy_only_test, copy_only_test, mod_hash<9>>, + tsl::robin_map<self_reference_member_test, self_reference_member_test, + mod_hash<9>>, + + // other GrowthPolicy + tsl::robin_map<move_only_test, move_only_test, mod_hash<9>, + std::equal_to<move_only_test>, + std::allocator<std::pair<move_only_test, move_only_test>>, + true, tsl::rh::power_of_two_growth_policy<4>>, + tsl::robin_pg_map<move_only_test, move_only_test, mod_hash<9>>, + tsl::robin_map<move_only_test, move_only_test, mod_hash<9>, + std::equal_to<move_only_test>, + std::allocator<std::pair<move_only_test, move_only_test>>, + false, tsl::rh::mod_growth_policy<>>, + + tsl::robin_map<copy_only_test, copy_only_test, mod_hash<9>, + std::equal_to<copy_only_test>, + std::allocator<std::pair<copy_only_test, copy_only_test>>, + false, tsl::rh::power_of_two_growth_policy<4>>, + tsl::robin_pg_map<copy_only_test, copy_only_test, mod_hash<9>>, + tsl::robin_map<copy_only_test, copy_only_test, mod_hash<9>, + std::equal_to<copy_only_test>, + std::allocator<std::pair<copy_only_test, copy_only_test>>, + true, tsl::rh::mod_growth_policy<>>>; + +/** + * insert + */ +BOOST_AUTO_TEST_CASE_TEMPLATE(test_insert, HMap, test_types) { + // insert x values, insert them again, check values + using key_t = typename HMap::key_type; + using value_t = typename HMap::mapped_type; + + const std::size_t nb_values = 1000; + HMap map(0); + BOOST_CHECK_EQUAL(map.bucket_count(), 0); + + typename HMap::iterator it; + bool inserted; + + for (std::size_t i = 0; i < nb_values; i++) { + std::tie(it, inserted) = + map.insert({utils::get_key<key_t>(i), utils::get_value<value_t>(i)}); + + BOOST_CHECK_EQUAL(it->first, utils::get_key<key_t>(i)); + BOOST_CHECK_EQUAL(it->second, utils::get_value<value_t>(i)); + BOOST_CHECK(inserted); + } + BOOST_CHECK_EQUAL(map.size(), nb_values); + + for (std::size_t i = 0; i < nb_values; i++) { + std::tie(it, inserted) = map.insert( + {utils::get_key<key_t>(i), utils::get_value<value_t>(i + 1)}); + + BOOST_CHECK_EQUAL(it->first, utils::get_key<key_t>(i)); + BOOST_CHECK_EQUAL(it->second, utils::get_value<value_t>(i)); + BOOST_CHECK(!inserted); + } + + for (std::size_t i = 0; i < nb_values; i++) { + it = map.find(utils::get_key<key_t>(i)); + + BOOST_CHECK_EQUAL(it->first, utils::get_key<key_t>(i)); + BOOST_CHECK_EQUAL(it->second, utils::get_value<value_t>(i)); + } +} + +BOOST_AUTO_TEST_CASE(test_range_insert) { + // create a vector<std::pair> of values to insert, insert part of them in the + // map, check values + const int nb_values = 1000; + std::vector<std::pair<int, int>> values_to_insert(nb_values); + for (int i = 0; i < nb_values; i++) { + values_to_insert[i] = std::make_pair(i, i + 1); + } + + tsl::robin_map<int, int> map = {{-1, 1}, {-2, 2}}; + map.insert(std::next(values_to_insert.begin(), 10), + values_to_insert.end() - 5); + + BOOST_CHECK_EQUAL(map.size(), 987); + + BOOST_CHECK_EQUAL(map.at(-1), 1); + BOOST_CHECK_EQUAL(map.at(-2), 2); + + for (int i = 10; i < nb_values - 5; i++) { + BOOST_CHECK_EQUAL(map.at(i), i + 1); + } +} + +BOOST_AUTO_TEST_CASE(test_rehash_0) { + tsl::robin_map<int, int, std::hash<int>, std::equal_to<int>, + std::allocator<std::pair<int, int>>, true> + map; + map.rehash(0); +} + +BOOST_AUTO_TEST_CASE(test_insert_with_hint) { + tsl::robin_map<int, int> map{{1, 0}, {2, 1}, {3, 2}}; + + // Wrong hint + BOOST_CHECK(map.insert(map.find(2), std::make_pair(3, 4)) == map.find(3)); + + // Good hint + BOOST_CHECK(map.insert(map.find(2), std::make_pair(2, 4)) == map.find(2)); + + // end() hint + BOOST_CHECK(map.insert(map.find(10), std::make_pair(2, 4)) == map.find(2)); + + BOOST_CHECK_EQUAL(map.size(), 3); + + // end() hint, new value + BOOST_CHECK_EQUAL(map.insert(map.find(10), std::make_pair(4, 3))->first, 4); + + // Wrong hint, new value + BOOST_CHECK_EQUAL(map.insert(map.find(2), std::make_pair(5, 4))->first, 5); + + BOOST_CHECK_EQUAL(map.size(), 5); +} + +/** + * emplace_hint + */ +BOOST_AUTO_TEST_CASE(test_emplace_hint) { + tsl::robin_map<int, int> map{{1, 0}, {2, 1}, {3, 2}}; + + // Wrong hint + BOOST_CHECK(map.emplace_hint(map.find(2), std::piecewise_construct, + std::forward_as_tuple(3), + std::forward_as_tuple(4)) == map.find(3)); + + // Good hint + BOOST_CHECK(map.emplace_hint(map.find(2), std::piecewise_construct, + std::forward_as_tuple(2), + std::forward_as_tuple(4)) == map.find(2)); + + // end() hint + BOOST_CHECK(map.emplace_hint(map.find(10), std::piecewise_construct, + std::forward_as_tuple(2), + std::forward_as_tuple(4)) == map.find(2)); + + BOOST_CHECK_EQUAL(map.size(), 3); + + // end() hint, new value + BOOST_CHECK_EQUAL( + map.emplace_hint(map.find(10), std::piecewise_construct, + std::forward_as_tuple(4), std::forward_as_tuple(3)) + ->first, + 4); + + // Wrong hint, new value + BOOST_CHECK_EQUAL( + map.emplace_hint(map.find(2), std::piecewise_construct, + std::forward_as_tuple(5), std::forward_as_tuple(4)) + ->first, + 5); + + BOOST_CHECK_EQUAL(map.size(), 5); +} + +/** + * emplace + */ +BOOST_AUTO_TEST_CASE(test_emplace) { + tsl::robin_map<std::int64_t, move_only_test> map; + tsl::robin_map<std::int64_t, move_only_test>::iterator it; + bool inserted; + + std::tie(it, inserted) = + map.emplace(std::piecewise_construct, std::forward_as_tuple(10), + std::forward_as_tuple(1)); + BOOST_CHECK_EQUAL(it->first, 10); + BOOST_CHECK_EQUAL(it->second, move_only_test(1)); + BOOST_CHECK(inserted); + + std::tie(it, inserted) = + map.emplace(std::piecewise_construct, std::forward_as_tuple(10), + std::forward_as_tuple(3)); + BOOST_CHECK_EQUAL(it->first, 10); + BOOST_CHECK_EQUAL(it->second, move_only_test(1)); + BOOST_CHECK(!inserted); +} + +/** + * try_emplace + */ +BOOST_AUTO_TEST_CASE(test_try_emplace) { + tsl::robin_map<std::int64_t, move_only_test> map; + tsl::robin_map<std::int64_t, move_only_test>::iterator it; + bool inserted; + + std::tie(it, inserted) = map.try_emplace(10, 1); + BOOST_CHECK_EQUAL(it->first, 10); + BOOST_CHECK_EQUAL(it->second, move_only_test(1)); + BOOST_CHECK(inserted); + + std::tie(it, inserted) = map.try_emplace(10, 3); + BOOST_CHECK_EQUAL(it->first, 10); + BOOST_CHECK_EQUAL(it->second, move_only_test(1)); + BOOST_CHECK(!inserted); +} + +BOOST_AUTO_TEST_CASE(test_try_emplace_2) { + // Insert x values with try_emplace, insert them again, check with find. + tsl::robin_map<std::string, move_only_test> map; + tsl::robin_map<std::string, move_only_test>::iterator it; + bool inserted; + + const std::size_t nb_values = 1000; + for (std::size_t i = 0; i < nb_values; i++) { + std::tie(it, inserted) = map.try_emplace(utils::get_key<std::string>(i), i); + + BOOST_CHECK_EQUAL(it->first, utils::get_key<std::string>(i)); + BOOST_CHECK_EQUAL(it->second, move_only_test(i)); + BOOST_CHECK(inserted); + } + BOOST_CHECK_EQUAL(map.size(), nb_values); + + for (std::size_t i = 0; i < nb_values; i++) { + std::tie(it, inserted) = + map.try_emplace(utils::get_key<std::string>(i), i + 1); + + BOOST_CHECK_EQUAL(it->first, utils::get_key<std::string>(i)); + BOOST_CHECK_EQUAL(it->second, move_only_test(i)); + BOOST_CHECK(!inserted); + } + + for (std::size_t i = 0; i < nb_values; i++) { + it = map.find(utils::get_key<std::string>(i)); + + BOOST_CHECK_EQUAL(it->first, utils::get_key<std::string>(i)); + BOOST_CHECK_EQUAL(it->second, move_only_test(i)); + } +} + +BOOST_AUTO_TEST_CASE(test_try_emplace_hint) { + tsl::robin_map<std::int64_t, move_only_test> map(0); + + // end() hint, new value + auto it = map.try_emplace(map.find(10), 10, 1); + BOOST_CHECK_EQUAL(it->first, 10); + BOOST_CHECK_EQUAL(it->second, move_only_test(1)); + + // Good hint + it = map.try_emplace(map.find(10), 10, 3); + BOOST_CHECK_EQUAL(it->first, 10); + BOOST_CHECK_EQUAL(it->second, move_only_test(1)); + + // Wrong hint, new value + it = map.try_emplace(map.find(10), 1, 3); + BOOST_CHECK_EQUAL(it->first, 1); + BOOST_CHECK_EQUAL(it->second, move_only_test(3)); +} + +/** + * insert_or_assign + */ +BOOST_AUTO_TEST_CASE(test_insert_or_assign) { + tsl::robin_map<std::int64_t, move_only_test> map; + tsl::robin_map<std::int64_t, move_only_test>::iterator it; + bool inserted; + + std::tie(it, inserted) = map.insert_or_assign(10, move_only_test(1)); + BOOST_CHECK_EQUAL(it->first, 10); + BOOST_CHECK_EQUAL(it->second, move_only_test(1)); + BOOST_CHECK(inserted); + + std::tie(it, inserted) = map.insert_or_assign(10, move_only_test(3)); + BOOST_CHECK_EQUAL(it->first, 10); + BOOST_CHECK_EQUAL(it->second, move_only_test(3)); + BOOST_CHECK(!inserted); +} + +BOOST_AUTO_TEST_CASE(test_insert_or_assign_hint) { + tsl::robin_map<std::int64_t, move_only_test> map(0); + + // end() hint, new value + auto it = map.insert_or_assign(map.find(10), 10, move_only_test(1)); + BOOST_CHECK_EQUAL(it->first, 10); + BOOST_CHECK_EQUAL(it->second, move_only_test(1)); + + // Good hint + it = map.insert_or_assign(map.find(10), 10, move_only_test(3)); + BOOST_CHECK_EQUAL(it->first, 10); + BOOST_CHECK_EQUAL(it->second, move_only_test(3)); + + // Bad hint, new value + it = map.insert_or_assign(map.find(10), 1, move_only_test(3)); + BOOST_CHECK_EQUAL(it->first, 1); + BOOST_CHECK_EQUAL(it->second, move_only_test(3)); +} + +/** + * erase + */ +BOOST_AUTO_TEST_CASE(test_range_erase_all) { + // insert x values, delete all with iterators + using HMap = tsl::robin_map<std::string, std::int64_t>; + + const std::size_t nb_values = 1000; + HMap map = utils::get_filled_hash_map<HMap>(nb_values); + + auto it = map.erase(map.begin(), map.end()); + BOOST_CHECK(it == map.end()); + BOOST_CHECK(map.empty()); +} + +BOOST_AUTO_TEST_CASE(test_range_erase) { + // insert x values, delete all with iterators except 10 first and 780 last + // values + using HMap = tsl::robin_map<std::string, std::int64_t>; + + const std::size_t nb_values = 1000; + HMap map = utils::get_filled_hash_map<HMap>(nb_values); + + auto it_first = std::next(map.begin(), 10); + auto it_last = std::next(map.begin(), 220); + + auto it = map.erase(it_first, it_last); + BOOST_CHECK_EQUAL(std::distance(it, map.end()), 780); + BOOST_CHECK_EQUAL(map.size(), 790); + BOOST_CHECK_EQUAL(std::distance(map.begin(), map.end()), 790); + + for (auto& val : map) { + BOOST_CHECK_EQUAL(map.count(val.first), 1); + } +} + +BOOST_AUTO_TEST_CASE_TEMPLATE(test_erase_loop, HMap, test_types) { + // insert x values, delete all one by one with iterator + std::size_t nb_values = 1000; + + HMap map = utils::get_filled_hash_map<HMap>(nb_values); + HMap map2 = utils::get_filled_hash_map<HMap>(nb_values); + + auto it = map.begin(); + // Use second map to check for key after delete as we may not copy the key + // with move-only types. + auto it2 = map2.begin(); + while (it != map.end()) { + it = map.erase(it); + --nb_values; + + BOOST_CHECK_EQUAL(map.count(it2->first), 0); + BOOST_CHECK_EQUAL(map.size(), nb_values); + ++it2; + } + + BOOST_CHECK(map.empty()); +} + +BOOST_AUTO_TEST_CASE_TEMPLATE(test_erase_loop_range, HMap, test_types) { + // insert x values, delete all five by five with iterators + const std::size_t hop = 5; + std::size_t nb_values = 1000; + + BOOST_REQUIRE_EQUAL(nb_values % hop, 0); + + HMap map = utils::get_filled_hash_map<HMap>(nb_values); + + auto it = map.begin(); + while (it != map.end()) { + it = map.erase(it, std::next(it, hop)); + nb_values -= hop; + + BOOST_CHECK_EQUAL(map.size(), nb_values); + } + + BOOST_CHECK(map.empty()); +} + +BOOST_AUTO_TEST_CASE_TEMPLATE(test_insert_erase_insert, HMap, test_types) { + // insert x/2 values, delete x/4 values, insert x/2 values, find each value + using key_t = typename HMap::key_type; + using value_t = typename HMap::mapped_type; + + const std::size_t nb_values = 2000; + HMap map(10); + typename HMap::iterator it; + bool inserted; + + // Insert nb_values/2 + for (std::size_t i = 0; i < nb_values / 2; i++) { + std::tie(it, inserted) = + map.insert({utils::get_key<key_t>(i), utils::get_value<value_t>(i)}); + + BOOST_CHECK_EQUAL(it->first, utils::get_key<key_t>(i)); + BOOST_CHECK_EQUAL(it->second, utils::get_value<value_t>(i)); + BOOST_CHECK(inserted); + } + BOOST_CHECK_EQUAL(map.size(), nb_values / 2); + + // Delete nb_values/4 + for (std::size_t i = 0; i < nb_values / 2; i++) { + if (i % 2 == 0) { + BOOST_CHECK_EQUAL(map.erase(utils::get_key<key_t>(i)), 1); + } + } + BOOST_CHECK_EQUAL(map.size(), nb_values / 4); + + // Insert nb_values/2 + for (std::size_t i = nb_values / 2; i < nb_values; i++) { + std::tie(it, inserted) = + map.insert({utils::get_key<key_t>(i), utils::get_value<value_t>(i)}); + + BOOST_CHECK_EQUAL(it->first, utils::get_key<key_t>(i)); + BOOST_CHECK_EQUAL(it->second, utils::get_value<value_t>(i)); + BOOST_CHECK(inserted); + } + BOOST_CHECK_EQUAL(map.size(), nb_values - nb_values / 4); + + // Find + for (std::size_t i = 0; i < nb_values; i++) { + if (i % 2 == 0 && i < nb_values / 2) { + it = map.find(utils::get_key<key_t>(i)); + + BOOST_CHECK(it == map.end()); + } else { + it = map.find(utils::get_key<key_t>(i)); + + BOOST_REQUIRE(it != map.end()); + BOOST_CHECK_EQUAL(it->first, utils::get_key<key_t>(i)); + BOOST_CHECK_EQUAL(it->second, utils::get_value<value_t>(i)); + } + } +} + +BOOST_AUTO_TEST_CASE(test_range_erase_same_iterators) { + // insert x values, test erase with same iterator as each parameter, check if + // returned mutable iterator is valid. + const std::size_t nb_values = 100; + auto map = + utils::get_filled_hash_map<tsl::robin_map<std::int64_t, std::int64_t>>( + nb_values); + + tsl::robin_map<std::int64_t, std::int64_t>::const_iterator it_const = + map.cbegin(); + std::advance(it_const, 10); + + tsl::robin_map<std::int64_t, std::int64_t>::iterator it_mutable = + map.erase(it_const, it_const); + BOOST_CHECK(it_const == it_mutable); + BOOST_CHECK(map.mutable_iterator(it_const) == it_mutable); + BOOST_CHECK_EQUAL(map.size(), 100); + + it_mutable.value() = -100; + BOOST_CHECK_EQUAL(it_const.value(), -100); +} + +/** + * max_load_factor + */ +BOOST_AUTO_TEST_CASE(test_max_load_factor_extreme_factors) { + tsl::robin_map<std::int64_t, std::int64_t> map; + + map.max_load_factor(0.0f); + BOOST_CHECK_GT(map.max_load_factor(), 0.0f); + + map.max_load_factor(10.0f); + BOOST_CHECK_LT(map.max_load_factor(), 1.0f); +} + +/** + * min_load_factor + */ +BOOST_AUTO_TEST_CASE(test_min_load_factor_extreme_factors) { + tsl::robin_map<std::int64_t, std::int64_t> map; + + BOOST_CHECK_EQUAL(map.min_load_factor(), 0.0f); + BOOST_CHECK_LT(map.min_load_factor(), map.max_load_factor()); + + map.min_load_factor(-10.0f); + BOOST_CHECK_EQUAL(map.min_load_factor(), 0.0f); + + map.min_load_factor(0.9f); + map.max_load_factor(0.1f); + + // max_load_factor should always be > min_load_factor. + // Factors should have been clamped. + BOOST_CHECK_LT(map.min_load_factor(), map.max_load_factor()); +} + +BOOST_AUTO_TEST_CASE(test_min_load_factor) { + // set min_load_factor to 0.15 and max_load_factor to 0.5. + // rehash to 100 buckets, insert 50 elements, erase until load_factor() < + // min_load_factor(), insert an element, check if map has shrinked. + const std::size_t nb_values = 50; + tsl::robin_map<std::int64_t, std::int64_t> map; + + map.min_load_factor(0.15f); + BOOST_CHECK_EQUAL(map.min_load_factor(), 0.15f); + + map.max_load_factor(0.5f); + BOOST_CHECK_EQUAL(map.max_load_factor(), 0.5f); + + map.rehash(nb_values * 2); + for (std::size_t i = 0; i < nb_values; i++) { + map.insert( + {utils::get_key<std::int64_t>(i), utils::get_value<std::int64_t>(i)}); + } + BOOST_CHECK_GT(map.load_factor(), map.min_load_factor()); + + while (map.load_factor() >= map.min_load_factor()) { + map.erase(map.begin()); + } + + // Shrink is done on insert. + map.insert({utils::get_key<std::int64_t>(map.bucket_count()), + utils::get_value<std::int64_t>(map.bucket_count())}); + BOOST_CHECK_GT(map.load_factor(), map.min_load_factor()); +} + +BOOST_AUTO_TEST_CASE(test_min_load_factor_range_erase) { + // set min_load_factor to 0.15 and max_load_factor to 0.5. + // rehash to 100 buckets, insert 50 elements, erase 40 with range erase, + // insert an element, check if map has shrinked. + const std::size_t nb_values = 50; + const std::size_t nb_values_erase = 40; + tsl::robin_map<std::int64_t, std::int64_t> map; + + map.min_load_factor(0.15f); + BOOST_CHECK_EQUAL(map.min_load_factor(), 0.15f); + + map.max_load_factor(0.5f); + BOOST_CHECK_EQUAL(map.max_load_factor(), 0.5f); + + map.rehash(nb_values * 2); + for (std::size_t i = 0; i < nb_values; i++) { + map.insert( + {utils::get_key<std::int64_t>(i), utils::get_value<std::int64_t>(i)}); + } + BOOST_CHECK_GT(map.load_factor(), map.min_load_factor()); + + map.erase(std::next(map.begin(), nb_values - nb_values_erase), map.end()); + + // Shrink is done on insert. + map.insert({utils::get_key<std::int64_t>(map.bucket_count()), + utils::get_value<std::int64_t>(map.bucket_count())}); + BOOST_CHECK_GT(map.load_factor(), map.min_load_factor()); + BOOST_CHECK_LT(map.bucket_count(), nb_values * 2); +} + +/** + * rehash + */ +BOOST_AUTO_TEST_CASE(test_rehash_empty) { + // test rehash(0), test find/erase/insert on map. + const std::size_t nb_values = 100; + auto map = + utils::get_filled_hash_map<tsl::robin_map<std::int64_t, std::int64_t>>( + nb_values); + + const std::size_t bucket_count = map.bucket_count(); + BOOST_CHECK(bucket_count >= nb_values); + + map.clear(); + BOOST_CHECK_EQUAL(map.bucket_count(), bucket_count); + BOOST_CHECK(map.empty()); + + map.rehash(0); + BOOST_CHECK_EQUAL(map.bucket_count(), 0); + BOOST_CHECK(map.empty()); + + BOOST_CHECK(map.find(1) == map.end()); + BOOST_CHECK_EQUAL(map.erase(1), 0); + BOOST_CHECK(map.insert({1, 10}).second); + BOOST_CHECK_EQUAL(map.at(1), 10); +} + +/** + * operator== and operator!= + */ +BOOST_AUTO_TEST_CASE(test_compare) { + const tsl::robin_map<std::string, std::int64_t> map1 = { + {"a", 1}, {"e", 5}, {"d", 4}, {"c", 3}, {"b", 2}}; + const tsl::robin_map<std::string, std::int64_t> map1_copy = { + {"e", 5}, {"c", 3}, {"b", 2}, {"a", 1}, {"d", 4}}; + const tsl::robin_map<std::string, std::int64_t> map2 = { + {"e", 5}, {"c", 3}, {"b", 2}, {"a", 1}, {"d", 4}, {"f", 6}}; + const tsl::robin_map<std::string, std::int64_t> map3 = { + {"e", 5}, {"c", 3}, {"b", 2}, {"a", 1}}; + const tsl::robin_map<std::string, std::int64_t> map4 = { + {"a", 1}, {"e", 5}, {"d", 4}, {"c", 3}, {"b", 26}}; + const tsl::robin_map<std::string, std::int64_t> map5 = { + {"a", 1}, {"e", 5}, {"d", 4}, {"c", 3}, {"z", 2}}; + + BOOST_CHECK(map1 == map1_copy); + BOOST_CHECK(map1_copy == map1); + + BOOST_CHECK(map1 != map2); + BOOST_CHECK(map2 != map1); + + BOOST_CHECK(map1 != map3); + BOOST_CHECK(map3 != map1); + + BOOST_CHECK(map1 != map4); + BOOST_CHECK(map4 != map1); + + BOOST_CHECK(map1 != map5); + BOOST_CHECK(map5 != map1); + + BOOST_CHECK(map2 != map3); + BOOST_CHECK(map3 != map2); + + BOOST_CHECK(map2 != map4); + BOOST_CHECK(map4 != map2); + + BOOST_CHECK(map2 != map5); + BOOST_CHECK(map5 != map2); + + BOOST_CHECK(map3 != map4); + BOOST_CHECK(map4 != map3); + + BOOST_CHECK(map3 != map5); + BOOST_CHECK(map5 != map3); + + BOOST_CHECK(map4 != map5); + BOOST_CHECK(map5 != map4); +} + +/** + * clear + */ +BOOST_AUTO_TEST_CASE(test_clear) { + // insert x values, clear map, test insert + using HMap = tsl::robin_map<std::int64_t, std::int64_t>; + + const std::size_t nb_values = 1000; + auto map = utils::get_filled_hash_map<HMap>(nb_values); + + map.clear(); + BOOST_CHECK_EQUAL(map.size(), 0); + BOOST_CHECK_EQUAL(std::distance(map.begin(), map.end()), 0); + + map.insert({5, -5}); + map.insert({{1, -1}, {2, -1}, {4, -4}, {3, -3}}); + + BOOST_CHECK(map == (HMap({{5, -5}, {1, -1}, {2, -1}, {4, -4}, {3, -3}}))); +} + +BOOST_AUTO_TEST_CASE(test_clear_with_min_load_factor) { + // insert x values, clear map, test insert + using HMap = tsl::robin_map<std::int64_t, std::int64_t>; + + const std::size_t nb_values = 1000; + auto map = utils::get_filled_hash_map<HMap>(nb_values); + map.min_load_factor(0.1f); + + map.clear(); + BOOST_CHECK_EQUAL(map.bucket_count(), 0); + BOOST_CHECK_EQUAL(map.size(), 0); + BOOST_CHECK_EQUAL(std::distance(map.begin(), map.end()), 0); + + map.insert({5, -5}); + map.insert({{1, -1}, {2, -1}, {4, -4}, {3, -3}}); + + BOOST_CHECK(map == (HMap({{5, -5}, {1, -1}, {2, -1}, {4, -4}, {3, -3}}))); +} + +/** + * iterator.value() + */ +BOOST_AUTO_TEST_CASE(test_modify_value_through_iterator) { + // insert x values, modify value of even keys with iterators, check values + const std::size_t nb_values = 100; + auto map = + utils::get_filled_hash_map<tsl::robin_map<std::int64_t, std::int64_t>>( + nb_values); + + for (auto it = map.begin(); it != map.end(); it++) { + if (it.key() % 2 == 0) { + it.value() = -1; + } + } + + for (auto& val : map) { + if (val.first % 2 == 0) { + BOOST_CHECK_EQUAL(val.second, -1); + } else { + BOOST_CHECK_NE(val.second, -1); + } + } +} + +BOOST_AUTO_TEST_CASE(test_modify_value_through_iterator_with_const_qualifier) { + tsl::robin_map<int, int> map = {{0, 1}}; + const auto it = map.begin(); + + BOOST_CHECK_EQUAL(it->second, 1); + it.value() += 10; + BOOST_CHECK_EQUAL(it->second, 11); +} + +/** + * constructor + */ + +BOOST_AUTO_TEST_CASE(test_extreme_bucket_count_value_construction) { + // std::bad_alloc or std::length_error will be thrown depending on the + // platform overcommit + TSL_RH_CHECK_THROW_EITHER( + (tsl::robin_map<int, int, std::hash<int>, std::equal_to<int>, + std::allocator<std::pair<int, int>>, false, + tsl::rh::power_of_two_growth_policy<2>>( + std::numeric_limits<std::size_t>::max())), + std::bad_alloc, std::length_error); + + TSL_RH_CHECK_THROW_EITHER( + (tsl::robin_map<int, int, std::hash<int>, std::equal_to<int>, + std::allocator<std::pair<int, int>>, false, + tsl::rh::power_of_two_growth_policy<2>>( + std::numeric_limits<std::size_t>::max() / 2 + 1)), + std::bad_alloc, std::length_error); + + TSL_RH_CHECK_THROW_EITHER( + (tsl::robin_map<int, int, std::hash<int>, std::equal_to<int>, + std::allocator<std::pair<int, int>>, false, + tsl::rh::prime_growth_policy>( + std::numeric_limits<std::size_t>::max())), + std::bad_alloc, std::length_error); + + TSL_RH_CHECK_THROW_EITHER( + (tsl::robin_map<int, int, std::hash<int>, std::equal_to<int>, + std::allocator<std::pair<int, int>>, false, + tsl::rh::prime_growth_policy>( + std::numeric_limits<std::size_t>::max() / 2)), + std::bad_alloc, std::length_error); + + TSL_RH_CHECK_THROW_EITHER( + (tsl::robin_map<int, int, std::hash<int>, std::equal_to<int>, + std::allocator<std::pair<int, int>>, false, + tsl::rh::mod_growth_policy<>>( + std::numeric_limits<std::size_t>::max())), + std::bad_alloc, std::length_error); +} + +BOOST_AUTO_TEST_CASE(test_range_construct) { + tsl::robin_map<int, int> map = {{2, 1}, {1, 0}, {3, 2}}; + + tsl::robin_map<int, int> map2(map.begin(), map.end()); + tsl::robin_map<int, int> map3(map.cbegin(), map.cend()); +} + +/** + * operator=(std::initializer_list) + */ +BOOST_AUTO_TEST_CASE(test_assign_operator) { + tsl::robin_map<std::int64_t, std::int64_t> map = {{0, 10}, {-2, 20}}; + BOOST_CHECK_EQUAL(map.size(), 2); + + map = {{1, 3}, {2, 4}}; + BOOST_CHECK_EQUAL(map.size(), 2); + BOOST_CHECK_EQUAL(map.at(1), 3); + BOOST_CHECK_EQUAL(map.at(2), 4); + BOOST_CHECK(map.find(0) == map.end()); + + map = {}; + BOOST_CHECK(map.empty()); +} + +/** + * move/copy constructor/operator + */ +BOOST_AUTO_TEST_CASE(test_move_constructor) { + // insert x values in map, move map into map_move with move constructor, check + // map and map_move, insert additional values in map_move, check map_move + using HMap = tsl::robin_map<std::string, move_only_test>; + + const std::size_t nb_values = 100; + HMap map = utils::get_filled_hash_map<HMap>(nb_values); + HMap map_move(std::move(map)); + + BOOST_CHECK(map_move == utils::get_filled_hash_map<HMap>(nb_values)); + BOOST_CHECK(map == (HMap())); + + for (std::size_t i = nb_values; i < nb_values * 2; i++) { + map_move.insert( + {utils::get_key<std::string>(i), utils::get_value<move_only_test>(i)}); + } + + BOOST_CHECK_EQUAL(map_move.size(), nb_values * 2); + BOOST_CHECK(map_move == utils::get_filled_hash_map<HMap>(nb_values * 2)); +} + +BOOST_AUTO_TEST_CASE(test_move_constructor_empty) { + tsl::robin_map<std::string, move_only_test> map(0); + tsl::robin_map<std::string, move_only_test> map_move(std::move(map)); + + BOOST_CHECK(map.empty()); + BOOST_CHECK(map_move.empty()); + + BOOST_CHECK(map.find("") == map.end()); + BOOST_CHECK(map_move.find("") == map_move.end()); +} + +BOOST_AUTO_TEST_CASE(test_move_operator) { + // insert x values in map, move map into map_move with move operator, check + // map and map_move, insert additional values in map_move, check map_move + using HMap = tsl::robin_map<std::string, move_only_test>; + + const std::size_t nb_values = 100; + HMap map = utils::get_filled_hash_map<HMap>(nb_values); + HMap map_move = utils::get_filled_hash_map<HMap>(1); + map_move = std::move(map); + + BOOST_CHECK(map_move == utils::get_filled_hash_map<HMap>(nb_values)); + BOOST_CHECK(map == (HMap())); + + for (std::size_t i = nb_values; i < nb_values * 2; i++) { + map_move.insert( + {utils::get_key<std::string>(i), utils::get_value<move_only_test>(i)}); + } + + BOOST_CHECK_EQUAL(map_move.size(), nb_values * 2); + BOOST_CHECK(map_move == utils::get_filled_hash_map<HMap>(nb_values * 2)); +} + +BOOST_AUTO_TEST_CASE(test_move_operator_empty) { + tsl::robin_map<std::string, move_only_test> map(0); + tsl::robin_map<std::string, move_only_test> map_move; + map_move = (std::move(map)); + + BOOST_CHECK(map.empty()); + BOOST_CHECK(map_move.empty()); + + BOOST_CHECK(map.find("") == map.end()); + BOOST_CHECK(map_move.find("") == map_move.end()); +} + +BOOST_AUTO_TEST_CASE(test_reassign_moved_object_move_constructor) { + using HMap = tsl::robin_map<std::string, std::string>; + + HMap map = {{"Key1", "Value1"}, {"Key2", "Value2"}, {"Key3", "Value3"}}; + HMap map_move(std::move(map)); + + BOOST_CHECK_EQUAL(map_move.size(), 3); + BOOST_CHECK_EQUAL(map.size(), 0); + + map = {{"Key4", "Value4"}, {"Key5", "Value5"}}; + BOOST_CHECK(map == (HMap({{"Key4", "Value4"}, {"Key5", "Value5"}}))); +} + +BOOST_AUTO_TEST_CASE(test_reassign_moved_object_move_operator) { + using HMap = tsl::robin_map<std::string, std::string>; + + HMap map = {{"Key1", "Value1"}, {"Key2", "Value2"}, {"Key3", "Value3"}}; + HMap map_move = std::move(map); + + BOOST_CHECK_EQUAL(map_move.size(), 3); + BOOST_CHECK_EQUAL(map.size(), 0); + + map = {{"Key4", "Value4"}, {"Key5", "Value5"}}; + BOOST_CHECK(map == (HMap({{"Key4", "Value4"}, {"Key5", "Value5"}}))); +} + +BOOST_AUTO_TEST_CASE(test_use_after_move_constructor) { + using HMap = tsl::robin_map<std::string, move_only_test>; + + const std::size_t nb_values = 100; + HMap map = utils::get_filled_hash_map<HMap>(nb_values); + HMap map_move(std::move(map)); + + BOOST_CHECK(map == (HMap())); + BOOST_CHECK_EQUAL(map.size(), 0); + BOOST_CHECK_EQUAL(map.bucket_count(), 0); + BOOST_CHECK_EQUAL(map.erase("a"), 0); + BOOST_CHECK(map.find("a") == map.end()); + + for (std::size_t i = 0; i < nb_values; i++) { + map.insert( + {utils::get_key<std::string>(i), utils::get_value<move_only_test>(i)}); + } + + BOOST_CHECK_EQUAL(map.size(), nb_values); + BOOST_CHECK(map == map_move); +} + +BOOST_AUTO_TEST_CASE(test_use_after_move_operator) { + using HMap = tsl::robin_map<std::string, move_only_test>; + + const std::size_t nb_values = 100; + HMap map = utils::get_filled_hash_map<HMap>(nb_values); + HMap map_move(0); + map_move = std::move(map); + + BOOST_CHECK(map == (HMap())); + BOOST_CHECK_EQUAL(map.size(), 0); + BOOST_CHECK_EQUAL(map.bucket_count(), 0); + BOOST_CHECK_EQUAL(map.erase("a"), 0); + BOOST_CHECK(map.find("a") == map.end()); + + for (std::size_t i = 0; i < nb_values; i++) { + map.insert( + {utils::get_key<std::string>(i), utils::get_value<move_only_test>(i)}); + } + + BOOST_CHECK_EQUAL(map.size(), nb_values); + BOOST_CHECK(map == map_move); +} + +BOOST_AUTO_TEST_CASE(test_copy_constructor_and_operator) { + using HMap = tsl::robin_map<std::string, std::string, mod_hash<9>>; + + const std::size_t nb_values = 100; + HMap map = utils::get_filled_hash_map<HMap>(nb_values); + + HMap map_copy = map; + HMap map_copy2(map); + HMap map_copy3 = utils::get_filled_hash_map<HMap>(1); + map_copy3 = map; + + BOOST_CHECK(map == map_copy); + map.clear(); + + BOOST_CHECK(map_copy == map_copy2); + BOOST_CHECK(map_copy == map_copy3); +} + +BOOST_AUTO_TEST_CASE(test_copy_constructor_empty) { + tsl::robin_map<std::string, int> map(0); + tsl::robin_map<std::string, int> map_copy(map); + + BOOST_CHECK(map.empty()); + BOOST_CHECK(map_copy.empty()); + + BOOST_CHECK(map.find("") == map.end()); + BOOST_CHECK(map_copy.find("") == map_copy.end()); +} + +BOOST_AUTO_TEST_CASE(test_copy_operator_empty) { + tsl::robin_map<std::string, int> map(0); + tsl::robin_map<std::string, int> map_copy(16); + map_copy = map; + + BOOST_CHECK(map.empty()); + BOOST_CHECK(map_copy.empty()); + + BOOST_CHECK(map.find("") == map.end()); + BOOST_CHECK(map_copy.find("") == map_copy.end()); +} + +/** + * at + */ +BOOST_AUTO_TEST_CASE(test_at) { + // insert x values, use at for known and unknown values. + const tsl::robin_map<std::int64_t, std::int64_t> map = {{0, 10}, {-2, 20}}; + + BOOST_CHECK_EQUAL(map.at(0), 10); + BOOST_CHECK_EQUAL(map.at(-2), 20); + TSL_RH_CHECK_THROW(map.at(1), std::out_of_range); +} + +/** + * contains + */ +BOOST_AUTO_TEST_CASE(test_contains) { + tsl::robin_map<std::int64_t, std::int64_t> map = {{0, 10}, {-2, 20}}; + + BOOST_CHECK(map.contains(0)); + BOOST_CHECK(map.contains(-2)); + BOOST_CHECK(!map.contains(-3)); +} + +/** + * equal_range + */ +BOOST_AUTO_TEST_CASE(test_equal_range) { + const tsl::robin_map<std::int64_t, std::int64_t> map = {{0, 10}, {-2, 20}}; + + auto it_pair = map.equal_range(0); + BOOST_REQUIRE_EQUAL(std::distance(it_pair.first, it_pair.second), 1); + BOOST_CHECK_EQUAL(it_pair.first->second, 10); + + it_pair = map.equal_range(1); + BOOST_CHECK(it_pair.first == it_pair.second); + BOOST_CHECK(it_pair.first == map.end()); +} + +/** + * operator[] + */ +BOOST_AUTO_TEST_CASE(test_access_operator) { + // insert x values, use at for known and unknown values. + tsl::robin_map<std::int64_t, std::int64_t> map = {{0, 10}, {-2, 20}}; + + BOOST_CHECK_EQUAL(map[0], 10); + BOOST_CHECK_EQUAL(map[-2], 20); + BOOST_CHECK_EQUAL(map[2], std::int64_t()); + + BOOST_CHECK_EQUAL(map.size(), 3); +} + +/** + * swap + */ +BOOST_AUTO_TEST_CASE(test_swap) { + tsl::robin_map<std::int64_t, std::int64_t> map = {{1, 10}, {8, 80}, {3, 30}}; + tsl::robin_map<std::int64_t, std::int64_t> map2 = {{4, 40}, {5, 50}}; + + using std::swap; + swap(map, map2); + + BOOST_CHECK(map == + (tsl::robin_map<std::int64_t, std::int64_t>{{4, 40}, {5, 50}})); + BOOST_CHECK(map2 == (tsl::robin_map<std::int64_t, std::int64_t>{ + {1, 10}, {8, 80}, {3, 30}})); + + map.insert({6, 60}); + map2.insert({4, 40}); + + BOOST_CHECK(map == (tsl::robin_map<std::int64_t, std::int64_t>{ + {4, 40}, {5, 50}, {6, 60}})); + BOOST_CHECK(map2 == (tsl::robin_map<std::int64_t, std::int64_t>{ + {1, 10}, {8, 80}, {3, 30}, {4, 40}})); +} + +BOOST_AUTO_TEST_CASE(test_swap_empty) { + tsl::robin_map<std::int64_t, std::int64_t> map = {{1, 10}, {8, 80}, {3, 30}}; + tsl::robin_map<std::int64_t, std::int64_t> map2; + + using std::swap; + swap(map, map2); + + BOOST_CHECK(map == (tsl::robin_map<std::int64_t, std::int64_t>{})); + BOOST_CHECK(map2 == (tsl::robin_map<std::int64_t, std::int64_t>{ + {1, 10}, {8, 80}, {3, 30}})); + + map.insert({6, 60}); + map2.insert({4, 40}); + + BOOST_CHECK(map == (tsl::robin_map<std::int64_t, std::int64_t>{{6, 60}})); + BOOST_CHECK(map2 == (tsl::robin_map<std::int64_t, std::int64_t>{ + {1, 10}, {8, 80}, {3, 30}, {4, 40}})); +} + +/** + * serialize and deserialize + */ +BOOST_AUTO_TEST_CASE(test_serialize_desearialize_empty) { + // serialize empty map; deserialize in new map; check equal. + // for deserialization, test it with and without hash compatibility. + const tsl::robin_map<std::string, move_only_test> empty_map(0); + + serializer serial; + empty_map.serialize(serial); + + deserializer dserial(serial.str()); + auto empty_map_deserialized = decltype(empty_map)::deserialize(dserial, true); + BOOST_CHECK(empty_map_deserialized == empty_map); + + deserializer dserial2(serial.str()); + empty_map_deserialized = decltype(empty_map)::deserialize(dserial2, false); + BOOST_CHECK(empty_map_deserialized == empty_map); +} + +BOOST_AUTO_TEST_CASE(test_serialize_desearialize) { + // insert x values; delete some values; serialize map; deserialize in new map; + // check equal. for deserialization, test it with and without hash + // compatibility. + const std::size_t nb_values = 1000; + + tsl::robin_map<std::int32_t, move_only_test> map; + for (std::size_t i = 0; i < nb_values + 40; i++) { + map.insert( + {utils::get_key<std::int32_t>(i), utils::get_value<move_only_test>(i)}); + } + + for (std::size_t i = nb_values; i < nb_values + 40; i++) { + map.erase(utils::get_key<std::int32_t>(i)); + } + BOOST_CHECK_EQUAL(map.size(), nb_values); + + serializer serial; + map.serialize(serial); + + deserializer dserial(serial.str()); + auto map_deserialized = decltype(map)::deserialize(dserial, true); + BOOST_CHECK(map == map_deserialized); + + deserializer dserial2(serial.str()); + map_deserialized = decltype(map)::deserialize(dserial2, false); + BOOST_CHECK(map_deserialized == map); + + // Deserializing a map with StoreHash=true from a map serialized with + // StoreHash=false with hash_compatible=true should throw an exception. + deserializer dserial3(serial.str()); + TSL_RH_CHECK_THROW( + (tsl::robin_map<std::int32_t, move_only_test, std::hash<std::int32_t>, + std::equal_to<std::int32_t>, + std::allocator<std::pair<std::int32_t, move_only_test>>, + true>::deserialize(dserial3, true)), + std::runtime_error); +} + +BOOST_AUTO_TEST_CASE(test_serialize_desearialize_with_store_hash) { + // insert x values; delete some values; serialize map; deserialize in new map; + // check equal. for deserialization, test it with and without hash + // compatibility. + const std::size_t nb_values = 1000; + + tsl::robin_map<std::int32_t, move_only_test, std::hash<std::int32_t>, + std::equal_to<std::int32_t>, + std::allocator<std::pair<std::int32_t, move_only_test>>, true> + map; + for (std::size_t i = 0; i < nb_values + 40; i++) { + map.insert( + {utils::get_key<std::int32_t>(i), utils::get_value<move_only_test>(i)}); + } + + for (std::size_t i = nb_values; i < nb_values + 40; i++) { + map.erase(utils::get_key<std::int32_t>(i)); + } + BOOST_CHECK_EQUAL(map.size(), nb_values); + + serializer serial; + map.serialize(serial); + + deserializer dserial(serial.str()); + auto map_deserialized = decltype(map)::deserialize(dserial, true); + BOOST_CHECK(map == map_deserialized); + + deserializer dserial2(serial.str()); + map_deserialized = decltype(map)::deserialize(dserial2, false); + BOOST_CHECK(map_deserialized == map); + + // Deserializing a map with StoreHash=false from a map serialized with + // StoreHash=true with hash_compatible=true should throw an exception. + deserializer dserial3(serial.str()); + TSL_RH_CHECK_THROW((tsl::robin_map<std::int32_t, move_only_test>::deserialize( + dserial3, true)), + std::runtime_error); +} + +BOOST_AUTO_TEST_CASE(test_serialize_desearialize_with_different_hash) { + // insert x values; serialize map; deserialize in new map which has a + // different hash; check equal + struct hash_str_diff { + std::size_t operator()(const std::string& str) const { + return std::hash<std::string>()(str) + 123; + } + }; + + const std::size_t nb_values = 1000; + + tsl::robin_map<std::string, move_only_test> map; + for (std::size_t i = 0; i < nb_values; i++) { + map.insert( + {utils::get_key<std::string>(i), utils::get_value<move_only_test>(i)}); + } + BOOST_CHECK_EQUAL(map.size(), nb_values); + + serializer serial; + map.serialize(serial); + + deserializer dserial(serial.str()); + auto map_deserialized = + tsl::robin_map<std::string, move_only_test, hash_str_diff>::deserialize( + dserial, false); + + BOOST_CHECK_EQUAL(map_deserialized.size(), map.size()); + for (const auto& val : map) { + BOOST_CHECK(map_deserialized.find(val.first) != map_deserialized.end()); + } +} + +/** + * KeyEqual + */ +BOOST_AUTO_TEST_CASE(test_key_equal) { + // Use a KeyEqual and Hash where any odd unsigned number 'x' is equal to + // 'x-1'. Make sure that KeyEqual is called (and not ==). + struct hash { + std::size_t operator()(std::uint64_t v) const { + if (v % 2u == 1u) { + return std::hash<std::uint64_t>()(v - 1); + } else { + return std::hash<std::uint64_t>()(v); + } + } + }; + + struct key_equal { + bool operator()(std::uint64_t lhs, std::uint64_t rhs) const { + if (lhs % 2u == 1u) { + lhs--; + } + + if (rhs % 2u == 1u) { + rhs--; + } + + return lhs == rhs; + } + }; + + tsl::robin_map<std::uint64_t, std::uint64_t, hash, key_equal> map; + BOOST_CHECK(map.insert({2, 10}).second); + BOOST_CHECK_EQUAL(map.at(2), 10); + BOOST_CHECK_EQUAL(map.at(3), 10); + BOOST_CHECK(!map.insert({3, 10}).second); + + BOOST_CHECK_EQUAL(map.size(), 1); +} + +/** + * other + */ +BOOST_AUTO_TEST_CASE(test_heterogeneous_lookups) { + struct hash_ptr { + std::size_t operator()(const std::unique_ptr<int>& p) const { + return std::hash<std::uintptr_t>()( + reinterpret_cast<std::uintptr_t>(p.get())); + } + + std::size_t operator()(std::uintptr_t p) const { + return std::hash<std::uintptr_t>()(p); + } + + std::size_t operator()(const int* const& p) const { + return std::hash<std::uintptr_t>()(reinterpret_cast<std::uintptr_t>(p)); + } + }; + + struct equal_to_ptr { + using is_transparent = std::true_type; + + bool operator()(const std::unique_ptr<int>& p1, + const std::unique_ptr<int>& p2) const { + return p1 == p2; + } + + bool operator()(const std::unique_ptr<int>& p1, std::uintptr_t p2) const { + return reinterpret_cast<std::uintptr_t>(p1.get()) == p2; + } + + bool operator()(std::uintptr_t p1, const std::unique_ptr<int>& p2) const { + return p1 == reinterpret_cast<std::uintptr_t>(p2.get()); + } + + bool operator()(const std::unique_ptr<int>& p1, + const int* const& p2) const { + return p1.get() == p2; + } + + bool operator()(const int* const& p1, + const std::unique_ptr<int>& p2) const { + return p1 == p2.get(); + } + }; + + std::unique_ptr<int> ptr1(new int(1)); + std::unique_ptr<int> ptr2(new int(2)); + std::unique_ptr<int> ptr3(new int(3)); + int other = -1; + + const std::uintptr_t addr1 = reinterpret_cast<std::uintptr_t>(ptr1.get()); + const int* const addr2 = ptr2.get(); + const int* const addr_unknown = &other; + + tsl::robin_map<std::unique_ptr<int>, int, hash_ptr, equal_to_ptr> map; + map.insert({std::move(ptr1), 4}); + map.insert({std::move(ptr2), 5}); + map.insert({std::move(ptr3), 6}); + + BOOST_CHECK_EQUAL(map.size(), 3); + + BOOST_CHECK_EQUAL(map.at(addr1), 4); + BOOST_CHECK_EQUAL(map.at(addr2), 5); + TSL_RH_CHECK_THROW(map.at(addr_unknown), std::out_of_range); + + BOOST_REQUIRE(map.find(addr1) != map.end()); + BOOST_CHECK_EQUAL(*map.find(addr1)->first, 1); + + BOOST_REQUIRE(map.find(addr2) != map.end()); + BOOST_CHECK_EQUAL(*map.find(addr2)->first, 2); + + BOOST_CHECK(map.find(addr_unknown) == map.end()); + + BOOST_CHECK_EQUAL(map.count(addr1), 1); + BOOST_CHECK_EQUAL(map.count(addr2), 1); + BOOST_CHECK_EQUAL(map.count(addr_unknown), 0); + + BOOST_CHECK_EQUAL(map.erase(addr1), 1); + BOOST_CHECK_EQUAL(map.erase(addr2), 1); + BOOST_CHECK_EQUAL(map.erase(addr_unknown), 0); + + BOOST_CHECK_EQUAL(map.size(), 1); +} + +/** + * Various operations on empty map + */ +BOOST_AUTO_TEST_CASE(test_empty_map) { + tsl::robin_map<std::string, int> map(0); + + BOOST_CHECK_EQUAL(map.bucket_count(), 0); + BOOST_CHECK_EQUAL(map.size(), 0); + BOOST_CHECK_EQUAL(map.load_factor(), 0); + BOOST_CHECK(map.empty()); + + BOOST_CHECK(map.begin() == map.end()); + BOOST_CHECK(map.begin() == map.cend()); + BOOST_CHECK(map.cbegin() == map.cend()); + + BOOST_CHECK(map.find("") == map.end()); + BOOST_CHECK(map.find("test") == map.end()); + + BOOST_CHECK_EQUAL(map.count(""), 0); + BOOST_CHECK_EQUAL(map.count("test"), 0); + + BOOST_CHECK(!map.contains("")); + BOOST_CHECK(!map.contains("test")); + + TSL_RH_CHECK_THROW(map.at(""), std::out_of_range); + TSL_RH_CHECK_THROW(map.at("test"), std::out_of_range); + + auto range = map.equal_range("test"); + BOOST_CHECK(range.first == range.second); + + BOOST_CHECK_EQUAL(map.erase("test"), 0); + BOOST_CHECK(map.erase(map.begin(), map.end()) == map.end()); + + BOOST_CHECK_EQUAL(map["new value"], int{}); +} + +/** + * Test precalculated hash + */ +BOOST_AUTO_TEST_CASE(test_precalculated_hash) { + tsl::robin_map<int, int, identity_hash<int>> map = { + {1, -1}, {2, -2}, {3, -3}, {4, -4}, {5, -5}, {6, -6}}; + const tsl::robin_map<int, int, identity_hash<int>> map_const = map; + + /** + * find + */ + BOOST_REQUIRE(map.find(3, map.hash_function()(3)) != map.end()); + BOOST_CHECK_EQUAL(map.find(3, map.hash_function()(3))->second, -3); + + BOOST_REQUIRE(map_const.find(3, map_const.hash_function()(3)) != + map_const.end()); + BOOST_CHECK_EQUAL(map_const.find(3, map_const.hash_function()(3))->second, + -3); + + BOOST_REQUIRE_NE(map.hash_function()(2), map.hash_function()(3)); + BOOST_CHECK(map.find(3, map.hash_function()(2)) == map.end()); + + /** + * at + */ + BOOST_CHECK_EQUAL(map.at(3, map.hash_function()(3)), -3); + BOOST_CHECK_EQUAL(map_const.at(3, map_const.hash_function()(3)), -3); + + BOOST_REQUIRE_NE(map.hash_function()(2), map.hash_function()(3)); + TSL_RH_CHECK_THROW(map.at(3, map.hash_function()(2)), std::out_of_range); + + /** + * contains + */ + BOOST_CHECK(map.contains(3, map.hash_function()(3))); + BOOST_CHECK(map_const.contains(3, map_const.hash_function()(3))); + + BOOST_REQUIRE_NE(map.hash_function()(2), map.hash_function()(3)); + BOOST_CHECK(!map.contains(3, map.hash_function()(2))); + + /** + * count + */ + BOOST_CHECK_EQUAL(map.count(3, map.hash_function()(3)), 1); + BOOST_CHECK_EQUAL(map_const.count(3, map_const.hash_function()(3)), 1); + + BOOST_REQUIRE_NE(map.hash_function()(2), map.hash_function()(3)); + BOOST_CHECK_EQUAL(map.count(3, map.hash_function()(2)), 0); + + /** + * equal_range + */ + auto it_range = map.equal_range(3, map.hash_function()(3)); + BOOST_REQUIRE_EQUAL(std::distance(it_range.first, it_range.second), 1); + BOOST_CHECK_EQUAL(it_range.first->second, -3); + + auto it_range_const = map_const.equal_range(3, map_const.hash_function()(3)); + BOOST_REQUIRE_EQUAL( + std::distance(it_range_const.first, it_range_const.second), 1); + BOOST_CHECK_EQUAL(it_range_const.first->second, -3); + + it_range = map.equal_range(3, map.hash_function()(2)); + BOOST_REQUIRE_NE(map.hash_function()(2), map.hash_function()(3)); + BOOST_CHECK_EQUAL(std::distance(it_range.first, it_range.second), 0); + + /** + * erase + */ + BOOST_CHECK_EQUAL(map.erase(3, map.hash_function()(3)), 1); + + BOOST_REQUIRE_NE(map.hash_function()(2), map.hash_function()(4)); + BOOST_CHECK_EQUAL(map.erase(4, map.hash_function()(2)), 0); +} + +BOOST_AUTO_TEST_CASE(test_erase_fast) { + using Map = tsl::robin_map<int, int>; + Map map; + map.emplace(4, 5); + auto it = map.find(4); + BOOST_CHECK(it != map.end()); + map.erase_fast(it); + BOOST_CHECK(map.size() == 0); +} + + +BOOST_AUTO_TEST_SUITE_END() diff --git a/thirdparty/robin-map/tests/robin_set_tests.cpp b/thirdparty/robin-map/tests/robin_set_tests.cpp new file mode 100644 index 000000000..e68b5140c --- /dev/null +++ b/thirdparty/robin-map/tests/robin_set_tests.cpp @@ -0,0 +1,174 @@ +/** + * MIT License + * + * Copyright (c) 2017 Thibaut Goetghebuer-Planchon <[email protected]> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include <tsl/robin_set.h> + +#include <boost/mpl/list.hpp> +#include <boost/test/unit_test.hpp> +#include <cstddef> +#include <cstdint> +#include <functional> +#include <memory> +#include <string> +#include <tuple> +#include <utility> + +#include "utils.h" + +BOOST_AUTO_TEST_SUITE(test_robin_set) + +using test_types = + boost::mpl::list<tsl::robin_set<std::int64_t>, tsl::robin_set<std::string>, + tsl::robin_set<self_reference_member_test>, + tsl::robin_set<move_only_test>, + tsl::robin_pg_set<self_reference_member_test>, + tsl::robin_set<move_only_test, std::hash<move_only_test>, + std::equal_to<move_only_test>, + std::allocator<move_only_test>, true, + tsl::rh::prime_growth_policy>, + tsl::robin_set<self_reference_member_test, + std::hash<self_reference_member_test>, + std::equal_to<self_reference_member_test>, + std::allocator<self_reference_member_test>, + true, tsl::rh::mod_growth_policy<>>, + tsl::robin_set<move_only_test, std::hash<move_only_test>, + std::equal_to<move_only_test>, + std::allocator<move_only_test>, false, + tsl::rh::mod_growth_policy<>>>; + +BOOST_AUTO_TEST_CASE_TEMPLATE(test_insert, HSet, test_types) { + // insert x values, insert them again, check values + using key_t = typename HSet::key_type; + + const std::size_t nb_values = 1000; + HSet set; + typename HSet::iterator it; + bool inserted; + + for (std::size_t i = 0; i < nb_values; i++) { + std::tie(it, inserted) = set.insert(utils::get_key<key_t>(i)); + + BOOST_CHECK_EQUAL(*it, utils::get_key<key_t>(i)); + BOOST_CHECK(inserted); + } + BOOST_CHECK_EQUAL(set.size(), nb_values); + + for (std::size_t i = 0; i < nb_values; i++) { + std::tie(it, inserted) = set.insert(utils::get_key<key_t>(i)); + + BOOST_CHECK_EQUAL(*it, utils::get_key<key_t>(i)); + BOOST_CHECK(!inserted); + } + + for (std::size_t i = 0; i < nb_values; i++) { + it = set.find(utils::get_key<key_t>(i)); + + BOOST_CHECK_EQUAL(*it, utils::get_key<key_t>(i)); + } +} + +BOOST_AUTO_TEST_CASE(test_compare) { + const tsl::robin_set<std::string> set1 = {"a", "e", "d", "c", "b"}; + const tsl::robin_set<std::string> set1_copy = {"e", "c", "b", "a", "d"}; + const tsl::robin_set<std::string> set2 = {"e", "c", "b", "a", "d", "f"}; + const tsl::robin_set<std::string> set3 = {"e", "c", "b", "a"}; + const tsl::robin_set<std::string> set4 = {"a", "e", "d", "c", "z"}; + + BOOST_CHECK(set1 == set1_copy); + BOOST_CHECK(set1_copy == set1); + + BOOST_CHECK(set1 != set2); + BOOST_CHECK(set2 != set1); + + BOOST_CHECK(set1 != set3); + BOOST_CHECK(set3 != set1); + + BOOST_CHECK(set1 != set4); + BOOST_CHECK(set4 != set1); + + BOOST_CHECK(set2 != set3); + BOOST_CHECK(set3 != set2); + + BOOST_CHECK(set2 != set4); + BOOST_CHECK(set4 != set2); + + BOOST_CHECK(set3 != set4); + BOOST_CHECK(set4 != set3); +} + +BOOST_AUTO_TEST_CASE(test_insert_pointer) { + // Test added mainly to be sure that the code compiles with MSVC due to a bug + // in the compiler. See robin_hash::insert_value_impl for details. + std::string value; + std::string* value_ptr = &value; + + tsl::robin_set<std::string*> set; + set.insert(value_ptr); + set.emplace(value_ptr); + + BOOST_CHECK_EQUAL(set.size(), 1); + BOOST_CHECK_EQUAL(**set.begin(), value); +} + +/** + * serialize and deserialize + */ +BOOST_AUTO_TEST_CASE(test_serialize_deserialize) { + // insert x values; delete some values; serialize set; deserialize in new set; + // check equal. for deserialization, test it with and without hash + // compatibility. + const std::size_t nb_values = 1000; + + tsl::robin_set<move_only_test> set; + for (std::size_t i = 0; i < nb_values + 40; i++) { + set.insert(utils::get_key<move_only_test>(i)); + } + + for (std::size_t i = nb_values; i < nb_values + 40; i++) { + set.erase(utils::get_key<move_only_test>(i)); + } + BOOST_CHECK_EQUAL(set.size(), nb_values); + + serializer serial; + set.serialize(serial); + + deserializer dserial(serial.str()); + auto set_deserialized = decltype(set)::deserialize(dserial, true); + BOOST_CHECK(set == set_deserialized); + + deserializer dserial2(serial.str()); + set_deserialized = decltype(set)::deserialize(dserial2, false); + BOOST_CHECK(set_deserialized == set); +} + +BOOST_AUTO_TEST_CASE(test_erase_fast) { + using Set = tsl::robin_set<int>; + Set set; + set.emplace(4); + auto it = set.find(4); + BOOST_CHECK(it != set.end()); + set.erase_fast(it); + BOOST_CHECK(set.size() == 0); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/thirdparty/robin-map/tests/utils.h b/thirdparty/robin-map/tests/utils.h new file mode 100644 index 000000000..74433a396 --- /dev/null +++ b/thirdparty/robin-map/tests/utils.h @@ -0,0 +1,443 @@ +/** + * MIT License + * + * Copyright (c) 2017 Thibaut Goetghebuer-Planchon <[email protected]> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef TSL_UTILS_H +#define TSL_UTILS_H + +#include <tsl/robin_hash.h> + +#include <cstddef> +#include <cstdint> +#include <functional> +#include <memory> +#include <ostream> +#include <string> +#include <utility> + +#ifdef TSL_RH_NO_EXCEPTIONS +#define TSL_RH_CHECK_THROW(S, E) +#define TSL_RH_CHECK_THROW_EITHER(S, E1, E2) +#else +#define TSL_RH_CHECK_THROW(S, E) BOOST_CHECK_THROW(S, E) +#define TSL_RH_CHECK_THROW_EITHER(S, E1, E2) \ + do { \ + try { \ + S; \ + BOOST_CHECK(false); \ + } catch (const E1&) { \ + } catch (const E2&) { \ + } \ + } while (0) +#endif + +template <typename T> +class identity_hash { + public: + std::size_t operator()(const T& value) const { + return static_cast<std::size_t>(value); + } +}; + +template <unsigned int MOD> +class mod_hash { + public: + template <typename T> + std::size_t operator()(const T& value) const { + return std::hash<T>()(value) % MOD; + } +}; + +class self_reference_member_test { + public: + self_reference_member_test() + : m_value(std::to_string(-1)), m_value_ptr(&m_value) {} + + explicit self_reference_member_test(std::int64_t value) + : m_value(std::to_string(value)), m_value_ptr(&m_value) {} + + self_reference_member_test(const self_reference_member_test& other) + : m_value(*other.m_value_ptr), m_value_ptr(&m_value) {} + + self_reference_member_test(self_reference_member_test&& other) + : m_value(*other.m_value_ptr), m_value_ptr(&m_value) {} + + self_reference_member_test& operator=( + const self_reference_member_test& other) { + m_value = *other.m_value_ptr; + m_value_ptr = &m_value; + + return *this; + } + + self_reference_member_test& operator=(self_reference_member_test&& other) { + m_value = *other.m_value_ptr; + m_value_ptr = &m_value; + + return *this; + } + + std::string value() const { return *m_value_ptr; } + + friend std::ostream& operator<<(std::ostream& stream, + const self_reference_member_test& value) { + stream << *value.m_value_ptr; + + return stream; + } + + friend bool operator==(const self_reference_member_test& lhs, + const self_reference_member_test& rhs) { + return *lhs.m_value_ptr == *rhs.m_value_ptr; + } + + friend bool operator!=(const self_reference_member_test& lhs, + const self_reference_member_test& rhs) { + return !(lhs == rhs); + } + + friend bool operator<(const self_reference_member_test& lhs, + const self_reference_member_test& rhs) { + return *lhs.m_value_ptr < *rhs.m_value_ptr; + } + + private: + std::string m_value; + std::string* m_value_ptr; +}; + +class move_only_test { + public: + explicit move_only_test(std::int64_t value) + : m_value(new std::string(std::to_string(value))) {} + + explicit move_only_test(std::string value) + : m_value(new std::string(std::move(value))) {} + + move_only_test(const move_only_test&) = delete; + move_only_test(move_only_test&&) = default; + move_only_test& operator=(const move_only_test&) = delete; + move_only_test& operator=(move_only_test&&) = default; + + friend std::ostream& operator<<(std::ostream& stream, + const move_only_test& value) { + if (value.m_value == nullptr) { + stream << "null"; + } else { + stream << *value.m_value; + } + + return stream; + } + + friend bool operator==(const move_only_test& lhs, const move_only_test& rhs) { + if (lhs.m_value == nullptr || rhs.m_value == nullptr) { + return lhs.m_value == nullptr && rhs.m_value == nullptr; + } else { + return *lhs.m_value == *rhs.m_value; + } + } + + friend bool operator!=(const move_only_test& lhs, const move_only_test& rhs) { + return !(lhs == rhs); + } + + friend bool operator<(const move_only_test& lhs, const move_only_test& rhs) { + if (lhs.m_value == nullptr && rhs.m_value == nullptr) { + return false; + } else if (lhs.m_value == nullptr) { + return true; + } else if (rhs.m_value == nullptr) { + return false; + } else { + return *lhs.m_value < *rhs.m_value; + } + } + + const std::string& value() const { return *m_value; } + + private: + std::unique_ptr<std::string> m_value; +}; + +class copy_only_test { + public: + explicit copy_only_test(std::int64_t value) + : m_value(std::to_string(value)) {} + + copy_only_test(const copy_only_test& other) : m_value(other.m_value) {} + + copy_only_test& operator=(const copy_only_test& other) { + m_value = other.m_value; + + return *this; + } + + ~copy_only_test() {} + + friend std::ostream& operator<<(std::ostream& stream, + const copy_only_test& value) { + stream << value.m_value; + + return stream; + } + + friend bool operator==(const copy_only_test& lhs, const copy_only_test& rhs) { + return lhs.m_value == rhs.m_value; + } + + friend bool operator!=(const copy_only_test& lhs, const copy_only_test& rhs) { + return !(lhs == rhs); + } + + friend bool operator<(const copy_only_test& lhs, const copy_only_test& rhs) { + return lhs.m_value < rhs.m_value; + } + + std::string value() const { return m_value; } + + private: + std::string m_value; +}; + +namespace std { +template <> +struct hash<self_reference_member_test> { + std::size_t operator()(const self_reference_member_test& val) const { + return std::hash<std::string>()(val.value()); + } +}; + +template <> +struct hash<move_only_test> { + std::size_t operator()(const move_only_test& val) const { + return std::hash<std::string>()(val.value()); + } +}; + +template <> +struct hash<copy_only_test> { + std::size_t operator()(const copy_only_test& val) const { + return std::hash<std::string>()(val.value()); + } +}; +} // namespace std + +class utils { + public: + template <typename T> + static T get_key(std::size_t counter); + + template <typename T> + static T get_value(std::size_t counter); + + template <typename HMap> + static HMap get_filled_hash_map(std::size_t nb_elements); +}; + +template <> +inline std::int32_t utils::get_key<std::int32_t>(std::size_t counter) { + return tsl::detail_robin_hash::numeric_cast<std::int32_t>(counter); +} + +template <> +inline std::int64_t utils::get_key<std::int64_t>(std::size_t counter) { + return tsl::detail_robin_hash::numeric_cast<std::int64_t>(counter); +} + +template <> +inline self_reference_member_test utils::get_key<self_reference_member_test>( + std::size_t counter) { + return self_reference_member_test( + tsl::detail_robin_hash::numeric_cast<std::int64_t>(counter)); +} + +template <> +inline std::string utils::get_key<std::string>(std::size_t counter) { + return "Key " + std::to_string(counter); +} + +template <> +inline move_only_test utils::get_key<move_only_test>(std::size_t counter) { + return move_only_test( + tsl::detail_robin_hash::numeric_cast<std::int64_t>(counter)); +} + +template <> +inline copy_only_test utils::get_key<copy_only_test>(std::size_t counter) { + return copy_only_test( + tsl::detail_robin_hash::numeric_cast<std::int64_t>(counter)); +} + +template <> +inline std::int32_t utils::get_value<std::int32_t>(std::size_t counter) { + return tsl::detail_robin_hash::numeric_cast<std::int32_t>(counter * 2); +} + +template <> +inline std::int64_t utils::get_value<std::int64_t>(std::size_t counter) { + return tsl::detail_robin_hash::numeric_cast<std::int64_t>(counter * 2); +} + +template <> +inline self_reference_member_test utils::get_value<self_reference_member_test>( + std::size_t counter) { + return self_reference_member_test( + tsl::detail_robin_hash::numeric_cast<std::int64_t>(counter * 2)); +} + +template <> +inline std::string utils::get_value<std::string>(std::size_t counter) { + return "Value " + std::to_string(counter); +} + +template <> +inline move_only_test utils::get_value<move_only_test>(std::size_t counter) { + return move_only_test( + tsl::detail_robin_hash::numeric_cast<std::int64_t>(counter * 2)); +} + +template <> +inline copy_only_test utils::get_value<copy_only_test>(std::size_t counter) { + return copy_only_test( + tsl::detail_robin_hash::numeric_cast<std::int64_t>(counter * 2)); +} + +template <typename HMap> +inline HMap utils::get_filled_hash_map(std::size_t nb_elements) { + using key_t = typename HMap::key_type; + using value_t = typename HMap::mapped_type; + + HMap map; + map.reserve(nb_elements); + + for (std::size_t i = 0; i < nb_elements; i++) { + map.insert({utils::get_key<key_t>(i), utils::get_value<value_t>(i)}); + } + + return map; +} + +template <class T> +struct is_pair : std::false_type {}; + +template <class T1, class T2> +struct is_pair<std::pair<T1, T2>> : std::true_type {}; + +/** + * serializer helper to test serialize(...) and deserialize(...) functions + */ +class serializer { + public: + serializer() { m_ostream.exceptions(m_ostream.badbit | m_ostream.failbit); } + + template <class T> + void operator()(const T& val) { + serialize_impl(val); + } + + std::string str() const { return m_ostream.str(); } + + private: + template <typename T, typename U> + void serialize_impl(const std::pair<T, U>& val) { + serialize_impl(val.first); + serialize_impl(val.second); + } + + void serialize_impl(const std::string& val) { + serialize_impl( + tsl::detail_robin_hash::numeric_cast<std::uint64_t>(val.size())); + m_ostream.write(val.data(), val.size()); + } + + void serialize_impl(const move_only_test& val) { + serialize_impl(val.value()); + } + + template <class T, typename std::enable_if< + std::is_arithmetic<T>::value>::type* = nullptr> + void serialize_impl(const T& val) { + m_ostream.write(reinterpret_cast<const char*>(&val), sizeof(val)); + } + + private: + std::stringstream m_ostream; +}; + +class deserializer { + public: + explicit deserializer(const std::string& init_str = "") + : m_istream(init_str) { + m_istream.exceptions(m_istream.badbit | m_istream.failbit | + m_istream.eofbit); + } + + template <class T> + T operator()() { + return deserialize_impl<T>(); + } + + private: + template <class T, + typename std::enable_if<is_pair<T>::value>::type* = nullptr> + T deserialize_impl() { + auto first = deserialize_impl<typename T::first_type>(); + return std::make_pair(std::move(first), + deserialize_impl<typename T::second_type>()); + } + + template <class T, typename std::enable_if< + std::is_same<std::string, T>::value>::type* = nullptr> + T deserialize_impl() { + const std::size_t str_size = + tsl::detail_robin_hash::numeric_cast<std::size_t>( + deserialize_impl<std::uint64_t>()); + + // TODO std::string::data() return a const pointer pre-C++17. Avoid the + // inefficient double allocation. + std::vector<char> chars(str_size); + m_istream.read(chars.data(), str_size); + + return std::string(chars.data(), chars.size()); + } + + template <class T, typename std::enable_if<std::is_same< + move_only_test, T>::value>::type* = nullptr> + move_only_test deserialize_impl() { + return move_only_test(deserialize_impl<std::string>()); + } + + template <class T, typename std::enable_if< + std::is_arithmetic<T>::value>::type* = nullptr> + T deserialize_impl() { + T val; + m_istream.read(reinterpret_cast<char*>(&val), sizeof(val)); + + return val; + } + + private: + std::stringstream m_istream; +}; + +#endif |