/* Copyright (c) 2019-2021 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef __TBB_test_common_concurrent_ordered_common_H #define __TBB_test_common_concurrent_ordered_common_H #define __TBB_TEST_CPP20_COMPARISONS __TBB_CPP20_COMPARISONS_PRESENT && __TBB_CPP20_CONCEPTS_PRESENT #include "config.h" #include "common/concurrent_associative_common.h" #include "test_comparisons.h" template inline void CheckContainerAllocator(MyTable &table, size_t expected_allocs, size_t expected_frees, bool exact) { typename MyTable::allocator_type a = table.get_allocator(); CheckAllocator(a, expected_allocs, expected_frees, exact); } template inline void CheckNoAllocations(Container&) { // TODO: enable // CheckContainerAllocator(cont, 0, 0, false); } template struct OrderChecker { typename Container::value_compare& val_comp; typename Container::key_compare& key_comp; OrderChecker(typename Container::value_compare& v_comp, typename Container::key_compare& k_comp) : val_comp(v_comp), key_comp(k_comp) {} bool operator()(const typename Container::value_type& lhs, const typename Container::value_type& rhs) { if (AllowMultimapping::value) // We need to use not greater comparator for multicontainer return !val_comp(rhs, lhs) && !key_comp(Value::key(rhs), Value::key(lhs)); return val_comp(lhs, rhs) && key_comp(Value::key(lhs), Value::key(rhs)); } }; // struct OrderChecker template void check_container_order( const Container& cont ) { if (!cont.empty()) { typename Container::key_compare key_comp = cont.key_comp(); typename Container::value_compare value_comp = cont.value_comp(); OrderChecker check_order(value_comp, key_comp); for (auto it = cont.begin(); std::next(it) != cont.end();) { auto pr_it = it++; REQUIRE_MESSAGE(check_order(*pr_it, *it), "The order of the elements is broken"); } } } template void test_ordered_methods() { Container cont; const Container& ccont = cont; int r, random_threshold = 10, uncontained_key = random_threshold / 2; for (int i = 0; i < 100; ++i) { r = std::rand() % random_threshold; if (r != uncontained_key) { cont.insert(Value::make(r)); } } check_container_order(cont); typename Container::value_compare val_comp = cont.value_comp(); typename Container::iterator l_bound_check, u_bound_check; for (int key = -1; key < random_threshold + 1; ++key) { auto eq_range = cont.equal_range(key); // Check equal_range content for (auto it = eq_range.first; it != eq_range.second; ++it) REQUIRE_MESSAGE(*it == Value::make(key), "equal_range contains wrong value"); // Manual search of upper and lower bounds l_bound_check = cont.end(); u_bound_check = cont.end(); for (auto it = cont.begin(); it != cont.end(); ++it){ if (!val_comp(*it, Value::make(key)) && l_bound_check == cont.end()) { l_bound_check = it; } if (val_comp(Value::make(key), *it) && u_bound_check == cont.end()) { u_bound_check = it; break; } } typename Container::range_type cont_range = cont.range(); typename Container::const_range_type ccont_range = ccont.range(); REQUIRE_MESSAGE(cont_range.size() == ccont_range.size(), "Incorrect ordered container range size"); REQUIRE_MESSAGE(cont_range.size() == cont.size(), "Incorrect ordered container range size"); typename Container::iterator l_bound = cont.lower_bound(key); typename Container::iterator u_bound = cont.upper_bound(key); REQUIRE_MESSAGE(l_bound == l_bound_check, "lower_bound() returned wrong iterator"); REQUIRE_MESSAGE(u_bound == u_bound_check, "upper_bound() returned wrong iterator"); using const_iterator = typename Container::const_iterator; const_iterator cl_bound = ccont.lower_bound(key); const_iterator cu_bound = ccont.upper_bound(key); REQUIRE_MESSAGE(cl_bound == const_iterator(l_bound), "lower_bound() const returned wrong iterator"); REQUIRE_MESSAGE(cu_bound == const_iterator(u_bound), "upper_bound() const returned wrong iterator"); REQUIRE((l_bound == eq_range.first && u_bound == eq_range.second)); } } template void test_basic() { test_basic_common(); test_ordered_methods(); } template void test_concurrent_order() { // TODO: MinThread - MaxThread loop auto num_threads = utils::get_platform_max_threads(); Container cont; int items = 1000; utils::NativeParallelFor(num_threads, [&](std::size_t index) { int step = index % 4 + 1; bool reverse = (step % 2 == 0); if (reverse) { for (int i = 0; i < items; i += step) { cont.insert(Value::make(i)); } } else { for (int i = items; i > 0; i -= step) { cont.insert(Value::make(i)); } } }); check_container_order(cont); } template void test_concurrent(bool asymptotic = false) { test_concurrent_common(asymptotic); test_concurrent_order(); } struct OrderedMoveTraitsBase { static constexpr std::size_t expected_number_of_items_to_allocate_for_steal_move = 584; // TODO: remove allocation of dummy_node template static OrderedType& construct_container( typename std::aligned_storage::type& storage, Iterator begin, Iterator end ) { OrderedType* ptr = reinterpret_cast(&storage); new (ptr) OrderedType(begin, end); return *ptr; } template static OrderedType& construct_container( typename std::aligned_storage::type& storage, Iterator begin, Iterator end, const Allocator& alloc ) { OrderedType* ptr = reinterpret_cast(&storage); new (ptr) OrderedType(begin, end, typename OrderedType::key_compare(), alloc); return *ptr; } template static bool equal( const OrderedType& c, Iterator begin, Iterator end ) { bool equal_sizes = std::size_t(std::distance(begin, end)) == c.size(); if (!equal_sizes) return false; for (Iterator it = begin; it != end; ++it) { if (!c.contains(Value::key((*it)))) return false; } return true; } }; namespace std { template <> struct less> { bool operator()( const std::weak_ptr& lhs, const std::weak_ptr& rhs ) const { return *lhs.lock() < *rhs.lock(); } }; template <> struct less> : less> {}; } template void Examine(Table c, const std::list& lst) { CommonExamine(c, lst); } template void TypeTester( const std::list& lst ) { REQUIRE_MESSAGE(lst.size() >= 5, "Array should have at least 5 elements"); REQUIRE_MESSAGE(lst.size() <= 100, "The test has O(n^2) complexity so a big number of elements can lead long execution time"); // Construct an empty table Table c1; c1.insert(lst.begin(), lst.end()); Examine(c1, lst); typename Table::key_compare compare; typename Table::allocator_type allocator; auto it = lst.begin(); auto init = {*it++, *it++, *it++}; // Constructor from an std::initializer_list Table c2(init); c2.insert(it, lst.end()); Examine(c2, lst); // Constructor from an std::initializer_list, default comparator and non-default allocator Table c2_alloc(init, allocator); c2_alloc.insert(it, lst.end()); Examine(c2_alloc, lst); // Constructor from an std::initializer_list, non-default comparator and allocator Table c2_comp_alloc(init, compare, allocator); c2_comp_alloc.insert(it, lst.end()); Examine(c2_comp_alloc, lst); // Copying constructor Table c3(c1); Examine(c3, lst); // Copying constructor with the allocator Table c3_alloc(c1, allocator); Examine(c3_alloc, lst); // Constructor with non-default compare Table c4(compare); c4.insert(lst.begin(), lst.end()); Examine(c4, lst); // Constructor with non-default allocator Table c5(allocator); c5.insert(lst.begin(), lst.end()); Examine(c5, lst); // Constructor with non-default compare and non-default allocator Table c6(compare, allocator); c6.insert(lst.begin(), lst.end()); Examine(c6, lst); // Constructor from an iteration range Table c7(c1.begin(), c1.end()); Examine(c7, lst); // Constructor from an iteration range, default compare and non-default allocator Table c8(c1.begin(), c1.end(), allocator); Examine(c8, lst); // Constructor from an iteration range, non-default compare and non-default allocator Table c9(c1.begin(), c1.end(), compare, allocator); Examine(c9, lst); } struct TransparentLess { template auto operator()( T&& lhs, U&& rhs ) const -> decltype(std::forward(lhs) < std::forward(rhs)) { return lhs < rhs; } using is_transparent = void; }; template