1 /*
2     Copyright (c) 2005-2020 Intel Corporation
3 
4     Licensed under the Apache License, Version 2.0 (the "License");
5     you may not use this file except in compliance with the License.
6     You may obtain a copy of the License at
7 
8         http://www.apache.org/licenses/LICENSE-2.0
9 
10     Unless required by applicable law or agreed to in writing, software
11     distributed under the License is distributed on an "AS IS" BASIS,
12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13     See the License for the specific language governing permissions and
14     limitations under the License.
15 */
16 
17 #define __TBB_UNORDERED_TEST 1
18 
19 #include "test_concurrent_associative_common.h"
20 
21 template<typename MyTable>
CheckEmptyContainerAllocator(MyTable & table,size_t expected_allocs,size_t expected_frees,bool exact,int line)22 inline void CheckEmptyContainerAllocator(MyTable &table, size_t expected_allocs, size_t expected_frees, bool exact, int line) {
23     typename MyTable::allocator_type a = table.get_allocator();
24     REMARK("#%d checking allocators: items %u/%u, allocs %u/%u\n", line,
25         unsigned(a.items_allocated), unsigned(a.items_freed), unsigned(a.allocations), unsigned(a.frees) );
26     ASSERT( a.items_allocated == a.allocations, NULL); ASSERT( a.items_freed == a.frees, NULL);
27     ASSERT( a.items_allocated == a.items_freed + 1, NULL);
28     CheckAllocator<MyTable>(a, expected_allocs, expected_frees, exact);
29 }
30 
31 template<typename T>
32 struct degenerate_hash {
operatordegenerate_hash33     size_t operator()(const T& /*a*/) const {
34         return 1;
35     }
36 };
37 
38 template <typename T>
test_unordered_methods()39 void test_unordered_methods(){
40     T cont;
41     cont.insert(Value<T>::make(1));
42     cont.insert(Value<T>::make(2));
43     // unordered_specific
44     // void rehash(size_type n);
45     cont.rehash(16);
46 
47     // float load_factor() const;
48     // float max_load_factor() const;
49     ASSERT(cont.load_factor() <= cont.max_load_factor(), "Load factor is invalid");
50 
51     // void max_load_factor(float z);
52     cont.max_load_factor(16.0f);
53     ASSERT(cont.max_load_factor() == 16.0f, "Max load factor has not been changed properly");
54 
55     // hasher hash_function() const;
56     cont.hash_function();
57 
58     // key_equal key_eq() const;
59     cont.key_eq();
60 
61     cont.clear();
62     CheckEmptyContainerAllocatorA(cont, 1, 0); // one dummy is always allocated
63     for (int i = 0; i < 256; i++)
64     {
65         std::pair<typename T::iterator, bool> ins3 = cont.insert(Value<T>::make(i));
66         ASSERT(ins3.second == true && Value<T>::get(*(ins3.first)) == i, "Element 1 has not been inserted properly");
67     }
68     ASSERT(cont.size() == 256, "Wrong number of elements have been inserted");
69     // size_type unsafe_bucket_count() const;
70     ASSERT(cont.unsafe_bucket_count() == 16, "Wrong number of buckets");
71 
72     // size_type unsafe_max_bucket_count() const;
73     ASSERT(cont.unsafe_max_bucket_count() > 65536, "Wrong max number of buckets");
74 
75     for (unsigned int i = 0; i < 256; i++)
76     {
77         typename T::size_type buck = cont.unsafe_bucket(i);
78 
79         // size_type unsafe_bucket(const key_type& k) const;
80         ASSERT(buck < 16, "Wrong bucket mapping");
81     }
82 
83     typename T::size_type bucketSizeSum = 0;
84     typename T::size_type iteratorSizeSum = 0;
85 
86     for (unsigned int i = 0; i < 16; i++)
87     {
88         bucketSizeSum += cont.unsafe_bucket_size(i);
89         for (typename T::iterator bit = cont.unsafe_begin(i); bit != cont.unsafe_end(i); bit++) iteratorSizeSum++;
90     }
91     ASSERT(bucketSizeSum == 256, "sum of bucket counts incorrect");
92     ASSERT(iteratorSizeSum == 256, "sum of iterator counts incorrect");
93 }
94 
95 template<typename T, typename do_check_element_state>
test_basic(const char * str,do_check_element_state)96 void test_basic(const char * str, do_check_element_state)
97 {
98     test_basic_common<T>(str, do_check_element_state());
99     test_unordered_methods<T>();
100 }
101 
102 template<typename T>
test_basic(const char * str)103 void test_basic(const char * str){
104     test_basic_common<T>(str, tbb::internal::false_type());
105     test_unordered_methods<T>();
106 }
107 
108 template<typename T>
109 void test_concurrent(const char *tablename, bool asymptotic = false) {
110     test_concurrent_common<T>(tablename, asymptotic);
111 }
112 
113 #if __TBB_CPP11_RVALUE_REF_PRESENT
114 struct unordered_move_traits_base {
115     enum{ expected_number_of_items_to_allocate_for_steal_move = 3 };
116 
117     template <typename unordered_type, typename iterator_type>
construct_containerunordered_move_traits_base118     static unordered_type& construct_container(tbb::aligned_space<unordered_type> & storage, iterator_type begin, iterator_type end){
119         new (storage.begin()) unordered_type(begin, end);
120         return * storage.begin();
121     }
122 
123     template <typename unordered_type, typename iterator_type, typename allocator_type>
construct_containerunordered_move_traits_base124     static unordered_type& construct_container(tbb::aligned_space<unordered_type> & storage, iterator_type begin, iterator_type end, allocator_type const& a ){
125         size_t deault_n_of_buckets = 8; //can not use concurrent_unordered_base::n_of_buckets as it is inaccessible
126         new (storage.begin()) unordered_type(begin, end, deault_n_of_buckets, typename unordered_type::hasher(), typename unordered_type::key_equal(), a);
127         return * storage.begin();
128     }
129 
130     template<typename unordered_type, typename iterator>
equalunordered_move_traits_base131     static bool equal(unordered_type const& c, iterator begin, iterator end){
132         bool equal_sizes = ( static_cast<size_t>(std::distance(begin, end)) == c.size() );
133         if (!equal_sizes)
134             return false;
135 
136         for (iterator it = begin; it != end; ++it ){
137             if (c.find( Value<unordered_type>::key(*it)) == c.end()){
138                 return false;
139             }
140         }
141         return true;
142     }
143 };
144 #endif /* __TBB_CPP11_RVALUE_REF_PRESENT*/
145 
146 #if __TBB_CPP11_SMART_POINTERS_PRESENT
147 namespace tbb {
148     template<> class tbb_hash< std::shared_ptr<int> > {
149     public:
operator()150         size_t operator()( const std::shared_ptr<int>& key ) const { return tbb_hasher( *key ); }
151     };
152     template<> class tbb_hash< const std::shared_ptr<int> > {
153     public:
operator()154         size_t operator()( const std::shared_ptr<int>& key ) const { return tbb_hasher( *key ); }
155     };
156     template<> class tbb_hash< std::weak_ptr<int> > {
157     public:
operator()158         size_t operator()( const std::weak_ptr<int>& key ) const { return tbb_hasher( *key.lock( ) ); }
159     };
160     template<> class tbb_hash< const std::weak_ptr<int> > {
161     public:
operator()162         size_t operator()( const std::weak_ptr<int>& key ) const { return tbb_hasher( *key.lock( ) ); }
163     };
164     template<> class tbb_hash< test::unique_ptr<int> > {
165     public:
operator()166         size_t operator()( const test::unique_ptr<int>& key ) const { return tbb_hasher( *key ); }
167     };
168     template<> class tbb_hash< const test::unique_ptr<int> > {
169     public:
operator()170         size_t operator()( const test::unique_ptr<int>& key ) const { return tbb_hasher( *key ); }
171     };
172 }
173 #endif /*__TBB_CPP11_SMART_POINTERS_PRESENT*/
174 
175 template <bool defCtorPresent, typename Table>
CustomExamine(Table c,const std::list<typename Table::value_type> lst)176 void CustomExamine( Table c, const std::list<typename Table::value_type> lst) {
177     typedef typename Table::value_type ValueType;
178     typedef typename Table::size_type SizeType;
179     const Table constC = c;
180 
181     const SizeType bucket_count = c.unsafe_bucket_count();
182     ASSERT( c.unsafe_max_bucket_count() >= bucket_count, NULL );
183     SizeType counter = SizeType( 0 );
184     for ( SizeType i = 0; i < bucket_count; ++i ) {
185         const SizeType size = c.unsafe_bucket_size( i );
186         typedef typename Table::difference_type diff_type;
187         ASSERT( std::distance( c.unsafe_begin( i ), c.unsafe_end( i ) ) == diff_type( size ), NULL );
188         ASSERT( std::distance( c.unsafe_cbegin( i ), c.unsafe_cend( i ) ) == diff_type( size ), NULL );
189         ASSERT( std::distance( constC.unsafe_begin( i ), constC.unsafe_end( i ) ) == diff_type( size ), NULL );
190         ASSERT( std::distance( constC.unsafe_cbegin( i ), constC.unsafe_cend( i ) ) == diff_type( size ), NULL );
191         counter += size;
192     }
193     ASSERT( counter == lst.size(), NULL );
194 
195     for ( typename std::list<ValueType>::const_iterator it = lst.begin(); it != lst.end(); ) {
196         const SizeType index = c.unsafe_bucket( Value<Table>::key( *it ) );
197         typename std::list<ValueType>::const_iterator prev_it = it++;
198         ASSERT( std::search( c.unsafe_begin( index ), c.unsafe_end( index ), prev_it, it, Harness::IsEqual() ) != c.unsafe_end( index ), NULL );
199     }
200 
201     c.rehash( 2 * bucket_count );
202     ASSERT( c.unsafe_bucket_count() > bucket_count, NULL );
203 
204     ASSERT( c.load_factor() <= c.max_load_factor(), NULL );
205 
206     c.max_load_factor( 1.0f );
207     c.hash_function();
208     c.key_eq();
209 }
210 
211 template <bool defCtorPresent, typename Table>
Examine(Table c,const std::list<typename Table::value_type> & lst)212 void Examine( Table c, const std::list<typename Table::value_type> &lst) {
213     CommonExamine<defCtorPresent>(c, lst);
214     CustomExamine<defCtorPresent>(c, lst);
215 }
216 
217 template <bool defCtorPresent, typename Table, typename TableDebugAlloc>
TypeTester(const std::list<typename Table::value_type> & lst)218 void TypeTester( const std::list<typename Table::value_type> &lst ) {
219     ASSERT( lst.size() >= 5, "Array should have at least 5 elements" );
220     ASSERT( lst.size() <= 100, "The test has O(n^2) complexity so a big number of elements can lead long execution time" );
221     // Construct an empty table.
222     Table c1;
223     c1.insert( lst.begin(), lst.end() );
224     Examine<defCtorPresent>( c1, lst );
225 
226     typename Table::size_type initial_bucket_number = 8;
227     typename Table::allocator_type allocator;
228     typename Table::hasher hasher;
229 #if __TBB_INITIALIZER_LISTS_PRESENT && !__TBB_CPP11_INIT_LIST_TEMP_OBJS_LIFETIME_BROKEN
230     // Constructor from an initializer_list.
231     typename std::list<typename Table::value_type>::const_iterator it = lst.begin();
232     Table c2( { *it++, *it++, *it++ } );
233     c2.insert( it, lst.end( ) );
234     Examine<defCtorPresent>( c2, lst );
235 
236     it = lst.begin();
237     // Constructor from an initializer_list, default hasher and key equality and non-default allocator
238     Table c2_alloc( { *it++, *it++, *it++ }, initial_bucket_number, allocator);
239     c2_alloc.insert( it, lst.end() );
240     Examine<defCtorPresent>( c2_alloc, lst );
241 
242     it = lst.begin();
243     // Constructor from an initializer_list, default key equality and non-default hasher and allocator
244     Table c2_hash_alloc( { *it++, *it++, *it++ }, initial_bucket_number, hasher, allocator );
245     c2_hash_alloc.insert( it, lst.end() );
246     Examine<defCtorPresent>( c2_hash_alloc, lst );
247 #endif
248     // Copying constructor.
249     Table c3( c1 );
250     Examine<defCtorPresent>( c3, lst );
251     // Construct with non-default allocator
252     TableDebugAlloc c4;
253     c4.insert( lst.begin(), lst.end() );
254     Examine<defCtorPresent>( c4, lst );
255     // Copying constructor for a container with a different allocator type.
256     TableDebugAlloc c5( c4 );
257     Examine<defCtorPresent>( c5, lst );
258     // Construction empty table with n preallocated buckets.
259     Table c6( lst.size() );
260     c6.insert( lst.begin(), lst.end() );
261     Examine<defCtorPresent>( c6, lst );
262 
263     // Construction empty table with n preallocated buckets, default hasher and key equality and non-default allocator
264     Table c6_alloc( lst.size(), allocator );
265     c6_alloc.insert( lst.begin(), lst.end() );
266     Examine<defCtorPresent>( c6_alloc, lst );
267 
268     // Construction empty table with n preallocated buckets, default key equality and non-default hasher and allocator
269     Table c6_hash_alloc( lst.size(), hasher, allocator );
270     c6_hash_alloc.insert( lst.begin(), lst.end() );
271     Examine<defCtorPresent>( c6_hash_alloc, lst );
272 
273     TableDebugAlloc c7( lst.size( ) );
274     c7.insert( lst.begin(), lst.end() );
275     Examine<defCtorPresent>( c7, lst );
276     // Construction with a copying iteration range and a given allocator instance.
277     Table c8( c1.begin(), c1.end() );
278     Examine<defCtorPresent>( c8, lst );
279 
280     // Construction with a copying iteration range, default hasher and key equality and non-default allocator
281     Table c8_alloc( c1.begin(), c1.end(), initial_bucket_number, allocator );
282     Examine<defCtorPresent>( c8_alloc, lst );
283 
284     // Construction with a copying iteration range, default key equality and non-default hasher and allocator
285     Table c8_hash_alloc( c1.begin(), c1.end(), initial_bucket_number, hasher, allocator );
286     Examine<defCtorPresent>( c8_hash_alloc, lst);
287 
288     // Construction with an instance of non-default allocator
289     typename TableDebugAlloc::allocator_type a;
290     TableDebugAlloc c9( a );
291     c9.insert( c7.begin(), c7.end() );
292     Examine<defCtorPresent>( c9, lst );
293 }
294