1 #include <vector>
2 #include <algorithm>
3 #include <string>
4 #if defined (STLPORT)
5 # include <unordered_map>
6 # include <unordered_set>
7 #endif
8
9 //#include <iostream>
10
11 #include "cppunit/cppunit_proxy.h"
12
13 #if !defined (STLPORT) || defined(_STLP_USE_NAMESPACES)
14 using namespace std;
15 # if defined (STLPORT)
16 using namespace std::tr1;
17 # endif
18 #endif
19
20 //
21 // TestCase class
22 //
23 class UnorderedTest : public CPPUNIT_NS::TestCase
24 {
25 CPPUNIT_TEST_SUITE(UnorderedTest);
26 #if !defined (STLPORT)
27 CPPUNIT_IGNORE;
28 #endif
29 CPPUNIT_TEST(uset);
30 CPPUNIT_TEST(umultiset);
31 CPPUNIT_TEST(umap);
32 CPPUNIT_TEST(umultimap);
33 CPPUNIT_TEST(user_case);
34 CPPUNIT_TEST(hash_policy);
35 CPPUNIT_TEST(buckets);
36 CPPUNIT_TEST(equal_range);
37 CPPUNIT_EXPLICIT_TEST(benchmark1);
38 CPPUNIT_EXPLICIT_TEST(benchmark2);
39 #if !defined (_STLP_USE_CONTAINERS_EXTENSION)
40 CPPUNIT_IGNORE;
41 #endif
42 CPPUNIT_TEST(template_methods);
43 CPPUNIT_TEST_SUITE_END();
44
45 protected:
46 void uset();
47 void umultiset();
48 void umap();
49 void umultimap();
50 void user_case();
51 void hash_policy();
52 void buckets();
53 void equal_range();
54 void benchmark1();
55 void benchmark2();
56 void template_methods();
57 };
58
59 CPPUNIT_TEST_SUITE_REGISTRATION(UnorderedTest);
60
61 const int NB_ELEMS = 2000;
62
63 //
64 // tests implementation
65 //
uset()66 void UnorderedTest::uset()
67 {
68 #if defined (STLPORT)
69 typedef unordered_set<int, hash<int>, equal_to<int> > usettype;
70 usettype us;
71
72 //Small compilation check of the copy constructor:
73 usettype us2(us);
74 //And assignment operator
75 us = us2;
76
77 int i;
78 pair<usettype::iterator, bool> ret;
79 for (i = 0; i < NB_ELEMS; ++i) {
80 ret = us.insert(i);
81 CPPUNIT_ASSERT( ret.second );
82 CPPUNIT_ASSERT( *ret.first == i );
83
84 ret = us.insert(i);
85 CPPUNIT_ASSERT( !ret.second );
86 CPPUNIT_ASSERT( *ret.first == i );
87 }
88
89 vector<int> us_val;
90
91 usettype::local_iterator lit, litEnd;
92 for (i = 0; i < NB_ELEMS; ++i) {
93 lit = us.begin(us.bucket(i));
94 litEnd = us.end(us.bucket(i));
95
96 usettype::size_type bucket_pos = us.bucket(*lit);
97 for (; lit != litEnd; ++lit) {
98 CPPUNIT_ASSERT( us.bucket(*lit) == bucket_pos );
99 us_val.push_back(*lit);
100 }
101 }
102
103 //A compilation time check to uncomment from time to time
104 {
105 //usettype::iterator it;
106 //CPPUNIT_ASSERT( it != lit );
107 }
108
109 sort(us_val.begin(), us_val.end());
110 for (i = 0; i < NB_ELEMS; ++i) {
111 CPPUNIT_ASSERT( us_val[i] == i );
112 }
113 #endif
114 }
115
umultiset()116 void UnorderedTest::umultiset()
117 {
118 #if defined (STLPORT)
119 typedef unordered_multiset<int, hash<int>, equal_to<int> > usettype;
120 usettype us;
121
122 int i;
123 usettype::iterator ret;
124 for (i = 0; i < NB_ELEMS; ++i) {
125 ret = us.insert(i);
126 CPPUNIT_ASSERT( *ret == i );
127
128 ret = us.insert(i);
129 CPPUNIT_ASSERT( *ret == i );
130 }
131
132 CPPUNIT_ASSERT( us.size() == 2 * NB_ELEMS );
133 vector<int> us_val;
134
135 usettype::local_iterator lit, litEnd;
136 for (i = 0; i < NB_ELEMS; ++i) {
137 lit = us.begin(us.bucket(i));
138 litEnd = us.end(us.bucket(i));
139
140 usettype::size_type bucket_pos = us.bucket(*lit);
141 for (; lit != litEnd; ++lit) {
142 CPPUNIT_ASSERT( us.bucket(*lit) == bucket_pos );
143 us_val.push_back(*lit);
144 }
145 }
146
147 sort(us_val.begin(), us_val.end());
148 for (i = 0; i < NB_ELEMS; ++i) {
149 CPPUNIT_ASSERT( us_val[2 * i] == i );
150 CPPUNIT_ASSERT( us_val[2 * i + 1] == i );
151 }
152 #endif
153 }
154
umap()155 void UnorderedTest::umap()
156 {
157 #if defined (STLPORT)
158 typedef unordered_map<int, int, hash<int>, equal_to<int> > umaptype;
159 umaptype us;
160
161 //Compilation check of the [] operator:
162 umaptype us2;
163 us[0] = us2[0];
164 us.clear();
165
166 {
167 //An other compilation check
168 typedef unordered_map<int, umaptype> uumaptype;
169 uumaptype uus;
170 umaptype const& uref = uus[0];
171 umaptype ucopy = uus[0];
172 ucopy = uref;
173 //Avoids warning:
174 //(void*)&uref;
175 }
176
177 int i;
178 pair<umaptype::iterator, bool> ret;
179 for (i = 0; i < NB_ELEMS; ++i) {
180 umaptype::value_type p1(i, i);
181 ret = us.insert(p1);
182 CPPUNIT_ASSERT( ret.second );
183 CPPUNIT_ASSERT( *ret.first == p1 );
184
185 umaptype::value_type p2(i, i + 1);
186 ret = us.insert(p2);
187 CPPUNIT_ASSERT( !ret.second );
188 CPPUNIT_ASSERT( *ret.first == p1 );
189 }
190
191 {
192 //Lets look for some values to see if everything is normal:
193 umaptype::iterator umit;
194 for (int j = 0; j < NB_ELEMS; j += NB_ELEMS / 100) {
195 umit = us.find(j);
196
197 CPPUNIT_ASSERT( umit != us.end() );
198 CPPUNIT_ASSERT( (*umit).second == j );
199 }
200 }
201
202 CPPUNIT_ASSERT( us.size() == (size_t)NB_ELEMS );
203 vector<pair<int, int> > us_val;
204
205 umaptype::local_iterator lit, litEnd;
206 for (i = 0; i < NB_ELEMS; ++i) {
207 lit = us.begin(us.bucket(i));
208 litEnd = us.end(us.bucket(i));
209
210 umaptype::size_type bucket_pos = us.bucket((*lit).first);
211 for (; lit != litEnd; ++lit) {
212 CPPUNIT_ASSERT( us.bucket((*lit).first) == bucket_pos );
213 us_val.push_back(make_pair((*lit).first, (*lit).second));
214 }
215 }
216
217 sort(us_val.begin(), us_val.end());
218 for (i = 0; i < NB_ELEMS; ++i) {
219 CPPUNIT_ASSERT( us_val[i] == make_pair(i, i) );
220 }
221 #endif
222 }
223
umultimap()224 void UnorderedTest::umultimap()
225 {
226 #if defined (STLPORT)
227 typedef unordered_multimap<int, int, hash<int>, equal_to<int> > umaptype;
228 umaptype us;
229
230 int i;
231 umaptype::iterator ret;
232 for (i = 0; i < NB_ELEMS; ++i) {
233 umaptype::value_type p(i, i);
234 ret = us.insert(p);
235 CPPUNIT_ASSERT( *ret == p );
236
237 ret = us.insert(p);
238 CPPUNIT_ASSERT( *ret == p );
239 }
240
241 CPPUNIT_ASSERT( us.size() == 2 * NB_ELEMS );
242 typedef pair<int, int> ptype;
243 vector<ptype> us_val;
244
245 umaptype::local_iterator lit, litEnd;
246 for (i = 0; i < NB_ELEMS; ++i) {
247 lit = us.begin(us.bucket(i));
248 litEnd = us.end(us.bucket(i));
249
250 umaptype::size_type bucket_pos = us.bucket((*lit).first);
251 for (; lit != litEnd; ++lit) {
252 CPPUNIT_ASSERT( us.bucket((*lit).first) == bucket_pos );
253 us_val.push_back(ptype((*lit).first, (*lit).second));
254 }
255 }
256
257 sort(us_val.begin(), us_val.end());
258 for (i = 0; i < NB_ELEMS; ++i) {
259 ptype p(i, i);
260 CPPUNIT_ASSERT( us_val[i * 2] == p );
261 CPPUNIT_ASSERT( us_val[i * 2 + 1] == p );
262 }
263 #endif
264 }
265
user_case()266 void UnorderedTest::user_case()
267 {
268 #if defined (STLPORT)
269 typedef unordered_map<int, string> UnorderedMap1;
270 typedef unordered_map<int, UnorderedMap1> UnorderedMap2;
271
272 UnorderedMap1 foo;
273 UnorderedMap2 bar;
274
275 foo.insert(UnorderedMap1::value_type(1, string("test1")));
276 foo.insert(UnorderedMap1::value_type(2, string("test2")));
277 foo.insert(UnorderedMap1::value_type(3, string("test3")));
278 foo.insert(UnorderedMap1::value_type(4, string("test4")));
279 foo.insert(UnorderedMap1::value_type(5, string("test5")));
280
281 bar.insert(UnorderedMap2::value_type(0, foo));
282 UnorderedMap2::iterator it = bar.find(0);
283 CPPUNIT_ASSERT( it != bar.end() );
284
285 UnorderedMap1 &body = it->second;
286 UnorderedMap1::iterator cur = body.find(3);
287 CPPUNIT_ASSERT( cur != body.end() );
288
289 body.erase(body.begin(), body.end());
290 CPPUNIT_ASSERT( body.empty() );
291 #endif
292 }
293
hash_policy()294 void UnorderedTest::hash_policy()
295 {
296 #if defined (STLPORT)
297 unordered_set<int> int_uset;
298
299 CPPUNIT_ASSERT( int_uset.max_load_factor() == 1.0f );
300 CPPUNIT_ASSERT( int_uset.load_factor() == 0.0f );
301
302 size_t nbInserts = int_uset.bucket_count() - 1;
303 for (int i = 0; (size_t)i < nbInserts; ++i) {
304 int_uset.insert(i);
305 }
306 CPPUNIT_ASSERT( int_uset.size() == nbInserts );
307
308 int_uset.max_load_factor(0.5f);
309 int_uset.rehash(0);
310 CPPUNIT_ASSERT( int_uset.load_factor() < int_uset.max_load_factor() );
311
312 size_t bucketsHint = int_uset.bucket_count() + 1;
313 int_uset.rehash(bucketsHint);
314 CPPUNIT_ASSERT( int_uset.bucket_count() >= bucketsHint );
315
316 CPPUNIT_ASSERT( int_uset.key_eq()(10, 10) );
317 CPPUNIT_ASSERT( int_uset.hash_function()(10) == 10 );
318 #endif
319 }
320
buckets()321 void UnorderedTest::buckets()
322 {
323 #if defined (STLPORT)
324 unordered_set<int> int_uset;
325
326 CPPUNIT_ASSERT( int_uset.bucket_count() < int_uset.max_bucket_count() );
327
328 int i;
329 size_t nbBuckets = int_uset.bucket_count();
330 size_t nbInserts = int_uset.bucket_count() - 1;
331 for (i = 0; (size_t)i < nbInserts; ++i) {
332 int_uset.insert(i);
333 }
334 CPPUNIT_ASSERT( nbBuckets == int_uset.bucket_count() );
335
336 size_t bucketSizes = 0;
337 for (i = 0; (size_t)i < nbBuckets; ++i) {
338 bucketSizes += int_uset.bucket_size(i);
339 }
340 CPPUNIT_ASSERT( bucketSizes == int_uset.size() );
341 #endif
342 }
343
equal_range()344 void UnorderedTest::equal_range()
345 {
346 #if defined (STLPORT)
347 typedef unordered_multiset<size_t> umset;
348 {
349 //General test
350 umset iumset;
351 iumset.max_load_factor(10.0f);
352
353 size_t nbBuckets = iumset.bucket_count();
354
355 for (size_t i = 0; i < nbBuckets; ++i) {
356 iumset.insert(i);
357 iumset.insert(i + nbBuckets);
358 iumset.insert(i + 2 * nbBuckets);
359 iumset.insert(i + 3 * nbBuckets);
360 iumset.insert(i + 4 * nbBuckets);
361 }
362
363 CPPUNIT_ASSERT( nbBuckets == iumset.bucket_count() );
364 CPPUNIT_ASSERT( iumset.size() == 5 * nbBuckets );
365
366 pair<umset::iterator, umset::iterator> p = iumset.equal_range(1);
367 CPPUNIT_ASSERT( p.first != p.second );
368
369 size_t nbElems = iumset.size();
370 nbElems -= distance(p.first, p.second);
371 for (umset::iterator j = p.first; j != p.second;) {
372 iumset.erase(j++);
373 }
374
375 CPPUNIT_ASSERT( nbElems == iumset.size() );
376
377 p = iumset.equal_range(2);
378 CPPUNIT_ASSERT( p.first != p.second );
379 nbElems -= distance(p.first, p.second);
380 iumset.erase(p.first, p.second);
381 CPPUNIT_ASSERT( nbElems == iumset.size() );
382 }
383
384 {
385 //More specific test that tries to put many values in the same bucket
386 umset iumset;
387
388 size_t i;
389 //We are going to add at least 20 values, to get a stable hash container while doing that
390 //we force a large number of buckets:
391 iumset.rehash(193);
392
393 size_t nbBuckets = iumset.bucket_count();
394 const size_t targetedBucket = nbBuckets / 2;
395
396 //Lets put 10 values in the targeted bucket:
397 for (i = 0; i < 10; ++i) {
398 iumset.insert(targetedBucket + (i * nbBuckets));
399 }
400
401 //We put again 10 values in the targeted bucket and in reverse order:
402 for (i = 9; i <= 10; --i) {
403 iumset.insert(targetedBucket + (i * nbBuckets));
404 }
405
406 //Now we put some more elements until hash container is resized:
407 i = 0;
408 while (iumset.bucket_count() == nbBuckets) {
409 iumset.insert(i++);
410 }
411
412 //CPPUNIT_ASSERT( iumset.bucket_size(targetedBucket) == 21 );
413
414 pair<umset::iterator, umset::iterator> p = iumset.equal_range(targetedBucket);
415 CPPUNIT_ASSERT( p.first != p.second );
416 CPPUNIT_ASSERT( distance(p.first, p.second) == 3 );
417
418 // Now we remove some elements until hash container is resized:
419 nbBuckets = iumset.bucket_count();
420 while (iumset.bucket_count() == nbBuckets &&
421 !iumset.empty()) {
422 iumset.erase(iumset.begin());
423 }
424 CPPUNIT_ASSERT( iumset.load_factor() <= iumset.max_load_factor() );
425
426 // Adding an element back shouldn't change number of buckets:
427 nbBuckets = iumset.bucket_count();
428 iumset.insert(0);
429 CPPUNIT_ASSERT( iumset.bucket_count() == nbBuckets );
430 }
431
432 {
433 srand(0);
434 for (int runs = 0; runs < 2; ++runs) {
435 size_t magic = rand();
436 umset hum;
437 size_t c = 0;
438 for (int i = 0; i < 10000; ++i) {
439 if ((rand() % 500) == 0) {
440 hum.insert(magic);
441 ++c;
442 }
443 else {
444 size_t r;
445 while ((r = rand()) == magic)
446 ;
447 hum.insert(r);
448 }
449
450 /*
451 if ((float)(hum.size() + 1) / (float)hum.bucket_count() > hum.max_load_factor()) {
452 cout << "Hash container dump: Nb elems: " << hum.size() << ", Nb buckets: " << hum.bucket_count() << "\n";
453 for (size_t b = 0; b < hum.bucket_count(); ++b) {
454 if (hum.bucket_size(b) != 0) {
455 umset::local_iterator litBegin(hum.begin(b)), litEnd(hum.end(b));
456 cout << "B" << b << ": ";
457 for (umset::local_iterator lit = litBegin; lit != litEnd; ++lit) {
458 if (lit != litBegin) {
459 cout << " - ";
460 }
461 cout << *lit;
462 }
463 cout << "\n";
464 }
465 }
466 cout << endl;
467 }
468 */
469 }
470 CPPUNIT_ASSERT( hum.count(magic) == c );
471 }
472 }
473 #endif
474 }
475
benchmark1()476 void UnorderedTest::benchmark1()
477 {
478 #if defined (STLPORT)
479 typedef unordered_multiset<size_t> umset;
480
481 const size_t target = 500000;
482 umset iumset;
483 iumset.max_load_factor(10);
484 size_t i;
485 for (i = 0; i < target; ++i) {
486 iumset.insert(i);
487 }
488
489 for (i = 0; i < target; ++i) {
490 iumset.erase(i);
491 }
492 #endif
493 }
494
benchmark2()495 void UnorderedTest::benchmark2()
496 {
497 #if defined (STLPORT)
498 typedef unordered_multiset<size_t> umset;
499
500 const size_t target = 500000;
501 umset iumset;
502 iumset.max_load_factor(10);
503 size_t i;
504 for (i = 0; i < target; ++i) {
505 iumset.insert(target - i);
506 }
507
508 for (i = 0; i < target; ++i) {
509 iumset.erase(target - i);
510 }
511 #endif
512 }
513
514 struct Key
515 {
KeyKey516 Key() : m_data(0) {}
KeyKey517 explicit Key(int data) : m_data(data) {}
518
519 int m_data;
520
521 #if defined (__DMC__) // slist<_Tp,_Alloc>::remove error
522 bool operator==(const Key&) const;
523 #endif
524 };
525
526 struct KeyHash
527 {
operator ()KeyHash528 size_t operator () (Key key) const
529 { return (size_t)key.m_data; }
530
operator ()KeyHash531 size_t operator () (int data) const
532 { return (size_t)data; }
533 };
534
535 struct KeyEqual
536 {
operator ()KeyEqual537 bool operator () (Key lhs, Key rhs) const
538 { return lhs.m_data == rhs.m_data; }
539
operator ()KeyEqual540 bool operator () (Key lhs, int rhs) const
541 { return lhs.m_data == rhs; }
542
operator ()KeyEqual543 bool operator () (int lhs, Key rhs) const
544 { return lhs == rhs.m_data; }
545 };
546
547 struct KeyHashPtr
548 {
operator ()KeyHashPtr549 size_t operator () (Key const volatile *key) const
550 { return (size_t)key->m_data; }
551
operator ()KeyHashPtr552 size_t operator () (int data) const
553 { return (size_t)data; }
554 };
555
556 struct KeyEqualPtr
557 {
operator ()KeyEqualPtr558 bool operator () (Key const volatile *lhs, Key const volatile *rhs) const
559 { return lhs->m_data == rhs->m_data; }
560
operator ()KeyEqualPtr561 bool operator () (Key const volatile *lhs, int rhs) const
562 { return lhs->m_data == rhs; }
563
operator ()KeyEqualPtr564 bool operator () (int lhs, Key const volatile *rhs) const
565 { return lhs == rhs->m_data; }
566 };
567
template_methods()568 void UnorderedTest::template_methods()
569 {
570 #if defined (STLPORT) && defined (_STLP_USE_CONTAINERS_EXTENSION)
571 {
572 typedef unordered_set<Key, KeyHash, KeyEqual> Container;
573 Container cont;
574 cont.insert(Key(1));
575 cont.insert(Key(2));
576 cont.insert(Key(3));
577 cont.insert(Key(4));
578
579 CPPUNIT_ASSERT( cont.count(Key(1)) == 1 );
580 CPPUNIT_ASSERT( cont.count(1) == 1 );
581 CPPUNIT_ASSERT( cont.count(5) == 0 );
582
583 CPPUNIT_ASSERT( cont.find(2) != cont.end() );
584 CPPUNIT_ASSERT( cont.equal_range(2) != make_pair(cont.begin(), cont.end()) );
585
586 Container const& ccont = cont;
587 CPPUNIT_ASSERT( ccont.find(2) != ccont.end() );
588 CPPUNIT_ASSERT( ccont.bucket(2) == ccont.bucket(2) );
589 CPPUNIT_ASSERT( ccont.equal_range(2) != make_pair(ccont.begin(), ccont.end()) );
590 }
591
592 {
593 typedef unordered_set<Key*, KeyHashPtr, KeyEqualPtr> Container;
594 Container cont;
595 Key key1(1), key2(2), key3(3), key4(4);
596 cont.insert(&key1);
597 cont.insert(&key2);
598 cont.insert(&key3);
599 cont.insert(&key4);
600
601 CPPUNIT_ASSERT( cont.count(1) == 1 );
602 CPPUNIT_ASSERT( cont.count(5) == 0 );
603
604 CPPUNIT_ASSERT( cont.find(2) != cont.end() );
605 CPPUNIT_ASSERT( cont.equal_range(2) != make_pair(cont.begin(), cont.end()) );
606
607 Container const& ccont = cont;
608 CPPUNIT_ASSERT( ccont.find(2) != ccont.end() );
609 CPPUNIT_ASSERT( ccont.bucket(2) == ccont.bucket(2) );
610 CPPUNIT_ASSERT( ccont.equal_range(2) != make_pair(ccont.begin(), ccont.end()) );
611 }
612 {
613 typedef unordered_multiset<Key, KeyHash, KeyEqual> Container;
614 Container cont;
615 cont.insert(Key(1));
616 cont.insert(Key(2));
617 cont.insert(Key(1));
618 cont.insert(Key(2));
619
620 CPPUNIT_ASSERT( cont.count(Key(1)) == 2 );
621 CPPUNIT_ASSERT( cont.count(1) == 2 );
622 CPPUNIT_ASSERT( cont.count(5) == 0 );
623
624 CPPUNIT_ASSERT( cont.find(2) != cont.end() );
625 CPPUNIT_ASSERT( cont.equal_range(1) != make_pair(cont.end(), cont.end()) );
626
627 Container const& ccont = cont;
628 CPPUNIT_ASSERT( ccont.find(2) != ccont.end() );
629 CPPUNIT_ASSERT( ccont.bucket(2) == ccont.bucket(2) );
630 CPPUNIT_ASSERT( ccont.equal_range(2) != make_pair(ccont.end(), ccont.end()) );
631 }
632
633 {
634 typedef unordered_multiset<Key const volatile*, KeyHashPtr, KeyEqualPtr> Container;
635 Container cont;
636 Key key1(1), key2(2), key3(3), key4(4);
637 cont.insert(&key1);
638 cont.insert(&key2);
639 cont.insert(&key3);
640 cont.insert(&key4);
641
642 CPPUNIT_ASSERT( cont.count(1) == 1 );
643 CPPUNIT_ASSERT( cont.count(5) == 0 );
644
645 CPPUNIT_ASSERT( cont.find(2) != cont.end() );
646 CPPUNIT_ASSERT( cont.equal_range(2) != make_pair(cont.begin(), cont.end()) );
647
648 Container const& ccont = cont;
649 CPPUNIT_ASSERT( ccont.find(2) != ccont.end() );
650 CPPUNIT_ASSERT( ccont.bucket(2) == ccont.bucket(2) );
651 CPPUNIT_ASSERT( ccont.equal_range(2) != make_pair(ccont.begin(), ccont.end()) );
652 }
653 #endif
654 }
655
656 #if defined (STLPORT) && \
657 (!defined (_STLP_USE_PTR_SPECIALIZATIONS) || defined (_STLP_CLASS_PARTIAL_SPECIALIZATION))
658 # if !defined (__DMC__)
659 /* Simple compilation test: Check that nested types like iterator
660 * can be access even if type used to instanciate container is not
661 * yet completely defined.
662 */
663 class IncompleteClass
664 {
665 unordered_set<IncompleteClass> usinstances;
666 typedef unordered_set<IncompleteClass>::iterator usit;
667 unordered_multiset<IncompleteClass> usminstances;
668 typedef unordered_multiset<IncompleteClass>::iterator usmit;
669
670 unordered_map<IncompleteClass, IncompleteClass> uminstances;
671 typedef unordered_map<IncompleteClass, IncompleteClass>::iterator umit;
672 unordered_multimap<IncompleteClass, IncompleteClass> umminstances;
673 typedef unordered_multimap<IncompleteClass, IncompleteClass>::iterator ummit;
674 };
675 # endif
676 #endif
677