mirror of
https://github.com/grumpycoders/pcsx-redux.git
synced 2025-04-02 10:41:54 -04:00
1586 lines
50 KiB
C++
1586 lines
50 KiB
C++
/////////////////////////////////////////////////////////////////////////////
|
|
// Copyright (c) Electronic Arts Inc. All rights reserved.
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
#ifndef EASTLTEST_H
|
|
#define EASTLTEST_H
|
|
|
|
|
|
#include <EABase/eabase.h>
|
|
#include <EATest/EATest.h>
|
|
|
|
EA_DISABLE_ALL_VC_WARNINGS()
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <vector> // For the STD_STL_TYPE defines below.
|
|
#if EASTL_EXCEPTIONS_ENABLED
|
|
#include <stdexcept>
|
|
#include <new>
|
|
#endif
|
|
EA_RESTORE_ALL_VC_WARNINGS();
|
|
|
|
|
|
int TestAlgorithm();
|
|
int TestAllocator();
|
|
int TestAny();
|
|
int TestArray();
|
|
int TestBitVector();
|
|
int TestBitset();
|
|
int TestCharTraits();
|
|
int TestChrono();
|
|
int TestCppCXTypeTraits();
|
|
int TestDeque();
|
|
int TestExtra();
|
|
int TestFinally();
|
|
int TestFixedFunction();
|
|
int TestFixedHash();
|
|
int TestFixedList();
|
|
int TestFixedMap();
|
|
int TestFixedSList();
|
|
int TestFixedSet();
|
|
int TestFixedString();
|
|
int TestFixedTupleVector();
|
|
int TestFixedVector();
|
|
int TestFunctional();
|
|
int TestHash();
|
|
int TestHeap();
|
|
int TestIntrusiveHash();
|
|
int TestIntrusiveList();
|
|
int TestIntrusiveSDList();
|
|
int TestIntrusiveSList();
|
|
int TestIterator();
|
|
int TestList();
|
|
int TestListMap();
|
|
int TestLruCache();
|
|
int TestMap();
|
|
int TestMemory();
|
|
int TestMeta();
|
|
int TestNumericLimits();
|
|
int TestOptional();
|
|
int TestRandom();
|
|
int TestRatio();
|
|
int TestRingBuffer();
|
|
int TestSList();
|
|
int TestSegmentedVector();
|
|
int TestSet();
|
|
int TestSmartPtr();
|
|
int TestSort();
|
|
int TestSpan();
|
|
int TestString();
|
|
int TestStringHashMap();
|
|
int TestStringMap();
|
|
int TestStringView();
|
|
int TestTuple();
|
|
int TestTupleVector();
|
|
int TestTypeTraits();
|
|
int TestUtility();
|
|
int TestVariant();
|
|
int TestVector();
|
|
int TestVectorMap();
|
|
int TestVectorSet();
|
|
int TestAtomicBasic();
|
|
int TestAtomicAsm();
|
|
int TestBitcast();
|
|
|
|
|
|
// Now enable warnings as desired.
|
|
#ifdef _MSC_VER
|
|
#pragma warning(disable: 4324) // 'struct_name' : structure was padded due to __declspec(align())
|
|
//#pragma warning(disable: 4512) // 'class' : assignment operator could not be generated
|
|
//#pragma warning(disable: 4100) // 'identifier' : unreferenced formal parameter
|
|
//#pragma warning(disable: 4706) // assignment within conditional expression
|
|
|
|
#pragma warning(default: 4056) // Floating-point constant arithmetic generates a result that exceeds the maximum allowable value
|
|
#pragma warning(default: 4061) // The enumerate has no associated handler in a switch statement
|
|
#pragma warning(default: 4062) // The enumerate has no associated handler in a switch statement, and there is no default label
|
|
#pragma warning(default: 4191) // Calling this function through the result pointer may cause your program to crash
|
|
#pragma warning(default: 4217) // Member template functions cannot be used for copy-assignment or copy-construction
|
|
//#pragma warning(default: 4242) // 'variable' : conversion from 'type' to 'type', possible loss of data
|
|
#pragma warning(default: 4254) // 'operator' : conversion from 'type1' to 'type2', possible loss of data
|
|
#pragma warning(default: 4255) // 'function' : no function prototype given: converting '()' to '(void)'
|
|
#pragma warning(default: 4263) // 'function' : member function does not override any base class virtual member function
|
|
#pragma warning(default: 4264) // 'virtual_function' : no override available for virtual member function from base 'class'; function is hidden
|
|
#pragma warning(default: 4287) // 'operator' : unsigned/negative constant mismatch
|
|
#pragma warning(default: 4289) // Nonstandard extension used : 'var' : loop control variable declared in the for-loop is used outside the for-loop scope
|
|
#pragma warning(default: 4296) // 'operator' : expression is always false
|
|
#pragma warning(default: 4302) // 'conversion' : truncation from 'type 1' to 'type 2'
|
|
#pragma warning(default: 4339) // 'type' : use of undefined type detected in CLR meta-data - use of this type may lead to a runtime exception
|
|
#pragma warning(default: 4347) // Behavior change: 'function template' is called instead of 'function'
|
|
//#pragma warning(default: 4514) // unreferenced inline/local function has been removed
|
|
#pragma warning(default: 4529) // 'member_name' : forming a pointer-to-member requires explicit use of the address-of operator ('&') and a qualified name
|
|
#pragma warning(default: 4545) // Expression before comma evaluates to a function which is missing an argument list
|
|
#pragma warning(default: 4546) // Function call before comma missing argument list
|
|
#pragma warning(default: 4547) // 'operator' : operator before comma has no effect; expected operator with side-effect
|
|
//#pragma warning(default: 4548) // expression before comma has no effect; expected expression with side-effect
|
|
#pragma warning(default: 4549) // 'operator' : operator before comma has no effect; did you intend 'operator'?
|
|
#pragma warning(default: 4536) // 'type name' : type-name exceeds meta-data limit of 'limit' characters
|
|
#pragma warning(default: 4555) // Expression has no effect; expected expression with side-effect
|
|
#pragma warning(default: 4557) // '__assume' contains effect 'effect'
|
|
//#pragma warning(default: 4619) // #pragma warning : there is no warning number 'number'
|
|
#pragma warning(default: 4623) // 'derived class' : default constructor could not be generated because a base class default constructor is inaccessible
|
|
//#pragma warning(default: 4625) // 'derived class' : copy constructor could not be generated because a base class copy constructor is inaccessible
|
|
//#pragma warning(default: 4626) // 'derived class' : assignment operator could not be generated because a base class assignment operator is inaccessible
|
|
#pragma warning(default: 4628) // Digraphs not supported with -Ze. Character sequence 'digraph' not interpreted as alternate token for 'char'
|
|
#pragma warning(default: 4640) // 'instance' : construction of local static object is not thread-safe
|
|
#pragma warning(default: 4668) // 'symbol' is not defined as a preprocessor macro, replacing with '0' for 'directives'
|
|
#pragma warning(default: 4682) // 'parameter' : no directional parameter attribute specified, defaulting to [in]
|
|
#pragma warning(default: 4686) // 'user-defined type' : possible change in behavior, change in UDT return calling convention
|
|
//#pragma warning(default: 4710) // 'function' : function not inlined
|
|
//#pragma warning(default: 4786) // 'identifier' : identifier was truncated to 'number' characters in the debug information
|
|
#pragma warning(default: 4793) // Native code generated for function 'function': 'reason'
|
|
//#pragma warning(default: 4820) // 'bytes' bytes padding added after member 'member'
|
|
#pragma warning(default: 4905) // Wide string literal cast to 'LPSTR'
|
|
#pragma warning(default: 4906) // String literal cast to 'LPWSTR'
|
|
#pragma warning(default: 4917) // 'declarator' : a GUID cannot only be associated with a class, interface or namespace
|
|
#pragma warning(default: 4928) // Illegal copy-initialization; more than one user-defined conversion has been implicitly applied
|
|
#pragma warning(default: 4931) // We are assuming the type library was built for number-bit pointers
|
|
#pragma warning(default: 4946) // reinterpret_cast used between related classes: 'class1' and 'class2'
|
|
|
|
#endif
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// EASTL includes
|
|
//
|
|
// Intentionally keep these includes below the warning settings specified above.
|
|
//
|
|
#include <EASTL/iterator.h>
|
|
#include <EASTL/algorithm.h>
|
|
|
|
|
|
|
|
|
|
/// EASTL_TestLevel
|
|
///
|
|
/// Defines how extensive our testing is. A low level is for a desktop or
|
|
/// nightly build in which the test can run quickly but still hit the
|
|
/// majority of functionality. High level is for heavy testing and internal
|
|
/// validation which may take numerous hours to run.
|
|
///
|
|
enum EASTL_TestLevel
|
|
{
|
|
kEASTL_TestLevelLow = 1, /// ~10 seconds for test completion.
|
|
kEASTL_TestLevelHigh = 10 /// Numerous hours for test completion.
|
|
};
|
|
|
|
extern int gEASTL_TestLevel;
|
|
|
|
|
|
|
|
/// EASTLTest_CheckMemory
|
|
///
|
|
/// Does a global memory heap validation check. Returns 0 if OK and
|
|
/// an error count if there is a problem.
|
|
///
|
|
/// Example usage:
|
|
/// EASTLTest_CheckMemory();
|
|
///
|
|
int EASTLTest_CheckMemory_Imp(const char* pFile, int nLine);
|
|
#define EASTLTest_CheckMemory() EASTLTest_CheckMemory_Imp(__FILE__, __LINE__)
|
|
|
|
|
|
|
|
// EASTLTEST_STD_STL_VER
|
|
//
|
|
#if defined(_STLPORT_VERSION)
|
|
#define EASTLTEST_STD_STL_VER_STLPORT
|
|
#elif defined(_RWSTD_VER_STR) || defined(_RWSTD_NAMESPACE_END)
|
|
#define EASTLTEST_STD_STL_VER_APACHE
|
|
#elif defined(_CPPLIB_VER)
|
|
#define EASTLTEST_STD_STL_VER_DINKUMWARE
|
|
#elif defined(__GNUC__) && defined(_CXXCONFIG)
|
|
#define EASTLTEST_STD_STL_VER_GCC
|
|
#else
|
|
#define EASTLTEST_STD_STL_VER_UNKNOWN
|
|
#endif
|
|
|
|
|
|
|
|
/// StdSTLType
|
|
///
|
|
enum StdSTLType
|
|
{
|
|
kSTLUnknown, // Unknown type
|
|
kSTLPort, // STLPort. Descendent of the old HP / SGI STL.
|
|
kSTLApache, // Apache stdcxx (previously RogueWave), which is a descendent of the old HP / SGI STL.
|
|
kSTLClang, // Clang native. a.k.a. libc++
|
|
kSTLGCC, // GCC native. a.k.a. libstdc++
|
|
kSTLMS, // Microsoft. Tweaked version of Dinkumware.
|
|
kSTLDinkumware // Generic Dinkumware
|
|
};
|
|
|
|
StdSTLType GetStdSTLType();
|
|
|
|
|
|
|
|
|
|
/// GetStdSTLName
|
|
///
|
|
/// Returns the name of the std C++ STL available to the current build.
|
|
/// The returned value will be one of:
|
|
/// "STLPort"
|
|
/// "GCC"
|
|
/// "VC++"
|
|
// "Apache" // Previously RogueWave
|
|
///
|
|
const char* GetStdSTLName();
|
|
|
|
|
|
/// gEASTLTest_AllocationCount
|
|
///
|
|
extern int gEASTLTest_AllocationCount;
|
|
extern int gEASTLTest_TotalAllocationCount;
|
|
|
|
|
|
|
|
// For backwards compatibility:
|
|
#define EASTLTest_Printf EA::UnitTest::Report
|
|
#define VERIFY EATEST_VERIFY
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/// EASTLTest_Rand
|
|
///
|
|
/// Implements a basic random number generator for EASTL unit tests. It's not
|
|
/// intended to be a robust random number generator (though it is decent),
|
|
/// but rather is present so the unit tests can have a portable random number
|
|
/// generator they can rely on being present.
|
|
///
|
|
/// Example usage:
|
|
/// EASTLTest_Rand rng;
|
|
/// eastl_size_t x = rng(); // Generate value in range of [0, 0xffffffff] (i.e. generate any uint32_t)
|
|
/// eastl_ssize_t y = rng.Rand(1000); // Generate value in range of [0, 1000)
|
|
/// eastl_ssize_t z = rng.RandRange(-50, +30); // Generate value in range of [-50, +30)
|
|
///
|
|
/// Example usage in the random_shuffle algorithm:
|
|
/// EASTLTest_Rand rng;
|
|
/// random_shuffle(first, last, rnd);
|
|
///
|
|
class EASTLTest_Rand
|
|
{
|
|
public:
|
|
EASTLTest_Rand(eastl_size_t nSeed) // The user must supply a seed; we don't provide default values.
|
|
: mnSeed(nSeed) { }
|
|
|
|
eastl_size_t Rand()
|
|
{
|
|
// This is not designed to be a high quality random number generator.
|
|
if(mnSeed == 0)
|
|
mnSeed = UINT64_C(0xfefefefefefefefe); // Can't have a seed of zero.
|
|
|
|
const uint64_t nResult64A = ((mnSeed * UINT64_C(6364136223846793005)) + UINT64_C(1442695040888963407));
|
|
const uint64_t nResult64B = ((nResult64A * UINT64_C(6364136223846793005)) + UINT64_C(1442695040888963407));
|
|
|
|
mnSeed = (nResult64A >> 32) ^ nResult64B;
|
|
|
|
return (eastl_size_t)mnSeed; // For eastl_size_t == uint32_t, this is a chop.
|
|
}
|
|
|
|
eastl_size_t operator()() // Returns a pseudorandom value in range of [0, 0xffffffffffffffff)] (i.e. generate any eastl_size_t)
|
|
{ return Rand(); }
|
|
|
|
eastl_size_t operator()(eastl_size_t n) // Returns a pseudorandom value in range of [0, n)
|
|
{ return RandLimit(n); }
|
|
|
|
eastl_size_t RandLimit(eastl_size_t nLimit) // Returns a pseudorandom value in range of [0, nLimit)
|
|
{
|
|
// Can't do the following correct solution because we don't have a portable int128_t to work with.
|
|
// We could implement a 128 bit multiply manually. See EAStdC/int128_t.cpp.
|
|
// return (eastl_size_t)((Rand() * (uint128_t)nLimit) >> 64);
|
|
|
|
return (Rand() % nLimit); // This results in an imperfect distribution, especially for the case of nLimit being high relative to eastl_size_t.
|
|
}
|
|
|
|
eastl_ssize_t RandRange(eastl_ssize_t nBegin, eastl_ssize_t nEnd) // Returns a pseudorandom value in range of [nBegin, nEnd)
|
|
{ return nBegin + (eastl_ssize_t)RandLimit((eastl_size_t)(nEnd - nBegin)); }
|
|
|
|
protected:
|
|
uint64_t mnSeed;
|
|
};
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/// RandGenT
|
|
///
|
|
/// A wrapper for EASTLTest_Rand which generates values of the given integral
|
|
/// data type. This is mostly useful for clearnly avoiding compiler warnings,
|
|
/// as we intentionally enable the highest warning levels in these tests.
|
|
///
|
|
template <typename Integer>
|
|
struct RandGenT
|
|
{
|
|
RandGenT(eastl_size_t nSeed)
|
|
: mRand(nSeed) { }
|
|
|
|
Integer operator()()
|
|
{ return (Integer)mRand.Rand(); }
|
|
|
|
Integer operator()(eastl_size_t n)
|
|
{ return (Integer)mRand.RandLimit(n); }
|
|
|
|
EASTLTest_Rand mRand;
|
|
};
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/// kMagicValue
|
|
///
|
|
/// Used as a unique integer. We assign this to TestObject in its constructor
|
|
/// and verify in the TestObject destructor that the value is unchanged.
|
|
/// This can be used to tell, for example, if an invalid object is being
|
|
/// destroyed.
|
|
///
|
|
const uint32_t kMagicValue = 0x01f1cbe8;
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/// TestObject
|
|
///
|
|
/// Implements a generic object that is suitable for use in container tests.
|
|
/// Note that we choose a very restricted set of functions that are available
|
|
/// for this class. Do not add any additional functions, as that would
|
|
/// compromise the intentions of the unit tests.
|
|
///
|
|
struct TestObject
|
|
{
|
|
int mX; // Value for the TestObject.
|
|
bool mbThrowOnCopy; // Throw an exception of this object is copied, moved, or assigned to another.
|
|
int64_t mId; // Unique id for each object, equal to its creation number. This value is not coped from other TestObjects during any operations, including moves.
|
|
uint32_t mMagicValue; // Used to verify that an instance is valid and that it is not corrupted. It should always be kMagicValue.
|
|
static int64_t sTOCount; // Count of all current existing TestObjects.
|
|
static int64_t sTOCtorCount; // Count of times any ctor was called.
|
|
static int64_t sTODtorCount; // Count of times dtor was called.
|
|
static int64_t sTODefaultCtorCount; // Count of times the default ctor was called.
|
|
static int64_t sTOArgCtorCount; // Count of times the x0,x1,x2 ctor was called.
|
|
static int64_t sTOCopyCtorCount; // Count of times copy ctor was called.
|
|
static int64_t sTOMoveCtorCount; // Count of times move ctor was called.
|
|
static int64_t sTOCopyAssignCount; // Count of times copy assignment was called.
|
|
static int64_t sTOMoveAssignCount; // Count of times move assignment was called.
|
|
static int sMagicErrorCount; // Number of magic number mismatch errors.
|
|
|
|
explicit TestObject(int x = 0, bool bThrowOnCopy = false)
|
|
: mX(x), mbThrowOnCopy(bThrowOnCopy), mMagicValue(kMagicValue)
|
|
{
|
|
++sTOCount;
|
|
++sTOCtorCount;
|
|
++sTODefaultCtorCount;
|
|
mId = sTOCtorCount;
|
|
}
|
|
|
|
// This constructor exists for the purpose of testing variadiac template arguments, such as with the emplace container functions.
|
|
TestObject(int x0, int x1, int x2, bool bThrowOnCopy = false)
|
|
: mX(x0 + x1 + x2), mbThrowOnCopy(bThrowOnCopy), mMagicValue(kMagicValue)
|
|
{
|
|
++sTOCount;
|
|
++sTOCtorCount;
|
|
++sTOArgCtorCount;
|
|
mId = sTOCtorCount;
|
|
}
|
|
|
|
TestObject(const TestObject& testObject)
|
|
: mX(testObject.mX), mbThrowOnCopy(testObject.mbThrowOnCopy), mMagicValue(testObject.mMagicValue)
|
|
{
|
|
++sTOCount;
|
|
++sTOCtorCount;
|
|
++sTOCopyCtorCount;
|
|
mId = sTOCtorCount;
|
|
if(mbThrowOnCopy)
|
|
{
|
|
#if EASTL_EXCEPTIONS_ENABLED
|
|
throw "Disallowed TestObject copy";
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// Due to the nature of TestObject, there isn't much special for us to
|
|
// do in our move constructor. A move constructor swaps its contents with
|
|
// the other object, whhich is often a default-constructed object.
|
|
TestObject(TestObject&& testObject)
|
|
: mX(testObject.mX), mbThrowOnCopy(testObject.mbThrowOnCopy), mMagicValue(testObject.mMagicValue)
|
|
{
|
|
++sTOCount;
|
|
++sTOCtorCount;
|
|
++sTOMoveCtorCount;
|
|
mId = sTOCtorCount; // testObject keeps its mId, and we assign ours anew.
|
|
testObject.mX = 0; // We are swapping our contents with the TestObject, so give it our "previous" value.
|
|
if(mbThrowOnCopy)
|
|
{
|
|
#if EASTL_EXCEPTIONS_ENABLED
|
|
throw "Disallowed TestObject copy";
|
|
#endif
|
|
}
|
|
}
|
|
|
|
TestObject& operator=(const TestObject& testObject)
|
|
{
|
|
++sTOCopyAssignCount;
|
|
|
|
if(&testObject != this)
|
|
{
|
|
mX = testObject.mX;
|
|
// Leave mId alone.
|
|
mMagicValue = testObject.mMagicValue;
|
|
mbThrowOnCopy = testObject.mbThrowOnCopy;
|
|
if(mbThrowOnCopy)
|
|
{
|
|
#if EASTL_EXCEPTIONS_ENABLED
|
|
throw "Disallowed TestObject copy";
|
|
#endif
|
|
}
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
TestObject& operator=(TestObject&& testObject)
|
|
{
|
|
++sTOMoveAssignCount;
|
|
|
|
if(&testObject != this)
|
|
{
|
|
eastl::swap(mX, testObject.mX);
|
|
// Leave mId alone.
|
|
eastl::swap(mMagicValue, testObject.mMagicValue);
|
|
eastl::swap(mbThrowOnCopy, testObject.mbThrowOnCopy);
|
|
|
|
if(mbThrowOnCopy)
|
|
{
|
|
#if EASTL_EXCEPTIONS_ENABLED
|
|
throw "Disallowed TestObject copy";
|
|
#endif
|
|
}
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
~TestObject()
|
|
{
|
|
if(mMagicValue != kMagicValue)
|
|
++sMagicErrorCount;
|
|
mMagicValue = 0;
|
|
--sTOCount;
|
|
++sTODtorCount;
|
|
}
|
|
|
|
static void Reset()
|
|
{
|
|
sTOCount = 0;
|
|
sTOCtorCount = 0;
|
|
sTODtorCount = 0;
|
|
sTODefaultCtorCount = 0;
|
|
sTOArgCtorCount = 0;
|
|
sTOCopyCtorCount = 0;
|
|
sTOMoveCtorCount = 0;
|
|
sTOCopyAssignCount = 0;
|
|
sTOMoveAssignCount = 0;
|
|
sMagicErrorCount = 0;
|
|
}
|
|
|
|
static bool IsClear() // Returns true if there are no existing TestObjects and the sanity checks related to that test OK.
|
|
{
|
|
return (sTOCount == 0) && (sTODtorCount == sTOCtorCount) && (sMagicErrorCount == 0);
|
|
}
|
|
};
|
|
|
|
// Operators
|
|
// We specifically define only == and <, in order to verify that
|
|
// our containers and algorithms are not mistakenly expecting other
|
|
// operators for the contained and manipulated classes.
|
|
inline bool operator==(const TestObject& t1, const TestObject& t2)
|
|
{ return t1.mX == t2.mX; }
|
|
|
|
inline bool operator<(const TestObject& t1, const TestObject& t2)
|
|
{ return t1.mX < t2.mX; }
|
|
|
|
|
|
// TestObject hash
|
|
// Normally you don't want to put your hash functions in the eastl namespace, as that namespace is owned by EASTL.
|
|
// However, these are the EASTL unit tests and we can say that they are also owned by EASTL.
|
|
namespace eastl
|
|
{
|
|
template <>
|
|
struct hash<TestObject>
|
|
{
|
|
size_t operator()(const TestObject& a) const
|
|
{ return static_cast<size_t>(a.mX); }
|
|
};
|
|
}
|
|
|
|
|
|
// use_mX
|
|
// Used for printing TestObject contents via the PrintSequence function,
|
|
// which is defined below. See the PrintSequence function for documentation.
|
|
// This function is an analog of the eastl::use_self and use_first functions.
|
|
// We declare this all in one line because the user should never need to
|
|
// debug usage of this function.
|
|
template <typename T> struct use_mX { int operator()(const T& t) const { return t.mX; } };
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// SizedPOD
|
|
//
|
|
// Exists for the purpose testing PODs that are larger than built-in types.
|
|
//
|
|
template <size_t kSize>
|
|
struct SizedPOD
|
|
{
|
|
char memory[kSize];
|
|
};
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/// ConstType
|
|
///
|
|
/// Used to test const type containers (e.g. vector<const ConstType>).
|
|
///
|
|
class ConstType
|
|
{
|
|
public:
|
|
ConstType(int value) : mDummy(value) {};
|
|
int mDummy;
|
|
};
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/// TestObjectHash
|
|
///
|
|
/// Implements a manually specified hash function for TestObjects.
|
|
///
|
|
struct TestObjectHash
|
|
{
|
|
size_t operator()(const TestObject& t) const
|
|
{
|
|
return (size_t)t.mX;
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/// Align16
|
|
///
|
|
|
|
#if defined(EA_PROCESSOR_ARM)
|
|
#define kEASTLTestAlign16 8 //ARM processors can only align to 8
|
|
#else
|
|
#define kEASTLTestAlign16 16
|
|
#endif
|
|
|
|
|
|
EA_PREFIX_ALIGN(kEASTLTestAlign16)
|
|
struct Align16
|
|
{
|
|
explicit Align16(int x = 0) : mX(x) {}
|
|
int mX;
|
|
} EA_POSTFIX_ALIGN(kEASTLTestAlign16);
|
|
|
|
inline bool operator==(const Align16& a, const Align16& b)
|
|
{ return (a.mX == b.mX); }
|
|
|
|
inline bool operator<(const Align16& a, const Align16& b)
|
|
{ return (a.mX < b.mX); }
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/// Align32
|
|
///
|
|
#if defined(EA_PROCESSOR_ARM)
|
|
#define kEASTLTestAlign32 8 //ARM processors can only align to 8
|
|
#elif defined(__GNUC__) && (((__GNUC__ * 100) + __GNUC_MINOR__) < 400) // GCC 2.x, 3.x
|
|
#define kEASTLTestAlign32 16 // Some versions of GCC fail to support any alignment beyond 16.
|
|
#else
|
|
#define kEASTLTestAlign32 32
|
|
#endif
|
|
|
|
EA_PREFIX_ALIGN(kEASTLTestAlign32)
|
|
struct Align32
|
|
{
|
|
explicit Align32(int x = 0) : mX(x) {}
|
|
int mX;
|
|
} EA_POSTFIX_ALIGN(kEASTLTestAlign32);
|
|
|
|
inline bool operator==(const Align32& a, const Align32& b)
|
|
{ return (a.mX == b.mX); }
|
|
|
|
inline bool operator<(const Align32& a, const Align32& b)
|
|
{ return (a.mX < b.mX); }
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/// Align64
|
|
///
|
|
/// Used for testing of alignment.
|
|
///
|
|
#if defined(EA_PROCESSOR_ARM)
|
|
#define kEASTLTestAlign64 8
|
|
#elif defined(__GNUC__) && (((__GNUC__ * 100) + __GNUC_MINOR__) < 400) // GCC 2.x, 3.x
|
|
#define kEASTLTestAlign64 16 // Some versions of GCC fail to support any alignment beyond 16.
|
|
#else
|
|
#define kEASTLTestAlign64 64
|
|
#endif
|
|
|
|
EA_PREFIX_ALIGN(kEASTLTestAlign64)
|
|
struct Align64
|
|
{
|
|
explicit Align64(int x = 0) : mX(x) {}
|
|
int mX;
|
|
} EA_POSTFIX_ALIGN(kEASTLTestAlign64);
|
|
|
|
inline bool operator==(const Align64& a, const Align64& b)
|
|
{ return (a.mX == b.mX); }
|
|
|
|
inline bool operator<(const Align64& a, const Align64& b)
|
|
{ return (a.mX < b.mX); }
|
|
|
|
namespace eastl
|
|
{
|
|
template <>
|
|
struct hash < Align64 >
|
|
{
|
|
size_t operator()(const Align64& a) const
|
|
{
|
|
return static_cast<size_t>(a.mX);
|
|
}
|
|
};
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// test_use_self
|
|
///
|
|
/// Intentionally avoiding a dependency on eastl::use_self.
|
|
///
|
|
template <typename T>
|
|
struct test_use_self
|
|
{
|
|
const T& operator()(const T& x) const
|
|
{ return x; }
|
|
};
|
|
|
|
|
|
|
|
/// GenerateIncrementalIntegers
|
|
///
|
|
/// Used to seed containers with incremental values based on integers.
|
|
///
|
|
/// Example usage:
|
|
/// vector<int> v(10, 0);
|
|
/// generate(v.begin(), v.end(), GenerateIncrementalIntegers<int>());
|
|
/// // v will now have 0, 1, 2, ... 8, 9.
|
|
///
|
|
/// generate_n(intArray.begin(), 10, GenerateIncrementalIntegers<int>());
|
|
/// // v will now have 0, 1, 2, ... 8, 9.
|
|
///
|
|
/// vector<TestObject> vTO(10, 0);
|
|
/// generate(vTO.begin(), vTO.end(), GenerateIncrementalIntegers<TestObject>());
|
|
/// // vTO will now have 0, 1, 2, ... 8, 9.
|
|
///
|
|
template <typename T>
|
|
struct GenerateIncrementalIntegers
|
|
{
|
|
int mX;
|
|
|
|
GenerateIncrementalIntegers(int x = 0)
|
|
: mX(x) { }
|
|
|
|
void reset(int x = 0)
|
|
{ mX = x; }
|
|
|
|
T operator()()
|
|
{ return T(mX++); }
|
|
};
|
|
|
|
|
|
|
|
/// SetIncrementalIntegers
|
|
///
|
|
/// Used to seed containers with incremental values based on integers.
|
|
///
|
|
/// Example usage:
|
|
/// vector<int> v(10, 0);
|
|
/// for_each(v.begin(), v.end(), SetIncrementalIntegers<int>());
|
|
/// // v will now have 0, 1, 2, ... 8, 9.
|
|
///
|
|
template <typename T>
|
|
struct SetIncrementalIntegers
|
|
{
|
|
int mX;
|
|
|
|
SetIncrementalIntegers(int x = 0)
|
|
: mX(x) { }
|
|
|
|
void reset(int x = 0)
|
|
{ mX = x; }
|
|
|
|
void operator()(T& t)
|
|
{ t = T(mX++); }
|
|
};
|
|
|
|
|
|
|
|
/// CompareContainers
|
|
///
|
|
/// Does a comparison between the contents of two containers.
|
|
///
|
|
/// Specifically tests for the following properties:
|
|
/// empty() is the same for both
|
|
/// size() is the same for both
|
|
/// iteration through both element by element yields equal values.
|
|
///
|
|
template <typename T1, typename T2, typename ExtractValue1, typename ExtractValue2>
|
|
int CompareContainers(const T1& t1, const T2& t2, const char* ppName,
|
|
ExtractValue1 ev1 = test_use_self<T1>(), ExtractValue2 ev2 = test_use_self<T2>())
|
|
{
|
|
int nErrorCount = 0;
|
|
|
|
// Compare emptiness.
|
|
VERIFY(t1.empty() == t2.empty());
|
|
|
|
// Compare sizes.
|
|
const size_t nSize1 = t1.size();
|
|
const size_t nSize2 = t2.size();
|
|
|
|
VERIFY(nSize1 == nSize2);
|
|
if(nSize1 != nSize2)
|
|
EASTLTest_Printf("%s: Container size difference: %u, %u\n", ppName, (unsigned)nSize1, (unsigned)nSize2);
|
|
|
|
// Compare values.
|
|
if(nSize1 == nSize2)
|
|
{
|
|
// Test iteration
|
|
typename T1::const_iterator it1 = t1.begin();
|
|
typename T2::const_iterator it2 = t2.begin();
|
|
|
|
for(unsigned j = 0; it1 != t1.end(); ++it1, ++it2, ++j)
|
|
{
|
|
const typename T1::value_type& v1 = *it1;
|
|
const typename T2::value_type& v2 = *it2;
|
|
|
|
VERIFY(ev1(v1) == ev2(v2));
|
|
if(!(ev1(v1) == ev2(v2)))
|
|
{
|
|
EASTLTest_Printf("%s: Container iterator difference at index %d\n", ppName, j);
|
|
break;
|
|
}
|
|
}
|
|
|
|
VERIFY(it1 == t1.end());
|
|
VERIFY(it2 == t2.end());
|
|
}
|
|
|
|
return nErrorCount;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// VerifySequence
|
|
///
|
|
/// Allows the user to specify that a container has a given set of values.
|
|
///
|
|
/// Example usage:
|
|
/// vector<int> v;
|
|
/// v.push_back(1); v.push_back(3); v.push_back(5);
|
|
/// VerifySequence(v.begin(), v.end(), int(), "v.push_back", 1, 3, 5, -1);
|
|
///
|
|
/// Note: The StackValue template argument is a hint to the compiler about what type
|
|
/// the passed vararg sequence is.
|
|
///
|
|
template <typename InputIterator, typename StackValue>
|
|
bool VerifySequence(InputIterator first, InputIterator last, StackValue /*unused*/, const char* pName, ...)
|
|
{
|
|
typedef typename eastl::iterator_traits<InputIterator>::value_type value_type;
|
|
|
|
int argIndex = 0;
|
|
int seqIndex = 0;
|
|
bool bReturnValue = true;
|
|
StackValue next;
|
|
|
|
va_list args;
|
|
va_start(args, pName);
|
|
|
|
for( ; first != last; ++first, ++argIndex, ++seqIndex)
|
|
{
|
|
next = va_arg(args, StackValue);
|
|
|
|
if((next == StackValue(-1)) || !(value_type(next) == *first))
|
|
{
|
|
if(pName)
|
|
EASTLTest_Printf("[%s] Mismatch at index %d\n", pName, argIndex);
|
|
else
|
|
EASTLTest_Printf("Mismatch at index %d\n", argIndex);
|
|
bReturnValue = false;
|
|
}
|
|
}
|
|
|
|
for(; first != last; ++first)
|
|
++seqIndex;
|
|
|
|
if(bReturnValue)
|
|
{
|
|
next = va_arg(args, StackValue);
|
|
|
|
if(!(next == StackValue(-1)))
|
|
{
|
|
do {
|
|
++argIndex;
|
|
next = va_arg(args, StackValue);
|
|
} while(!(next == StackValue(-1)));
|
|
|
|
if(pName)
|
|
EASTLTest_Printf("[%s] Too many elements: expected %d, found %d\n", pName, argIndex, seqIndex);
|
|
else
|
|
EASTLTest_Printf("Too many elements: expected %d, found %d\n", argIndex, seqIndex);
|
|
bReturnValue = false;
|
|
}
|
|
}
|
|
|
|
va_end(args);
|
|
|
|
return bReturnValue;
|
|
}
|
|
|
|
|
|
|
|
|
|
/// PrintSequence
|
|
///
|
|
/// Allows the user to print a sequence of values.
|
|
///
|
|
/// Example usage:
|
|
/// vector<int> v;
|
|
/// PrintSequence(v.begin(), v.end(), use_self<int>(), 100, "vector", 1, 3, 5, -1);
|
|
///
|
|
/// Example usage:
|
|
/// template <typename T> struct use_mX { int operator()(const T& t) const { return t.mX; } };
|
|
/// vector<TestObject> v;
|
|
/// PrintSequence(v.begin(), v.end(), use_mX<TestObject>(), 100, "vector", 1, 3, 5, -1);
|
|
///
|
|
template <typename InputIterator, typename ExtractInt>
|
|
void PrintSequence(InputIterator first, InputIterator last, ExtractInt extractInt, int nMaxCount, const char* pName, ...)
|
|
{
|
|
if(pName)
|
|
EASTLTest_Printf("[%s]", pName);
|
|
|
|
for(int i = 0; (i < nMaxCount) && (first != last); ++i, ++first)
|
|
{
|
|
EASTLTest_Printf("%d ", (int)extractInt(*first));
|
|
}
|
|
|
|
EASTLTest_Printf("\n");
|
|
}
|
|
|
|
|
|
|
|
|
|
/// demoted_iterator
|
|
///
|
|
/// Converts an iterator into a demoted category. For example, you can convert
|
|
/// an iterator of type bidirectional_iterator_tag to forward_iterator_tag.
|
|
/// The following is a list of iterator types. A demonted iterator can be demoted
|
|
/// only to a lower iterator category (earlier in the following list):
|
|
/// input_iterator_tag
|
|
/// forward_iterator_tag
|
|
/// bidirectional_iterator_tag
|
|
/// random_access_iterator_tag
|
|
/// contiguous_iterator_tag
|
|
///
|
|
/// Converts something which can be iterated into a formal input iterator.
|
|
/// This class is useful for testing functions and algorithms that expect
|
|
/// InputIterators, which are the lowest and 'weakest' form of iterators.
|
|
///
|
|
/// Key traits of InputIterators:
|
|
/// Algorithms on input iterators should never attempt to pass
|
|
/// through the same iterator twice. They should be single pass
|
|
/// algorithms. value_type T is not required to be an lvalue type.
|
|
///
|
|
/// Example usage:
|
|
/// typedef demoted_iterator<int*, eastl::bidirectional_iterator_tag> PointerAsBidirectionalIterator;
|
|
/// typedef demoted_iterator<MyVector::iterator, eastl::forward_iterator_tag> VectorIteratorAsForwardIterator;
|
|
///
|
|
/// Example usage:
|
|
/// IntVector v;
|
|
/// comb_sort(to_forward_iterator(v.begin()), to_forward_iterator(v.end()));
|
|
///
|
|
template <typename Iterator, typename IteratorCategory>
|
|
class demoted_iterator
|
|
{
|
|
protected:
|
|
Iterator mIterator;
|
|
|
|
public:
|
|
typedef demoted_iterator<Iterator, IteratorCategory> this_type;
|
|
typedef Iterator iterator_type;
|
|
typedef IteratorCategory iterator_category;
|
|
typedef typename eastl::iterator_traits<Iterator>::value_type value_type;
|
|
typedef typename eastl::iterator_traits<Iterator>::difference_type difference_type;
|
|
typedef typename eastl::iterator_traits<Iterator>::reference reference;
|
|
typedef typename eastl::iterator_traits<Iterator>::pointer pointer;
|
|
|
|
demoted_iterator()
|
|
: mIterator() { }
|
|
|
|
explicit demoted_iterator(const Iterator& i)
|
|
: mIterator(i) { }
|
|
|
|
demoted_iterator(const this_type& x)
|
|
: mIterator(x.mIterator) { }
|
|
|
|
this_type& operator=(const Iterator& i)
|
|
{ mIterator = i; return *this; }
|
|
|
|
this_type& operator=(const this_type& x)
|
|
{ mIterator = x.mIterator; return *this; }
|
|
|
|
reference operator*() const
|
|
{ return *mIterator; }
|
|
|
|
pointer operator->() const
|
|
{ return mIterator; }
|
|
|
|
this_type& operator++()
|
|
{ ++mIterator; return *this; }
|
|
|
|
this_type operator++(int)
|
|
{ return this_type(mIterator++); }
|
|
|
|
this_type& operator--()
|
|
{ --mIterator; return *this; }
|
|
|
|
this_type operator--(int)
|
|
{ return this_type(mIterator--); }
|
|
|
|
reference operator[](const difference_type& n) const
|
|
{ return mIterator[n]; }
|
|
|
|
this_type& operator+=(const difference_type& n)
|
|
{ mIterator += n; return *this; }
|
|
|
|
this_type operator+(const difference_type& n) const
|
|
{ return this_type(mIterator + n); }
|
|
|
|
this_type& operator-=(const difference_type& n)
|
|
{ mIterator -= n; return *this; }
|
|
|
|
this_type operator-(const difference_type& n) const
|
|
{ return this_type(mIterator - n); }
|
|
|
|
const iterator_type& base() const
|
|
{ return mIterator; }
|
|
|
|
}; // class demoted_iterator
|
|
|
|
template<typename Iterator1, typename IteratorCategory1, typename Iterator2, typename IteratorCategory2>
|
|
inline bool
|
|
operator==(const demoted_iterator<Iterator1, IteratorCategory1>& a, const demoted_iterator<Iterator2, IteratorCategory2>& b)
|
|
{ return a.base() == b.base(); }
|
|
|
|
template<typename Iterator1, typename IteratorCategory1, typename Iterator2, typename IteratorCategory2>
|
|
inline bool
|
|
operator!=(const demoted_iterator<Iterator1, IteratorCategory1>& a, const demoted_iterator<Iterator2, IteratorCategory2>& b)
|
|
{ return !(a == b); }
|
|
|
|
template<typename Iterator1, typename IteratorCategory1, typename Iterator2, typename IteratorCategory2>
|
|
inline bool
|
|
operator<(const demoted_iterator<Iterator1, IteratorCategory1>& a, const demoted_iterator<Iterator2, IteratorCategory2>& b)
|
|
{ return a.base() < b.base(); }
|
|
|
|
template<typename Iterator1, typename IteratorCategory1, typename Iterator2, typename IteratorCategory2>
|
|
inline bool
|
|
operator<=(const demoted_iterator<Iterator1, IteratorCategory1>& a, const demoted_iterator<Iterator2, IteratorCategory2>& b)
|
|
{ return !(b < a); }
|
|
|
|
template<typename Iterator1, typename IteratorCategory1, typename Iterator2, typename IteratorCategory2>
|
|
inline bool
|
|
operator>(const demoted_iterator<Iterator1, IteratorCategory1>& a, const demoted_iterator<Iterator2, IteratorCategory2>& b)
|
|
{ return b < a; }
|
|
|
|
template<typename Iterator1, typename IteratorCategory1, typename Iterator2, typename IteratorCategory2>
|
|
inline bool
|
|
operator>=(const demoted_iterator<Iterator1, IteratorCategory1>& a, const demoted_iterator<Iterator2, IteratorCategory2>& b)
|
|
{ return !(a < b); }
|
|
|
|
template<typename Iterator1, typename IteratorCategory1, typename Iterator2, typename IteratorCategory2>
|
|
inline demoted_iterator<Iterator1, IteratorCategory1>
|
|
operator-(const demoted_iterator<Iterator1, IteratorCategory1>& a, const demoted_iterator<Iterator2, IteratorCategory2>& b)
|
|
{ return demoted_iterator<Iterator1, IteratorCategory1>(a.base() - b.base()); }
|
|
|
|
template<typename Iterator1, typename IteratorCategory1>
|
|
inline demoted_iterator<Iterator1, IteratorCategory1>
|
|
operator+(typename demoted_iterator<Iterator1, IteratorCategory1>::difference_type n, const demoted_iterator<Iterator1, IteratorCategory1>& a)
|
|
{ return a + n; }
|
|
|
|
|
|
// to_xxx_iterator
|
|
//
|
|
// Returns a demoted iterator
|
|
//
|
|
template <typename Iterator>
|
|
inline demoted_iterator<Iterator, EASTL_ITC_NS::input_iterator_tag>
|
|
to_input_iterator(const Iterator& i)
|
|
{ return demoted_iterator<Iterator, EASTL_ITC_NS::input_iterator_tag>(i); }
|
|
|
|
template <typename Iterator>
|
|
inline demoted_iterator<Iterator, EASTL_ITC_NS::forward_iterator_tag>
|
|
to_forward_iterator(const Iterator& i)
|
|
{ return demoted_iterator<Iterator, EASTL_ITC_NS::forward_iterator_tag>(i); }
|
|
|
|
template <typename Iterator>
|
|
inline demoted_iterator<Iterator, EASTL_ITC_NS::bidirectional_iterator_tag>
|
|
to_bidirectional_iterator(const Iterator& i)
|
|
{ return demoted_iterator<Iterator, EASTL_ITC_NS::bidirectional_iterator_tag>(i); }
|
|
|
|
template <typename Iterator>
|
|
inline demoted_iterator<Iterator, EASTL_ITC_NS::random_access_iterator_tag>
|
|
to_random_access_iterator(const Iterator& i)
|
|
{ return demoted_iterator<Iterator, EASTL_ITC_NS::random_access_iterator_tag>(i); }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// MallocAllocator
|
|
//
|
|
// Implements an EASTL allocator that uses malloc/free as opposed to
|
|
// new/delete or PPMalloc Malloc/Free. This is useful for testing
|
|
// allocator behaviour of code.
|
|
//
|
|
// Example usage:
|
|
// vector<int, MallocAllocator> intVector;
|
|
//
|
|
class MallocAllocator
|
|
{
|
|
public:
|
|
MallocAllocator(const char* = EASTL_NAME_VAL("MallocAllocator"))
|
|
: mAllocCount(0), mFreeCount(0), mAllocVolume(0) {}
|
|
|
|
MallocAllocator(const MallocAllocator& x)
|
|
: mAllocCount(x.mAllocCount), mFreeCount(x.mFreeCount), mAllocVolume(x.mAllocVolume) {}
|
|
|
|
MallocAllocator(const MallocAllocator& x, const char*) : MallocAllocator(x) {}
|
|
|
|
MallocAllocator& operator=(const MallocAllocator& x)
|
|
{
|
|
mAllocCount = x.mAllocCount;
|
|
mFreeCount = x.mFreeCount;
|
|
mAllocVolume = x.mAllocVolume;
|
|
return *this;
|
|
}
|
|
|
|
void* allocate(size_t n, int = 0);
|
|
void* allocate(size_t n, size_t, size_t, int = 0); // We don't support alignment, so you can't use this class where alignment is required.
|
|
void deallocate(void* p, size_t n);
|
|
|
|
const char* get_name() const { return "MallocAllocator"; }
|
|
void set_name(const char*) {}
|
|
|
|
static void reset_all()
|
|
{
|
|
mAllocCountAll = 0;
|
|
mFreeCountAll = 0;
|
|
mAllocVolumeAll = 0;
|
|
mpLastAllocation = NULL;
|
|
}
|
|
|
|
public:
|
|
int mAllocCount;
|
|
int mFreeCount;
|
|
size_t mAllocVolume;
|
|
|
|
static int mAllocCountAll;
|
|
static int mFreeCountAll;
|
|
static size_t mAllocVolumeAll;
|
|
static void* mpLastAllocation;
|
|
};
|
|
|
|
inline bool operator==(const MallocAllocator&, const MallocAllocator&) { return true; }
|
|
inline bool operator!=(const MallocAllocator&, const MallocAllocator&) { return false; }
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// CustomAllocator
|
|
//
|
|
// Implements an allocator that works just like eastl::allocator but is defined
|
|
// within this test as opposed to within EASTL.
|
|
//
|
|
// Example usage:
|
|
// vector<int, CustomAllocator> intVector;
|
|
//
|
|
class CustomAllocator
|
|
{
|
|
public:
|
|
CustomAllocator(const char* = NULL) {}
|
|
CustomAllocator(const CustomAllocator&) {}
|
|
CustomAllocator(const CustomAllocator&, const char*) {}
|
|
CustomAllocator& operator=(const CustomAllocator&) { return *this; }
|
|
|
|
void* allocate(size_t n, int flags = 0);
|
|
void* allocate(size_t n, size_t, size_t, int flags = 0);
|
|
void deallocate(void* p, size_t n);
|
|
|
|
const char* get_name() const { return "CustomAllocator"; }
|
|
void set_name(const char*) {}
|
|
};
|
|
|
|
inline bool operator==(const CustomAllocator&, const CustomAllocator&) { return true; }
|
|
inline bool operator!=(const CustomAllocator&, const CustomAllocator&) { return false; }
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/// UnequalAllocator
|
|
///
|
|
/// Acts the same as eastl::allocator, but always compares as unequal to an
|
|
/// instance of itself.
|
|
///
|
|
class UnequalAllocator
|
|
{
|
|
public:
|
|
EASTL_ALLOCATOR_EXPLICIT UnequalAllocator(const char* pName = EASTL_NAME_VAL(EASTL_ALLOCATOR_DEFAULT_NAME))
|
|
: mAllocator(pName) {}
|
|
|
|
UnequalAllocator(const UnequalAllocator& x) : mAllocator(x.mAllocator) {}
|
|
UnequalAllocator(const UnequalAllocator& x, const char* pName) : mAllocator(x.mAllocator) { set_name(pName); }
|
|
UnequalAllocator& operator=(const UnequalAllocator& x)
|
|
{
|
|
mAllocator = x.mAllocator;
|
|
return *this;
|
|
}
|
|
|
|
void* allocate(size_t n, int flags = 0) { return mAllocator.allocate(n, flags); }
|
|
void* allocate(size_t n, size_t alignment, size_t offset, int flags = 0) { return mAllocator.allocate(n, alignment, offset, flags); }
|
|
void deallocate(void* p, size_t n) { return mAllocator.deallocate(p, n); }
|
|
|
|
const char* get_name() const { return mAllocator.get_name(); }
|
|
void set_name(const char* pName) { mAllocator.set_name(pName); }
|
|
|
|
protected:
|
|
eastl::allocator mAllocator;
|
|
};
|
|
|
|
inline bool operator==(const UnequalAllocator&, const UnequalAllocator&) { return false; }
|
|
inline bool operator!=(const UnequalAllocator&, const UnequalAllocator&) { return true; }
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/// CountingAllocator
|
|
///
|
|
/// Counts allocation events allowing unit tests to validate assumptions.
|
|
///
|
|
class CountingAllocator : public eastl::allocator
|
|
{
|
|
public:
|
|
using base_type = eastl::allocator;
|
|
|
|
EASTL_ALLOCATOR_EXPLICIT CountingAllocator(const char* pName = EASTL_NAME_VAL(EASTL_ALLOCATOR_DEFAULT_NAME))
|
|
: base_type(pName)
|
|
{
|
|
totalCtorCount++;
|
|
defaultCtorCount++;
|
|
}
|
|
|
|
CountingAllocator(const CountingAllocator& x) : base_type(x)
|
|
{
|
|
totalCtorCount++;
|
|
copyCtorCount++;
|
|
}
|
|
|
|
CountingAllocator(const CountingAllocator& x, const char* pName) : base_type(x)
|
|
{
|
|
totalCtorCount++;
|
|
copyCtorCount++;
|
|
set_name(pName);
|
|
}
|
|
|
|
CountingAllocator& operator=(const CountingAllocator& x)
|
|
{
|
|
base_type::operator=(x);
|
|
assignOpCount++;
|
|
return *this;
|
|
}
|
|
|
|
virtual void* allocate(size_t n, int flags = 0)
|
|
{
|
|
activeAllocCount++;
|
|
totalAllocCount++;
|
|
totalAllocatedMemory += n;
|
|
activeAllocatedMemory += n;
|
|
return base_type::allocate(n, flags);
|
|
}
|
|
|
|
virtual void* allocate(size_t n, size_t alignment, size_t offset, int flags = 0)
|
|
{
|
|
activeAllocCount++;
|
|
totalAllocCount++;
|
|
totalAllocatedMemory += n;
|
|
activeAllocatedMemory += n;
|
|
return base_type::allocate(n, alignment, offset, flags);
|
|
}
|
|
|
|
void deallocate(void* p, size_t n)
|
|
{
|
|
activeAllocCount--;
|
|
totalDeallocCount--;
|
|
activeAllocatedMemory -= n;
|
|
return base_type::deallocate(p, n);
|
|
}
|
|
|
|
const char* get_name() const { return base_type::get_name(); }
|
|
void set_name(const char* pName) { base_type::set_name(pName); }
|
|
|
|
static auto getTotalAllocationCount() { return totalAllocCount; }
|
|
static auto getTotalAllocationSize() { return totalAllocatedMemory; }
|
|
static auto getActiveAllocationSize() { return activeAllocatedMemory; }
|
|
static auto getActiveAllocationCount() { return activeAllocCount; }
|
|
static auto neverUsed() { return totalAllocCount == 0; }
|
|
|
|
static void resetCount()
|
|
{
|
|
activeAllocCount = 0;
|
|
totalAllocCount = 0;
|
|
totalDeallocCount = 0;
|
|
totalCtorCount = 0;
|
|
defaultCtorCount = 0;
|
|
copyCtorCount = 0;
|
|
assignOpCount = 0;
|
|
totalAllocatedMemory = 0;
|
|
activeAllocatedMemory = 0;
|
|
}
|
|
|
|
static uint64_t activeAllocCount;
|
|
static uint64_t totalAllocCount;
|
|
static uint64_t totalDeallocCount;
|
|
static uint64_t totalCtorCount;
|
|
static uint64_t defaultCtorCount;
|
|
static uint64_t copyCtorCount;
|
|
static uint64_t assignOpCount;
|
|
static uint64_t totalAllocatedMemory; // the total amount of memory allocated
|
|
static uint64_t activeAllocatedMemory; // currently allocated memory by allocator
|
|
};
|
|
|
|
inline bool operator==(const CountingAllocator& rhs, const CountingAllocator& lhs) { return operator==(CountingAllocator::base_type(rhs), CountingAllocator::base_type(lhs)); }
|
|
inline bool operator!=(const CountingAllocator& rhs, const CountingAllocator& lhs) { return !(rhs == lhs); }
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// InstanceAllocator
|
|
//
|
|
// Implements an allocator which has a instance id that makes it different
|
|
// from other InstanceAllocators of a different id. Allocations between
|
|
// InstanceAllocators of different ids are incompatible. An allocation done
|
|
// by an InstanceAllocator of id=0 cannot be freed by an InstanceAllocator
|
|
// of id=1.
|
|
//
|
|
// Example usage:
|
|
// InstanceAllocator ia0((uint8_t)0);
|
|
// InstanceAllocator ia1((uint8_t)1);
|
|
//
|
|
// eastl::list<int, InstanceAllocator> list0(1, ia0);
|
|
// eastl::list<int, InstanceAllocator> list1(1, ia1);
|
|
//
|
|
// list0 = list1; // list0 cannot free it's current contents with list1's allocator, and InstanceAllocator's purpose is to detect if it mistakenly does so.
|
|
//
|
|
class InstanceAllocator
|
|
{
|
|
public:
|
|
enum
|
|
{
|
|
kMultiplier = 16
|
|
}; // Use 16 because it's the highest currently known platform alignment requirement.
|
|
|
|
InstanceAllocator(const char* = NULL, uint8_t instanceId = 0) : mInstanceId(instanceId) {}
|
|
InstanceAllocator(uint8_t instanceId) : mInstanceId(instanceId) {}
|
|
InstanceAllocator(const InstanceAllocator& x) : mInstanceId(x.mInstanceId) {}
|
|
InstanceAllocator(const InstanceAllocator& x, const char*) : mInstanceId(x.mInstanceId) {}
|
|
|
|
InstanceAllocator& operator=(const InstanceAllocator& x)
|
|
{
|
|
mInstanceId = x.mInstanceId;
|
|
return *this;
|
|
}
|
|
|
|
void* allocate(size_t n, int = 0)
|
|
{ // +1 so that we always have space to write mInstanceId.
|
|
uint8_t* p8 =
|
|
static_cast<uint8_t*>(malloc(n + (kMultiplier * (mInstanceId + 1)))); // We make allocations between
|
|
// different instances incompatible by
|
|
// tweaking their return values.
|
|
eastl::fill(p8, p8 + kMultiplier, 0xff);
|
|
EA_ANALYSIS_ASSUME(p8 != NULL);
|
|
*p8 = mInstanceId;
|
|
return p8 + (kMultiplier * (mInstanceId + 1));
|
|
}
|
|
|
|
void* allocate(size_t n, size_t, size_t, int = 0)
|
|
{ // +1 so that we always have space to write mInstanceId.
|
|
uint8_t* p8 =
|
|
static_cast<uint8_t*>(malloc(n + (kMultiplier * (mInstanceId + 1)))); // We make allocations between
|
|
// different instances incompatible by
|
|
// tweaking their return values.
|
|
eastl::fill(p8, p8 + kMultiplier, 0xff);
|
|
EA_ANALYSIS_ASSUME(p8 != NULL);
|
|
*p8 = mInstanceId;
|
|
return p8 + (kMultiplier * (mInstanceId + 1));
|
|
}
|
|
|
|
void deallocate(void* p, size_t /*n*/)
|
|
{
|
|
uint8_t* p8 = static_cast<uint8_t*>(p) - (kMultiplier * (mInstanceId + 1));
|
|
EASTL_ASSERT(*p8 == mInstanceId); // mInstanceId must match the id used in allocate(), otherwise the behavior is
|
|
// undefined (probably a heap assert).
|
|
if (*p8 == mInstanceId) // It's possible that *p8 coincidentally matches mInstanceId if p8 is offset into memory
|
|
// we don't control.
|
|
free(p8);
|
|
else
|
|
++mMismatchCount;
|
|
}
|
|
|
|
const char* get_name()
|
|
{
|
|
sprintf(mName, "InstanceAllocator %u", mInstanceId);
|
|
return mName;
|
|
}
|
|
|
|
void set_name(const char*) {}
|
|
|
|
static void reset_all() { mMismatchCount = 0; }
|
|
|
|
public:
|
|
uint8_t mInstanceId;
|
|
char mName[32];
|
|
|
|
static int mMismatchCount;
|
|
};
|
|
|
|
inline bool operator==(const InstanceAllocator& a, const InstanceAllocator& b) { return (a.mInstanceId == b.mInstanceId); }
|
|
inline bool operator!=(const InstanceAllocator& a, const InstanceAllocator& b) { return (a.mInstanceId != b.mInstanceId); }
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// ThrowingAllocator
|
|
//
|
|
// Implements an EASTL allocator that uses malloc/free as opposed to
|
|
// new/delete or PPMalloc Malloc/Free. This is useful for testing
|
|
// allocator behaviour of code.
|
|
//
|
|
// Example usage:
|
|
// vector<int, ThrowingAllocator< false<> > intVector;
|
|
//
|
|
template <bool initialShouldThrow = true>
|
|
class ThrowingAllocator
|
|
{
|
|
public:
|
|
ThrowingAllocator(const char* = EASTL_NAME_VAL("ThrowingAllocator")) : mbShouldThrow(initialShouldThrow) {}
|
|
ThrowingAllocator(const ThrowingAllocator& x) : mbShouldThrow(x.mbShouldThrow) {}
|
|
ThrowingAllocator(const ThrowingAllocator& x, const char*) : mbShouldThrow(x.mbShouldThrow) {}
|
|
|
|
ThrowingAllocator& operator=(const ThrowingAllocator& x)
|
|
{
|
|
mbShouldThrow = x.mbShouldThrow;
|
|
return *this;
|
|
}
|
|
|
|
void* allocate(size_t n, int = 0)
|
|
{
|
|
#if EASTL_EXCEPTIONS_ENABLED
|
|
if (mbShouldThrow)
|
|
throw std::bad_alloc();
|
|
#endif
|
|
return malloc(n);
|
|
}
|
|
|
|
void* allocate(size_t n, size_t, size_t, int = 0)
|
|
{
|
|
#if EASTL_EXCEPTIONS_ENABLED
|
|
if (mbShouldThrow)
|
|
throw std::bad_alloc();
|
|
#endif
|
|
return malloc(n); // We don't support alignment, so you can't use this class where alignment is required.
|
|
}
|
|
|
|
void deallocate(void* p, size_t) { free(p); }
|
|
|
|
const char* get_name() const { return "ThrowingAllocator"; }
|
|
void set_name(const char*) {}
|
|
|
|
void set_should_throw(bool shouldThrow) { mbShouldThrow = shouldThrow; }
|
|
bool get_should_throw() const { return mbShouldThrow; }
|
|
|
|
protected:
|
|
bool mbShouldThrow;
|
|
};
|
|
|
|
template <bool initialShouldThrow>
|
|
inline bool operator==(const ThrowingAllocator<initialShouldThrow>&, const ThrowingAllocator<initialShouldThrow>&)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
template <bool initialShouldThrow>
|
|
inline bool operator!=(const ThrowingAllocator<initialShouldThrow>&, const ThrowingAllocator<initialShouldThrow>&)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Helper utility that does a case insensitive string comparsion with two sets of overloads
|
|
//
|
|
struct TestStrCmpI_2
|
|
{
|
|
bool operator()(const char* pCStr, const eastl::string& str) const { return str.comparei(pCStr) == 0; }
|
|
bool operator()(const eastl::string& str, const char* pCStr) const { return str.comparei(pCStr) == 0; }
|
|
};
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// StompDetectAllocator
|
|
//
|
|
// An allocator that has sentinal values surrounding its allocator in an
|
|
// effort to detected if its internal memory has been stomped.
|
|
//
|
|
static uint64_t STOMP_MAGIC_V1 = 0x0101DEC1A551F1ED;
|
|
static uint64_t STOMP_MAGIC_V2 = 0x12345C1A551F1ED5;
|
|
|
|
struct StompDetectAllocator
|
|
{
|
|
StompDetectAllocator() { Validate(); }
|
|
~StompDetectAllocator() { Validate(); }
|
|
|
|
StompDetectAllocator(const char*) { Validate(); }
|
|
|
|
void* allocate(size_t n, int = 0) { return mMallocAllocator.allocate(n); }
|
|
void* allocate(size_t n, size_t, size_t, int = 0) { return mMallocAllocator.allocate(n); }
|
|
void deallocate(void* p, size_t n) { mMallocAllocator.deallocate(p, n); }
|
|
|
|
const char* get_name() const { return "FatAllocator"; }
|
|
void set_name(const char*) {}
|
|
|
|
void Validate() const
|
|
{
|
|
EASTL_ASSERT(mSentinal1 == STOMP_MAGIC_V1);
|
|
EASTL_ASSERT(mSentinal2 == STOMP_MAGIC_V2);
|
|
}
|
|
|
|
uint64_t mSentinal1 = STOMP_MAGIC_V1;
|
|
MallocAllocator mMallocAllocator;
|
|
uint64_t mSentinal2 = STOMP_MAGIC_V2;
|
|
};
|
|
|
|
inline bool operator==(const StompDetectAllocator& a, const StompDetectAllocator& b)
|
|
{
|
|
a.Validate();
|
|
b.Validate();
|
|
|
|
return (a.mMallocAllocator == b.mMallocAllocator);
|
|
}
|
|
|
|
inline bool operator!=(const StompDetectAllocator& a, const StompDetectAllocator& b)
|
|
{
|
|
a.Validate();
|
|
b.Validate();
|
|
|
|
return (a.mMallocAllocator != b.mMallocAllocator);
|
|
}
|
|
|
|
|
|
// Commonly used free-standing functions to test callables
|
|
inline int ReturnVal(int param) { return param; }
|
|
inline int ReturnZero() { return 0; }
|
|
inline int ReturnOne() { return 1; }
|
|
|
|
|
|
// ValueInit
|
|
template<class T>
|
|
struct ValueInitOf
|
|
{
|
|
ValueInitOf() : mV() {}
|
|
~ValueInitOf() = default;
|
|
|
|
ValueInitOf(const ValueInitOf&) = default;
|
|
ValueInitOf(ValueInitOf&&) = default;
|
|
|
|
ValueInitOf& operator=(const ValueInitOf&) = default;
|
|
ValueInitOf& operator=(ValueInitOf&&) = default;
|
|
|
|
T get() { return mV; }
|
|
|
|
T mV;
|
|
};
|
|
|
|
// MoveOnlyType - useful for verifying containers that may hold, e.g., unique_ptrs to make sure move ops are implemented
|
|
struct MoveOnlyType
|
|
{
|
|
MoveOnlyType() = delete;
|
|
MoveOnlyType(int val) : mVal(val) {}
|
|
MoveOnlyType(const MoveOnlyType&) = delete;
|
|
MoveOnlyType(MoveOnlyType&& x) : mVal(x.mVal) { x.mVal = 0; }
|
|
MoveOnlyType& operator=(const MoveOnlyType&) = delete;
|
|
MoveOnlyType& operator=(MoveOnlyType&& x)
|
|
{
|
|
mVal = x.mVal;
|
|
x.mVal = 0;
|
|
return *this;
|
|
}
|
|
bool operator==(const MoveOnlyType& o) const { return mVal == o.mVal; }
|
|
|
|
int mVal;
|
|
};
|
|
|
|
// MoveOnlyTypeDefaultCtor - useful for verifying containers that may hold, e.g., unique_ptrs to make sure move ops are implemented
|
|
struct MoveOnlyTypeDefaultCtor
|
|
{
|
|
MoveOnlyTypeDefaultCtor() = default;
|
|
MoveOnlyTypeDefaultCtor(int val) : mVal(val) {}
|
|
MoveOnlyTypeDefaultCtor(const MoveOnlyTypeDefaultCtor&) = delete;
|
|
MoveOnlyTypeDefaultCtor(MoveOnlyTypeDefaultCtor&& x) : mVal(x.mVal) { x.mVal = 0; }
|
|
MoveOnlyTypeDefaultCtor& operator=(const MoveOnlyTypeDefaultCtor&) = delete;
|
|
MoveOnlyTypeDefaultCtor& operator=(MoveOnlyTypeDefaultCtor&& x)
|
|
{
|
|
mVal = x.mVal;
|
|
x.mVal = 0;
|
|
return *this;
|
|
}
|
|
bool operator==(const MoveOnlyTypeDefaultCtor& o) const { return mVal == o.mVal; }
|
|
|
|
int mVal;
|
|
};
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Utility RAII class that sets a new default allocator for the scope
|
|
//
|
|
struct AutoDefaultAllocator
|
|
{
|
|
eastl::allocator* mPrevAllocator = nullptr;
|
|
|
|
AutoDefaultAllocator(eastl::allocator* nextAllocator) { mPrevAllocator = SetDefaultAllocator(nextAllocator); }
|
|
~AutoDefaultAllocator() { SetDefaultAllocator(mPrevAllocator); }
|
|
};
|
|
|
|
|
|
#endif // Header include guard
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|