1 | /* |
2 | Copyright (c) 2005-2019 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> |
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 { |
33 | size_t operator()(const T& /*a*/) const { |
34 | return 1; |
35 | } |
36 | }; |
37 | |
38 | template <typename T> |
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> |
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> |
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> |
118 | 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> |
124 | 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> |
131 | 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: |
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: |
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: |
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: |
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: |
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: |
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> |
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> |
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> |
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 | |