int TestFixedSList() { int nErrorCount = 0; { fixed_slist<int, 64> list0101; VERIFY(list0101.empty()); VERIFY(list0101.size() == 0); VERIFY(list0101.max_size() == 64); list0101.push_front(1); VERIFY(!list0101.empty()); VERIFY(list0101.size() == 1); list0101.resize(3, 2); VERIFY(!list0101.empty()); VERIFY(list0101.size() == 3); fixed_slist<int, 64>::iterator i = list0101.begin(); VERIFY(*i == 1); ++i; VERIFY(*i == 2); ++i; VERIFY(*i == 2); ++i; VERIFY(i == list0101.end()); list0101.resize(0); VERIFY(list0101.empty()); VERIFY(list0101.size() == 0); } { fixed_slist<int, 64, true, MallocAllocator> list0101; VERIFY(list0101.empty()); VERIFY(list0101.size() == 0); VERIFY(list0101.max_size() == 64); list0101.push_front(1); VERIFY(!list0101.empty()); VERIFY(list0101.size() == 1); list0101.resize(3, 2); VERIFY(!list0101.empty()); VERIFY(list0101.size() == 3); fixed_slist<int, 64>::iterator i = list0101.begin(); VERIFY(*i == 1); ++i; VERIFY(*i == 2); ++i; VERIFY(*i == 2); ++i; VERIFY(i == list0101.end()); while(list0101.size() < 64 + 16) list0101.push_front(0); list0101.resize(0); VERIFY(list0101.empty()); VERIFY(list0101.size() == 0); } { // Test fixed slist with overflow and alignment requirements. typedef fixed_slist<Align64, 1, true, CustomAllocator> FixedSListWithAlignment; FixedSListWithAlignment fsl; Align64 a; fsl.push_front(a); fsl.push_front(a); fsl.push_front(a); fsl.push_front(a); fsl.push_front(a); for (FixedSListWithAlignment::const_iterator it = fsl.begin(); it != fsl.end(); ++it) { const Align64* ptr = &(*it); EATEST_VERIFY((uint64_t)ptr % EASTL_ALIGN_OF(Align64) == 0); } } { // bool empty() const // bool has_overflowed() const // size_type size() const; // size_type max_size() const // Test a list that has overflow disabled. fixed_slist<int, 5, false> listInt5; VERIFY(listInt5.max_size() == 5); VERIFY(listInt5.size() == 0); VERIFY(listInt5.empty()); VERIFY(!listInt5.has_overflowed()); listInt5.push_front(37); listInt5.push_front(37); listInt5.push_front(37); VERIFY(listInt5.size() == 3); VERIFY(!listInt5.empty()); VERIFY(!listInt5.has_overflowed()); listInt5.push_front(37); listInt5.push_front(37); VERIFY(listInt5.size() == 5); VERIFY(!listInt5.empty()); VERIFY(!listInt5.has_overflowed()); listInt5.pop_front(); VERIFY(listInt5.size() == 4); VERIFY(!listInt5.empty()); VERIFY(!listInt5.has_overflowed()); } { // bool empty() const // bool has_overflowed() const // size_type size() const; // size_type max_size() const // Test a list that has overflow enabled. fixed_slist<int, 5, true> listInt5; VERIFY(listInt5.max_size() == 5); VERIFY(listInt5.size() == 0); VERIFY(listInt5.empty()); VERIFY(!listInt5.has_overflowed()); listInt5.push_front(37); listInt5.push_front(37); listInt5.push_front(37); VERIFY(listInt5.size() == 3); VERIFY(!listInt5.empty()); VERIFY(!listInt5.has_overflowed()); listInt5.push_front(37); listInt5.push_front(37); VERIFY(listInt5.size() == 5); VERIFY(!listInt5.empty()); VERIFY(!listInt5.has_overflowed()); listInt5.push_front(37); VERIFY(listInt5.size() == 6); VERIFY(!listInt5.empty()); VERIFY(listInt5.has_overflowed()); listInt5.pop_front(); VERIFY(listInt5.size() == 5); VERIFY(!listInt5.empty()); //VERIFY(listInt5.has_overflowed()); Disabled because currently has_overflowed can't detect this situation in non-debug builds. } { // fixed_slist(this_type&& x); // fixed_slist(this_type&&, const allocator_type&); // this_type& operator=(this_type&& x); #if EASTL_MOVE_SEMANTICS_ENABLED fixed_slist<TestObject, 16> slist3TO33(3, TestObject(33)); fixed_slist<TestObject, 16> toListA(eastl::move(slist3TO33)); EATEST_VERIFY((toListA.size() == 3) && (toListA.front().mX == 33) /* && (slist3TO33.size() == 0) fixed_list usually can't honor the move request. */); // The following is not as strong a test of this ctor as it could be. A stronger test would be to use IntanceAllocator with different instances. fixed_slist<TestObject, 16, true, MallocAllocator> slist4TO44(4, TestObject(44)); fixed_slist<TestObject, 16, true, MallocAllocator> toListB(eastl::move(slist4TO44), MallocAllocator()); EATEST_VERIFY((toListB.size() == 4) && (toListB.front().mX == 44) /* && (slist4TO44.size() == 0) fixed_list usually can't honor the move request. */); fixed_slist<TestObject, 16, true, MallocAllocator> slist5TO55(5, TestObject(55)); toListB = eastl::move(slist5TO55); EATEST_VERIFY((toListB.size() == 5) && (toListB.front().mX == 55) /* && (slist5TO55.size() == 0) fixed_list usually can't honor the move request. */); #endif } { #if EASTL_MOVE_SEMANTICS_ENABLED && EASTL_VARIADIC_TEMPLATES_ENABLED // template <class... Args> // void emplace_front(Args&&... args); // template <class... Args> // iterator emplace_after(const_iterator position, Args&&... args); #else #if EASTL_MOVE_SEMANTICS_ENABLED // void emplace_front(value_type&& value); // iterator emplace_after(const_iterator position, value_type&& value); #endif // void emplace_front(const value_type& value); // iterator emplace_after(const_iterator position, const value_type& value); #endif #if EASTL_MOVE_SEMANTICS_ENABLED && EASTL_VARIADIC_TEMPLATES_ENABLED TestObject::Reset(); fixed_slist<TestObject, 16> toListA; toListA.emplace_front(1, 2, 3); // This uses the TestObject(int x0, int x1, int x2, bool bThrowOnCopy) constructor. EATEST_VERIFY((toListA.size() == 1) && (toListA.front().mX == (1+2+3)) && (TestObject::sTOCtorCount == 1)); toListA.emplace_after(toListA.before_begin(), 3, 4, 5); EATEST_VERIFY((toListA.size() == 2) && (toListA.front().mX == (3+4+5)) && (TestObject::sTOCtorCount == 2)); #else #if EASTL_MOVE_SEMANTICS_ENABLED TestObject::Reset(); // We have a potential problem here in that the compiler is not required to use move construction below. // It is allowed to use standard copy construction if it wants. We could force it with eastl::move() usage. fixed_slist<TestObject, 16> toListA; toListA.emplace_front(TestObject(1, 2, 3)); EATEST_VERIFY((toListA.size() == 1) && (toListA.front().mX == (1+2+3)) && (TestObject::sTOMoveCtorCount == 1)); toListA.emplace_after(toListA.before_begin(), TestObject(3, 4, 5)); EATEST_VERIFY((toListA.size() == 2) && (toListA.front().mX == (3+4+5)) && (TestObject::sTOMoveCtorCount == 2)); #endif TestObject::Reset(); fixed_slist<TestObject, 16> toListB; TestObject to123(1, 2, 3); TestObject to345(3, 4, 5); toListB.emplace_front(to123); // This should use the const value_type& version and not the value_type&& version of emplace_front. EATEST_VERIFY((toListB.size() == 1) && (toListB.front().mX == (1+2+3)) && (TestObject::sTOCopyCtorCount == 1)); toListB.emplace_after(toListB.before_begin(), to345); EATEST_VERIFY((toListB.size() == 2) && (toListB.front().mX == (3+4+5)) && (TestObject::sTOCopyCtorCount == 2)); EATEST_VERIFY(to123.mX == (1+2+3)); // Verify that the object was copied and not moved. If it was moved then mX would be 0 and not 1+2+3. EATEST_VERIFY(to345.mX == (3+4+5)); #endif #if EASTL_MOVE_SEMANTICS_ENABLED // This test is similar to the emplace EASTL_MOVE_SEMANTICS_ENABLED pathway above. TestObject::Reset(); // void push_front(T&& x); // iterator insert(const_iterator position, T&& x); fixed_slist<TestObject, 16> toListC; toListC.push_front(TestObject(1, 2, 3)); EATEST_VERIFY((toListC.size() == 1) && (toListC.front().mX == (1+2+3)) && (TestObject::sTOMoveCtorCount == 1)); toListC.insert_after(toListC.before_begin(), TestObject(3, 4, 5)); EATEST_VERIFY((toListC.size() == 2) && (toListC.front().mX == (3+4+5)) && (TestObject::sTOMoveCtorCount == 2)); #endif } { // slist(std::initializer_list<value_type> ilist, const allocator_type& allocator = EASTL_SLIST_DEFAULT_ALLOCATOR); // this_type& operator=(std::initializer_list<value_type>); // void assign(std::initializer_list<value_type> ilist); // iterator insert_after(iterator position, std::initializer_list<value_type> ilist); #if !defined(EA_COMPILER_NO_INITIALIZER_LISTS) fixed_slist<int, 8> intList = { 0, 1, 2 }; EATEST_VERIFY(VerifySequence(intList.begin(), intList.end(), int(), "fixed_slist std::initializer_list", 0, 1, 2, -1)); intList = { 13, 14, 15 }; EATEST_VERIFY(VerifySequence(intList.begin(), intList.end(), int(), "fixed_slist std::initializer_list", 13, 14, 15, -1)); intList.assign({ 16, 17, 18 }); EATEST_VERIFY(VerifySequence(intList.begin(), intList.end(), int(), "fixed_slist std::initializer_list", 16, 17, 18, -1)); fixed_slist<int, 8>::iterator it = intList.insert_after(intList.before_begin(), { 14, 15 }); EATEST_VERIFY(VerifySequence(intList.begin(), intList.end(), int(), "fixed_slist std::initializer_list", 14, 15, 16, 17, 18, -1)); EATEST_VERIFY(*it == 15); // Note that slist::insert_after returns the last inserted element, not the first as with list::insert. #endif } { // Test construction of a container with an overflow allocator constructor argument. // // GCC 4.4 has a hard time compiling this code correctly in optimized builds as it // omits the increment of the mAllocCount field when calling overflowAllocator.allocate. #if defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION == 4004) MallocAllocator overflowAllocator; fixed_slist<int, 64, true, MallocAllocator> c(overflowAllocator); c.resize(65); VERIFY(c.get_overflow_allocator().mAllocCount == 1); // 1 for overflowing from 64 to 65. #else MallocAllocator overflowAllocator; void* p = overflowAllocator.allocate(1); fixed_slist<int, 64, true, MallocAllocator> c(overflowAllocator); c.resize(65); VERIFY(c.get_overflow_allocator().mAllocCount == 2); // 1 for above, and 1 for overflowing from 64 to 65. overflowAllocator.deallocate(p, 1); #endif } // We can't do this, due to how Reset is used above: // EATEST_VERIFY(TestObject::IsClear()); EATEST_VERIFY(TestObject::sMagicErrorCount == 0); TestObject::Reset(); return nErrorCount; }
int TestFixedHash() { EASTLTest_Printf("TestFixedHash\n"); int nErrorCount = 0; { // fixed_hash_map { // Test version *without* pool overflow. typedef eastl::fixed_hash_map<int, int, 100, 100, false> FixedHashMapFalse; FixedHashMapFalse fixedHashMap; fixedHashMap[0] = 0; fixedHashMap.insert(FixedHashMapFalse::value_type(0, 0)); VERIFY(fixedHashMap.max_size() == 100); VERIFY(fixedHashMap.size() == 1); fixedHashMap.clear(); VERIFY(fixedHashMap.size() == 0); for(int i = 0; i < 100; i++) fixedHashMap.insert(FixedHashMapFalse::value_type(i, i)); VERIFY(fixedHashMap.size() == 100); // Verify that we allocated enough space for exactly N items. // It's possible that due to alignments, there might be room for N + 1. FixedHashMapFalse::allocator_type& allocator = fixedHashMap.get_allocator(); void* pResult = allocator.allocate(sizeof(FixedHashMapFalse::node_type)); if(pResult) { pResult = allocator.allocate(sizeof(FixedHashMapFalse::node_type)); VERIFY(pResult == NULL); } fixedHashMap.clear(true); VERIFY(fixedHashMap.validate()); VERIFY(fixedHashMap.size() == 0); VERIFY(fixedHashMap.bucket_count() == 1); } { // Test version *with* pool overflow. typedef eastl::fixed_hash_map<int, int, 100, 100, true> FixedHashMapTrue; FixedHashMapTrue fixedHashMap; fixedHashMap[0] = 0; fixedHashMap.insert(FixedHashMapTrue::value_type(0, 0)); VERIFY(fixedHashMap.max_size() == 100); VERIFY(fixedHashMap.size() == 1); fixedHashMap.clear(); VERIFY(fixedHashMap.size() == 0); for(int i = 0; i < 100; i++) fixedHashMap.insert(FixedHashMapTrue::value_type(i, i)); VERIFY(fixedHashMap.size() == 100); FixedHashMapTrue::allocator_type& allocator = fixedHashMap.get_allocator(); void* pResult = allocator.allocate(sizeof(FixedHashMapTrue::node_type)); VERIFY(pResult != NULL); allocator.deallocate(pResult, sizeof(FixedHashMapTrue::node_type)); fixedHashMap.clear(true); VERIFY(fixedHashMap.validate()); VERIFY(fixedHashMap.size() == 0); VERIFY(fixedHashMap.bucket_count() == 1); // get_overflow_allocator / set_overflow_allocator // This is a weak test which should be improved. EASTLAllocatorType a = fixedHashMap.get_allocator().get_overflow_allocator(); fixedHashMap.get_allocator().set_overflow_allocator(a); } { // Test fixed_hash_map *with* overflow and ensure the underlying hashtable rehashes. typedef eastl::fixed_hash_map<unsigned int, unsigned int, 512, 513, true, eastl::hash<unsigned int>, eastl::equal_to<unsigned int>, false, MallocAllocator> FixedHashMap; FixedHashMap fixedHashMap; auto old_bucket_count = fixedHashMap.bucket_count(); auto old_load_factor = fixedHashMap.load_factor(); for (int i = 0; i < 1000; i++) fixedHashMap.insert(i); auto new_bucket_count = fixedHashMap.bucket_count(); auto new_load_factor = fixedHashMap.load_factor(); VERIFY(new_bucket_count != old_bucket_count); VERIFY(new_bucket_count > old_bucket_count); VERIFY(new_load_factor != old_load_factor); VERIFY(fixedHashMap.get_overflow_allocator().mAllocCountAll != 0); } { // Test version with overflow and alignment requirements. typedef fixed_hash_map<Align64, int, 1, 2, true> FixedHashMapWithAlignment; typedef fixed_hash_multimap<Align64, int, 1, 2, true> FixedHashMultiMapWithAlignment; typedef fixed_hash_set<Align64, 1, 2, true> FixedHashSetWithAlignment; typedef fixed_hash_multiset<Align64, 1, 2, true> FixedHashMultiSetWithAlignment; FixedHashMapWithAlignment fhm; FixedHashMultiMapWithAlignment fhmm; FixedHashSetWithAlignment fhs; FixedHashMultiSetWithAlignment fhms; Align64 a; a.mX = 1; Align64 b; b.mX = 2; Align64 c; c.mX = 3; Align64 d; d.mX = 4; Align64 e; e.mX = 5; fhm.insert(a); fhm.insert(b); fhm.insert(c); fhm.insert(d); fhm.insert(e); for (FixedHashMapWithAlignment::const_iterator it = fhm.begin(); it != fhm.end(); ++it) { const Align64* ptr = &((*it).first); EATEST_VERIFY((uint64_t)ptr % EASTL_ALIGN_OF(Align64) == 0); } fhmm.insert(a); fhmm.insert(b); fhmm.insert(c); fhmm.insert(d); fhmm.insert(e); for (FixedHashMultiMapWithAlignment::const_iterator it = fhmm.begin(); it != fhmm.end(); ++it) { const Align64* ptr = &((*it).first); EATEST_VERIFY((uint64_t)ptr % EASTL_ALIGN_OF(Align64) == 0); } fhs.insert(a); fhs.insert(b); fhs.insert(c); fhs.insert(d); fhs.insert(e); for (FixedHashSetWithAlignment::const_iterator it = fhs.begin(); it != fhs.end(); ++it) { const Align64* ptr = &(*it); EATEST_VERIFY((uint64_t)ptr % EASTL_ALIGN_OF(Align64) == 0); } fhms.insert(a); fhms.insert(b); fhms.insert(c); fhms.insert(d); fhms.insert(e); for (FixedHashMultiSetWithAlignment::const_iterator it = fhms.begin(); it != fhms.end(); ++it) { const Align64* ptr = &(*it); EATEST_VERIFY((uint64_t)ptr % EASTL_ALIGN_OF(Align64) == 0); } } { typedef eastl::fixed_hash_map<int, A, 100, 100> FixedHashMap; FixedHashMap fixedHashMap; fixedHashMap[0] = A(); fixedHashMap.insert(FixedHashMap::value_type(0, A())); VERIFY(fixedHashMap.size() == 1); } { typedef eastl::fixed_hash_map<A, int, 100, 100> FixedHashMap; FixedHashMap fixedHashMap; fixedHashMap[A()] = 0; fixedHashMap.insert(FixedHashMap::value_type(A(), 0)); VERIFY(fixedHashMap.size() == 1); } // explicitly instantiate some templated member functions { typedef eastl::fixed_hash_map<int, int, 100, 100, true> FixedHashMapTrue; FixedHashMapTrue::value_type testValues[] = { eastl::make_pair(0, 0), eastl::make_pair(1,1) }; FixedHashMapTrue fixedHashMap(testValues, testValues + EAArrayCount(testValues)); VERIFY(fixedHashMap.size() == 2); } } { // fixed_hash_multimap { typedef eastl::fixed_hash_multimap<int, int, 100, 100> FixedHashMultiMap; FixedHashMultiMap fixedHashMultiMap; fixedHashMultiMap.insert(FixedHashMultiMap::value_type(0, 0)); fixedHashMultiMap.insert(FixedHashMultiMap::value_type(0, 0)); VERIFY(fixedHashMultiMap.max_size() == 100); VERIFY(fixedHashMultiMap.size() == 2); } // explicitly instantiate some templated member functions { typedef eastl::fixed_hash_multimap<int, int, 100, 100, true> FixedHashMultiMap; FixedHashMultiMap::value_type testValues[] = { eastl::make_pair(0, 0), eastl::make_pair(1,1) }; FixedHashMultiMap fixedHashMultiMap(testValues, testValues + EAArrayCount(testValues)); VERIFY(fixedHashMultiMap.size() == 2); } } { // fixed_hash_set { typedef eastl::fixed_hash_set<int, 100, 100> FixedHashSet; FixedHashSet fixedHashSet; fixedHashSet.insert(0); fixedHashSet.insert(0); VERIFY(fixedHashSet.size() == 1); fixedHashSet.clear(); VERIFY(fixedHashSet.size() == 0); for(int i = 0; i < 100; i++) fixedHashSet.insert(i); VERIFY(fixedHashSet.max_size() == 100); VERIFY(fixedHashSet.size() == 100); fixedHashSet.clear(true); VERIFY(fixedHashSet.validate()); VERIFY(fixedHashSet.size() == 0); VERIFY(fixedHashSet.bucket_count() == 1); } { typedef eastl::fixed_hash_set<A, 100, 100> FixedHashSet; FixedHashSet fixedHashSet; fixedHashSet.insert(A()); fixedHashSet.insert(A()); VERIFY(fixedHashSet.max_size() == 100); VERIFY(fixedHashSet.size() == 1); } // explicitly instantiate some templated member functions { typedef eastl::fixed_hash_set<A, 100, 100> FixedHashSet; FixedHashSet::value_type testValues[] = { 0, 1 }; FixedHashSet fixedHashSet(testValues, testValues + EAArrayCount(testValues)); VERIFY(fixedHashSet.size() == 2); } } { // fixed_hash_multiset { typedef eastl::fixed_hash_multiset<int, 100, 100> FixedHashMultiSet; FixedHashMultiSet fixedHashMultiSet; fixedHashMultiSet.insert(0); fixedHashMultiSet.insert(0); VERIFY(fixedHashMultiSet.size() == 2); } // explicitly instantiate some templated member functions { typedef eastl::fixed_hash_multiset<A, 100, 100> FixedHashMultiSet; FixedHashMultiSet::value_type testValues[] = { 0, 1 }; FixedHashMultiSet fixedHashMultiSet(testValues, testValues + EAArrayCount(testValues)); VERIFY(fixedHashMultiSet.size() == 2); } } { // Tests of various bucketCount values. { typedef eastl::fixed_hash_set<int, 1, 2> FixedHashSet; FixedHashSet fixedHashSet; fixedHashSet.insert(0); VERIFY(fixedHashSet.size() == 1); } { typedef eastl::fixed_hash_set<int, 2, 2> FixedHashSet; FixedHashSet fixedHashSet; fixedHashSet.insert(0); fixedHashSet.insert(1); VERIFY(fixedHashSet.size() == 2); } { typedef eastl::fixed_hash_set<int, 11, 11> FixedHashSet; // 11 is one of the hashtable prime numbers. FixedHashSet fixedHashSet; for(int i = 0; i < 11; i++) fixedHashSet.insert(i); VERIFY(fixedHashSet.size() == 11); } { typedef eastl::fixed_hash_set<int, 11, 11> FixedHashSet; // 11 is one of the hashtable prime numbers. FixedHashSet fixedHashSet; VERIFY(fixedHashSet.validate()); VERIFY(fixedHashSet.size() == 0); // Clear a newly constructed, already empty container. fixedHashSet.clear(true); VERIFY(fixedHashSet.validate()); VERIFY(fixedHashSet.size() == 0); VERIFY(fixedHashSet.bucket_count() == 1); for(int i = 0; i < 11; i++) fixedHashSet.insert(i); VERIFY(fixedHashSet.size() == 11); VERIFY(fixedHashSet.bucket_count() > 1); fixedHashSet.clear(true); VERIFY(fixedHashSet.validate()); VERIFY(fixedHashSet.size() == 0); VERIFY(fixedHashSet.bucket_count() == 1); for(int i = 0; i < 11; i++) fixedHashSet.insert(i); VERIFY(fixedHashSet.size() == 11); } } { // Test of user-reported crash. // MemoryAddressToGroupMap is a container used by one team to associate debug // information with memory allocations. A crash due to corruption of the // fixed size node pool was reported on consoles (no crash on PC platform). const eastl_size_t kMemoryAddressMapNodeCount = 500000; typedef eastl::fixed_hash_map< const void*, // Key MemoryEntry, // Value kMemoryAddressMapNodeCount, // Node Count kMemoryAddressMapNodeCount + 1, // Bucket Count true, // Enable Overflow eastl::hash<const void*>, // Hash eastl::equal_to<const void*>, // Predicate false, // Cache Hash Code eastl::allocator // Allocator > MemoryAddressToGroupMap; MemoryAddressToGroupMap* pMap = new MemoryAddressToGroupMap; EA::UnitTest::Rand rng(EA::UnitTest::GetRandSeed()); // We simulate the usage of MemoryAddressToGroupMap via simulated alloc/free actions. for(eastl_size_t i = 0; i < kMemoryAddressMapNodeCount * 2; i++) { void* const p = (void*)(uintptr_t)rng.RandLimit(kMemoryAddressMapNodeCount); if(pMap->find(p) == pMap->end()) (*pMap)[p] = MemoryEntry(); else pMap->erase(p); } delete pMap; } { // Test of bug reported by Dave Wall, May 14, 2008. const size_t kNumBuckets = 10; // Bug only occurred with kNumBuckets == 10 or 11. typedef eastl::fixed_hash_map<const InstanceRenderData, uint32_t, kNumBuckets, kNumBuckets + 1, false> Map; Map map; InstanceRenderData renderData; uint32_t count = (uint32_t)kNumBuckets; while(count--) { renderData.mPad[0] = count; map.insert(Map::value_type(renderData, count)); } } { // Test construction of a container with an overflow allocator constructor argument. MallocAllocator overflowAllocator; void* p = overflowAllocator.allocate(1); typedef eastl::fixed_hash_map<int, int, 64, 100, true, eastl::hash<int>, eastl::equal_to<int>, false, MallocAllocator> Container; Container c(overflowAllocator); for(int i = 0; i < 65; i++) c.insert(Container::value_type(i, i)); VERIFY(c.get_overflow_allocator().mAllocCount == 2); // 1 for above, and 1 for overflowing from 64 to 65. overflowAllocator.deallocate(p, 1); } { // C++11 emplace and related functionality nErrorCount += TestMapCpp11<eastl::fixed_hash_map<int, TestObject, 2, 7, true> >(); // Exercize a low-capacity fixed-size container. nErrorCount += TestMapCpp11<eastl::fixed_hash_map<int, TestObject, 32, 7, true> >(); nErrorCount += TestMapCpp11NonCopyable<eastl::fixed_hash_map<int, NonCopyable, 2, 7, true>>(); nErrorCount += TestSetCpp11<eastl::fixed_hash_set<TestObject, 2, 7, true> >(); nErrorCount += TestSetCpp11<eastl::fixed_hash_set<TestObject, 32, 7, true> >(); nErrorCount += TestMultimapCpp11<eastl::fixed_hash_multimap<int, TestObject, 2, 7, true> >(); nErrorCount += TestMultimapCpp11<eastl::fixed_hash_multimap<int, TestObject, 32, 7, true> >(); nErrorCount += TestMultisetCpp11<eastl::fixed_hash_multiset<TestObject, 2, 7, true> >(); nErrorCount += TestMultisetCpp11<eastl::fixed_hash_multiset<TestObject, 32, 7, true> >(); } { // initializer_list support. #if !defined(EA_COMPILER_NO_INITIALIZER_LISTS) && !defined(_MSC_VER) //MSVC2013 cannot handle nested initializer lists properly. A bug report will be submitted for this. // fixed_hash_set(std::initializer_list<value_type> ilist, const overflow_allocator_type& overflowAllocator = EASTL_FIXED_HASH_SET_DEFAULT_ALLOCATOR) // this_type& operator=(std::initializer_list<value_type> ilist); // void insert(std::initializer_list<value_type> ilist); fixed_hash_set<int, 11> intHashSet = { 12, 13, 14 }; EATEST_VERIFY(intHashSet.size() == 3); EATEST_VERIFY(intHashSet.find(12) != intHashSet.end()); EATEST_VERIFY(intHashSet.find(13) != intHashSet.end()); EATEST_VERIFY(intHashSet.find(14) != intHashSet.end()); intHashSet = { 22, 23, 24 }; EATEST_VERIFY(intHashSet.size() == 3); EATEST_VERIFY(intHashSet.find(22) != intHashSet.end()); EATEST_VERIFY(intHashSet.find(23) != intHashSet.end()); EATEST_VERIFY(intHashSet.find(24) != intHashSet.end()); intHashSet.insert({ 42, 43, 44 }); EATEST_VERIFY(intHashSet.size() == 6); EATEST_VERIFY(intHashSet.find(42) != intHashSet.end()); EATEST_VERIFY(intHashSet.find(43) != intHashSet.end()); EATEST_VERIFY(intHashSet.find(44) != intHashSet.end()); // hash_map(std::initializer_list<value_type> ilist, const overflow_allocator_type& overflowAllocator = EASTL_FIXED_HASH_SET_DEFAULT_ALLOCATOR) // this_type& operator=(std::initializer_list<value_type> ilist); // void insert(std::initializer_list<value_type> ilist); fixed_hash_map<int, double, 11> intHashMap = { {12,12.0}, {13,13.0}, {14,14.0} }; EATEST_VERIFY(intHashMap.size() == 3); EATEST_VERIFY(intHashMap.find(12) != intHashMap.end()); EATEST_VERIFY(intHashMap.find(13) != intHashMap.end()); EATEST_VERIFY(intHashMap.find(14) != intHashMap.end()); intHashMap = { {22,22.0}, {23,23.0}, {24,24.0} }; EATEST_VERIFY(intHashMap.size() == 3); EATEST_VERIFY(intHashMap.find(22) != intHashMap.end()); EATEST_VERIFY(intHashMap.find(23) != intHashMap.end()); EATEST_VERIFY(intHashMap.find(24) != intHashMap.end()); intHashMap.insert({ {42,42.0}, {43,43.0}, {44,44.0} }); EATEST_VERIFY(intHashMap.size() == 6); EATEST_VERIFY(intHashMap.find(42) != intHashMap.end()); EATEST_VERIFY(intHashMap.find(43) != intHashMap.end()); EATEST_VERIFY(intHashMap.find(44) != intHashMap.end()); #endif } return nErrorCount; }
int TestFixedVector() { int nErrorCount = 0; TestObject::Reset(); { // Test the aligned_buffer template { eastl::aligned_buffer<sizeof(TestObject), EASTL_ALIGN_OF(TestObject)> toAlignedBuffer; TestObject* const pTO = new(toAlignedBuffer.buffer) TestObject; #if !defined(__GNUC__) // GCC complains about strict aliasing here. EATEST_VERIFY(pTO->mX == ((TestObject*)&toAlignedBuffer.buffer[0])->mX); #endif pTO->~TestObject(); } { eastl::aligned_buffer<sizeof(Align64), EASTL_ALIGN_OF(Align64)> a64AlignedBuffer; Align64* const pAlign64 = new(a64AlignedBuffer.buffer) Align64; #if !defined(__GNUC__) // GCC complains about strict aliasing here. EATEST_VERIFY(pAlign64->mX == ((Align64*)&a64AlignedBuffer.buffer[0])->mX); #endif pAlign64->~Align64(); } } { // fixed_vector(); // size_type max_size() const; fixed_vector<int, 1, true> v; EATEST_VERIFY(VerifySequence(v.begin(), v.end(), int(), "fixed_vector", -1)); EATEST_VERIFY(v.max_size() == 1); // fixed_vector(); typedef fixed_vector<int, 8, false> FixedVectorInt8; FixedVectorInt8 fv1; EATEST_VERIFY(fv1.size() == 0); EATEST_VERIFY(fv1.capacity() == 8); // this_type& operator=(const base_type& x); FixedVectorInt8 fv2 = fv1; EATEST_VERIFY(fv2.size() == 0); EATEST_VERIFY(fv2.capacity() == 8); // fixed_vector(const base_type& x); FixedVectorInt8 fv3(fv1); EATEST_VERIFY(fv3.size() == 0); EATEST_VERIFY(fv3.capacity() == 8); // explicit fixed_vector(size_type n); FixedVectorInt8 fv4(5); EATEST_VERIFY(fv4.size() == 5); EATEST_VERIFY(fv4.capacity() == 8); EATEST_VERIFY((fv4[0] == 0) && (fv4[4] == 0)); // fixed_vector(size_type n, const value_type& value); FixedVectorInt8 fv5((eastl_size_t)5, (int)3); EATEST_VERIFY(fv5.size() == 5); EATEST_VERIFY(fv5.capacity() == 8); EATEST_VERIFY((fv5[0] == 3) && (fv5[4] == 3)); // fixed_vector(InputIterator first, InputIterator last); const int intArray[8] = { 0, 1, 2, 3, 4, 5, 6, 7 }; FixedVectorInt8 fv6(intArray, intArray + 8); EATEST_VERIFY(fv6.size() == 8); EATEST_VERIFY(fv5.capacity() == 8); EATEST_VERIFY((fv6[0] == 0) && (fv6[7] == 7)); // void reset_lose_memory(); fv6.reset_lose_memory(); EATEST_VERIFY(fv6.size() == 0); EATEST_VERIFY(fv6.capacity() == 8); // void set_capacity(size_type); fv6.set_capacity(100); // overflow is disabled, so this should have no effect. EATEST_VERIFY(fv6.size() == 0); EATEST_VERIFY(fv6.capacity() == 8); // EATEST_VERIFY that the capacity is unchanged. fv6.resize(8); EATEST_VERIFY(fv6.size() == 8); fv6.set_capacity(1); EATEST_VERIFY(fv6.size() == 1); EATEST_VERIFY(fv6.capacity() == 8); // Exercise the freeing of memory in set_capacity. fixed_vector<int, 8, true> fv88; eastl_size_t capacity = fv88.capacity(); fv88.resize(capacity); fv88.set_capacity(capacity * 2); EATEST_VERIFY(fv88.capacity() >= (capacity * 2)); // void swap(this_type& x); FixedVectorInt8 fv7(5, 3); FixedVectorInt8 fv8(intArray, intArray + 8); swap(fv7, fv8); EATEST_VERIFY(fv7.size() == 8); EATEST_VERIFY((fv7[0] == 0) && (fv7[7] == 7)); EATEST_VERIFY(fv8.size() == 5); EATEST_VERIFY((fv8[0] == 3) && (fv8[4] == 3)); fv7.swap(fv8); EATEST_VERIFY(fv8.size() == 8); EATEST_VERIFY((fv8[0] == 0) && (fv8[7] == 7)); EATEST_VERIFY(fv7.size() == 5); EATEST_VERIFY((fv7[0] == 3) && (fv7[4] == 3)); // Test a recent optimization we added, which was to do a pointer swap of the fixed_vector pointers // for the case that both fixed_vectors were overflowed and using the heap instead of their fixed buffers. fixed_vector<int8_t, 4, true> fvo5; fixed_vector<int8_t, 4, true> fvo6; fvo5.resize(5, 5); EATEST_VERIFY(fvo5.has_overflowed()); fvo6.resize(6, 6); EATEST_VERIFY(fvo6.has_overflowed()); fvo5.swap(fvo6); EATEST_VERIFY(fvo5.size() == 6); // Verify that sizes are swapped. EATEST_VERIFY(fvo6.size() == 5); EATEST_VERIFY(EA::StdC::Memcheck8(fvo5.data(), 6, fvo5.size()) == NULL); // Verify that contents are swapped. EATEST_VERIFY(EA::StdC::Memcheck8(fvo6.data(), 5, fvo6.size()) == NULL); // global operators EATEST_VERIFY( fv7 != fv8); EATEST_VERIFY(!(fv7 == fv8)); fv7 = fv8; EATEST_VERIFY( fv7 == fv8); EATEST_VERIFY(!(fv7 != fv8)); EATEST_VERIFY(fv7.validate()); EATEST_VERIFY(fv8.validate()); } { // POD types typedef fixed_vector<int, 1, true> vInt; vInt v; int n = 5; int* pN = &n; v.insert(v.begin(), pN, pN + 1); EATEST_VERIFY(VerifySequence(v.begin(), v.end(), int(), "fixed_vector", 5, -1)); EATEST_VERIFY(v.validate()); } { // non POD types typedef fixed_vector<TestObject, 1, true> VTO; VTO v; TestObject to(5); TestObject* pTO = &to; v.insert(v.begin(), pTO, pTO + 1); EATEST_VERIFY(VerifySequence(v.begin(), v.end(), int(), "fixed_vector", 5, -1)); EATEST_VERIFY(v.validate()); } { // non POD types // The variables used here are declared above in the global space. vA64.insert(vA64.begin(), pA64, pA64 + 1); EATEST_VERIFY(VerifySequence(vA64.begin(), vA64.end(), int(), "fixed_vector", 5, -1)); EATEST_VERIFY(((uintptr_t)&a64 % kEASTLTestAlign64) == 0); EATEST_VERIFY(((uintptr_t)vA64.data() % kEASTLTestAlign64) == 0); EATEST_VERIFY(((uintptr_t)&vA64[0] % kEASTLTestAlign64) == 0); EATEST_VERIFY(vA64.max_size() == 3); EATEST_VERIFY(vA64.validate()); } { // Test for potential bug reported Sep. 19, 2006. typedef eastl::fixed_vector<void*, 160, false> FixedVector; FixedVector v; int* p = (int*)(uintptr_t)0; for(int i = 0; i < 100; i++, p++) v.push_back(p); EATEST_VERIFY(v.size() == 100); EATEST_VERIFY(eastl::unique(v.begin(), v.end()) == v.end()); FixedVector::iterator it = eastl::lower_bound(v.begin(), v.end(), p - 30); EATEST_VERIFY(v.validate_iterator(it) == (isf_valid | isf_current | isf_can_dereference)); EATEST_VERIFY((*it) == (p - 30)); v.erase(it); EATEST_VERIFY(v.size() == 99); EATEST_VERIFY(eastl::unique(v.begin(), v.end()) == v.end()); } { typedef fixed_vector<Align64, 4, true, CustomAllocator> FixedVectorWithAlignment; FixedVectorWithAlignment fv; Align64 a; fv.push_back(a); fv.push_back(a); fv.push_back(a); fv.push_back(a); fv.push_back(a); for (FixedVectorWithAlignment::const_iterator it = fv.begin(); it != fv.end(); ++it) { const Align64* ptr = &(*it); EATEST_VERIFY((uint64_t)ptr % EASTL_ALIGN_OF(Align64) == 0); } } { // Test overflow allocator specification typedef fixed_vector<char8_t, 64, true, MallocAllocator> FixedString64Malloc; FixedString64Malloc fs; fs.push_back('a'); EATEST_VERIFY(fs.size() == 1); EATEST_VERIFY(fs[0] == 'a'); fs.resize(95); fs[94] = 'b'; EATEST_VERIFY(fs[0] == 'a'); EATEST_VERIFY(fs[94] == 'b'); EATEST_VERIFY(fs.size() == 95); EATEST_VERIFY(fs.validate()); fs.clear(); EATEST_VERIFY(fs.empty()); fs.push_back('a'); EATEST_VERIFY(fs.size() == 1); EATEST_VERIFY(fs[0] == 'a'); EATEST_VERIFY(fs.validate()); fs.resize(195); fs[194] = 'b'; EATEST_VERIFY(fs[0] == 'a'); EATEST_VERIFY(fs[194] == 'b'); EATEST_VERIFY(fs.size() == 195); EATEST_VERIFY(fs.validate()); // get_overflow_allocator / set_overflow_allocator fs.set_capacity(0); // This should free all memory allocated by the existing (overflow) allocator. EATEST_VERIFY(fs.validate()); MallocAllocator a; fs.get_allocator().set_overflow_allocator(a); EATEST_VERIFY(fs.validate()); fs.resize(400); EATEST_VERIFY(fs.validate()); } { //Test clear(bool freeOverflow) const size_t nodeCount = 4; typedef fixed_vector<int, nodeCount, true> vInt4; vInt4 fv; for (int i = 0; (unsigned)i < nodeCount+1; i++) { fv.push_back(i); } vInt4::size_type capacity = fv.capacity(); EATEST_VERIFY(capacity >= nodeCount+1); fv.clear(false); EATEST_VERIFY(fv.size() == 0); EATEST_VERIFY(fv.capacity() == capacity); fv.push_back(1); fv.clear(true); EATEST_VERIFY(fv.size() == 0); EATEST_VERIFY(fv.capacity() == nodeCount); } { // bool empty() const // bool has_overflowed() const // size_type size() const; // size_type max_size() const // Test a vector that has overflow disabled. fixed_vector<int, 5, false> vInt5; EATEST_VERIFY(vInt5.max_size() == 5); EATEST_VERIFY(vInt5.size() == 0); EATEST_VERIFY(vInt5.empty()); EATEST_VERIFY(!vInt5.has_overflowed()); vInt5.push_back(37); vInt5.push_back(37); vInt5.push_back(37); EATEST_VERIFY(vInt5.size() == 3); EATEST_VERIFY(!vInt5.empty()); EATEST_VERIFY(!vInt5.has_overflowed()); vInt5.push_back(37); vInt5.push_back(37); EATEST_VERIFY(vInt5.size() == 5); EATEST_VERIFY(!vInt5.empty()); EATEST_VERIFY(!vInt5.has_overflowed()); vInt5.pop_back(); EATEST_VERIFY(vInt5.size() == 4); EATEST_VERIFY(!vInt5.empty()); EATEST_VERIFY(!vInt5.has_overflowed()); EATEST_VERIFY(vInt5.validate()); } { // bool empty() const // bool has_overflowed() const // size_type size() const; // size_type max_size() const // Test a list that has overflow enabled. fixed_vector<int, 5, true> vInt5; EATEST_VERIFY(vInt5.max_size() == 5); EATEST_VERIFY(vInt5.size() == 0); EATEST_VERIFY(vInt5.empty()); EATEST_VERIFY(!vInt5.has_overflowed()); vInt5.push_back(37); vInt5.push_back(37); vInt5.push_back(37); EATEST_VERIFY(vInt5.size() == 3); EATEST_VERIFY(!vInt5.empty()); EATEST_VERIFY(!vInt5.has_overflowed()); vInt5.push_back(37); vInt5.push_back(37); EATEST_VERIFY(vInt5.size() == 5); EATEST_VERIFY(!vInt5.empty()); EATEST_VERIFY(!vInt5.has_overflowed()); vInt5.push_back(37); EATEST_VERIFY(vInt5.size() == 6); EATEST_VERIFY(!vInt5.empty()); EATEST_VERIFY(vInt5.has_overflowed()); vInt5.clear(); EATEST_VERIFY(vInt5.size() == 0); EATEST_VERIFY(vInt5.empty()); EATEST_VERIFY(vInt5.has_overflowed()); // Note that we declare the container full, as it is no longer using the fixed-capacity. EATEST_VERIFY(vInt5.validate()); } { // void* push_back_uninitialized(); int64_t toCount0 = TestObject::sTOCount; eastl::fixed_vector<TestObject, 32, false> vTO1; // <-- bEnableOverflow = false EATEST_VERIFY(TestObject::sTOCount == toCount0); for(int i = 0; i < 25; i++) // 25 is simply a number that is <= 32. { void* pTO1 = vTO1.push_back_uninitialized(); EATEST_VERIFY(TestObject::sTOCount == (toCount0 + i)); new(pTO1) TestObject(i); EATEST_VERIFY(TestObject::sTOCount == (toCount0 + i + 1)); EATEST_VERIFY(vTO1.back().mX == i); EATEST_VERIFY(vTO1.validate()); } } { // void* push_back_uninitialized(); int64_t toCount0 = TestObject::sTOCount; eastl::fixed_vector<TestObject, 15, true> vTO2; // <-- bEnableOverflow = true EATEST_VERIFY(TestObject::sTOCount == toCount0); for(int i = 0; i < 25; i++) // 25 is simply a number that is > 15. { void* pTO2 = vTO2.push_back_uninitialized(); EATEST_VERIFY(TestObject::sTOCount == (toCount0 + i)); new(pTO2) TestObject(i); EATEST_VERIFY(TestObject::sTOCount == (toCount0 + i + 1)); EATEST_VERIFY(vTO2.back().mX == i); EATEST_VERIFY(vTO2.validate()); } } { // Try to repro user report that fixed_vector on the stack crashes. eastl::fixed_vector<int, 10, false> fvif; eastl::fixed_vector<int, 10, true> fvit; eastl::fixed_vector<TestObject, 10, false> fvof; eastl::fixed_vector<TestObject, 10, true> fvot; eastl::fixed_vector<int, 10, false, MallocAllocator> fvimf; eastl::fixed_vector<int, 10, true, MallocAllocator> fvimt; eastl::fixed_vector<TestObject, 10, false, MallocAllocator> fvomf; eastl::fixed_vector<TestObject, 10, true, MallocAllocator> fvomt; fvif.push_back(1); fvit.push_back(1); fvimf.push_back(1); fvimt.push_back(1); fvif.clear(); fvit.clear(); fvimf.clear(); fvimt.clear(); } { // Test construction of a container with an overflow allocator constructor argument. MallocAllocator overflowAllocator; void* p = overflowAllocator.allocate(1); fixed_vector<int, 64, true, MallocAllocator> c(overflowAllocator); c.resize(65); EATEST_VERIFY(c.get_overflow_allocator().mAllocCount == 2); // 1 for above, and 1 for overflowing from 64 to 65. overflowAllocator.deallocate(p, 1); } EATEST_VERIFY(TestObject::IsClear()); TestObject::Reset(); { // Test for crash bug reported by Arpit Baldeva. eastl::fixed_vector<void*, 1, true> test; test.push_back(NULL); test.push_back(NULL); test.erase(eastl::find(test.begin(), test.end(), (void*)NULL)); test.erase(eastl::find(test.begin(), test.end(), (void*)NULL)); EATEST_VERIFY(test.empty()); EATEST_VERIFY(test.validate()); test.set_capacity(0); // "Does nothing currently." EATEST_VERIFY(test.capacity() == 0); EATEST_VERIFY(test.validate()); } // "Crash here." { const int FV_SIZE = 100; fixed_vector<unique_ptr<unsigned int>, FV_SIZE> fvmv1; // to move via move assignment operator fixed_vector<unique_ptr<unsigned int>, FV_SIZE> fvmv2; // to move via move copy constructor for (unsigned int i = 0; i < FV_SIZE; ++i) // populate fvmv1 fvmv1.push_back(make_unique<unsigned int>(i)); fvmv2 = eastl::move(fvmv1); // Test move assignment operator for (unsigned int i = 0; i < FV_SIZE; ++i) { EATEST_VERIFY(!fvmv1[i]); EATEST_VERIFY(*fvmv2[i] == i); } EATEST_VERIFY(fvmv2.validate()); swap(fvmv1, fvmv2); // Test swap with move-only objects for (unsigned int i = 0; i < FV_SIZE; ++i) { EATEST_VERIFY(*fvmv1[i] == i); EATEST_VERIFY(!fvmv2[i]); } EATEST_VERIFY(fvmv1.validate()); EATEST_VERIFY(fvmv2.validate()); fixed_vector<unique_ptr<unsigned int>, FV_SIZE> fv = eastl::move(fvmv1); // Test move copy constructor for (unsigned int i = 0; i < FV_SIZE; ++i) { EATEST_VERIFY(!fvmv1[i]); EATEST_VERIFY(*fv[i] == i); } EATEST_VERIFY(fv.validate()); } #if defined(EA_COMPILER_CPP17_ENABLED) //Test pairing of std::variant with fixed_vector { eastl::fixed_vector<std::variant<int>, 4> v; eastl::fixed_vector<std::variant<int>, 4> b = eastl::move(v); } #endif return nErrorCount; }