First
This commit is contained in:
@@ -0,0 +1,698 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Electronic Arts Inc. All rights reserved.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// This header provides:
|
||||
// EAAlignOf
|
||||
// AlignOf<T>
|
||||
//
|
||||
// AlignUp<T> -- Provides basic address and object alignment.
|
||||
// AlignDown<T>
|
||||
// GetAlignment
|
||||
// IsAligned<T>
|
||||
//
|
||||
// AlignedArray -- Allows for specifying alignment of objects at runtime and in a compiler-independent way.
|
||||
// AlignedObject
|
||||
//
|
||||
// ReadMisalignedUint16 -- Allows for reading from misaligned memory.
|
||||
// ReadMisalignedUint32
|
||||
// ReadMisalignedUint64
|
||||
// WriteMisalignedUint16 -- Allows for writing to misaligned memory.
|
||||
// WriteMisalignedUint32
|
||||
// WriteMisalignedUint64
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#ifndef EASTDC_EAALIGNMENT_H
|
||||
#define EASTDC_EAALIGNMENT_H
|
||||
|
||||
|
||||
#include <EAStdC/internal/Config.h>
|
||||
#include <EABase/eabase.h>
|
||||
|
||||
EA_DISABLE_ALL_VC_WARNINGS()
|
||||
EA_DISABLE_VC_WARNING(4548 4836 4574)
|
||||
#include <stddef.h>
|
||||
#include <new>
|
||||
EA_RESTORE_VC_WARNING()
|
||||
EA_RESTORE_ALL_VC_WARNINGS()
|
||||
|
||||
|
||||
|
||||
// The default HasTrivialDestructor template implementation doesn't always work on all
|
||||
// compilers for vector intrinsic types. It is more reliable for us to use the built-in
|
||||
// compiler traits. It's possible we may want to do this for other compilers as well.
|
||||
// Currently this is only supported on newer versions of GCC and Clang.
|
||||
#if defined EA_COMPILER_GNUC || defined EA_COMPILER_CLANG
|
||||
#if defined EA_COMPILER_GNUC && (EA_COMPILER_VERSION <= 4004)
|
||||
#define EASTDC_TRIVIAL_DTOR_VIA_TRAITS 0
|
||||
#elif defined EA_PLATFORM_APPLE
|
||||
#define EASTDC_TRIVIAL_DTOR_VIA_TRAITS 0
|
||||
#else
|
||||
#define EASTDC_TRIVIAL_DTOR_VIA_TRAITS 1
|
||||
#endif
|
||||
#else
|
||||
#define EASTDC_TRIVIAL_DTOR_VIA_TRAITS 0
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/// EAAlignOf
|
||||
///
|
||||
/// Note: This has been superceded by EABase's EA_ALIGN_OF.
|
||||
///
|
||||
/// Gives you the alignment of a given data type. If you are using
|
||||
/// a compiler that doesn't support a built-in alignof then you
|
||||
/// are stuck with being able to use EAAlignOf only on "POD" types
|
||||
/// (i.e. basic C structures).
|
||||
///
|
||||
/// Example usage:
|
||||
/// printf("Alignment of type 'int' is %u.", (unsigned)EAAlignOf(int));
|
||||
///
|
||||
#ifndef EAAlignOf
|
||||
#define EAAlignOf(x) EA_ALIGN_OF(x)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
namespace EA
|
||||
{
|
||||
namespace StdC
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
/// Type Traits
|
||||
template <typename T, T v>
|
||||
struct IntegralConstant
|
||||
{
|
||||
static const T value = v;
|
||||
typedef T value_type;
|
||||
typedef IntegralConstant<T, v> type;
|
||||
};
|
||||
|
||||
typedef IntegralConstant<bool, true> TrueType;
|
||||
typedef IntegralConstant<bool, false> FalseType;
|
||||
|
||||
#if EASTDC_TRIVIAL_DTOR_VIA_TRAITS
|
||||
template <typename T> struct HasTrivialDestructor : public IntegralConstant<bool, __has_trivial_destructor(T) || __is_pod(T)> {};
|
||||
#else
|
||||
template <typename T> struct HasTrivialDestructor : public FalseType{};
|
||||
#endif
|
||||
|
||||
// Example usage:
|
||||
// EASTDC_DECLARE_TRIVIAL_DESTRUCTOR(__m128)
|
||||
#define EASTDC_DECLARE_TRIVIAL_DESTRUCTOR(T) namespace EA {namespace StdC { \
|
||||
template <> struct HasTrivialDestructor<T> : public TrueType{}; \
|
||||
template <> struct HasTrivialDestructor<const T> : public TrueType{}; \
|
||||
template <> struct HasTrivialDestructor<volatile T> : public TrueType{}; \
|
||||
template <> struct HasTrivialDestructor<const volatile T> : public TrueType{}; } }
|
||||
|
||||
|
||||
|
||||
/// AlignOf
|
||||
///
|
||||
/// Gives you the alignment of a given data type. If you are using
|
||||
/// a compiler that doesn't support a built-in alignof then you
|
||||
/// are stuck with being able to use EAAlignOf only on "POD" types
|
||||
/// (i.e. basic C structures).
|
||||
///
|
||||
/// Example usage:
|
||||
/// int x;
|
||||
/// printf("Alignment of type int is %u.", (unsigned)AlignOf<int>());
|
||||
///
|
||||
template <typename T>
|
||||
inline size_t AlignOf()
|
||||
{
|
||||
return EAAlignOf(T);
|
||||
}
|
||||
|
||||
|
||||
/// AlignOf
|
||||
///
|
||||
/// This is an instance-based version of AlignOf.
|
||||
///
|
||||
/// Example usage:
|
||||
/// int x;
|
||||
/// printf("Alignment of x is %u.", (unsigned)AlignOf(x));
|
||||
///
|
||||
template <typename T>
|
||||
inline size_t AlignOf(const T&)
|
||||
{
|
||||
return EAAlignOf(T);
|
||||
}
|
||||
|
||||
|
||||
/// AlignUp
|
||||
/// Rounds a scalar value (integer or pointer) up to the nearest multiple of
|
||||
/// Rounds values towards positive infinity.
|
||||
/// Returns 0 for an input of 0.
|
||||
/// The alignment 'a' must be an even power of 2.
|
||||
/// This version of AlignUp is at least as efficient as the version which takes alignment as an argument.
|
||||
/// Example:
|
||||
/// AlignUp<int, 4>(3) -> 4
|
||||
/// AlignUp<int, 4>(8) -> 8
|
||||
/// AlignUp<int, 4>(0) -> 0
|
||||
/// AlignUp<int, 4>(-7) -> -4
|
||||
template <typename T, size_t a>
|
||||
inline T AlignUp(T x){
|
||||
return (T)((x + (a - 1)) & ~(a - 1));
|
||||
}
|
||||
|
||||
template <typename T, size_t a>
|
||||
inline T* AlignUp(T* p){
|
||||
return (T*)(((uintptr_t)p + (a - 1)) & ~(a - 1));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// AlignUp
|
||||
/// Rounds values towards positive infinity.
|
||||
/// Returns 0 for an input of 0.
|
||||
/// The alignment 'a' must be an even power of 2.
|
||||
/// Example:
|
||||
/// AlignUp(3, 4) -> 4
|
||||
/// AlignUp(8, 4) -> 8
|
||||
/// AlignUp(0, 4) -> 0
|
||||
/// AlignUp(-7, 4) -> -4
|
||||
template <typename T>
|
||||
inline T AlignUp(T x, size_t a){
|
||||
return (T)((x + (a - 1)) & ~(a - 1));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T* AlignUp(T* p, size_t a){
|
||||
return (T*)(((uintptr_t)p + (a - 1)) & ~(a - 1));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// AlignDown
|
||||
/// Rounds a scalar value (integer or pointer) down to nearest multiple of template parameter <a>.
|
||||
/// Returns 0 for an input of 0.
|
||||
/// Rounds values towards negative infinity.
|
||||
/// The alignment 'a' must be an even power of 2.
|
||||
/// This version of AlignDown is at least as efficient as the version which takes alignment as an argument.
|
||||
/// Example:
|
||||
/// AlignDown<int, 4>(5) -> 4
|
||||
/// AlignDown<int, 4>(4) -> 4
|
||||
/// AlignDown<int, 4>(0) -> 0
|
||||
/// AlignDown<int, 4>(-7) -> -8
|
||||
template <typename T, size_t a>
|
||||
inline T AlignDown(T x){
|
||||
return (T)(x & ~(a - 1));
|
||||
}
|
||||
|
||||
template <typename T, size_t a>
|
||||
inline T* AlignDown(T* p){
|
||||
return (T*)((uintptr_t)p & ~(a - 1));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// AlignDown
|
||||
/// Rounds a scalar value (integer or pointer) down to nearest multiple of of template parameter <a>.
|
||||
/// x must be a scalar value (integer or pointer), else the results are undefined.
|
||||
/// Returns 0 for an input of 0.
|
||||
/// Rounds values towards negative infinity.
|
||||
/// The alignment 'a' must be an even power of 2.
|
||||
/// Example:
|
||||
/// AlignDown(5, 4) -> 4
|
||||
/// AlignDown(4, 4) -> 4
|
||||
/// AlignDown(0, 4) -> 0
|
||||
/// AlignDown(-7, 4) -> -8
|
||||
template <typename T>
|
||||
inline T AlignDown(T x, size_t a){
|
||||
return (T)(x & ~(a - 1));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T* AlignDown(T* p, size_t a){
|
||||
return (T*)((uintptr_t)p & ~(a - 1));
|
||||
}
|
||||
|
||||
|
||||
/// GetAlignment
|
||||
///
|
||||
/// Returns the highest power-of-two alignment of the given value x.
|
||||
/// x must be a scalar value (integer or pointer), else the results are undefined.
|
||||
/// Returns 0 for an input a value of 0.
|
||||
/// Beware that GetAlignment returns the highest power-of-two alignment, which
|
||||
/// may result in a return value that is higher than you expect. Consider using
|
||||
/// the IsAligned functions to test for a specific alignment.
|
||||
/// Example:
|
||||
/// GetAlignment(0) -> 0
|
||||
/// GetAlignment(1) -> 1
|
||||
/// GetAlignment(2) -> 2
|
||||
/// GetAlignment(3) -> 1
|
||||
/// GetAlignment(4) -> 4
|
||||
/// GetAlignment(5) -> 1
|
||||
/// GetAlignment(6) -> 2
|
||||
/// GetAlignment(7) -> 1
|
||||
/// GetAlignment(8) -> 8
|
||||
/// GetAlignment(9) -> 1
|
||||
template <typename T>
|
||||
inline size_t GetAlignment(T x)
|
||||
{
|
||||
return (size_t)((x ^ (x - 1)) >> 1) + 1;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline size_t GetAlignment(T* p)
|
||||
{
|
||||
return (size_t)(((uintptr_t)p ^ ((uintptr_t)p - 1)) >> 1) + 1;
|
||||
}
|
||||
|
||||
|
||||
/// IsAligned
|
||||
///
|
||||
/// Tells if a given integer is aligned to a given power-of-2 boundary.
|
||||
/// Returns true for an input x value of 0, regardless of the value of a.
|
||||
/// The template <a> value must be >= 1.
|
||||
/// Example:
|
||||
/// IsAligned<int, 8>(64) -> true
|
||||
/// IsAligned<int, 8>(67) -> false
|
||||
///
|
||||
/// To consider: wouldn't it be better if the template arguments were reversed?
|
||||
///
|
||||
template <typename T, int a>
|
||||
inline bool IsAligned(T x)
|
||||
{
|
||||
return (x & (a - 1)) == 0;
|
||||
}
|
||||
|
||||
template <typename T, int a>
|
||||
inline bool IsAligned(T* p)
|
||||
{
|
||||
return ((uintptr_t)p & (a - 1)) == 0;
|
||||
}
|
||||
|
||||
|
||||
/// IsAligned
|
||||
///
|
||||
/// Tells if a given integer is aligned to a given power-of-2 boundary.
|
||||
/// Returns true for an input x value of 0, regardless of the value of a.
|
||||
/// The alignment value a must be >= 1.
|
||||
/// Example:
|
||||
/// IsAligned(64, 8) -> true
|
||||
/// IsAligned(67, 8) -> false
|
||||
///
|
||||
template <typename T>
|
||||
inline bool IsAligned(T x, size_t a)
|
||||
{
|
||||
return (x & (a - 1)) == 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool IsAligned(T* p, size_t a)
|
||||
{
|
||||
return ((uintptr_t)p & (a - 1)) == 0;
|
||||
}
|
||||
|
||||
|
||||
/// AlignedType
|
||||
///
|
||||
/// This class makes an aligned typedef for a given class based on a user-supplied
|
||||
/// class and alignment. This class exists because of a weakness in VC++ whereby
|
||||
/// it can only accept __declspec(align) and thus EA_PREFIX_ALIGN usage via an
|
||||
/// integer literal (e.g. "16") and not an otherwise equivalent constant integral
|
||||
/// expression (e.g. sizeof Foo).
|
||||
///
|
||||
/// Example usage:
|
||||
/// const size_t kAlignment = 32;
|
||||
/// AlignedType<int, kAlignment>::Type intAlignedAt128;
|
||||
/// intAlignedAt128 = 12345;
|
||||
///
|
||||
#if !defined(__GNUC__) || (__GNUC__ < 4) // GCC4 lost the ability to do this
|
||||
template <typename T, size_t alignment> struct AlignedType { };
|
||||
|
||||
#if defined(EA_COMPILER_MSVC) && EA_COMPILER_VERSION >= 1900 // VS2015+
|
||||
EA_DISABLE_VC_WARNING(5029); // nonstandard extension used: alignment attributes in C++ apply to variables, data members and tag types only
|
||||
#endif
|
||||
template<typename T> struct AlignedType<T, 2> { typedef EA_PREFIX_ALIGN( 2) T EA_POSTFIX_ALIGN( 2) Type; };
|
||||
template<typename T> struct AlignedType<T, 4> { typedef EA_PREFIX_ALIGN( 4) T EA_POSTFIX_ALIGN( 4) Type; };
|
||||
template<typename T> struct AlignedType<T, 8> { typedef EA_PREFIX_ALIGN( 8) T EA_POSTFIX_ALIGN( 8) Type; };
|
||||
template<typename T> struct AlignedType<T, 16> { typedef EA_PREFIX_ALIGN( 16) T EA_POSTFIX_ALIGN( 16) Type; };
|
||||
template<typename T> struct AlignedType<T, 32> { typedef EA_PREFIX_ALIGN( 32) T EA_POSTFIX_ALIGN( 32) Type; };
|
||||
template<typename T> struct AlignedType<T, 64> { typedef EA_PREFIX_ALIGN( 64) T EA_POSTFIX_ALIGN( 64) Type; };
|
||||
template<typename T> struct AlignedType<T, 128> { typedef EA_PREFIX_ALIGN( 128) T EA_POSTFIX_ALIGN( 128) Type; };
|
||||
template<typename T> struct AlignedType<T, 256> { typedef EA_PREFIX_ALIGN( 256) T EA_POSTFIX_ALIGN( 256) Type; };
|
||||
template<typename T> struct AlignedType<T, 512> { typedef EA_PREFIX_ALIGN( 512) T EA_POSTFIX_ALIGN( 512) Type; };
|
||||
template<typename T> struct AlignedType<T, 1024> { typedef EA_PREFIX_ALIGN(1024) T EA_POSTFIX_ALIGN(1024) Type; };
|
||||
template<typename T> struct AlignedType<T, 2048> { typedef EA_PREFIX_ALIGN(2048) T EA_POSTFIX_ALIGN(2048) Type; };
|
||||
template<typename T> struct AlignedType<T, 4096> { typedef EA_PREFIX_ALIGN(4096) T EA_POSTFIX_ALIGN(4096) Type; };
|
||||
#if defined(EA_COMPILER_MSVC) && EA_COMPILER_VERSION >= 1900 // VS2015+
|
||||
EA_RESTORE_VC_WARNING();
|
||||
#endif
|
||||
#else
|
||||
// Need to deal with this under GCC 4, now that they took this functionality away.
|
||||
#endif
|
||||
|
||||
|
||||
/// AlignedArray
|
||||
///
|
||||
/// Allows you to align an array of objects, regardless of when or where they are declared.
|
||||
/// Alignment must be a power-of-two value, such as 2, 4, 8, 16, 32, etc.
|
||||
/// A common use of this would be to have an object on the stack be aligned to a specific
|
||||
/// value. It also allows for a member variable of a struct to be aligned to a given size
|
||||
/// without having any special requirements about the alignment of the struct itself.
|
||||
/// This is advantageous relative to compiler-specific alignment mechanisms.
|
||||
/// The downside of this mechanism relative to compiler-specific alignment mechanisms is
|
||||
/// that this mechanism may use more memory.
|
||||
///
|
||||
/// Question: Should each element in the array be aligned or should just the beginning of
|
||||
/// the array be aligned? Normally the answer should be just the beginning. The reason for
|
||||
/// this is that the C/C++ standard expects array elements to be sizeof(T) apart from each
|
||||
/// other when in an array.
|
||||
///
|
||||
/// Example usage:
|
||||
/// AlignedArray<Vector, 10, 64> mVectorArray;
|
||||
/// mVectorArray[0] = mVectorArray[1];
|
||||
/// NormalizeVector(mVectorArray[3]);
|
||||
/// ProcessVectorArray(mVectorArray);
|
||||
///
|
||||
/// To do: Make this class act like AlignedType and retain the smart pointer functionality.
|
||||
///
|
||||
template <class T, int count, int alignment>
|
||||
class AlignedArray
|
||||
{
|
||||
public:
|
||||
typedef T value_type;
|
||||
typedef AlignedArray<T, count, alignment> this_type;
|
||||
|
||||
AlignedArray()
|
||||
{
|
||||
mpT = (T*)(((intptr_t)(mBuffer + (alignment - 1))) & ~(alignment - 1));
|
||||
|
||||
for(T *pT = mpT, *pTEnd = mpT + count; pT < pTEnd; ++pT)
|
||||
new(pT) T; // Note that we are not allocating memory here; we are constructing existing memory.
|
||||
}
|
||||
|
||||
AlignedArray(const this_type& x)
|
||||
{
|
||||
mpT = (T*)(((intptr_t)(mBuffer + (alignment - 1))) & ~(alignment - 1));
|
||||
|
||||
for(T *pT = mpT, *pTSrc = x.mpT, *pTEnd = mpT + count; pT < pTEnd; ++pT, ++pTSrc)
|
||||
new(pT) T(*pTSrc); // Note that we are not allocating memory here; we are constructing existing memory.
|
||||
}
|
||||
|
||||
void DeleteMembers(TrueType) // true_type means yes it's true that T has a trivial destructor.
|
||||
{
|
||||
}
|
||||
|
||||
void DeleteMembers(FalseType) // false_type means the destructor is non-trivial.
|
||||
{
|
||||
for(T *pT = mpT + count - 1, *pTEnd = mpT; pT >= pTEnd; --pT)
|
||||
pT->~T();
|
||||
}
|
||||
|
||||
~AlignedArray()
|
||||
{
|
||||
DeleteMembers(HasTrivialDestructor<value_type>());
|
||||
}
|
||||
|
||||
// The following set of supported operators is currently experimental. A little more
|
||||
// thinking and testing will be needed to determine the proper best set of operators.
|
||||
|
||||
this_type& operator=(const this_type& x)
|
||||
{
|
||||
for(int i = 0; i < count; i++) // To consider: We could possibly improve this a little bit by
|
||||
mpT[i] = x.mpT[i]; // taking advantage of type traits or the STL/EASTL copy algorithm.
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator T*()
|
||||
{ return mpT; }
|
||||
|
||||
operator const T*() const
|
||||
{ return mpT; }
|
||||
|
||||
protected:
|
||||
T* mpT; // Points to a place within mBuffer.
|
||||
char mBuffer[(sizeof(T) * count) + alignment]; // This max possible space usage required to align an array of type T.
|
||||
};
|
||||
|
||||
|
||||
/// AlignedObject
|
||||
///
|
||||
/// Allows you to align an object, regardless of when or where it is declared.
|
||||
/// Alignment must be a power-of-two value, such as 2, 4, 8, 16, 32, etc.
|
||||
/// A common use of this would be to have an object on the stack be aligned to a specific
|
||||
/// value. It also allows for a member variable of a struct to be aligned to a given size
|
||||
/// without having any special requirements about the alignment of the struct itself.
|
||||
/// This is advantageous relative to compiler-specific alignment mechansims.
|
||||
/// The downside of this mechanism relative to compiler-specific alignment mechanisms is
|
||||
/// that this mechanism may use more memory.
|
||||
///
|
||||
/// This class lets you have most of the functionality you would have if the
|
||||
/// alignment functionality was native. For the most part, the only limitations
|
||||
/// are that you sometimes need reference the object in a slightly different
|
||||
/// way than usual. For example, you need to use operator->() to reference a
|
||||
/// class member instead of operator.() even though the object is not a pointer.
|
||||
///
|
||||
/// Example usage:
|
||||
/// AlignedObject<Matrix, 64> matrix;
|
||||
/// matrix->Normalize();
|
||||
/// NormalizeMatrix(&matrix);
|
||||
///
|
||||
/// To do: Make this class act like AlignedType and retain the smart pointer functionality.
|
||||
///
|
||||
template <class T, int alignment>
|
||||
class AlignedObject
|
||||
{
|
||||
public:
|
||||
typedef T value_type;
|
||||
typedef AlignedObject<T, alignment> this_type;
|
||||
|
||||
AlignedObject()
|
||||
: mT(*(T*)(((intptr_t)(mBuffer + (alignment - 1))) & ~(alignment - 1)))
|
||||
{ new((void*)(((intptr_t)(mBuffer + (alignment - 1))) & ~(alignment - 1))) T; } // Note that we are not allocating memory here; we are constructing existing memory.
|
||||
|
||||
AlignedObject(const this_type& x)
|
||||
: mT(*(T*)(((intptr_t)(mBuffer + (alignment - 1))) & ~(alignment - 1)))
|
||||
{ mT = x.mT; }
|
||||
|
||||
AlignedObject(const T& t)
|
||||
: mT(*(T*)(((intptr_t)(mBuffer + (alignment - 1))) & ~(alignment - 1)))
|
||||
{ mT = t; }
|
||||
|
||||
~AlignedObject()
|
||||
{ mT.~T(); }
|
||||
|
||||
// The following set of supported operators is currently experimental. A little more
|
||||
// thinking and testing will be needed to determine the proper best set of operators.
|
||||
|
||||
this_type& operator=(const this_type& x)
|
||||
{ mT = x.mT; return *this; }
|
||||
|
||||
this_type& operator=(const T& t)
|
||||
{ mT = t; return *this; }
|
||||
|
||||
T* operator &()
|
||||
{ return &mT; }
|
||||
|
||||
T* Get()
|
||||
{ return &mT; }
|
||||
|
||||
const T* Get() const
|
||||
{ return &mT; }
|
||||
|
||||
operator T&()
|
||||
{ return mT; }
|
||||
|
||||
operator const T&() const
|
||||
{ return mT; }
|
||||
|
||||
// C++ doesn't allow overloading the . operator.
|
||||
// Use operator -> or the T& cast operator instead.
|
||||
//T& operator.() const
|
||||
// { return mT; }
|
||||
|
||||
T* operator->()
|
||||
{ return &mT; }
|
||||
|
||||
const T* operator->() const
|
||||
{ return &mT; }
|
||||
|
||||
operator T*()
|
||||
{ return &mT; }
|
||||
|
||||
operator const T*() const
|
||||
{ return &mT; }
|
||||
|
||||
protected:
|
||||
T& mT; // Points to a place within mBuffer.
|
||||
char mBuffer[sizeof(T) + alignment]; // This max possible space usage required to align an object of type T.
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/// ReadMisalignedUint16
|
||||
///
|
||||
/// Get an unsigned integer from a possibly non-aligned address.
|
||||
/// The MIPS processor on the PS2 cannot read a 32-bit value
|
||||
/// from an unaligned address, so this function can be used
|
||||
/// to make reading misaligned data portable.
|
||||
///
|
||||
inline uint16_t ReadMisalignedUint16(const void* p)
|
||||
{
|
||||
return *((const uint16_t*)p);
|
||||
}
|
||||
|
||||
|
||||
/// ReadMisalignedUint32
|
||||
///
|
||||
/// Get an unsigned integer from a possibly non-aligned address.
|
||||
/// The MIPS processor on the PS2 cannot read a 32-bit value
|
||||
/// from an unaligned address, so this function can be used
|
||||
/// to make reading misaligned data portable.
|
||||
///
|
||||
inline uint32_t ReadMisalignedUint32(const void* p)
|
||||
{
|
||||
return *((const uint32_t*)p);
|
||||
}
|
||||
|
||||
/// ReadMisalignedUint64
|
||||
///
|
||||
/// Get an unsigned integer from a possibly non-aligned address.
|
||||
/// The MIPS processor on the PS2 cannot read a 32-bit value
|
||||
/// from an unaligned address, so this function can be used
|
||||
/// to make reading misaligned data portable.
|
||||
///
|
||||
inline uint64_t ReadMisalignedUint64(const void* p)
|
||||
{
|
||||
return *((const uint64_t*)p);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// WriteMisalignedUint16
|
||||
inline void WriteMisalignedUint16(uint16_t n, void* p)
|
||||
{
|
||||
*(uint16_t*)p = n;
|
||||
}
|
||||
|
||||
/// WriteMisalignedUint32
|
||||
inline void WriteMisalignedUint32(uint32_t n, void* p)
|
||||
{
|
||||
*(uint32_t*)p = n;
|
||||
}
|
||||
|
||||
/// WriteMisalignedUint64
|
||||
inline void WriteMisalignedUint64(uint64_t n, void* p)
|
||||
{
|
||||
*(uint64_t*)p = n;
|
||||
}
|
||||
|
||||
|
||||
} // namespace StdC
|
||||
} // namespace EA
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
namespace EA
|
||||
{
|
||||
namespace StdC
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Deprecated functions
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// AlignAddressUp
|
||||
///
|
||||
/// Aligns a given addess up to a specified power-of-two.
|
||||
/// Example usage:
|
||||
/// void* p;
|
||||
/// p = AlignAddressUp(p, 64);
|
||||
///
|
||||
inline void* AlignAddressUp(const void* p, size_t a)
|
||||
{
|
||||
return (void*)(((uintptr_t)p + (a - 1)) & ~(a - 1));
|
||||
}
|
||||
|
||||
|
||||
/// AlignObjectUp
|
||||
///
|
||||
/// Aligns a given object up to an address of a specified power-of-two.
|
||||
/// Example usage:
|
||||
/// Matrix* pM;
|
||||
/// pM = AlignObjectUp(pM, 16);
|
||||
///
|
||||
template <typename T>
|
||||
inline T* AlignObjectUp(const T* p, size_t a)
|
||||
{
|
||||
return (T*)(((uintptr_t)p + (a - 1)) & ~(a - 1));
|
||||
}
|
||||
|
||||
/// AlignAddressDown
|
||||
///
|
||||
/// Aligns a given addess down to a specified power-of-two.
|
||||
/// Example usage:
|
||||
/// void* p;
|
||||
/// p = AlignAddressDown(p, 8);
|
||||
///
|
||||
inline void* AlignAddressDown(const void* p, size_t a)
|
||||
{
|
||||
return (void*)((uintptr_t)p & ~(a - 1));
|
||||
}
|
||||
|
||||
|
||||
/// AlignObjectDown
|
||||
///
|
||||
/// Aligns a given object down to an address of a specified power-of-two.
|
||||
/// Example usage:
|
||||
/// Matrix* pM;
|
||||
/// pM = AlignObjectDown(pM, 16);
|
||||
///
|
||||
template <typename T>
|
||||
inline T* AlignObjectDown(const T* p, size_t a)
|
||||
{
|
||||
return (T*)((uintptr_t)p & ~(a - 1));
|
||||
}
|
||||
|
||||
|
||||
/// IsAddressAligned
|
||||
///
|
||||
/// Tells if a given address is aligned to a given power-of-2 boundary.
|
||||
/// Always returns true for a pointer 'p' value of NULL.
|
||||
/// The alignment 'a' must be >= 1.
|
||||
/// Example:
|
||||
/// IsAddressAligned((char*)0x00000000, 8) -> true
|
||||
/// IsAddressAligned((char*)0x00000001, 8) -> false
|
||||
///
|
||||
inline bool IsAddressAligned(const void* p, size_t a)
|
||||
{
|
||||
return ((uintptr_t)p & (a - 1)) == 0;
|
||||
}
|
||||
|
||||
|
||||
/// IsObjectAligned
|
||||
///
|
||||
/// Tells if a given object is aligned to a given power-of-2 boundary.
|
||||
/// This is redundant with respect to IsAddressAligned, but is provided
|
||||
/// for consistency.
|
||||
/// The alignment 'a' must be >= 1.
|
||||
///
|
||||
template <typename T>
|
||||
inline bool IsObjectAligned(const T* p, size_t a)
|
||||
{
|
||||
return ((uintptr_t)p & (a - 1)) == 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace StdC
|
||||
} // namespace EA
|
||||
|
||||
|
||||
#endif // Header include guard
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,99 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Electronic Arts Inc. All rights reserved.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// This file implements safe "byte crackers" and builders for all primary
|
||||
// built-in integral data types. These are particularly useful because working
|
||||
// with signed values creates opportunities for mistakes, and the macros present
|
||||
// in EAByteCrackers remove such possibilities for error.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#if !defined(EASTDC_EABYTECRACKERS_H) && !defined(FOUNDATION_EABYTECRACKERS_H)
|
||||
#define EASTDC_EABYTECRACKERS_H
|
||||
#define FOUNDATION_EABYTECRACKERS_H
|
||||
|
||||
|
||||
#include <EAStdC/internal/Config.h>
|
||||
#include <EABase/eabase.h>
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Conventions:
|
||||
// 0 (zero) refers to the lowest byte, 1 refers to the second lowest byte, etc.
|
||||
// UINT8_1_FROM_UINT32(0x12345678) for example, returns 0x56.
|
||||
//
|
||||
// b means 8 bit byte
|
||||
// w means 16 bit word
|
||||
// d means 32 bit dword
|
||||
// q means 64 bit quadword
|
||||
//
|
||||
// Example usage:
|
||||
// uint8_t u8 = UINT8_0_FROM_UINT16(0x1100); // Returns 0x00
|
||||
// uint8_t u8 = UINT8_2_FROM_UINT64(0x7766554433221100); // Returns 0x22
|
||||
// uint16_t u16 = UINT16_3_FROM_UINT64(0x7766554433221100); // Returns 0x7766
|
||||
//
|
||||
// uint16_t u16 = UINT16_FROM_UINT8(0x11, 0x00); // Returns 0x1100
|
||||
// uint32_t u32 = UINT32_FROM_UINT8(0x33, 0x22, 0x11, 0x00); // Returns 0x33221100
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#ifndef UINT8_0_FROM_UINT16
|
||||
|
||||
// uint8_t byte manipulators
|
||||
#define UINT8_0_FROM_UINT16(w) ((uint8_t)(w)) // Get the right-most byte from a uint16_t. (e.g. 0x1234 returns 0x34)
|
||||
#define UINT8_1_FROM_UINT16(w) ((uint8_t)((uint16_t)(w) >> 8)) // Get the left-most byte from a uint16_t. (e.g. 0x1234 returns 0x12)
|
||||
|
||||
#define UINT8_0_FROM_UINT32(d) ((uint8_t)(d)) // Get byte 0 from a uint32_t. (e.g. 0x12345678 returns 0x78)
|
||||
#define UINT8_1_FROM_UINT32(d) ((uint8_t)((uint32_t)(d) >> 8)) // Get byte 1 from a uint32_t. (e.g. 0x12345678 returns 0x56)
|
||||
#define UINT8_2_FROM_UINT32(d) ((uint8_t)((uint32_t)(d) >> 16)) // Get byte 2 from a uint32_t. (e.g. 0x12345678 returns 0x34)
|
||||
#define UINT8_3_FROM_UINT32(d) ((uint8_t)((uint32_t)(d) >> 24)) // Get byte 3 from a uint32_t. (e.g. 0x12345678 returns 0x12)
|
||||
|
||||
#define UINT8_0_FROM_UINT64(q) ((uint8_t)(q))
|
||||
#define UINT8_1_FROM_UINT64(q) ((uint8_t)((uint64_t)(q) >> 8))
|
||||
#define UINT8_2_FROM_UINT64(q) ((uint8_t)((uint64_t)(q) >> 16))
|
||||
#define UINT8_3_FROM_UINT64(q) ((uint8_t)((uint64_t)(q) >> 24))
|
||||
#define UINT8_4_FROM_UINT64(q) ((uint8_t)((uint64_t)(q) >> 32))
|
||||
#define UINT8_5_FROM_UINT64(q) ((uint8_t)((uint64_t)(q) >> 40))
|
||||
#define UINT8_6_FROM_UINT64(q) ((uint8_t)((uint64_t)(q) >> 48))
|
||||
#define UINT8_7_FROM_UINT64(q) ((uint8_t)((uint64_t)(q) >> 56))
|
||||
|
||||
|
||||
// uint16_t byte manipulators
|
||||
#define UINT16_0_FROM_UINT32(d) ((uint16_t)(d)) // Get the right-most word from a uint32_t. (e.g. 0x12345678 returns 0x5678)
|
||||
#define UINT16_1_FROM_UINT32(d) ((uint16_t)((uint32_t)(d) >> 16)) // Get the left-most word from a uint32_t. (e.g. 0x12345678 returns 0x1234)
|
||||
|
||||
#define UINT16_0_FROM_UINT64(q) ((uint16_t)(q))
|
||||
#define UINT16_1_FROM_UINT64(q) ((uint16_t)((uint64_t)(q) >> 16))
|
||||
#define UINT16_2_FROM_UINT64(q) ((uint16_t)((uint64_t)(q) >> 32))
|
||||
#define UINT16_3_FROM_UINT64(q) ((uint16_t)((uint64_t)(q) >> 48))
|
||||
|
||||
#define UINT16_FROM_UINT8(b1, b0) ((uint16_t)(((uint8_t)(b1) << 8) | (uint8_t)(b0)))
|
||||
|
||||
|
||||
// uint32_t byte manipulators
|
||||
#define UINT32_0_FROM_UINT64(q) ((uint32_t)(q)) // Get the right-most dword from a uint64_t. (e.g. 0x0000000012345678 returns 0x12345678)
|
||||
#define UINT32_1_FROM_UINT64(q) ((uint32_t)((uint64_t)(q) >> 32)) // Get the left-most dword from a uint64_t. (e.g. 0x1234567800000000 returns 0x12345678)
|
||||
|
||||
#define UINT32_FROM_UINT8(b3, b2, b1, b0) ((uint32_t)(((uint8_t)(b3) << 24) | ((uint8_t)(b2) << 16) | ((uint8_t)(b1) << 8) | (uint8_t)(b0))) //Build a uint32_t from four Uint8s
|
||||
#define UINT32_FROM_UINT16(w1, w0) ((uint32_t)(((uint16_t)(w1) << 16) | (uint16_t)(w0))) // Build a uint32_t from two Uint16s
|
||||
|
||||
|
||||
// uint64_t byte manipulators
|
||||
#define UINT64_FROM_UINT8(b7, b6, b5, b4, b3, b2, b1, b0) ((uint64_t)(((uint64_t)((uint8_t)(b7)) << 56) | ((uint64_t)((uint8_t)(b6)) << 48) | ((uint64_t)((uint8_t)(b5)) << 40) | ((uint64_t)((uint8_t)(b4)) << 32) | ((uint64_t)((uint8_t)(b3)) << 24) | ((uint64_t)((uint8_t)(b2)) << 16) | ((uint64_t)((uint8_t)(b1)) << 8) | (uint64_t)((uint8_t)(b0))))
|
||||
#define UINT64_FROM_UINT16(w3, w2, w1, w0) ((uint64_t)(((uint64_t)((uint16_t)(w3)) << 48) | ((uint64_t)((uint16_t)(w2)) << 32) | ((uint64_t)((uint16_t)(w1)) << 16) | (uint64_t)((uint16_t)(w0))))
|
||||
#define UINT64_FROM_UINT32(d1, d0) ((uint64_t)(((uint64_t)((uint32_t)(d1)) << 32) | (uint64_t)((uint32_t)(d0))))
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif // Header include guard
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,532 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Electronic Arts Inc. All rights reserved.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// This module implements the following functions.
|
||||
//
|
||||
// int Isalnum(char_t c);
|
||||
// int Isalpha(char_t c);
|
||||
// int Isdigit(char_t c);
|
||||
// int Isxdigit(char_t c);
|
||||
// int Isgraph(char_t c);
|
||||
// int Islower(char_t c);
|
||||
// char_t Tolower(char_t c);
|
||||
// int Isupper(char_t c);
|
||||
// char_t Toupper(char_t c);
|
||||
// int Isprint(char_t c);
|
||||
// int Ispunct(char_t c);
|
||||
// int Isspace(char_t c);
|
||||
// int Iscntrl(char_t c);
|
||||
// int Isascii(char_t c);
|
||||
//
|
||||
// By design, the 16 bit versions of these functions work only for chars
|
||||
// up to 255. Characters above that always yield a return value of zero.
|
||||
// If you want Unicode-correct character and string classification
|
||||
// functionality for all Unicode characters, use the EATextUnicode module
|
||||
// from the EAText package. The Unicode module may be split off into a
|
||||
// unique package by the time you read this.
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#ifndef EASTDC_EACTYPE_H
|
||||
#define EASTDC_EACTYPE_H
|
||||
|
||||
|
||||
#include <EABase/eabase.h>
|
||||
#include <EAStdC/internal/Config.h>
|
||||
|
||||
|
||||
|
||||
namespace EA
|
||||
{
|
||||
namespace StdC
|
||||
{
|
||||
|
||||
EASTDC_API int Isalnum(char c);
|
||||
EASTDC_API int Isalnum(char16_t c);
|
||||
EASTDC_API int Isalnum(char32_t c);
|
||||
#if EA_WCHAR_UNIQUE
|
||||
EASTDC_API int Isalnum(wchar_t c);
|
||||
#endif
|
||||
|
||||
EASTDC_API int Isalpha(char c);
|
||||
EASTDC_API int Isalpha(char16_t c);
|
||||
EASTDC_API int Isalpha(char32_t c);
|
||||
#if EA_WCHAR_UNIQUE
|
||||
EASTDC_API int Isalpha(wchar_t c);
|
||||
#endif
|
||||
|
||||
EASTDC_API int Isdigit(char c);
|
||||
EASTDC_API int Isdigit(char16_t c);
|
||||
EASTDC_API int Isdigit(char32_t c);
|
||||
#if EA_WCHAR_UNIQUE
|
||||
EASTDC_API int Isdigit(wchar_t c);
|
||||
#endif
|
||||
|
||||
EASTDC_API int Isxdigit(char c);
|
||||
EASTDC_API int Isxdigit(char16_t c);
|
||||
EASTDC_API int Isxdigit(char32_t c);
|
||||
#if EA_WCHAR_UNIQUE
|
||||
EASTDC_API int Isxdigit(wchar_t c);
|
||||
#endif
|
||||
|
||||
EASTDC_API int Isgraph(char c);
|
||||
EASTDC_API int Isgraph(char16_t c);
|
||||
EASTDC_API int Isgraph(char32_t c);
|
||||
#if EA_WCHAR_UNIQUE
|
||||
EASTDC_API int Isgraph(wchar_t c);
|
||||
#endif
|
||||
|
||||
EASTDC_API int Islower(char c);
|
||||
EASTDC_API int Islower(char16_t c);
|
||||
EASTDC_API int Islower(char32_t c);
|
||||
#if EA_WCHAR_UNIQUE
|
||||
EASTDC_API int Islower(wchar_t c);
|
||||
#endif
|
||||
|
||||
EASTDC_API char Tolower(char c);
|
||||
EASTDC_API char16_t Tolower(char16_t c);
|
||||
EASTDC_API char32_t Tolower(char32_t c);
|
||||
#if EA_WCHAR_UNIQUE
|
||||
EASTDC_API wchar_t Tolower(wchar_t c);
|
||||
#endif
|
||||
|
||||
EASTDC_API int Isupper(char c);
|
||||
EASTDC_API int Isupper(char16_t c);
|
||||
EASTDC_API int Isupper(char32_t c);
|
||||
#if EA_WCHAR_UNIQUE
|
||||
EASTDC_API int Isupper(wchar_t c);
|
||||
#endif
|
||||
|
||||
EASTDC_API char Toupper(char c);
|
||||
EASTDC_API char16_t Toupper(char16_t c);
|
||||
EASTDC_API char32_t Toupper(char32_t c);
|
||||
#if EA_WCHAR_UNIQUE
|
||||
EASTDC_API wchar_t Toupper(wchar_t c);
|
||||
#endif
|
||||
|
||||
EASTDC_API int Isprint(char c);
|
||||
EASTDC_API int Isprint(char16_t c);
|
||||
EASTDC_API int Isprint(char32_t c);
|
||||
#if EA_WCHAR_UNIQUE
|
||||
EASTDC_API int Isprint(wchar_t c);
|
||||
#endif
|
||||
|
||||
EASTDC_API int Ispunct(char c);
|
||||
EASTDC_API int Ispunct(char16_t c);
|
||||
EASTDC_API int Ispunct(char32_t c);
|
||||
#if EA_WCHAR_UNIQUE
|
||||
EASTDC_API int Ispunct(wchar_t c);
|
||||
#endif
|
||||
|
||||
EASTDC_API int Isspace(char c);
|
||||
EASTDC_API int Isspace(char16_t c);
|
||||
EASTDC_API int Isspace(char32_t c);
|
||||
#if EA_WCHAR_UNIQUE
|
||||
EASTDC_API int Isspace(wchar_t c);
|
||||
#endif
|
||||
|
||||
EASTDC_API int Iscntrl(char c);
|
||||
EASTDC_API int Iscntrl(char16_t c);
|
||||
EASTDC_API int Iscntrl(char32_t c);
|
||||
#if EA_WCHAR_UNIQUE
|
||||
EASTDC_API int Iscntrl(wchar_t c);
|
||||
#endif
|
||||
|
||||
EASTDC_API int Isascii(char c);
|
||||
EASTDC_API int Isascii(char16_t c);
|
||||
EASTDC_API int Isascii(char32_t c);
|
||||
#if EA_WCHAR_UNIQUE
|
||||
EASTDC_API int Isascii(wchar_t c);
|
||||
#endif
|
||||
|
||||
} // namespace StdC
|
||||
} // namespace EA
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Inlines
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace EA
|
||||
{
|
||||
namespace StdC
|
||||
{
|
||||
const size_t EASTDC_WCMAP_SIZE = 256;
|
||||
|
||||
extern EASTDC_API uint8_t EASTDC_WCTYPE_MAP[EASTDC_WCMAP_SIZE];
|
||||
extern EASTDC_API uint8_t EASTDC_WLOWER_MAP[EASTDC_WCMAP_SIZE];
|
||||
extern EASTDC_API uint8_t EASTDC_WUPPER_MAP[EASTDC_WCMAP_SIZE];
|
||||
|
||||
#define EASTDC_WCTYPE_CONTROL_1 0x01
|
||||
#define EASTDC_WCTYPE_MOTION 0x02
|
||||
#define EASTDC_WCTYPE_SPACE_1 0x04
|
||||
#define EASTDC_WCTYPE_PUNCT 0x08
|
||||
#define EASTDC_WCTYPE_DIGIT 0x10
|
||||
#define EASTDC_WCTYPE_XDIGIT 0x20
|
||||
#define EASTDC_WCTYPE_LOWER 0x40
|
||||
#define EASTDC_WCTYPE_UPPER 0x80
|
||||
#define EASTDC_WCTYPE_ALPHA (EASTDC_WCTYPE_LOWER | EASTDC_WCTYPE_UPPER)
|
||||
#define EASTDC_WCTYPE_ALNUM (EASTDC_WCTYPE_ALPHA | EASTDC_WCTYPE_DIGIT)
|
||||
#define EASTDC_WCTYPE_GRAPH (EASTDC_WCTYPE_ALNUM | EASTDC_WCTYPE_PUNCT)
|
||||
#define EASTDC_WCTYPE_PRINT (EASTDC_WCTYPE_GRAPH | EASTDC_WCTYPE_SPACE)
|
||||
#define EASTDC_WCTYPE_SPACE (EASTDC_WCTYPE_SPACE_1 | EASTDC_WCTYPE_MOTION)
|
||||
#define EASTDC_WCTYPE_CONTROL (EASTDC_WCTYPE_CONTROL_1 | EASTDC_WCTYPE_MOTION)
|
||||
|
||||
|
||||
|
||||
inline int Isalnum(char c) // char is the same as char -- it is a signed or unsigned 8 bit value
|
||||
{
|
||||
return EASTDC_WCTYPE_MAP[(uint8_t)c] & EASTDC_WCTYPE_ALNUM;
|
||||
}
|
||||
|
||||
inline int Isalnum(char16_t c) // char16_t is an unsigned 16 bit value (the same as wchar_t when wchar_t is 16 bit).
|
||||
{
|
||||
return (((uint16_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_ALNUM) : 0);
|
||||
}
|
||||
|
||||
inline int Isalnum(char32_t c) // char16_t is an unsigned 16 bit value (the same as wchar_t when wchar_t is 16 bit).
|
||||
{
|
||||
return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_ALNUM) : 0);
|
||||
}
|
||||
|
||||
#if EA_WCHAR_UNIQUE
|
||||
inline int Isalnum(wchar_t c) // char16_t is an unsigned 16 bit value (the same as wchar_t when wchar_t is 16 bit).
|
||||
{
|
||||
return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_ALNUM) : 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
inline int Isalpha(char c)
|
||||
{
|
||||
return EASTDC_WCTYPE_MAP[(uint8_t)c] & EASTDC_WCTYPE_ALPHA;
|
||||
}
|
||||
|
||||
inline int Isalpha(char16_t c)
|
||||
{
|
||||
return (((uint16_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_ALPHA) : 0);
|
||||
}
|
||||
|
||||
inline int Isalpha(char32_t c)
|
||||
{
|
||||
return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_ALPHA) : 0);
|
||||
}
|
||||
|
||||
#if EA_WCHAR_UNIQUE
|
||||
inline int Isalpha(wchar_t c)
|
||||
{
|
||||
return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_ALPHA) : 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
inline int Isdigit(char c)
|
||||
{
|
||||
return EASTDC_WCTYPE_MAP[(uint8_t)c] & EASTDC_WCTYPE_DIGIT;
|
||||
|
||||
// Alternative which may be faster due to avoiding a memory hit:
|
||||
// return (((unsigned)(int)c - '0') < 10) ? 1 : 0;
|
||||
}
|
||||
|
||||
inline int Isdigit(char16_t c)
|
||||
{
|
||||
// return (((uint16_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_DIGIT) : 0);
|
||||
// Since the above has a conditional, we can use a similar conditional here but avoid a memory hit.
|
||||
return (((unsigned)c - '0') < 10) ? 1 : 0;
|
||||
}
|
||||
|
||||
inline int Isdigit(char32_t c)
|
||||
{
|
||||
// return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_DIGIT) : 0);
|
||||
// Since the above has a conditional, we can use a similar conditional here but avoid a memory hit.
|
||||
return (((uint32_t)c - '0') < 10) ? 1 : 0;
|
||||
}
|
||||
|
||||
#if EA_WCHAR_UNIQUE
|
||||
inline int Isdigit(wchar_t c)
|
||||
{
|
||||
// return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_DIGIT) : 0);
|
||||
// Since the above has a conditional, we can use a similar conditional here but avoid a memory hit.
|
||||
return (((uint32_t)c - '0') < 10) ? 1 : 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
inline int Isxdigit(char c)
|
||||
{
|
||||
return EASTDC_WCTYPE_MAP[(uint8_t)c] & EASTDC_WCTYPE_XDIGIT;
|
||||
}
|
||||
|
||||
inline int Isxdigit(char16_t c)
|
||||
{
|
||||
return (((uint16_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_XDIGIT) : 0);
|
||||
}
|
||||
|
||||
inline int Isxdigit(char32_t c)
|
||||
{
|
||||
return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_XDIGIT) : 0);
|
||||
}
|
||||
|
||||
#if EA_WCHAR_UNIQUE
|
||||
inline int Isxdigit(wchar_t c)
|
||||
{
|
||||
return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_XDIGIT) : 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
inline int Isgraph(char c)
|
||||
{
|
||||
return EASTDC_WCTYPE_MAP[(uint8_t)c] & EASTDC_WCTYPE_GRAPH;
|
||||
}
|
||||
|
||||
inline int Isgraph(char16_t c)
|
||||
{
|
||||
return (((uint16_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_GRAPH) : 0);
|
||||
}
|
||||
|
||||
inline int Isgraph(char32_t c)
|
||||
{
|
||||
return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_GRAPH) : 0);
|
||||
}
|
||||
|
||||
#if EA_WCHAR_UNIQUE
|
||||
inline int Isgraph(wchar_t c)
|
||||
{
|
||||
return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_GRAPH) : 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
inline int Islower(char c)
|
||||
{
|
||||
return EASTDC_WCTYPE_MAP[(uint8_t)c] & EASTDC_WCTYPE_LOWER;
|
||||
}
|
||||
|
||||
inline int Islower(char16_t c)
|
||||
{
|
||||
return (((uint16_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_LOWER) : 0);
|
||||
}
|
||||
|
||||
inline int Islower(char32_t c)
|
||||
{
|
||||
return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_LOWER) : 0);
|
||||
}
|
||||
|
||||
#if EA_WCHAR_UNIQUE
|
||||
inline int Islower(wchar_t c)
|
||||
{
|
||||
return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_LOWER) : 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
inline char Tolower(char c)
|
||||
{
|
||||
return (char)EASTDC_WLOWER_MAP[(uint8_t)c];
|
||||
}
|
||||
|
||||
inline char16_t Tolower(char16_t c)
|
||||
{
|
||||
return (((uint16_t)c < EASTDC_WCMAP_SIZE) ? (char16_t)EASTDC_WLOWER_MAP[(char16_t)c] : c);
|
||||
}
|
||||
|
||||
inline char32_t Tolower(char32_t c)
|
||||
{
|
||||
return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (char32_t)EASTDC_WLOWER_MAP[(char16_t)c] : c);
|
||||
}
|
||||
|
||||
#if EA_WCHAR_UNIQUE
|
||||
inline wchar_t Tolower(wchar_t c)
|
||||
{
|
||||
return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (wchar_t)EASTDC_WLOWER_MAP[(char16_t)c] : c);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
inline int Isupper(char c)
|
||||
{
|
||||
return (char)EASTDC_WCTYPE_MAP[(uint8_t)c] & EASTDC_WCTYPE_UPPER;
|
||||
}
|
||||
|
||||
inline int Isupper(char16_t c)
|
||||
{
|
||||
return (((uint16_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_UPPER) : 0);
|
||||
}
|
||||
|
||||
inline int Isupper(char32_t c)
|
||||
{
|
||||
return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_UPPER) : 0);
|
||||
}
|
||||
|
||||
#if EA_WCHAR_UNIQUE
|
||||
inline int Isupper(wchar_t c)
|
||||
{
|
||||
return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_UPPER) : 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
inline char Toupper(char c)
|
||||
{
|
||||
return (char)EASTDC_WUPPER_MAP[(uint8_t)c];
|
||||
}
|
||||
|
||||
inline char16_t Toupper(char16_t c)
|
||||
{
|
||||
return (((uint16_t)c < EASTDC_WCMAP_SIZE) ? (char16_t)EASTDC_WUPPER_MAP[(char16_t)c] : c);
|
||||
}
|
||||
|
||||
inline char32_t Toupper(char32_t c)
|
||||
{
|
||||
return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (char32_t)EASTDC_WUPPER_MAP[(char16_t)c] : c);
|
||||
}
|
||||
|
||||
#if EA_WCHAR_UNIQUE
|
||||
inline wchar_t Toupper(wchar_t c)
|
||||
{
|
||||
return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (wchar_t)EASTDC_WUPPER_MAP[(char16_t)c] : c);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
inline int Isprint(char c)
|
||||
{
|
||||
return EASTDC_WCTYPE_MAP[(uint8_t)c] & EASTDC_WCTYPE_PRINT;
|
||||
}
|
||||
|
||||
inline int Isprint(char16_t c)
|
||||
{
|
||||
return (((uint16_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_PRINT) : 0);
|
||||
}
|
||||
|
||||
inline int Isprint(char32_t c)
|
||||
{
|
||||
return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_PRINT) : 0);
|
||||
}
|
||||
|
||||
#if EA_WCHAR_UNIQUE
|
||||
inline int Isprint(wchar_t c)
|
||||
{
|
||||
return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_PRINT) : 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
inline int Ispunct(char c)
|
||||
{
|
||||
return EASTDC_WCTYPE_MAP[(uint8_t)c] & EASTDC_WCTYPE_PUNCT;
|
||||
}
|
||||
|
||||
inline int Ispunct(char16_t c)
|
||||
{
|
||||
return (((uint16_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_PUNCT) : 0);
|
||||
}
|
||||
|
||||
inline int Ispunct(char32_t c)
|
||||
{
|
||||
return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_PUNCT) : 0);
|
||||
}
|
||||
|
||||
#if EA_WCHAR_UNIQUE
|
||||
inline int Ispunct(wchar_t c)
|
||||
{
|
||||
return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_PUNCT) : 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
inline int Isspace(char c)
|
||||
{
|
||||
return EASTDC_WCTYPE_MAP[(uint8_t)c] & EASTDC_WCTYPE_SPACE;
|
||||
}
|
||||
|
||||
inline int Isspace(char16_t c)
|
||||
{
|
||||
return (((uint16_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_SPACE) : 0);
|
||||
}
|
||||
|
||||
inline int Isspace(char32_t c)
|
||||
{
|
||||
return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_SPACE) : 0);
|
||||
}
|
||||
|
||||
#if EA_WCHAR_UNIQUE
|
||||
inline int Isspace(wchar_t c)
|
||||
{
|
||||
return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_SPACE) : 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
inline int Iscntrl(char c)
|
||||
{
|
||||
return EASTDC_WCTYPE_MAP[(uint8_t)c] & EASTDC_WCTYPE_CONTROL;
|
||||
}
|
||||
|
||||
inline int Iscntrl(char16_t c)
|
||||
{
|
||||
return (((uint16_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_CONTROL) : 0);
|
||||
}
|
||||
|
||||
inline int Iscntrl(char32_t c)
|
||||
{
|
||||
return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_CONTROL) : 0);
|
||||
}
|
||||
|
||||
#if EA_WCHAR_UNIQUE
|
||||
inline int Iscntrl(wchar_t c)
|
||||
{
|
||||
return (((uint32_t)c < EASTDC_WCMAP_SIZE) ? (EASTDC_WCTYPE_MAP[c] & EASTDC_WCTYPE_CONTROL) : 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
inline int Isascii(char c)
|
||||
{
|
||||
return (uint8_t)c < 0x80;
|
||||
}
|
||||
|
||||
inline int Isascii(char16_t c)
|
||||
{
|
||||
return (uint8_t)c < 0x80;
|
||||
}
|
||||
|
||||
inline int Isascii(char32_t c)
|
||||
{
|
||||
return (uint8_t)c < 0x80;
|
||||
}
|
||||
|
||||
#if EA_WCHAR_UNIQUE
|
||||
inline int Isascii(wchar_t c)
|
||||
{
|
||||
return (uint8_t)c < 0x80;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
} // namespace StdC
|
||||
} // namespace EA
|
||||
|
||||
|
||||
#endif // Header include guard
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,556 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Electronic Arts Inc. All rights reserved.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// EACallback is a callback timer, also known as an asynchronous timer or alarm.
|
||||
// You provide a callback function and your function will be called at a fixed
|
||||
// periodic rate or will be called just once ("one-shot").
|
||||
//
|
||||
// Callback timers are useful for implementing systems that must respond to
|
||||
// something periodically or after some amount of time. Examples include:
|
||||
// - Streaming buffer periodic refilling
|
||||
// - Text editor flashing carets
|
||||
// - Alarm clocks
|
||||
// - Starting and stopping of background tasks
|
||||
//
|
||||
// Notes:
|
||||
// - Time-based callbacks are specified in nanoseconds.
|
||||
// - The callback system can work synchronously or asynchronously (user's choice).
|
||||
// In the former case, callbacks are serviced via a manually-called Tick function.
|
||||
// In the latter case, multithreading is used.
|
||||
// - In the case of asynchronous callbacks, your callback function will likely be
|
||||
// called from a different thread than the thread used to set up the callback.
|
||||
// - The precision specification of a callback is merely a hint and not a guarantee,
|
||||
// due to the fact that the platforms we are running on are not strictly
|
||||
// real-time systems. This is particularly true with desktop systems like
|
||||
// Windows which implement pre-emptive thread time-slicing.
|
||||
// - The period of a callback will be respected to the extent possible.
|
||||
// However, if another callback is being serviced ahead of your callback,
|
||||
// it might cause delays in the calling of your callback.
|
||||
// - Callbacks may operate in synchronous and asynchronous modes with respect to
|
||||
// the rest of the application. Asynchronous mode is implemented via a separate thread.
|
||||
// - Periodicity may be time or tick based, depending on what you request.
|
||||
// - The callback parameters (e.g. period, function, mode) can be changed at any
|
||||
// time and from any place, including within the callback function itself.
|
||||
// - You can stop a periodic callback at any time and from any place, including
|
||||
// within the callback function itself.
|
||||
// - As of this writing, EACallback doesn't guarantee ordering of event notifications.
|
||||
// Thus if you have two callbacks, one to call back in 15 ms and another in 20 ms,
|
||||
// and the callback system doesn't get polled until 30 ms later, the callback for
|
||||
// 20 ms might be notified before the callback for 15 ms. This may be changed in the future.
|
||||
//
|
||||
// Example usage:
|
||||
//
|
||||
// void MyCallback(Callback* pCallback, void* pContext, uint64_t absoluteValue, uint64_t deltaValue)
|
||||
// {
|
||||
// printf("%llu nanoseconds have passed since the last callback.\n");
|
||||
// }
|
||||
//
|
||||
// int main(int, char**)
|
||||
// {
|
||||
// // Manager setup
|
||||
// CallbackManager callbackManager;
|
||||
// SetCallbackManager(&callbackManager);
|
||||
// callbackManager.Init(false, false); // Run in synchronous mode.
|
||||
//
|
||||
// // Create and register a couple callbacks.
|
||||
// Callback callbackA(MyCallback, NULL, 100000, 0);
|
||||
// Callback callbackB(MyCallback, NULL, 300000, 0);
|
||||
//
|
||||
// callbackManager.Add(&callbackA, false); // periodic callback.
|
||||
// callbackManager.Add(&callbackB, true); // one-shot callback.
|
||||
//
|
||||
// // Run the app.
|
||||
// while(!ShouldQuit())
|
||||
// callbackManager.Update(); // If we ran in asunchronous (threaded) mode, we wouldn't need to call this.
|
||||
//
|
||||
// return 0;
|
||||
// }
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef EASTDC_EACALLBACK_H
|
||||
#define EASTDC_EACALLBACK_H
|
||||
|
||||
|
||||
#include <EABase/eabase.h>
|
||||
#include <EAStdC/internal/Config.h>
|
||||
#include <EAStdC/EAStopwatch.h>
|
||||
#include <EAStdC/EARandom.h>
|
||||
#include <eathread/eathread_atomic.h> // Used even if EASTDC_THREADING_SUPPORTED is zero, as it's still present. Just no actual threads.
|
||||
#if EASTDC_THREADING_SUPPORTED
|
||||
#include <eathread/eathread_thread.h>
|
||||
#include <eathread/eathread_mutex.h>
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4371) // Layout of class may have changed from a previous version of the compiler due to better packing of member.
|
||||
#pragma warning(disable: 4251) // class (some template) needs to have dll-interface to be used by clients.
|
||||
#pragma warning(disable: 4121) // 'EA::StdC::CallbackT<T>' : alignment of a member was sensitive to packing.
|
||||
#endif
|
||||
|
||||
|
||||
namespace EA
|
||||
{
|
||||
namespace StdC
|
||||
{
|
||||
class ICallbackManager;
|
||||
class CallbackManager;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
/// Callback
|
||||
///
|
||||
/// This is the user class for a callback.
|
||||
///
|
||||
class EASTDC_API Callback
|
||||
{
|
||||
public:
|
||||
/// Defines the prototype of a function that can be used with a callback.
|
||||
/// absoluteValue is a value of total of time/ticks passed since the system have been started.
|
||||
/// deltaValue is the value of time/ticks passed since last callback.
|
||||
/// If the user enables RefCounting then this function can be called with
|
||||
/// absoluteValue = kMessageAddRef or kMessageRelease instead of a time value.
|
||||
/// Your callback function may safely call back into the CallbackManager.
|
||||
typedef void (*CallbackFunctionType)(Callback* pCallback, void* pContext, uint64_t absoluteValue, uint64_t deltaValue);
|
||||
|
||||
/// The running mode
|
||||
enum Mode
|
||||
{
|
||||
kModeAsync, /// Asynchronous/threaded callbacks.
|
||||
kModeSync /// Synchronous/polled callbacks.
|
||||
};
|
||||
|
||||
/// The type of event to trigger a callback
|
||||
enum Type
|
||||
{
|
||||
kTypeTime, /// For callbacks based on time in nanoseconds.
|
||||
kTypeTick, /// For callbacks based on a number of passed ticks. Every time the CallbackSystem::Update function is called (usually once per application loop), the tick count is incremented.
|
||||
kTypeUserEvent /// For callbacks based on some external user event (e.g. screen vblank (vertical retrace)). CallbackSystem::OnUserEvent needs to be called whenever such an event occurs.
|
||||
};
|
||||
|
||||
enum Message
|
||||
{
|
||||
kMessageAddRef = 0, // If your callback is called with timeNS == kMessageAddRef, then this means you are being notified of first usage. See bEnableRefCount.
|
||||
kMessageRelease = 1 // If your callback is called with timeNS == kMessageRelease, then this means you are being notified of last usage. See bEnableRefCount.
|
||||
};
|
||||
|
||||
// static const bool kPeriodic = false; /// kPeriodic is a synonym for 'false', which is how it's used in the API.
|
||||
// static const bool kOneShot = true; /// kOneShot is a synonym for 'true', which is how it's used in the API.
|
||||
|
||||
public:
|
||||
/// Constructor
|
||||
Callback();
|
||||
|
||||
/// Constructor
|
||||
/// See SetFunctionInfo for relevant documentation.
|
||||
Callback(CallbackFunctionType pCallbackFunction, void* pCallbackArgument,
|
||||
uint64_t periodNs, uint64_t precisionNs = 500000,
|
||||
Type type = kTypeTime, bool bEnableRefCount = false);
|
||||
|
||||
virtual ~Callback();
|
||||
|
||||
/// Sets the function which is called when the time/tick/event count expire.
|
||||
/// Note that if the in asynch mode, the callback could occur in a different
|
||||
/// thread from the thread that started resumed it.
|
||||
///
|
||||
/// If bEnableRefCount is true, kMessageAddRef will be sent immediately
|
||||
/// and kMessageRelease will be sent immediately after callback is stopped.
|
||||
/// For a one-shot timer, the kMessageRelease is sent immediately after the callback
|
||||
/// completes. For timers that are never stopped, the kMessageRelease is called when
|
||||
/// the associated CallbackManager is shut down or when you call Stop. You should use
|
||||
/// the reference counting system if there is any chance there is an opportunity for
|
||||
/// multithreaded race conditions regarding the lifetime of called code and its data.
|
||||
bool SetFunctionInfo(CallbackFunctionType pCallbackFunction, void* pCallbackArgument, bool bEnableRefCount);
|
||||
|
||||
/// Sets new function information.
|
||||
/// This can be called at any time, including after the callback is already started.
|
||||
void GetFunctionInfo(CallbackFunctionType& pCallbackFunction, void*& pCallbackArgument) const;
|
||||
|
||||
/// Calls the callback function directly.
|
||||
/// You don't need to call this function unless you want to manually trigger a callback ad-hoc.
|
||||
void Call(uint64_t absoluteValue, uint64_t deltaValue);
|
||||
|
||||
/// Gets the period value in nanoseconds/ticks/events.
|
||||
uint64_t GetPeriod() const;
|
||||
|
||||
/// Sets the period of the callback in nanoseconds/ticks/events. For a periodic callback,
|
||||
/// this is the time between repetitive callbacks. For a one-shot callback, this is the time
|
||||
/// before its call. Changing the period has effect only after the next callback or if
|
||||
/// the user calls Stop and then Start to restart the callback.
|
||||
///
|
||||
/// As of this writing, Callback periods times refer to the time from one
|
||||
/// callback to the next callback, and not in absolute time. For example,
|
||||
/// If you request a callback with a period of 100 ms and the first one comes
|
||||
/// at 105 ms, the second one is targeted to arrive 100 ms later than 105 ms
|
||||
/// (i.e. 205 ms) and not at 200 ms. Thus the system doesn't try to catch up
|
||||
/// if it gets behind, nor does it slow down if it gets ahead.
|
||||
bool SetPeriod(uint64_t nPeriodNs);
|
||||
|
||||
/// Gets the user-specified precision of the callback in nanoseconds/ticks/events.
|
||||
/// Lower (i.e. more accurate) precision values may incur increased execution overhead
|
||||
/// on some systems, though usually it doesn't.
|
||||
uint64_t GetPrecision() const;
|
||||
|
||||
/// The callback will occur every GetPeriod() +/- GetPrecisison() units of nanoseconds/ticks/events.
|
||||
/// The default precision is 0, which means to be as accurate as possible. This may or may
|
||||
/// not incur any extra overhead, depending on the circumstances, though in most cases it
|
||||
/// doesn't. One benefit of setting a non-zero precision is that in the case of there
|
||||
/// being multiple callbacks of equal periods setting a non-zero precision results in some
|
||||
/// slight randomization of the callback times and avoids all the calls happening at once
|
||||
/// and causing an execution pause (lack of smoothness) in the app.
|
||||
bool SetPrecision(uint64_t nPrecisionNs);
|
||||
|
||||
/// Activates the callback.
|
||||
/// If pCallbackManager is NULL, the default CallbackManager is used.
|
||||
/// 'bOneShot' controls whether the callback is a call-once or a periodic kind.
|
||||
/// The return value indicates whether or not the callback is started.
|
||||
/// If the function is already Started then calling it a second time will
|
||||
/// result in a true return value. Calling Start with bOneShot=true twice in
|
||||
/// a row will result in a true return value but only one callback and not two.
|
||||
/// It's possible to start in periodic (not one-shot) mode and Stop the callback at any point.
|
||||
bool Start(ICallbackManager* pCallbackManager, bool bOneShot);
|
||||
|
||||
/// Stops callback function being called. In asynch mode it is theoretically
|
||||
/// possible to receive a callback very shortly after calling Stop, as a call to
|
||||
/// your callback might already be 'in flight' as you are calling Stop. If you
|
||||
/// need to make sure this doesn't happen, you can check the IsStarted
|
||||
void Stop();
|
||||
|
||||
/// Returns true if the callback has been started (is running).
|
||||
/// If the callback is a one-shot, this returns true if the callback has been
|
||||
/// started but its one timeup hasn't occurred yet.
|
||||
bool IsStarted() const;
|
||||
bool IsRunning() const { return IsStarted(); }
|
||||
|
||||
/// Sets running mode (kModeAsync or kModeSync).
|
||||
/// Asynch mode will result in being called back from a different thread.
|
||||
/// Changes won't take effect until the next time the callback is Started.
|
||||
//bool SetMode(Mode mode);
|
||||
|
||||
/// Gets the running mode (kModeAsync or kModeSync).
|
||||
//Mode GetMode() const;
|
||||
|
||||
/// Sets the callback type (kTypeTime, kTypeTick, or kTypeUserEvent).
|
||||
/// kTypeTime means that period/precision functions refer time in nanoseconds,
|
||||
/// while ticks are just a snapshot of the count of times called.
|
||||
bool SetType(Type type);
|
||||
|
||||
/// Gets the callback type.
|
||||
/// Changes won't take effect until the next time the callback is Started.
|
||||
Type GetType() const;
|
||||
|
||||
/// Manually sends the callback function kMessageAddRef.
|
||||
/// You normally don't need to manually call this function but should rather
|
||||
/// let the Callback instance call it as needed.
|
||||
void AddRefCallback();
|
||||
|
||||
/// Manually sends the callback function kMessageRelease.
|
||||
/// You normally don't need to manually call this function but should rather
|
||||
/// let the Callback instance call it as needed.
|
||||
void ReleaseCallback();
|
||||
|
||||
protected:
|
||||
friend class CallbackManager;
|
||||
|
||||
uint64_t mPeriod; /// Period in units that match Type (nanoseconds if Type is kTypeTime).
|
||||
uint64_t mPrecision; /// Precision in units that match Type (nanoseconds if Type is kTypeTime).
|
||||
ICallbackManager* mpCallbackManager; /// The manager we use for callbacks.
|
||||
CallbackFunctionType mpFunction; /// The user function we call.
|
||||
void* mpFunctionArg; /// The context we pass to the user function.
|
||||
Type mType; /// One of enum Type.
|
||||
#if EASTDC_THREADING_SUPPORTED
|
||||
EA::Thread::AtomicInt32 mbStarted; /// True if there is an active callback.
|
||||
#else
|
||||
int32_t mbStarted;
|
||||
#endif
|
||||
bool mbOneShot; /// True if this is a one-shot event.
|
||||
bool mbEnableRefCount; /// If true, the callback is notified before first usage and after last usage. Allows for safe lifetime maintentance of callback in the case that it has a dynamic lifetime.
|
||||
|
||||
private: // Internal data owned by CallbackManager.
|
||||
int64_t mNextCallbackEvent; /// Time, tick count, or user event count of next callback we should do.
|
||||
int64_t mLastCallbackEvent; /// Time, tick count, or user event count of the previous callback we did.
|
||||
|
||||
}; // class Callback
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
/// CallbackT
|
||||
///
|
||||
/// This is a template class based on Callback. In addition to callbacks
|
||||
/// based on static/global functions, this class allows one to use class
|
||||
/// member functions as callbacks.
|
||||
///
|
||||
/// Example usage:
|
||||
/// // In this example, MyFunction will be called very 1000 nanoseconds.
|
||||
///
|
||||
/// class SomeClass : public EA::CallbackT<SomeClass>
|
||||
/// {
|
||||
/// SomeClass()
|
||||
/// : EA::StdC::CallbackT<SomeClass>(MyFunction, this, 1000)
|
||||
/// {
|
||||
/// Start(NULL, false);
|
||||
/// }
|
||||
///
|
||||
/// void MyFunction(uint64_t absoluteValue, uint64_t deltaValue)
|
||||
/// {
|
||||
/// // Do something. If mType is kTypeTime, values are in nanoseconds. Else they are event counts.
|
||||
/// }
|
||||
/// };
|
||||
///
|
||||
template <typename T>
|
||||
class CallbackT : public Callback
|
||||
{
|
||||
public:
|
||||
typedef CallbackT<T> this_type;
|
||||
|
||||
typedef void (T::*CallbackFunctionTypeT)(Callback* pCallback, uint64_t absoluteValue, uint64_t deltaValue);
|
||||
|
||||
CallbackT()
|
||||
: mpMemberFunctionObject(NULL), mpMemberFunction(NULL) { }
|
||||
|
||||
CallbackT(CallbackFunctionTypeT pMemberFunc, T* pMemberFuncObject, uint64_t periodNs,
|
||||
uint64_t precisionNs = 500000, Type type = kTypeTime, bool bEnableRefCount = false)
|
||||
: Callback(NULL, NULL, periodNs, precisionNs, type)
|
||||
{
|
||||
SetFunctionInfo(pMemberFunc, pMemberFuncObject, bEnableRefCount);
|
||||
}
|
||||
|
||||
bool SetFunctionInfo(CallbackFunctionTypeT function, T* pT, bool bEnableRefCount)
|
||||
{
|
||||
mpMemberFunction = function;
|
||||
mpMemberFunctionObject = pT;
|
||||
|
||||
return Callback::SetFunctionInfo(generic_callback, this, bEnableRefCount);
|
||||
}
|
||||
|
||||
void GetFunctionInfo(CallbackFunctionTypeT& function, T*& pT) const
|
||||
{
|
||||
function = mpMemberFunction;
|
||||
pT = mpMemberFunctionObject;
|
||||
}
|
||||
|
||||
protected:
|
||||
static void generic_callback(Callback* pCallback, void* /*arg*/, uint64_t absoluteValue, uint64_t deltaValue)
|
||||
{
|
||||
this_type* pThis = static_cast<this_type*>(pCallback);
|
||||
|
||||
if(pThis && pThis->mpMemberFunctionObject && pThis->mpMemberFunction)
|
||||
(pThis->mpMemberFunctionObject->*pThis->mpMemberFunction)(pCallback, absoluteValue, deltaValue);
|
||||
}
|
||||
|
||||
protected:
|
||||
T* mpMemberFunctionObject;
|
||||
CallbackFunctionTypeT mpMemberFunction;
|
||||
};
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
/// ICallbackManager
|
||||
///
|
||||
class EASTDC_API ICallbackManager
|
||||
{
|
||||
public:
|
||||
/// Virtual destructor.
|
||||
virtual ~ICallbackManager() { }
|
||||
|
||||
/// This function needs to be called regularly (every frame) from the main application loop.
|
||||
virtual void Update() = 0;
|
||||
|
||||
/// This function needs to be called by the application whenever a callback driving event occur.
|
||||
virtual void OnUserEvent() = 0;
|
||||
|
||||
/// Returns time in nanoseconds as the Callback system sees it
|
||||
virtual uint64_t GetTime() = 0;
|
||||
|
||||
/// Add a new callback
|
||||
virtual bool Add(EA::StdC::Callback* pCallback, bool bOneShot) = 0;
|
||||
|
||||
/// Remove a callback
|
||||
virtual bool Remove(EA::StdC::Callback* pCallback) = 0;
|
||||
};
|
||||
|
||||
// Global default CallbackManager.
|
||||
// This is a global default CallbackManager.
|
||||
// Your application doesn't need to call SetCallbackManager unless the you want to
|
||||
// call the Callback::Start function with a NULL (default) CallbackManager.
|
||||
EASTDC_API ICallbackManager* GetCallbackManager();
|
||||
EASTDC_API void SetCallbackManager(ICallbackManager* pCallbackManager);
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
/// CallbackManager
|
||||
///
|
||||
/// Maintains a set of Callback instances.
|
||||
///
|
||||
/// As of this writing, Callback periods times refer to the time from one
|
||||
/// callback to the next callback, and not in absolute time. For example,
|
||||
/// If you request a callback with a period of 100 ms and the first one comes
|
||||
/// at 105 ms, the second one is targeted to arrive 100 ms later than 105 ms
|
||||
/// (i.e. 205 ms) and not at 200 ms. Thus the system doesn't try to catch up
|
||||
/// if it gets behind, nor does it slow down if it gets ahead.
|
||||
/// To consider: Make a flag that enables the behaviour of going by absolute
|
||||
/// time instead of inter-callback time. Note that such a feature would have
|
||||
/// to watch out for getting too far behind that callbacks get backlogged.
|
||||
///
|
||||
/// CallbackManager uses 64 bit integers to measure nanoseconds of passed
|
||||
/// time. The CallbackManager can theoretically run for 290 years without
|
||||
/// the integers overflowing.
|
||||
///
|
||||
class EASTDC_API CallbackManager : public ICallbackManager
|
||||
{
|
||||
public:
|
||||
/// CallbackManager
|
||||
/// No siginficant processing is done in this constructor. All initialization
|
||||
/// code is done in the Init function.
|
||||
CallbackManager();
|
||||
~CallbackManager();
|
||||
|
||||
/// This needs to be called in order to use CallbackManager.
|
||||
/// If bAsync is true then the CallbackManager is run in a separate thread
|
||||
/// and callbacks are made to user code from that thread automatically.
|
||||
/// If bAsync is false then the user must periodically call Update to
|
||||
/// do processing and callbacks are made to user code from the current thread.
|
||||
/// You can do manual callbacks even if Async (threaded) mode is enabled.
|
||||
bool Init(bool bAsync,
|
||||
bool bAsyncStart = false
|
||||
#if EASTDC_THREADING_SUPPORTED
|
||||
, EA::Thread::ThreadParameters threadParam = EA::Thread::ThreadParameters()
|
||||
#endif
|
||||
);
|
||||
|
||||
/// This needs to be called by application on shutdown to deinitialize
|
||||
/// the CallbackManager. It cancels and unregisters any callbacks and returns
|
||||
/// CallbackManager to the newly constructed state (i.e. before Init).
|
||||
void Shutdown();
|
||||
|
||||
/// This must be called by the application on every iteration of the main loop if
|
||||
/// async (threaded) mode is not used. However, this function can also be called
|
||||
/// if threaded mode is used.
|
||||
/// This function calls user-registered timers which have expired.
|
||||
void Update();
|
||||
|
||||
/// Returns the number of ticks (calls to Update) that have occurred.
|
||||
uint64_t GetTickCounter() { return (uint64_t)mTickCounter; }
|
||||
|
||||
/// This must be called by the application on every user event whatever that might be.
|
||||
/// An alternative way would be to use the CallbackManager class below.
|
||||
void OnUserEvent();
|
||||
|
||||
// Returns the number of times OnUserEvent has been called.
|
||||
uint64_t GetUserEventCounter() { return (uint64_t)mUserEventCounter; }
|
||||
|
||||
/// Returns time in nanoseconds as the Callback system sees it
|
||||
uint64_t GetTime();
|
||||
|
||||
/// Add a new callback
|
||||
bool Add(EA::StdC::Callback* pCallback, bool bOneShot);
|
||||
|
||||
/// Remove a callback.
|
||||
/// Stops the Callback if it is started.
|
||||
bool Remove(EA::StdC::Callback* pCallback);
|
||||
|
||||
/// GetStopwatch
|
||||
/// This is a debug function. Don't call mutating functions on it (e.g. Stop).
|
||||
/// To do: Remove this function once this class is debugged.
|
||||
EA::StdC::Stopwatch& GetStopwatch() { return mStopwatch; }
|
||||
|
||||
#if EASTDC_THREADING_SUPPORTED
|
||||
// Manipulate the thread used for async (i.e. threaded) execution.
|
||||
EA::Thread::Thread& GetThread();
|
||||
void Lock();
|
||||
void Unlock();
|
||||
#endif
|
||||
|
||||
protected:
|
||||
//struct CallbackInfo
|
||||
//{
|
||||
// Callback* mpCallback; // The user Callback object associated with this info.
|
||||
// int64_t mNextCallbackEvent; // Time, tick count, or user event count of next callback we should do.
|
||||
// int64_t mLastCallbackEvent; // Time, tick count, or user event count of the previous callback we did.
|
||||
// bool mbOneShot; // If true then we Stop the callback after it's called once.
|
||||
//
|
||||
// CallbackInfo() : mpCallback(NULL), mNextCallbackEvent(0), mLastCallbackEvent(0), mbOneShot(false){}
|
||||
//};
|
||||
|
||||
// We implement a tiny vector class.
|
||||
class EASTDC_API CallbackVector
|
||||
{
|
||||
public:
|
||||
typedef Callback* value_type;
|
||||
typedef value_type* iterator;
|
||||
|
||||
CallbackVector();
|
||||
~CallbackVector();
|
||||
|
||||
void reserve(size_t) { }
|
||||
void clear() { mpEnd = mpBegin; }
|
||||
bool empty() const { return mpBegin == mpEnd; }
|
||||
iterator begin() { return mpBegin; }
|
||||
iterator end() { return mpEnd; }
|
||||
value_type& back() { return *(mpEnd - 1); }
|
||||
size_t size() const { return (size_t)(mpEnd - mpBegin); }
|
||||
value_type& operator[](size_t i) { return mpBegin[i]; }
|
||||
iterator erase(iterator it);
|
||||
iterator push_back(value_type value);
|
||||
|
||||
protected:
|
||||
value_type* mpBegin;
|
||||
value_type* mpEnd;
|
||||
value_type* mpCapacity;
|
||||
value_type mLocalBuffer[8]; // Initial local buffer capable of holding some pointers without us needing to hit the heap.
|
||||
};
|
||||
|
||||
protected:
|
||||
static intptr_t RunStatic(void* pContext) { return static_cast<CallbackManager*>(pContext)->Run(); }
|
||||
intptr_t Run();
|
||||
void UpdateInternal(int64_t& curTick, int64_t& curTime, int64_t& curUserEvent);
|
||||
bool StartThread();
|
||||
void StopThread();
|
||||
|
||||
CallbackManager(const CallbackManager&); // Declared but not defined.
|
||||
void operator=(const CallbackManager&); // Declared but not defined.
|
||||
|
||||
protected:
|
||||
CallbackVector mCallbackArray; // Our current callbacks.
|
||||
EA::StdC::Stopwatch mStopwatch; //
|
||||
EA::Thread::AtomicInt64 mTickCounter; //
|
||||
EA::Thread::AtomicInt64 mUserEventCounter; //
|
||||
bool mbInitialized; //
|
||||
volatile bool mbRunning; //
|
||||
bool mbAsync; // This is whether the processor is in a separate thread.
|
||||
EA::StdC::RandomFast mRandom; //
|
||||
double mNSecPerTick; // Used for guessing for how long to sleep our thread until the next callback time arrives. We need to know this because some events are based on the number of ticks (number of times Update is called), while others are based on time.
|
||||
int64_t mNSecPerTickLastTimeMeasured; // Last time when average nsec/tick was measured.
|
||||
int64_t mNSecPerTickLastTickMeasured; // Last time when average nsec/tick was measured.
|
||||
int64_t mNextCallbackEventTime; // The next earliest time callback event. If we have five registered user callbacks, we want to wake up whenever the soonest one is due.
|
||||
int64_t mNextCallbackEventTick; // The next earliest tick callback event.
|
||||
|
||||
#if EASTDC_THREADING_SUPPORTED
|
||||
EA::Thread::Mutex mMutex;
|
||||
EA::Thread::Thread mThread;
|
||||
EA::Thread::AtomicInt32 mbThreadStarted;
|
||||
EA::Thread::ThreadParameters mThreadParam;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace StdC
|
||||
|
||||
} // namespace EA
|
||||
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
|
||||
#endif // Header include guard
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,726 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Electronic Arts Inc. All rights reserved.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Implements a suite of functionality to manage dates, times, and calendars.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#ifndef EASTDC_EADATETIME_H
|
||||
#define EASTDC_EADATETIME_H
|
||||
|
||||
|
||||
#include <EAStdC/internal/Config.h>
|
||||
#include <EAStdC/Int128_t.h>
|
||||
|
||||
#if EASTDC_TIME_H_AVAILABLE
|
||||
#include <time.h>
|
||||
#else
|
||||
typedef int64_t time_t;
|
||||
typedef int32_t suseconds_t;
|
||||
typedef int64_t clock_t;
|
||||
|
||||
struct tm
|
||||
{
|
||||
int tm_sec;
|
||||
int tm_min;
|
||||
int tm_hour;
|
||||
int tm_mday;
|
||||
int tm_mon;
|
||||
int tm_year;
|
||||
int tm_wday;
|
||||
int tm_yday;
|
||||
int tm_isdst;
|
||||
};
|
||||
|
||||
time_t time(time_t*);
|
||||
tm* gmtime(const time_t*);
|
||||
tm* localtime(const time_t*);
|
||||
#endif
|
||||
|
||||
#if defined(EA_PLATFORM_MICROSOFT)
|
||||
#if (defined(EA_PLATFORM_WINDOWS) && !EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP))
|
||||
typedef int32_t suseconds_t;
|
||||
#define EASTDC_TIMEVAL_NEEDED
|
||||
#define EASTDC_TIMEZONE_NEEDED
|
||||
#else
|
||||
struct timeval; // We have a problem: Microsoft defines timeval, but only within the Windows header system.
|
||||
typedef time_t suseconds_t; // And we have a rule against #including windows headers within header files. So we forward
|
||||
#define EASTDC_TIMEZONE_NEEDED // declare timeval and expect the user to #include <WinSock2.h> in order to use it.
|
||||
#endif
|
||||
|
||||
#elif EASTDC_SYS_TIME_H_AVAILABLE
|
||||
#include <sys/time.h> // Defines timeval and timezone
|
||||
struct timezone_ : public timezone{};
|
||||
|
||||
#elif EASTDC_SYS__TIMEVAL_H_AVAILABLE
|
||||
#include <sys/_timeval.h> // Defines timeval
|
||||
#define EASTDC_TIMEZONE_NEEDED
|
||||
|
||||
#else
|
||||
#define EASTDC_TIMEVAL_NEEDED
|
||||
#define EASTDC_TIMEZONE_NEEDED
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef EASTDC_TIMEVAL_NEEDED
|
||||
struct timeval {
|
||||
time_t tv_sec; // seconds
|
||||
suseconds_t tv_usec; // microseconds
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef EASTDC_TIMEZONE_NEEDED
|
||||
struct timezone_ { // Defined as timezone_ instead of timezone because otherwise it conflicts with some platforms' existing identifiers.
|
||||
int tz_minuteswest; // This is the number of minutes west of GMT.
|
||||
int tz_dsttime; // If nonzero, daylight savings time applies during some part of the year.
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// Forward declare the Microsoft time structs. We do this for all platforms,
|
||||
// including non-Microsoft platforms in order to simply the code. This won't
|
||||
// result in excess code as these functions go away when unused.
|
||||
struct _FILETIME;
|
||||
struct _SYSTEMTIME;
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// EASTDC_UTC_TIME_AVAILABLE
|
||||
//
|
||||
// Defined as 0 or 1. It's value is fixed based on the platform.
|
||||
// This is a read-only value which is set by this module.
|
||||
// If you become aware of a way to get UTC time for a platform which
|
||||
// currently is flagged as unavailable here, notify the package owners.
|
||||
// For platforms where UTC time is not available, UTC time is reported to
|
||||
// be the same as local time. If UTC time is not available, then time
|
||||
// zone information is by definition not available either.
|
||||
//
|
||||
#if !defined(EASTDC_UTC_TIME_AVAILABLE)
|
||||
#define EASTDC_UTC_TIME_AVAILABLE 1
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
namespace EA
|
||||
{
|
||||
namespace StdC
|
||||
{
|
||||
// Used for Parameter values (see below)
|
||||
static const uint32_t kDateTimeDefault = 0xffffffff;
|
||||
static const uint32_t kDateTimeIgnored = 0xffffffff;
|
||||
|
||||
/// Month
|
||||
enum Month
|
||||
{
|
||||
kMonthUnknown = 0,
|
||||
kMonthJanuary = 1,
|
||||
kMonthFebruary = 2,
|
||||
kMonthMarch = 3,
|
||||
kMonthApril = 4,
|
||||
kMonthMay = 5,
|
||||
kMonthJune = 6,
|
||||
kMonthJuly = 7,
|
||||
kMonthAugust = 8,
|
||||
kMonthSeptember = 9,
|
||||
kMonthOctober = 10,
|
||||
kMonthNovember = 11,
|
||||
kMonthDecember = 12
|
||||
};
|
||||
|
||||
enum DayOfMonth
|
||||
{
|
||||
kDayOfMonthUnknown = 0,
|
||||
kDayOfMonthMin = 1,
|
||||
kDayOfMonthMax = 31 /// The actual max month is dependent on which month is being referred to.
|
||||
};
|
||||
|
||||
enum DayOfWeek
|
||||
{
|
||||
kDayOfWeekUnknown = 0,
|
||||
kDayOfWeekSunday = 1,
|
||||
kDayOfWeekMonday = 2,
|
||||
kDayOfWeekTuesday = 3,
|
||||
kDayOfWeekWednesday = 4,
|
||||
kDayOfWeekThursday = 5,
|
||||
kDayOfWeekFriday = 6,
|
||||
kDayOfWeekSaturday = 7
|
||||
};
|
||||
|
||||
/// TimeFrame
|
||||
enum TimeFrame
|
||||
{
|
||||
kTimeFrameUnknown = 0, /// Unspecified time frame.
|
||||
kTimeFrameUTC = 1, /// Universal Coordinated Time. This is the time based on the time zone at Greenwich, England.
|
||||
kTimeFrameLocal = 2 /// Same time as current machine.
|
||||
};
|
||||
|
||||
/// TimeZone
|
||||
/// Standard time zone definitions, with their values corresponding to the nmuber of hours they are
|
||||
/// off relative to UTC (Universal Coordinated Time, which is the time at Greenwich England).
|
||||
/// Note, for example, that kTimeZonePacific is -8 hours relative to Greenwich, England.
|
||||
/// It should be noted that time zone biases (e.g. central australia) are properly represented in
|
||||
/// seconds and not hours, as some time zone biases relative to UTC are on half-hour increments.
|
||||
/// Such time zones are not listed below, as the list below is merely the UTC standard 24 hour list.
|
||||
enum TimeZone
|
||||
{
|
||||
kTimeZoneEniwetok = -12,
|
||||
kTimeZoneSamoa = -11,
|
||||
kTimeZoneHawaii = -10,
|
||||
kTimeZoneAlaska = -9,
|
||||
kTimeZonePacific = -8,
|
||||
kTimeZoneMountain = -7,
|
||||
kTimeZoneCentral = -6,
|
||||
kTimeZoneEastern = -5,
|
||||
kTimeZoneAltantic = -4,
|
||||
kTimeZoneBrazilia = -3,
|
||||
kTimeZoneMidAtlantic = -2,
|
||||
kTimeZoneAzores = -1,
|
||||
kTimeZoneGreenwich = 0,
|
||||
kTimeZoneRome = +1,
|
||||
kTimeZoneIsrael = +2,
|
||||
kTimeZoneMoscow = +3,
|
||||
kTimeZoneBaku = +4,
|
||||
kTimeZoneNewDelhi = +5,
|
||||
kTimeZoneDhakar = +6,
|
||||
kTimeZoneBangkok = +7,
|
||||
kTimeZoneHongKong = +8,
|
||||
kTimeZoneTokyo = +9,
|
||||
kTimeZoneSydney = +10,
|
||||
kTimeZoneMagadan = +11,
|
||||
kTimeZoneWellington = +12
|
||||
};
|
||||
|
||||
/// Epoch
|
||||
/// The use of an epoch is to provide a timeframe with which to work.
|
||||
/// Most of the time you don't need to know about this.
|
||||
/// https://en.wikipedia.org/wiki/Epoch_%28reference_date%29
|
||||
enum Epoch
|
||||
{
|
||||
kEpochUnknown = 0,
|
||||
kEpochJulian = 1, /// Began at -4712/01/01/12:00:00 (Year 1858, January 1, noon).
|
||||
kEpochGregorian = 2, /// Began at 1752/09/14/00:00:00 (Year 1752, January 1, midnight). Beginning of the Gregorian calendar.
|
||||
kEpochModifiedJulian = 3, /// Began at 1858/11/17/00:00:00 (Year 1858, January 1, midnight). 2,400,000.5 days after Julian epoch began.
|
||||
kEpoch1900 = 4, /// Began at 1900/01/01/00:00:00 (Year 1900, January 1, midnight). Same epoch used by the Network Time Protocol.
|
||||
kEpoch1950 = 5, /// Began at 1950/01/01/00:00:00 (Year 1950, January 1, midnight). Used by some gaming systems.
|
||||
kEpoch1970 = 6, /// Began at 1970/01/01/00:00:00 (Year 1970, January 1, midnight). Same epoch used by C runtime library and Unix OS.
|
||||
kEpoch2000 = 7, /// Began at 2000/01/01/00:00:00 (Year 2000, January 1, midnight). Same epoch used by Apple file systems.
|
||||
kEpochJ2000 = 8, /// Began at 2000/01/01/11:58:55 (Year 2000, January 1, ~noon). Coordinated Universal Time, also includes 816 milliseconds.
|
||||
kEpochDateTime = 9, /// Began at 0000/01/01/00:00:00 (Year 0000, January 1, midnight).
|
||||
kEpochCount
|
||||
};
|
||||
|
||||
/// Era
|
||||
enum Era
|
||||
{
|
||||
kEraUnknown = 0,
|
||||
kEraBC = 1,
|
||||
kEraAD = 2
|
||||
};
|
||||
|
||||
/// Parameter
|
||||
/// Defines a date or time parameter.
|
||||
enum Parameter
|
||||
{
|
||||
kParameterUnknown = 0,
|
||||
kParameterYear = 1, /// Refers to full year value, such as 1994, 2006, etc. but not a two digit value such as 94 or 04. The valid range is 0-INT_MAX.
|
||||
kParameterMonth = 2, /// Refers to month of year, starting with 1 for January. The valid range is 1-12.
|
||||
kParameterWeekOfYear = 3, /// Refers to the week of the year, starting with 1 for the week of January 1. The valid range is 1-52.
|
||||
kParameterWeekOfMonth = 4, /// Refers to the week of the month, starting with 1 for the first week. The valid range is 1-5.
|
||||
kParameterDayOfYear = 5, /// Refers to a day of the year, starting with 1 for January 1st. The valid range is 1-366.
|
||||
kParameterDayOfMonth = 6, /// Refers to the day of the month, starting with 1 for the first of the month. The valid range is 1-31.
|
||||
kParameterDayOfWeek = 7, /// Refers to the day of the week, starting with 1 for Sunday. The valid range is 1-7.
|
||||
kParameterHour = 8, /// Refers to the hour of a day in 24 hour format, starting with 0 for midnight. The valid range is 0-23.
|
||||
kParameterMinute = 9, /// Refers to the minute of the hour, starting with 0 for the first minute. The valid range is 0-59.
|
||||
kParameterSecond = 10, /// Refers to the second of the minute, starting with 0 for the first second. The valid range is 0-60, with the range usually being 0-59, but the occasional leap second could cause it to be 60.
|
||||
kParameterNanosecond = 11 /// Refers to the nanosecond of the second. The valid range is 0-999999999.
|
||||
};
|
||||
|
||||
/// Conversions
|
||||
/// Defines useful conversion multipliers between seconds, minutes, hours, and days.
|
||||
/// Conversions are not defined for some entities (e.g. days per year) because the
|
||||
/// value changes based on the particular year.
|
||||
enum Conversions
|
||||
{
|
||||
kSecondsPerMinute = 60, // We ignore than on rare occasions in real time there are 61 seconds in a minute when there is a leap second.
|
||||
kSecondsPerHour = 3600,
|
||||
kSecondsPerDay = 86400,
|
||||
kMinutesPerHour = 60,
|
||||
kMinutesPerDay = 1440,
|
||||
kHoursPerDay = 24,
|
||||
kDaysPerWeek = 7,
|
||||
kWeeksPerYear = 52,
|
||||
kMonthsPerYear = 12
|
||||
};
|
||||
|
||||
|
||||
/// DateTimeParameters
|
||||
/// Specifies a struct that holds one of each of the date/time Parameter type.
|
||||
/// This struct is a bit like the C tm struct, though with more flexibility and precision.
|
||||
/// Any value can be kDateTimeIgnored to indicate that it isn't used.
|
||||
/// Some parameters are potentially mutually exclusive with others (e.g. mMonth and mWeekOfYear).
|
||||
/// The user is expected to avoid such conflicts, or else the code specifies how it handles
|
||||
/// such conflicts.
|
||||
/// All values default to kDateTimeIgnored.
|
||||
/// Values can be negative in order to identify date/time deltas as opposed to absolute date/time values.
|
||||
/// Values can be outside their normal range in order to indicate date/time deltas greater
|
||||
/// than normal. For example, mMonth can be -16 to indicate subtraction of 16 months. Negative or
|
||||
/// out-of-range values may have no meaning when DateTimeParameters is used to indicate absolute date/time.
|
||||
struct DateTimeParameters
|
||||
{
|
||||
DateTimeParameters();
|
||||
|
||||
int32_t mYear;
|
||||
int32_t mMonth;
|
||||
int32_t mWeekOfYear;
|
||||
int32_t mWeekOfMonth;
|
||||
int32_t mDayOfYear;
|
||||
int32_t mDayOfMonth;
|
||||
int32_t mDayOfWeek;
|
||||
int32_t mHour;
|
||||
int32_t mMinute;
|
||||
int32_t mSecond;
|
||||
int32_t mNanosecond;
|
||||
};
|
||||
|
||||
|
||||
/// DateTime
|
||||
/// Represents date and time in a single class. Unlike other date/time systems,
|
||||
/// this class doesn't have a strictly limited resolution, due to its use of
|
||||
/// 64 bits (instead of 32 bits) of storage for the date/time value.
|
||||
///
|
||||
/// DateTime is internally represented as nanoseconds since 0000/01/01/00:00:00
|
||||
/// (Year 0000, January 1, midnight). Thus the beginning of time is considered
|
||||
/// to be this date. This is differnet from the old Unix concept of time starting
|
||||
/// at 1970 and is so because it provides more flexibility and portably allows
|
||||
/// for date representation prior to 1970. You can convert between time_t and DateTime
|
||||
/// seconds with the DateTimeSecondsToTimeTSeconds and TimeTSecondsSecondsToDateTime functions.
|
||||
///
|
||||
/// This class does not support the formatting of date and time strings for
|
||||
/// display to a users. Such functionality is outside the scope of this class
|
||||
/// primarily due to the issue of multiple string encodings (e.g. ASCII and Unicode)
|
||||
/// and due to the issue of multiple localization (e.g. English and French).
|
||||
/// As separate module exists which implements string formatting.
|
||||
///
|
||||
/// For best practices regarding usage of DateTime, see the following discussion
|
||||
/// http://stackoverflow.com/questions/2532729/daylight-saving-time-and-timezone-best-practices
|
||||
///
|
||||
class EASTDC_API DateTime
|
||||
{
|
||||
public:
|
||||
static const uint32_t kValueDefault = kDateTimeDefault;
|
||||
static const uint32_t kValueIgnored = kDateTimeIgnored;
|
||||
|
||||
public:
|
||||
DateTime(TimeFrame timeFrame = kTimeFrameLocal)
|
||||
: mnSeconds(0), mnNanosecond(0){ Set(timeFrame); }
|
||||
|
||||
/// nSeconds/nNanosecond refers to elapsed time since 0000/01/01/00:00:00
|
||||
/// (Year 0000, January 1, midnight). This function is thus different from
|
||||
/// the old Unix concept of time starting at 1970 and is so because it provides
|
||||
/// more flexibility and portably allows for date representation prior to 1970.
|
||||
/// You can convert between time_t and DateTime seconds with the DateTimeSecondsToTimeTSeconds
|
||||
/// and TimeTSecondsSecondsToDateTime functions.
|
||||
/// Nanosecond refers to a fraction of seconds.
|
||||
DateTime(int64_t nSeconds, uint32_t nNanosecond = 0)
|
||||
: mnSeconds(nSeconds), mnNanosecond(nNanosecond) { }
|
||||
|
||||
// See the SetNanoseconds function for the specification of nanoseconds.
|
||||
DateTime(const int128_t& nanoseconds)
|
||||
: mnSeconds(0), mnNanosecond(0) { SetNanoseconds(nanoseconds); }
|
||||
|
||||
DateTime(const DateTime& dateTime)
|
||||
: mnSeconds(dateTime.mnSeconds), mnNanosecond(dateTime.mnNanosecond) { }
|
||||
|
||||
/// DateTime
|
||||
/// Constructs a DateTime class object from some standard parameters.
|
||||
/// To construct a DateTime class with a different set of parameter types,
|
||||
/// you'll need to use the Set function or in odd cases do manual calculations.
|
||||
DateTime(uint32_t nYear, uint32_t nMonth, uint32_t nDayOfMonth, uint32_t nHour = 0,
|
||||
uint32_t nMinute = 0, uint32_t nSecond = 0, uint32_t nNanosecond = 0)
|
||||
: mnSeconds(0), mnNanosecond(0)
|
||||
{ Set(nYear, nMonth, nDayOfMonth, nHour, nMinute, nSecond, nNanosecond); }
|
||||
|
||||
DateTime& operator=(const DateTime& dateTime)
|
||||
{ mnSeconds = dateTime.mnSeconds; mnNanosecond= dateTime.mnNanosecond; return *this; }
|
||||
|
||||
/// Compare
|
||||
/// This function compares this object with another DateTime object and returns an integer result.
|
||||
/// The return value is the same as with the strcmp string comparison function. If this object is
|
||||
/// less than the argument dateTime, the return value is < 0.
|
||||
/// Both involved DateTime objects are assumed to be in the same time zone, and if you want to compare times
|
||||
/// in different time zones then you will have to translate one of them to the other's time zone, at least
|
||||
/// until some day that explicit support for that is added to this package.
|
||||
///
|
||||
/// If bCompareDate is true and bCompareTime is false, we compare the absolute day and nothing more precise.
|
||||
/// If bCompareDate is false and bCompareTime is true, we compare the time (seconds+nanoseconds) within a day.
|
||||
/// If bCompareDate is true and bCompareTime is true, we compare absolute time (seconds+nanoseconds).
|
||||
/// If bCompareDate is false and bComareTime is false, we compare absolute time (seconds+nanoseconds).
|
||||
/// Comparison operators are defined outside this class, though they use the Compare function to do their work.
|
||||
int Compare(const DateTime& dateTime, bool bCompareDate = true, bool bCompareTime = true) const;
|
||||
|
||||
/// GetParameter
|
||||
/// Gets the given parameter. If you want to get the year, you would call Get(kParameterYear).
|
||||
uint32_t GetParameter(Parameter parameter) const;
|
||||
|
||||
/// SetParameter
|
||||
/// Sets the given parameter. If you want to set the year to 1999, you would call Set(kParameterYear, 1999).
|
||||
/// You have to be careful with the following parameters, becuase they adjust time in a relative way as opposed
|
||||
/// to an absolute way, and thus the current date/time value may affect the resulting absolute date/time.
|
||||
/// As a result the effects of these parameters when called with other paramters can be order-dependent.
|
||||
/// For example, setting kParameterDayOfWeek before kParameterDayOfMonth could result in a different month
|
||||
/// than if you called them in reverse order.
|
||||
/// kParameterDayOfYear, kParameterDayOfWeek, kParameterWeekOfYear, kParameterWeekOfMonth.
|
||||
void SetParameter(Parameter parameter, uint32_t nValue);
|
||||
|
||||
/// Set
|
||||
/// Sets the time based on the current time. If the timeFrame is kTimeFrameUTC, the time is set
|
||||
/// to what the current UTC time is, which will be a fixed number of hours off of what the current
|
||||
/// local time is.
|
||||
void Set(TimeFrame timeFrame = kTimeFrameLocal, bool bSetNanoseconds = true);
|
||||
|
||||
/// Set
|
||||
/// Sets the time based on various inputs. If any input is kValueIgnored (uint32_t)-1, then the
|
||||
/// input is ignored and the current value is used. If any of the cyclic inputs is beyond its
|
||||
/// valid range, the modulo of the value is used and the division of the value is added to the
|
||||
/// next higher bracket. For example, if the input nMinute is 65, then the minute used is 5 and
|
||||
/// 1 is added to the current hour value.
|
||||
void Set(uint32_t nYear, uint32_t nMonth, uint32_t nDayOfMonth, uint32_t nHour,
|
||||
uint32_t nMinute, uint32_t nSecond, uint32_t nNanosecond = kValueIgnored);
|
||||
|
||||
/// AddTime
|
||||
/// Allows you to increment (or decrement) the given parameter by the given nValue amount.
|
||||
/// The amount can be less than zero and can be more than the conventional type limit.
|
||||
/// For example, if parameter = kParameterMinute, nValue can be any int64_t value and not
|
||||
/// just in the range of 0 - 60.
|
||||
void AddTime(Parameter parameter, int64_t nValue);
|
||||
|
||||
/// GetSeconds
|
||||
/// Returns the DateTime internal representation. The internal representation is such that
|
||||
/// a value of zero refers to 0000/01/01/00:00:00 (Year 0000, January 1, midnight).
|
||||
/// This function thus gets the number of seconds "since the beginning of time".
|
||||
int64_t GetSeconds() const;
|
||||
|
||||
/// SetSeconds
|
||||
/// Sets the DateTime internal representation.
|
||||
/// This is not the same as SetParameter(kParameterSeconds), which sets the current day's
|
||||
/// seconds and not the absolute seconds since the beginning of time like this function does.
|
||||
/// This function thus sets the number of seconds "since the beginning of time".
|
||||
void SetSeconds(int64_t nSeconds);
|
||||
|
||||
/// GetMilliseconds
|
||||
/// Returns the DateTime internal representation. The internal representation is such that
|
||||
/// a value of zero refers to 0000/01/01/00:00:00 (Year 0000, January 1, midnight).
|
||||
/// This function thus gets the number of milliseconds "since the beginning of time".
|
||||
uint64_t GetMilliseconds() const;
|
||||
|
||||
/// SetMilliseconds
|
||||
/// Sets the DateTime internal representation.
|
||||
/// This is not the same as SetParameter, which sets the current day's
|
||||
/// seconds and not the absolute seconds since the beginning of time like this function does.
|
||||
/// This function thus sets the number of milliseconds "since the beginning of time".
|
||||
void SetMilliseconds(uint64_t milliseconds);
|
||||
|
||||
/// GetNanoseconds
|
||||
/// Note that this is not the same thing as GetParameter(kParamaterNanosecond), as GetParameter retrieves
|
||||
/// the fraction of the current second in nanoseconds.
|
||||
EA::StdC::int128_t GetNanoseconds() const;
|
||||
|
||||
/// SetNanoseconds
|
||||
/// Sets the time based in absolute nanoseconds since 0000/01/01/00:00:00 (Year 0000, January 1, midnight).
|
||||
/// Note that this is not the same thing as SetParameter(kParameterNanosecond), as SetParameter set the
|
||||
/// number of nanoseconds that have elapsed since the current second, whereas this function sets the
|
||||
/// number of nanoseconds that have elapsed since Jan 1, year 0000).
|
||||
void SetNanoseconds(const EA::StdC::int128_t& nanoseconds);
|
||||
|
||||
protected:
|
||||
int64_t mnSeconds; /// Time as seconds. The internal representation is such that a value of zero refers to 0000/01/01/00:00:00 (Year 0000, January 1, midnight). This function thus gets the number of seconds "since the beginning of time".
|
||||
uint32_t mnNanosecond; /// The nanosecond value within the current second. In the range of [0, 999999999]. This variable is not an analogue of the DateTime mnSeconds member variable, as this is a fraction whereas that is absolute.
|
||||
|
||||
}; // class DateTime
|
||||
|
||||
|
||||
|
||||
|
||||
// Utility functions
|
||||
// The following utilities refer to the Gregorian calendar, which is the current
|
||||
// standard calendar. These form the basis for the implementation of a basic calendar.
|
||||
|
||||
/// GetTime
|
||||
/// Returns the number of nanoseconds elapsed since January 1, 1970 UTC.
|
||||
/// This is like the C time() function, except it returns nanoseconds.
|
||||
/// However, there is a key difference between this function and the C time
|
||||
/// function: this function returns actual real time progression in nanoseconds
|
||||
/// and will always return a value that's the actual time progressed since
|
||||
/// it was first called. Thus time will never appear to go backwards, slow down,
|
||||
/// or speed up. Thus this function cannot be used for accurate calendaring
|
||||
/// like an desktop productivity app might want. For that you should use the
|
||||
/// GetTimeOfDay function instead. This function's time consistency is what
|
||||
/// Posix refers to as CLOCK_MONOTONIC as opposed to CLOCK_REALTIME.
|
||||
/// This function is not guaranteed to give results that are precisely aligned
|
||||
/// with the results of GetTimeOfDay, as this function has different behaviour
|
||||
/// guarantees.
|
||||
/// Not all platforms have nanosecond-level precision, and in that case
|
||||
/// a nanosecond value will be returned but it may be with precision
|
||||
/// that is lower.
|
||||
/// You can use the TimeTSecondsSecondsToDateTime function to help convert
|
||||
/// GetTime values to seconds/nanoseconds used by the DateTime class.
|
||||
EASTDC_API uint64_t GetTime();
|
||||
|
||||
/// GetTimeMilliseconds
|
||||
/// Returns the number of milliseconds elapsed since January 1, 1970 UTC.
|
||||
/// This is simply a convenient wrapper for GetTime() / 1000000 (ie.
|
||||
/// converting the nanosecond resolution of GetTime() to milliseconds).
|
||||
EASTDC_API uint64_t GetTimeMilliseconds();
|
||||
|
||||
/// GetTimePrecision
|
||||
/// Returns the precision, in nanoseconds, of GetTime().
|
||||
/// A return value of 1 means GetTime indeed returns nanosecond values with nanosecond precision.
|
||||
/// A value of 1000 means that GetTime returns nanoseconds values with microsecond precision.
|
||||
EASTDC_API uint64_t GetTimePrecision();
|
||||
|
||||
/// IsLeapYear
|
||||
/// Returns true if the given year is a leap year.
|
||||
EASTDC_API bool IsLeapYear(uint32_t nYear);
|
||||
|
||||
/// GetDaysInYear
|
||||
/// Returns the number of days in the given year. The value will vary based on whether
|
||||
/// the year is a leap year or not.
|
||||
EASTDC_API uint32_t GetDaysInYear(uint32_t nYear);
|
||||
|
||||
/// GetDaysInMonth
|
||||
/// Returns the number of days in the given month. The value will vary based on the
|
||||
/// month and based on whether the year is a leap year. The input nMonth takes one
|
||||
/// of enum Month or an integer equivalent.
|
||||
EASTDC_API uint32_t GetDaysInMonth(uint32_t nMonth, uint32_t nYear);
|
||||
|
||||
/// GetDayOfYear
|
||||
/// The input nMonth takes one of enum Month or an integer equivalent.
|
||||
/// The input nDayOfMonth takes an integer consistent with enum DayOfMonth.
|
||||
EASTDC_API uint32_t GetDayOfYear(uint32_t nMonth, uint32_t nDayOfMonth, uint32_t nYear);
|
||||
|
||||
/// Convert4DigitTo2DigitYear
|
||||
/// Note that two-digit years bring a number of problems; they are best used for text
|
||||
/// display only and not for any internal processing.
|
||||
inline int Convert4DigitTo2DigitYear(int nYear4)
|
||||
{ return (nYear4 % 100); }
|
||||
|
||||
/// Convert2DigitTo4DigitYear
|
||||
/// This code returns a year in the 1900s if the input year is > 30 but returns
|
||||
/// a year in the 2000s if the year is <= 68. This is merely a guess and in fact there
|
||||
/// really is no good way to reliably convert a two digit year to a four digit year.
|
||||
inline int Convert2DigitTo4DigitYear(int nYear2)
|
||||
{ return nYear2 > 68 ? (1900 + nYear2) : (2000 + nYear2); }
|
||||
|
||||
/// DateTimeSecondsToTimeTSeconds
|
||||
/// DateTime seconds are based on 0000/01/01/00:00:00 (Year 0000, January 1, midnight).
|
||||
/// time_t seconds are based on 1970/01/01/00:00:00.
|
||||
inline int64_t DateTimeSecondsToTimeTSeconds(int64_t dateTimeSeconds)
|
||||
{ return dateTimeSeconds - INT64_C(62135683200);}
|
||||
|
||||
/// TimeTSecondsSecondsToDateTime
|
||||
/// time_t seconds are based on 1970/01/01/00:00:00.
|
||||
/// DateTime seconds are based on 0000/01/01/00:00:00 (Year 0000, January 1, midnight).
|
||||
inline int64_t TimeTSecondsSecondsToDateTime(int64_t TimeTSeconds)
|
||||
{ return TimeTSeconds + INT64_C(62135683200);}
|
||||
|
||||
/// ConvertEpochSeconds
|
||||
/// Converts seconds in an epoch to seconds in another epoch. You don't need to convert
|
||||
/// the nanoseconds portion of a DateTime (e.g. dateTime.GetNanoseconds()), as that value
|
||||
/// is a fractional second which is unchanged regardless of epoch. Recall that an epoch is
|
||||
/// a starting timepoint, such as how the Unix Epoch (time_t == 0) starts at Jan 1, 1970.
|
||||
/// This function extends and generalizes the functionality in DateTimeSecondsToTimeTSeconds and
|
||||
/// TimeTSecondsSecondsToDateTime.
|
||||
/// Returns seconds in the destination Epoch. Returns 0 if srcEpoch or destEpoch are invalid.
|
||||
/// Example usage:
|
||||
/// // Convert myDateTime to Unix time.
|
||||
/// int64_t unixSeconds = ConvertEpochSeconds(kEpochDateTime, myDateTime.GetSeconds(), kEpoch1970);
|
||||
EASTDC_API int64_t ConvertEpochSeconds(Epoch srcEpoch, int64_t srcSeconds, Epoch destEpoch);
|
||||
|
||||
/// GetCurrent
|
||||
/// Returns the current year, month, hour, etc.
|
||||
EASTDC_API uint32_t GetCurrent(Parameter parameter, TimeFrame timeFrame = kTimeFrameLocal);
|
||||
|
||||
/// IsDST
|
||||
/// Returns true if the current time is daylight savings time. This function assumes
|
||||
/// that DST is valid for the given current locale. Some locales within the
|
||||
/// United States don't observe DST. Daylight savings time is changed every few
|
||||
/// years by the government, so it's probably best not to rely on this function
|
||||
/// being accurate. The computer this software is running on may not know the
|
||||
/// current rules for daylight savings time.
|
||||
EASTDC_API bool IsDST();
|
||||
|
||||
/// IsDSTDateTime
|
||||
/// Returns IsDST for a DateTime seconds value and the current location.
|
||||
/// The dateTimeSeconds argument is expected to be in Universal time.
|
||||
/// DateTime seconds are based on 0000/01/01/00:00:00 (Year 0000, January 1, midnight) (not time_t).
|
||||
EASTDC_API bool IsDSTDateTime(int64_t dateTimeSeconds);
|
||||
|
||||
/// GetDaylightSavingsBias
|
||||
/// Returns the number of seconds that the current time is daylight savings adjusted from
|
||||
/// the conventional time. Adding this value to the conventional time yields the time when
|
||||
/// adjusted for daylight savings.
|
||||
EASTDC_API int64_t GetDaylightSavingsBias();
|
||||
|
||||
/// GetTimeZoneBias
|
||||
/// Returns the number of seconds that the local time zone is off of UTC.
|
||||
/// Adding this value to the current UTC yields the current local time.
|
||||
/// The return value will be zero in the case that EASTDC_UTC_TIME_AVAILABLE == 0.
|
||||
EASTDC_API int64_t GetTimeZoneBias();
|
||||
|
||||
/// GetTimeZoneName
|
||||
/// Retrieves the name of the time zone. This is not a localizable function.
|
||||
/// The supplied pName must have a capacity of at least kTimeZoneNameCapacity bytes.
|
||||
/// For example names, see http://www.worldtimezone.com/wtz-names/timezonenames.html.
|
||||
/// The returned string isn't guaranteed to match equivalent results from other platforms.
|
||||
/// The return value will be false in the case that EASTDC_UTC_TIME_AVAILABLE == 0.
|
||||
enum { kTimeZoneNameCapacity = 64 };
|
||||
EASTDC_API bool GetTimeZoneName(char* pName, bool bDaylightSavingsName);
|
||||
|
||||
/// DateTimeToTm
|
||||
/// Converts a DateTime to a C tm struct.
|
||||
EASTDC_API void DateTimeToTm(const DateTime& dateTime, tm& time);
|
||||
|
||||
/// TmToDateTime
|
||||
/// Converts a C tm struct to a DateTime.
|
||||
EASTDC_API void TmToDateTime(const tm& time, DateTime& dateTime);
|
||||
|
||||
|
||||
/// DateTimeToFileTime
|
||||
/// Converts a DateTime to a FILETIME struct.
|
||||
/// A FILETIME contains a 64-bit value representing the number of 100-nanosecond
|
||||
/// intervals since January 1, 1601 (UTC).
|
||||
EASTDC_API void DateTimeToFileTime(const DateTime& dateTime, _FILETIME& time);
|
||||
|
||||
/// FileTimeToDateTime
|
||||
/// Converts a FILETIME struct to a DateTime.
|
||||
EASTDC_API void FileTimeToDateTime(const _FILETIME& time, DateTime& dateTime);
|
||||
|
||||
|
||||
/// DateTimeToSystemTime
|
||||
/// Converts a DateTime to a SYSTEMTIME struct.
|
||||
EASTDC_API void DateTimeToSystemTime(const DateTime& dateTime, _SYSTEMTIME& time);
|
||||
|
||||
/// SystemTimeToDateTime
|
||||
/// Converts a SYSTEMTIME struct to a DateTime.
|
||||
EASTDC_API void SystemTimeToDateTime(const _SYSTEMTIME& time, DateTime& dateTime);
|
||||
|
||||
|
||||
/// GetTimeOfDay
|
||||
/// This is nearly identical to the Posix gettimeofday function, except this function adds a bUTC parameter to allow returning as local time instead of UTC.
|
||||
/// The gettimeofday() function shall obtain the current time, expressed as seconds and microseconds
|
||||
/// since the Epoch, and store it in the timeval structure pointed to by tp. The resolution of the
|
||||
/// system clock is unspecified. If tzp is not a null pointer, the behavior is unspecified.
|
||||
/// In other words: obtains the current time, expressed as seconds and microseconds since 00:00 Coordinated Universal Time (UTC), January 1, 1970.
|
||||
/// Returns 0 upon success or -1 upon error.
|
||||
/// Note that a timeval has the same meaning as time_t except that it contains sub-second information.
|
||||
/// The bUTC parameter defines if the time is returned in UTC time (a.k.a. Greenwich Mean Time) or local time.
|
||||
/// Recall that UTC refers to the time zone in Greenwich, Switzerland, whereas local time refers to whatever
|
||||
/// time zone the computer is currently in.
|
||||
/// This function is a calendar function as opposed to being a time progression measurement function. As such it's possible
|
||||
/// that the time reported by it may go backwards occasionally, as would be the case when the user's clock is changed.
|
||||
/// For a time function that's meant to measure real time progression in a non-adjusting, use the GetTime function.
|
||||
/// The time zone parameter may be NULL, in which case the time zone is not returned.
|
||||
/// Time zone information will not be available in the case that EASTDC_UTC_TIME_AVAILABLE == 0.
|
||||
EASTDC_API int GetTimeOfDay(timeval* pTV, timezone_* pTZ, bool bUTC = true);
|
||||
|
||||
|
||||
/// TimevalDifference
|
||||
/// Calculates the result of TVA - TVB.
|
||||
/// Returns 1 if TVA >= TVB, 0 if TVA == TVB, -1 if TVA < TVB. Much like strcmp().
|
||||
/// Note that a timeval has the same meaning as time_t except that it contains
|
||||
/// sub-second information.
|
||||
EASTDC_API int TimevalDifference(const timeval* pTVA, const timeval* pTVB, timeval* pTVResult);
|
||||
|
||||
|
||||
/// TimeLocale
|
||||
/// Allows user to override strings used by time and date formatting.
|
||||
struct TimeLocale
|
||||
{
|
||||
const char* mAbbrevDay[7]; // e.g. "Sun", etc.
|
||||
const char* mDay[7]; // e.g. "Sunday", etc.
|
||||
const char* mAbbrevMonth[12]; // e.g. "Jan", etc.
|
||||
const char* mMonth[12]; // e.g. "January", etc.
|
||||
const char* mAmPm[2]; // e.g. "AM" and "PM".
|
||||
const char* mDateTimeFormat; // e.g. "%a %b %d %H:%M:%S %Y"
|
||||
const char* mDateFormat; // e.g. "%m/%d/%y"
|
||||
const char* mTimeFormat; // e.g. "%H:%M:%S"
|
||||
const char* mTimeFormatAmPm; // e.g. "%I:%M:%S %p"
|
||||
};
|
||||
|
||||
/// Strftime
|
||||
/// Converts tm struct to time string.
|
||||
/// Has equivalent behaviour to the C strftime function as defined by the Posix Standard.
|
||||
/// If the total number of resulting bytes including the terminating null byte is not more
|
||||
/// than timeStringCapacity, returns the number of bytes placed into the array pointed to
|
||||
/// by pTimeString, not including the terminating null byte. Otherwise, 0 is returned and
|
||||
/// the contents of the array are indeterminate.
|
||||
/// See http://www.opengroup.org/onlinepubs/009695399/functions/strftime.html
|
||||
/// This function doesn't guarantee locale-correctness. For localized time and date
|
||||
/// formatting, consider the EALocale package or Localizer package.11
|
||||
EASTDC_API size_t Strftime(char* EA_RESTRICT pTimeString, size_t timeStringCapacity, const char* EA_RESTRICT pFormat,
|
||||
const tm* EA_RESTRICT pTime, const TimeLocale* EA_RESTRICT pTimeLocale = NULL);
|
||||
|
||||
/// Strptime
|
||||
/// Converts time string to tm struct.
|
||||
/// Has equivalent behaviour to the C strftime function as defined by the Posix Standard.
|
||||
/// Returns a pointer to the character following the last character parsed. Otherwise, a null pointer is returned.
|
||||
/// See http://www.opengroup.org/onlinepubs/009695399/functions/strptime.html
|
||||
/// This function doesn't guarantee locale-correctness. For localized time and date
|
||||
/// formatting, consider the EALocale package or Localizer package.
|
||||
EASTDC_API char* Strptime(const char* EA_RESTRICT pTimeString, const char* EA_RESTRICT pFormat,
|
||||
tm* EA_RESTRICT pTime, const TimeLocale* EA_RESTRICT pTimeLocale = NULL);
|
||||
|
||||
|
||||
} // namespace StdC
|
||||
} // namespace EA
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// DateTime inline operators
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace EA {
|
||||
namespace StdC {
|
||||
|
||||
inline bool operator==(const EA::StdC::DateTime& dateTime1, const EA::StdC::DateTime& dateTime2)
|
||||
{ return dateTime1.Compare(dateTime2) == 0; }
|
||||
|
||||
inline bool operator>(const EA::StdC::DateTime& dateTime1, const EA::StdC::DateTime& dateTime2)
|
||||
{ return dateTime1.Compare(dateTime2) > 0; }
|
||||
|
||||
inline bool operator>=(const EA::StdC::DateTime& dateTime1, const EA::StdC::DateTime& dateTime2)
|
||||
{ return dateTime1.Compare(dateTime2) >= 0; }
|
||||
|
||||
inline bool operator<(const EA::StdC::DateTime& dateTime1, const EA::StdC::DateTime& dateTime2)
|
||||
{ return dateTime1.Compare(dateTime2) < 0; }
|
||||
|
||||
inline bool operator<=(const EA::StdC::DateTime& dateTime1, const EA::StdC::DateTime& dateTime2)
|
||||
{ return dateTime1.Compare(dateTime2) <= 0; }
|
||||
|
||||
inline bool operator!=(const EA::StdC::DateTime& dateTime1, const EA::StdC::DateTime& dateTime2)
|
||||
{ return dateTime1.Compare(dateTime2) != 0; }
|
||||
|
||||
} // namespace StdC
|
||||
} // namespace EA
|
||||
|
||||
|
||||
|
||||
#endif // Header include guard
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,806 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Electronic Arts Inc. All rights reserved.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// OS globals are process-wide globals (singletons, actually) and are shared
|
||||
// between an EXE and DLLs, though you can use them on platforms that don't
|
||||
// support DLLs. The Windows OS does not directly support this concept of
|
||||
// global object sharing, as the only way to accomplish this under Windows
|
||||
// is to sequester the global object in its own DLL of which the EXE and all
|
||||
// DLLs explicitly reference. OS Globals allow you to have the global object
|
||||
// anywhere (including the EXE) and does so without requiring explicit linking.
|
||||
// The OS global system works at the operating system level and has
|
||||
// auto-discovery logic so that no pointers or init calls need to be made
|
||||
// between modules for them to link their OS global systems together.
|
||||
//
|
||||
// A primary use of OS globals is in the creation of application singletons
|
||||
// such as the main heap, messaging servers, asset managers, etc.
|
||||
//
|
||||
// Note that the implementation behind OS globals (EAOSGlobal.cpp) may seem
|
||||
// a bit convoluted; this is because it needs to be thread-safe, cross-module,
|
||||
// and independent of application-level memory allocators. For objects for
|
||||
// which order of initialization is clearer and you don't need to reference
|
||||
// singletons across DLLs, EASingleton is probably a better choice, as it is
|
||||
// simpler and has lower overhead. Indeed, another way of looking at OS globals
|
||||
// is to view them as process-wide singletons.
|
||||
//
|
||||
// Caveats:
|
||||
// - OS globals have the potential for resulting in code that's duplicated
|
||||
// within each DLL and thus increasing code memory usage. It might be
|
||||
// useful to use an OS global to store a pointer to an interface rather
|
||||
// than an instance of an interface, and have just a single entity within
|
||||
// the application provide the implementation. With this approach you still
|
||||
// get the benefit of users of the global not having to know ahead of time
|
||||
// where the implementation is as you otherwise need to do with standard
|
||||
// DLL linking via "dllexport."
|
||||
// - OS globals probably aren't needed in cases where you can simply use
|
||||
// dllexport (and other platforms' equivalents).
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#ifndef EASTDC_EAGLOBAL_H
|
||||
#define EASTDC_EAGLOBAL_H
|
||||
|
||||
|
||||
#include <EABase/eabase.h>
|
||||
#include <EAStdC/internal/Config.h>
|
||||
#include <EAStdC/internal/IntrusiveList.h>
|
||||
|
||||
|
||||
EA_DISABLE_ALL_VC_WARNINGS()
|
||||
#include <stddef.h>
|
||||
#include <new>
|
||||
EA_RESTORE_ALL_VC_WARNINGS()
|
||||
|
||||
#if EASTDC_GLOBALPTR_SUPPORT_ENABLED
|
||||
|
||||
namespace EA
|
||||
{
|
||||
namespace StdC
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
/// GlobalPtr
|
||||
///
|
||||
/// GlobalPtr acts as a reference to a pointer which is global throughout
|
||||
/// the process (includes the application and any loaded DLLs). The object
|
||||
/// pointed to must define a unique 32-bit kGlobalID if one is not given.
|
||||
/// The pointer is set to NULL on creation.
|
||||
///
|
||||
/// Global pointers may be used from multiple threads once initialized
|
||||
/// to point to an object, but are _not_ thread-safe when being set.
|
||||
/// If you have a situation where two threads may attempt to set a global
|
||||
/// pointer at the same time, you should use OS globals instead to serialize
|
||||
/// the creators on the OS global lock and prevent race conditions.
|
||||
///
|
||||
/// A GlobalPtr is not the same thing as simply declaring a pointer at
|
||||
/// a globally accessible scope, especially on platforms with dynamic
|
||||
/// libraries such as Windows with its DLLs. A GlobalPtr allows multiple
|
||||
/// pieces of code to declare independent pointers to an object, even if
|
||||
/// the pieced of code are in independent DLLs.
|
||||
///
|
||||
/// The pointer assigned to a GlobalPointer need not be a pointer allocated
|
||||
/// dynamically on the heap. It can just as well be the address of some
|
||||
/// static or local variable.
|
||||
///
|
||||
/// Example usage:
|
||||
/// GlobalPtr<int, 0x11111111> pInteger;
|
||||
/// GlobalPtr<int, 0x11111111> pInteger2;
|
||||
///
|
||||
/// assert(pInteger == NULL);
|
||||
///
|
||||
/// pInteger = new int[2];
|
||||
/// pInteger[0] = 10;
|
||||
/// pInteger[1] = 20;
|
||||
/// assert(pInteger2 == pInteger);
|
||||
/// assert(pInteger2[0] == pInteger[0]);
|
||||
///
|
||||
/// delete[] pInteger;
|
||||
/// pInteger = NULL;
|
||||
/// assert(pInteger2 == NULL);
|
||||
///
|
||||
template<class T, uint32_t kGlobalId = T::kGlobalId>
|
||||
class GlobalPtr
|
||||
{
|
||||
public:
|
||||
/// this_type
|
||||
/// This is an alias for this class.
|
||||
typedef GlobalPtr<T, kGlobalId> this_type;
|
||||
|
||||
/// GlobalPtr
|
||||
///
|
||||
/// Default constructor. Sets member pointer to whatever the
|
||||
/// shared version is. If this is the first usage of the shared
|
||||
/// version, the pointer will be set to NULL.
|
||||
///
|
||||
/// Example usage:
|
||||
/// GlobalPtr<SomeClass, 0x12345678> pSomeClass;
|
||||
///
|
||||
GlobalPtr()
|
||||
: mppObject(GetStaticPtr())
|
||||
{
|
||||
}
|
||||
|
||||
/// GlobalPtr (copy constructor)
|
||||
///
|
||||
/// Default constructor. Sets member pointer to whatever the
|
||||
/// shared version is. If this is the first usage of the shared
|
||||
/// version, the pointer will be set to NULL.
|
||||
///
|
||||
/// Example usage:
|
||||
/// GlobalPtr<SomeClass, 0x12345678> pSomeClass1;
|
||||
/// pSomeClass1 = new pSomeClass;
|
||||
/// GlobalPtr<SomeClass, 0x12345678> pSomeClass2(pSomeClass1);
|
||||
/// pSomeClass2->DoSomething();
|
||||
///
|
||||
explicit GlobalPtr(const this_type& /*globalPtr*/)
|
||||
: mppObject(GetStaticPtr())
|
||||
{
|
||||
}
|
||||
|
||||
/// operator =
|
||||
///
|
||||
/// Example usage:
|
||||
/// GlobalPtr<SomeClass, 0x12345678> pSomeClass1;
|
||||
/// pSomeClass1 = new pSomeClass;
|
||||
/// GlobalPtr<SomeClass, 0x12345678> pSomeClass2(pSomeClass1);
|
||||
/// pSomeClass2->DoSomething();
|
||||
///
|
||||
this_type& operator=(const this_type& /*globalPtr*/) {
|
||||
// We need do nothing, as both this and the function argument
|
||||
// are references to the same thing.
|
||||
// mppObject = globalPtr.mppObject;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// operator =
|
||||
///
|
||||
/// Example usage:
|
||||
/// GlobalPtr<SomeClass, 0x12345678> pSomeClass1;
|
||||
/// pSomeClass1 = new pSomeClass;
|
||||
/// delete pSomeClass1;
|
||||
/// pSomeClass1 = new pSomeClass;
|
||||
///
|
||||
this_type& operator=(T* p)
|
||||
{
|
||||
mppObject = p;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// operator T*
|
||||
///
|
||||
/// Example usage:
|
||||
/// GlobalPtr<SomeClass, 0x12345678> pSomeClass;
|
||||
/// FunctionWhichUsesSomeClassPtr(pSomeClass);
|
||||
///
|
||||
operator T*() const
|
||||
{
|
||||
return mppObject;
|
||||
}
|
||||
|
||||
/// operator T*
|
||||
///
|
||||
/// Example usage:
|
||||
/// GlobalPtr<SomeClass, 0x12345678> pSomeClass;
|
||||
/// CallFunctionWhichUsesSomeClassPtr(pSomeClass);
|
||||
///
|
||||
T& operator*() const
|
||||
{
|
||||
return *mppObject;
|
||||
}
|
||||
|
||||
/// operator ->
|
||||
///
|
||||
/// Example usage:
|
||||
/// GlobalPtr<SomeClass, 0x12345678> pSomeClass;
|
||||
/// pSomeClass->DoSomething();
|
||||
///
|
||||
T* operator->() const
|
||||
{
|
||||
return mppObject;
|
||||
}
|
||||
|
||||
/// operator !
|
||||
///
|
||||
/// Example usage:
|
||||
/// GlobalPtr<SomeClass, 0x12345678> pSomeClass;
|
||||
/// if(!pSomeClass)
|
||||
/// pSomeClass = new SomeClass;
|
||||
///
|
||||
bool operator!() const
|
||||
{
|
||||
return mppObject == NULL;
|
||||
}
|
||||
|
||||
/// get
|
||||
///
|
||||
/// Returns the owned pointer.
|
||||
///
|
||||
/// Example usage:
|
||||
/// GlobalPtr<SomeClass, 0x12345678> pSomeClass = new SomeClass;
|
||||
/// SomeClass* pSC = pSomeClass.get();
|
||||
/// pSC->DoSomething();
|
||||
///
|
||||
T* get() const
|
||||
{
|
||||
return mppObject;
|
||||
}
|
||||
|
||||
/// Implicit operator bool
|
||||
///
|
||||
/// Allows for using a scoped_ptr as a boolean.
|
||||
///
|
||||
/// Example usage:
|
||||
/// GlobalPtr<SomeClass, 0x12345678> pSomeClass = new SomeClass;
|
||||
/// if(pSomeClass)
|
||||
/// pSomeClass->DoSomething();
|
||||
///
|
||||
/// Note that below we do not use operator bool(). The reason for this
|
||||
/// is that booleans automatically convert up to short, int, float, etc.
|
||||
/// The result is that this: if(scopedPtr == 1) would yield true (bad).
|
||||
///
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// Disabled as long as operatorT*() exists, as these two are
|
||||
// incompatible. By some arguments, such an operator isn't safe.
|
||||
//
|
||||
// typedef T* (this_type::*bool_)() const;
|
||||
// operator bool_() const
|
||||
// {
|
||||
// if(mppObject)
|
||||
// return &this_type::get;
|
||||
// return NULL;
|
||||
// }
|
||||
|
||||
protected:
|
||||
T*& mppObject;
|
||||
|
||||
static T*& GetStaticPtr();
|
||||
};
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
/// OSGlobalNode
|
||||
///
|
||||
/// All OS globals must derive from this object or act as if they do.
|
||||
/// If you are using AutoOSGlobalPtr or AutoStaticOSGlobalPtr this
|
||||
/// derivation is handled for you.
|
||||
///
|
||||
/// Note that above we say 'or act as if they do'. You can make a smart
|
||||
/// pointer container that overrides operator ->, operator *, etc. while
|
||||
/// referring to an actual object that is somewhere else. In fact, the
|
||||
/// AutoOSGlobalPtr and AutoStaticOSGlobalPtr templates we provide do this.
|
||||
///
|
||||
struct EASTDC_API OSGlobalNode : public EA::StdC::intrusive_list_node
|
||||
{
|
||||
uint32_t mOSGlobalID; // Globally unique id.
|
||||
uint32_t mOSGlobalRefCount; // Reference count. This is modified via atomic operations.
|
||||
};
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
/// OSGlobalFactoryPtr
|
||||
///
|
||||
/// Defines a factory function for a given OSGlobalNode.
|
||||
///
|
||||
typedef OSGlobalNode *(*OSGlobalFactoryPtr)();
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
/// GetOSGlobal
|
||||
///
|
||||
/// Browses for a OS global with the given ID and either returns the
|
||||
/// existing object or attempts to create and register one. Returns NULL
|
||||
/// if the OS global could not be created. This won't happen in practice
|
||||
/// because the memory allocator will have already successfully
|
||||
/// initialized the OSGlobal system.
|
||||
///
|
||||
/// Each successful call to GetOSGlobal must be matched with a call to
|
||||
/// ReleaseOSGlobal.
|
||||
///
|
||||
/// This function doesn't directly allocate any memory. However, the
|
||||
/// user-supplied factory function may allocate memory, depending on
|
||||
/// the implementation of the function.
|
||||
///
|
||||
/// This function can safely be called from multiple threads.
|
||||
///
|
||||
EASTDC_API OSGlobalNode *GetOSGlobal(uint32_t id, OSGlobalFactoryPtr pFactory);
|
||||
|
||||
/// Kettle doesn't have native support for OS Globals, so instead we have to
|
||||
/// search physical memory for a fixed free location to share globals from
|
||||
/// starting at the very end of virtual memory and working backwards.
|
||||
/// This constant defines the size of the search space. Normally this would
|
||||
/// be a hidden implementation detail, however it's useful for memory systems
|
||||
/// to know where EAStdC might be consuming memory in an otherwise empty heap
|
||||
const uint64_t kKettleOSGlobalSearchSpace = 256 * 1024 * 1024;
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
/// SetOSGlobal
|
||||
///
|
||||
/// Adds a user-specified OSGlobal.
|
||||
/// This is useful for setting a specifi instance of an object before
|
||||
/// any automatic creation of the object is done.
|
||||
///
|
||||
EASTDC_API bool SetOSGlobal(uint32_t id, OSGlobalNode *);
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
/// ReleaseOSGlobal
|
||||
///
|
||||
/// Releases a reference to a OS global obtained from GetOSGlobal.
|
||||
///
|
||||
/// Returns false if the OS global is still in use, and true if the last
|
||||
/// reference was just released. The caller is responsible for destroying
|
||||
/// the OS global in the latter case.
|
||||
///
|
||||
/// This function can safely be called from multiple threads.
|
||||
///
|
||||
EASTDC_API bool ReleaseOSGlobal(OSGlobalNode *p);
|
||||
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
/// AutoOSGlobalPtr
|
||||
///
|
||||
/// Holds a reference to an OS global of the specified type and Id.
|
||||
/// If the OS global does not exist, a new one is created in the
|
||||
/// shared heap. The Id parameter is an arbitrary guid and allows the
|
||||
/// user to have multiple OSGlobalPtrs of the same stored type T.
|
||||
///
|
||||
/// AutoStaticOSGlobalPtrs and AutoOSGlobalPtrs should not be mixed when
|
||||
/// referring to a OS global. You should reference an OSGlobal via either
|
||||
/// one or more AutoOSGlobalPtrs, one or more AutoStaticOSGlobalPtrs, but
|
||||
/// not both at the same time.
|
||||
///
|
||||
/// OS global lookup is not very fast so the preferred usage of this
|
||||
/// class is to wrap it in an accessor. This also ensures that the
|
||||
/// OS global stays around while created.
|
||||
///
|
||||
/// This class can safely be used from multiple threads.
|
||||
///
|
||||
/// Example usage:
|
||||
/// AutoOSGlobalPtr<Widget, 0x11111111> gpWidget1A;
|
||||
/// AutoOSGlobalPtr<Widget, 0x11111111> gpWidget1B;
|
||||
/// AutoOSGlobalPtr<Widget, 0x22222222> gpWidget2;
|
||||
///
|
||||
/// void Foo() {
|
||||
/// assert(gpWidget1A == gpWidget1B);
|
||||
/// assert(gpWidget1A != gpWidget2);
|
||||
///
|
||||
/// gpWidget1A->DoSomething();
|
||||
/// gpWidget2->DoSomething();
|
||||
/// }
|
||||
///
|
||||
/// Example of how to write an accessor wrapper:
|
||||
/// Foo *GetFoo() {
|
||||
/// static AutoOSGlobalPtr<Foo, kFooId> sPtr;
|
||||
/// return sPtr;
|
||||
/// }
|
||||
///
|
||||
/// Better yet, double-wrap it so clients don't have
|
||||
/// to #include this header:
|
||||
///
|
||||
/// Foo *InlineGetFoo() {
|
||||
/// extern void GetFoo();
|
||||
/// static Foo * const sPtr = GetFoo();
|
||||
/// return sPtr;
|
||||
/// }
|
||||
///
|
||||
template<class T, uint32_t id>
|
||||
class AutoOSGlobalPtr
|
||||
{
|
||||
public:
|
||||
/// value_type
|
||||
/// This is an alias for the contained class T.
|
||||
typedef T value_type;
|
||||
|
||||
/// this_type
|
||||
/// This is an alias for this class.
|
||||
typedef AutoOSGlobalPtr<T, id> this_type;
|
||||
|
||||
public:
|
||||
/// AutoOSGlobalPtr
|
||||
///
|
||||
/// Default constructor. Creates a new version of the OSGlobalPtr
|
||||
/// object if it hasn't been created yet and sets its reference
|
||||
/// count to one.
|
||||
///
|
||||
AutoOSGlobalPtr();
|
||||
|
||||
/// AutoOSGlobalPtr (copy constructor)
|
||||
///
|
||||
/// Copy constructor. Copies the OSGlobalPtr from the source.
|
||||
/// Increases the reference count on the copied OSGlobalPtr.
|
||||
///
|
||||
AutoOSGlobalPtr(const this_type& x);
|
||||
|
||||
/// AutoOSGlobalPtr
|
||||
///
|
||||
/// Decrements the reference count on the held OSGlobalPtr if
|
||||
/// it has been set. Destroys the OSGlobalPtr node if the reference
|
||||
/// count goes to zero.
|
||||
///
|
||||
~AutoOSGlobalPtr();
|
||||
|
||||
/// operator =
|
||||
///
|
||||
/// Assignment operator. Copies the OSGlobalPtr from the source.
|
||||
/// Increases the reference count on the copied OSGlobalPtr.
|
||||
/// Decreases the reference count on the previous OSGlobalPtr.
|
||||
///
|
||||
this_type& operator=(const this_type& x);
|
||||
|
||||
/// operator T*
|
||||
///
|
||||
/// Allows this class to act like a pointer.
|
||||
///
|
||||
operator T*() const;
|
||||
|
||||
/// operator *
|
||||
///
|
||||
/// Allows this class to act like a pointer.
|
||||
///
|
||||
T& operator*() const;
|
||||
|
||||
/// operator ->
|
||||
///
|
||||
/// Allows this class to act like a pointer.
|
||||
///
|
||||
T *operator->() const;
|
||||
|
||||
/// get
|
||||
///
|
||||
/// Returns the owned pointer.
|
||||
///
|
||||
T *get() const;
|
||||
|
||||
protected:
|
||||
struct Node : public OSGlobalNode {
|
||||
T mObject;
|
||||
Node() : mObject() {}
|
||||
};
|
||||
|
||||
enum NodeAlignment {
|
||||
kNodeAlignment = EA_ALIGN_OF(Node)
|
||||
};
|
||||
|
||||
static OSGlobalNode *Create();
|
||||
|
||||
Node * const mpOSGlobal;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
namespace AutoOSGlobalInternal
|
||||
{
|
||||
/// AlignedBuffer
|
||||
///
|
||||
/// The AlignedBuffer template is required due to a weakness of VC++ __declspec(align).
|
||||
/// In particular, VC++ is only able to use __declspec(align) via an integral constant.
|
||||
/// Thus, you cannot do this: __declspec(align(__alignof(T))) but can only do things like
|
||||
/// this __declspec(align(4)). This has the unfortunate result of making it impossible to
|
||||
/// declare an object X as having the same alignment as object Y. Yet that's what we need
|
||||
/// to do. It turns out that the template constructs below allow for us to work around
|
||||
/// the VC++ limitation, albeit via some template trickery.
|
||||
///
|
||||
template <size_t size, size_t alignment> struct AlignedBuffer { typedef char Type[size]; };
|
||||
|
||||
#if defined(EA_COMPILER_MSVC) && EA_COMPILER_VERSION >= 1900 // VS2015+
|
||||
EA_DISABLE_VC_WARNING(5029); // nonstandard extension used: alignment attributes in C++ apply to variables, data members and tag types only
|
||||
#endif
|
||||
template<size_t size> struct AlignedBuffer<size, 2> { typedef EA_PREFIX_ALIGN(2) char Type[size] EA_POSTFIX_ALIGN(2); };
|
||||
template<size_t size> struct AlignedBuffer<size, 4> { typedef EA_PREFIX_ALIGN(4) char Type[size] EA_POSTFIX_ALIGN(4); };
|
||||
template<size_t size> struct AlignedBuffer<size, 8> { typedef EA_PREFIX_ALIGN(8) char Type[size] EA_POSTFIX_ALIGN(8); };
|
||||
template<size_t size> struct AlignedBuffer<size, 16> { typedef EA_PREFIX_ALIGN(16) char Type[size] EA_POSTFIX_ALIGN(16); };
|
||||
template<size_t size> struct AlignedBuffer<size, 32> { typedef EA_PREFIX_ALIGN(32) char Type[size] EA_POSTFIX_ALIGN(32); };
|
||||
template<size_t size> struct AlignedBuffer<size, 64> { typedef EA_PREFIX_ALIGN(64) char Type[size] EA_POSTFIX_ALIGN(64); };
|
||||
template<size_t size> struct AlignedBuffer<size, 128> { typedef EA_PREFIX_ALIGN(128) char Type[size] EA_POSTFIX_ALIGN(128); };
|
||||
template<size_t size> struct AlignedBuffer<size, 256> { typedef EA_PREFIX_ALIGN(256) char Type[size] EA_POSTFIX_ALIGN(256); };
|
||||
template<size_t size> struct AlignedBuffer<size, 512> { typedef EA_PREFIX_ALIGN(512) char Type[size] EA_POSTFIX_ALIGN(512); };
|
||||
template<size_t size> struct AlignedBuffer<size, 1024> { typedef EA_PREFIX_ALIGN(1024) char Type[size] EA_POSTFIX_ALIGN(1024); };
|
||||
template<size_t size> struct AlignedBuffer<size, 2048> { typedef EA_PREFIX_ALIGN(2048) char Type[size] EA_POSTFIX_ALIGN(2048); };
|
||||
template<size_t size> struct AlignedBuffer<size, 4096> { typedef EA_PREFIX_ALIGN(4096) char Type[size] EA_POSTFIX_ALIGN(4096); };
|
||||
#if defined(EA_COMPILER_MSVC) && EA_COMPILER_VERSION >= 1900 // VS2015+
|
||||
EA_RESTORE_VC_WARNING();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
/// AutoStaticOSGlobalPtr
|
||||
///
|
||||
/// Holds a reference to a OS global of the specified type and ID.
|
||||
/// If the OS global does not exist, a new one is created using static memory.
|
||||
/// The Id parameter is an arbitrary guid and allows the user to have
|
||||
/// multiple OSGlobalPtrs of the same stored type T.
|
||||
///
|
||||
/// AutoStaticOSGlobalPtrs and AutoOSGlobalPtrs should not be mixed when
|
||||
/// referring to a OS global. You should reference an OSGlobal via either
|
||||
/// one or more AutoOSGlobalPtrs, one or more AutoStaticOSGlobalPtrs, but
|
||||
/// not both at the same time.
|
||||
///
|
||||
/// The advantage of this class is that it uses static memory, so it does
|
||||
/// not contribute to heap usage and always succeeds in allocating the object.
|
||||
/// The disadvantage is that if multiple DLLs are involved each will have
|
||||
/// its own static space reserved for the OS global.
|
||||
///
|
||||
/// This class can safely be used from multiple threads.
|
||||
///
|
||||
/// Example usage:
|
||||
/// AutoStaticOSGlobalPtr<Widget, 0x11111111> gpWidget1A;
|
||||
/// AutoStaticOSGlobalPtr<Widget, 0x11111111> gpWidget1B;
|
||||
/// AutoStaticOSGlobalPtr<Widget, 0x22222222> gpWidget2;
|
||||
///
|
||||
/// void Foo() {
|
||||
/// assert(gpWidget1A == gpWidget1B);
|
||||
/// assert(gpWidget1A != gpWidget2);
|
||||
///
|
||||
/// gpWidget1A->DoSomething();
|
||||
/// gpWidget2->DoSomething();
|
||||
/// }
|
||||
///
|
||||
template<class T, uint32_t id>
|
||||
class AutoStaticOSGlobalPtr
|
||||
{
|
||||
public:
|
||||
/// value_type
|
||||
/// This is an alias for the contained class T.
|
||||
typedef T value_type;
|
||||
|
||||
/// this_type
|
||||
/// This is an alias for this class.
|
||||
typedef AutoStaticOSGlobalPtr<T, id> this_type;
|
||||
|
||||
public:
|
||||
/// AutoStaticOSGlobalPtr
|
||||
///
|
||||
/// Default constructor. Creates a new version of the OSGlobalPtr
|
||||
/// object if it hasn't been created yet and sets its reference
|
||||
/// count to one.
|
||||
///
|
||||
AutoStaticOSGlobalPtr();
|
||||
|
||||
/// AutoStaticOSGlobalPtr (copy constructor)
|
||||
///
|
||||
/// Copy constructor. Copies the OSGlobalPtr from the source.
|
||||
/// Increases the reference count on the copied OSGlobalPtr.
|
||||
///
|
||||
AutoStaticOSGlobalPtr(const this_type& x);
|
||||
|
||||
/// ~AutoStaticOSGlobalPtr
|
||||
///
|
||||
/// Decrements the reference count on the held OSGlobalPtr if
|
||||
/// it has been set. Destroys the OSGlobalPtr node if the reference
|
||||
/// count goes to zero.
|
||||
///
|
||||
~AutoStaticOSGlobalPtr();
|
||||
|
||||
/// operator =
|
||||
///
|
||||
/// Assignment operator. Copies the OSGlobalPtr from the source.
|
||||
/// Increases the reference count on the copied OSGlobalPtr.
|
||||
/// Decreases the reference count on the previous OSGlobalPtr.
|
||||
///
|
||||
this_type& operator=(const this_type& x);
|
||||
|
||||
/// operator T*
|
||||
///
|
||||
/// Allows this class to act like a pointer.
|
||||
///
|
||||
operator T*() const;
|
||||
|
||||
/// operator *
|
||||
///
|
||||
/// Allows this class to act like a pointer.
|
||||
///
|
||||
T& operator*() const;
|
||||
|
||||
/// operator ->
|
||||
///
|
||||
/// Allows this class to act like a pointer.
|
||||
///
|
||||
T* operator->() const;
|
||||
|
||||
/// get
|
||||
///
|
||||
/// Returns the owned pointer.
|
||||
///
|
||||
T* get() const;
|
||||
|
||||
protected:
|
||||
static OSGlobalNode* Create();
|
||||
|
||||
struct Node : public OSGlobalNode {
|
||||
T mObject;
|
||||
Node() : mObject() {}
|
||||
};
|
||||
|
||||
static const size_t kNodeAlignment = EA_ALIGN_OF(Node);
|
||||
static const int kStorageTypeSize = sizeof(Node) + (kNodeAlignment - 1); // Add sufficient additional storage space so that we can align the base pointer to the correct boundary in the worst case.
|
||||
|
||||
Node * const mpOSGlobal;
|
||||
static char sStorage[kStorageTypeSize];
|
||||
};
|
||||
|
||||
} // namespace StdC
|
||||
|
||||
} // namespace EA
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Implementation
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace EA
|
||||
{
|
||||
namespace StdC
|
||||
{
|
||||
|
||||
template<class T, uint32_t kGlobalId>
|
||||
T*& GlobalPtr<T, kGlobalId>::GetStaticPtr()
|
||||
{
|
||||
// For now, we use the OS global system to hold the pointer.
|
||||
// This may change in the future. The pointer must be static
|
||||
// because we need something to hold onto the OS global.
|
||||
// Note: Using static here may be problematic with GCC,
|
||||
// because it doesn't do static variable shutdown properly.
|
||||
static EA::StdC::AutoOSGlobalPtr<T*, kGlobalId> sSharedPtr;
|
||||
|
||||
return *sSharedPtr;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// AutoOSGlobalPtr
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <class T, uint32_t id>
|
||||
inline AutoOSGlobalPtr<T, id>::AutoOSGlobalPtr()
|
||||
: mpOSGlobal(static_cast<Node *>(GetOSGlobal(id, Create)))
|
||||
{
|
||||
}
|
||||
|
||||
template <class T, uint32_t id>
|
||||
inline AutoOSGlobalPtr<T, id>::AutoOSGlobalPtr(const this_type&)
|
||||
: mpOSGlobal(static_cast<Node *>(GetOSGlobal(id, Create)))
|
||||
{
|
||||
}
|
||||
|
||||
template <class T, uint32_t id>
|
||||
inline AutoOSGlobalPtr<T, id>::~AutoOSGlobalPtr() {
|
||||
if (ReleaseOSGlobal(mpOSGlobal))
|
||||
delete mpOSGlobal; // Matches the new from Create.
|
||||
}
|
||||
|
||||
template <class T, uint32_t id>
|
||||
inline typename AutoOSGlobalPtr<T, id>::this_type&
|
||||
AutoOSGlobalPtr<T, id>::operator=(const this_type&) {
|
||||
// Intentionally do nothing. We should already point to the correct object.
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class T, uint32_t id>
|
||||
inline AutoOSGlobalPtr<T, id>::operator T*() const {
|
||||
return &mpOSGlobal->mObject;
|
||||
}
|
||||
|
||||
template <class T, uint32_t id>
|
||||
inline T * AutoOSGlobalPtr<T, id>::operator->() const {
|
||||
return &mpOSGlobal->mObject;
|
||||
}
|
||||
|
||||
template <class T, uint32_t id>
|
||||
inline T& AutoOSGlobalPtr<T, id>::operator*() const {
|
||||
return mpOSGlobal->mObject;
|
||||
}
|
||||
|
||||
template <class T, uint32_t id>
|
||||
inline T * AutoOSGlobalPtr<T, id>::get() const {
|
||||
return &mpOSGlobal->mObject;
|
||||
}
|
||||
|
||||
template <class T, uint32_t id>
|
||||
inline OSGlobalNode * AutoOSGlobalPtr<T, id>::Create() {
|
||||
return EASTDC_NEW_ALIGNED(kNodeAlignment, (size_t)0, EASTDC_ALLOC_PREFIX "OSGlobal") Node;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// AutoStaticOSGlobalPtr
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <class T, uint32_t id>
|
||||
inline AutoStaticOSGlobalPtr<T, id>::AutoStaticOSGlobalPtr()
|
||||
: mpOSGlobal(static_cast<Node *>(GetOSGlobal(id, Create)))
|
||||
{
|
||||
}
|
||||
|
||||
template <class T, uint32_t id>
|
||||
inline AutoStaticOSGlobalPtr<T, id>::AutoStaticOSGlobalPtr(const this_type& /*x*/)
|
||||
: mpOSGlobal(static_cast<Node *>(GetOSGlobal(id, Create)))
|
||||
{
|
||||
}
|
||||
|
||||
template <class T, uint32_t id>
|
||||
inline AutoStaticOSGlobalPtr<T, id>::~AutoStaticOSGlobalPtr() {
|
||||
if (ReleaseOSGlobal(mpOSGlobal))
|
||||
mpOSGlobal->~Node();
|
||||
}
|
||||
|
||||
template <class T, uint32_t id>
|
||||
inline typename AutoStaticOSGlobalPtr<T, id>::this_type&
|
||||
AutoStaticOSGlobalPtr<T, id>::operator=(const this_type&) {
|
||||
// Intentionally do nothing. We should already point to the correct object.
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class T, uint32_t id>
|
||||
inline AutoStaticOSGlobalPtr<T, id>::operator T*() const {
|
||||
return &mpOSGlobal->mObject;
|
||||
}
|
||||
|
||||
template <class T, uint32_t id>
|
||||
inline T * AutoStaticOSGlobalPtr<T, id>::operator->() const {
|
||||
return &mpOSGlobal->mObject;
|
||||
}
|
||||
|
||||
template <class T, uint32_t id>
|
||||
inline T& AutoStaticOSGlobalPtr<T, id>::operator*() const {
|
||||
return mpOSGlobal->mObject;
|
||||
}
|
||||
|
||||
template <class T, uint32_t id>
|
||||
inline T * AutoStaticOSGlobalPtr<T, id>::get() const
|
||||
{
|
||||
return &mpOSGlobal->mObject;
|
||||
}
|
||||
|
||||
template <class T, uint32_t id>
|
||||
inline OSGlobalNode * AutoStaticOSGlobalPtr<T, id>::Create() {
|
||||
char* pStorageAligned = (char*)(((uintptr_t)&sStorage[0] + (kNodeAlignment - 1)) & ~(kNodeAlignment - 1));
|
||||
return ::new(pStorageAligned) Node;
|
||||
}
|
||||
|
||||
// AutoStaticOSGlobalPtr::sStorage declaration
|
||||
template<class T, uint32_t id>
|
||||
char AutoStaticOSGlobalPtr<T, id>::sStorage[kStorageTypeSize];
|
||||
|
||||
} // namespace StdC
|
||||
|
||||
} // namespace EA
|
||||
|
||||
#endif // EASTDC_GLOBALPTR_SUPPORT_ENABLED
|
||||
|
||||
#endif // Header include guard
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Electronic Arts Inc. All rights reserved.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Defines the following:
|
||||
// uint16_t CRC16(const void* pData, size_t nLength, uint16_t nInitialValue = kCRC16InitialValue, bool bFinalize = true);
|
||||
// uint32_t CRC24(const void* pData, size_t nLength, uint32_t nInitialValue = kCRC24InitialValue, bool bFinalize = true);
|
||||
// uint32_t CRC32(const void* pData, size_t nLength, uint32_t nInitialValue = kCRC32InitialValue, bool bFinalize = true);
|
||||
// uint64_t CRC64(const void* pData, size_t nLength, uint64_t nInitialValue = kCRC64InitialValue, bool bFinalize = true);
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#ifndef EASTDC_EAHASHCRC_H
|
||||
#define EASTDC_EAHASHCRC_H
|
||||
|
||||
|
||||
#include <EAStdC/internal/Config.h>
|
||||
#include <EABase/eabase.h>
|
||||
EA_DISABLE_ALL_VC_WARNINGS()
|
||||
#include <stddef.h>
|
||||
EA_RESTORE_ALL_VC_WARNINGS()
|
||||
|
||||
|
||||
namespace EA
|
||||
{
|
||||
namespace StdC
|
||||
{
|
||||
|
||||
/// CRC16 (Cyclical Redundancy Code)
|
||||
///
|
||||
/// Classification:
|
||||
/// Binary hash
|
||||
/// CRC16 is not a cryptographic-level hash.
|
||||
///
|
||||
/// Description:
|
||||
/// This is a well-known standardized hash which does a decent
|
||||
/// job of generating non-colliding hash values.
|
||||
///
|
||||
/// You can use this function as a one-shot CRC or iteratively
|
||||
/// when your data is not contiguous. In the case of iterative
|
||||
/// calculation, you must set bFinalize to false for all but the
|
||||
/// final iteration. If you don't know that an iteration is the
|
||||
/// final iteration until after you have called this function,
|
||||
/// then you merely need to do this to 'finalize' the crc value:
|
||||
/// crc = ~crc;
|
||||
///
|
||||
/// Example usage (single call):
|
||||
/// uint16_t crc = CRC16(pSomeData, nSomeDataLength);
|
||||
///
|
||||
/// Example usage (multiple successive calls):
|
||||
/// char pDataArray[32][1024];
|
||||
/// uint16_t crc = kCRC16InitialValue;
|
||||
///
|
||||
/// for(int i = 0; i < 32; i++) // Calculate the cumulative CRC for the data array.
|
||||
/// crc = CRC16(pDataArray[i], 1024, crc, i == 31);
|
||||
///
|
||||
const uint16_t kCRC16InitialValue = 0xffff;
|
||||
|
||||
EASTDC_API uint16_t CRC16(const void* pData, size_t nLength, uint16_t nInitialValue = kCRC16InitialValue, bool bFinalize = true);
|
||||
|
||||
|
||||
|
||||
/// CRC24 (Cyclical Redundancy Code)
|
||||
///
|
||||
/// Classification:
|
||||
/// Binary hash
|
||||
/// CRC24 is not a cryptographic-level hash.
|
||||
/// Description:
|
||||
/// This is a well-known standardized hash which does a decent
|
||||
/// job of generating non-colliding hash values.
|
||||
///
|
||||
/// You can use this function as a one-shot CRC or iteratively
|
||||
/// when your data is not contiguous. In the case of iterative
|
||||
/// calculation, you must set bFinalize to false for all but the
|
||||
/// final iteration. If you don't know that an iteration is the
|
||||
/// final iteration until after you have called this function,
|
||||
/// then you merely need to do this to 'finalize' the crc value:
|
||||
/// crc = ~crc;
|
||||
///
|
||||
/// Example usage (single call):
|
||||
/// uint32_t crc = CRC24(pSomeData, nSomeDataLength);
|
||||
///
|
||||
/// Example usage (multiple successive calls):
|
||||
/// char pDataArray[32][1024];
|
||||
/// uint32_t crc = kCRC24InitialValue;
|
||||
///
|
||||
/// for(int i = 0; i < 32; i++) // Calculate the cumulative CRC for the data array.
|
||||
/// crc = CRC24(pDataArray[i], 1024, crc, i == 31);
|
||||
///
|
||||
const uint32_t kCRC24InitialValue = 0x00b704ce; // This is the conventionally used initial value.
|
||||
|
||||
EASTDC_API uint32_t CRC24(const void* pData, size_t nLength, uint32_t nInitialValue = kCRC24InitialValue, bool bFinalize = true);
|
||||
|
||||
|
||||
|
||||
/// CRC32 (Cyclical Redundancy Code)
|
||||
///
|
||||
/// Classification:
|
||||
/// Binary hash
|
||||
/// CRC32 is not a cryptographic-level hash.
|
||||
///
|
||||
/// Description:
|
||||
/// This is a well-known standardized hash which does a decent
|
||||
/// job of generating non-colliding hash values.
|
||||
///
|
||||
/// You can use this function as a one-shot CRC or iteratively
|
||||
/// when your data is not contiguous. In the case of iterative
|
||||
/// calculation, you must set bFinalize to false for all but the
|
||||
/// final iteration. If you don't know that an iteration is the
|
||||
/// final iteration until after you have called this function,
|
||||
/// then you merely need to do this to 'finalize' the crc value:
|
||||
/// crc = ~crc;
|
||||
///
|
||||
/// Example usage (single call):
|
||||
/// uint32_t crc = CRC32(pSomeData, nSomeDataLength);
|
||||
///
|
||||
/// Example usage (multiple successive calls):
|
||||
/// char pDataArray[32][1024];
|
||||
/// uint32_t crc = kCRC32InitialValue;
|
||||
///
|
||||
/// for(int i = 0; i < 32; i++) // Calculate the cumulative CRC for the data array.
|
||||
/// crc = CRC32(pDataArray[i], 1024, crc, i == 31);
|
||||
///
|
||||
const uint32_t kCRC32InitialValue = 0xffffffff;
|
||||
|
||||
EASTDC_API uint32_t CRC32(const void* pData, size_t nLength, uint32_t nInitialValue = kCRC32InitialValue, bool bFinalize = true);
|
||||
|
||||
|
||||
/// CRC32Reverse
|
||||
///
|
||||
/// This implements reverse CRC32 as used by some software.
|
||||
/// See http://en.wikipedia.org/wiki/Cyclic_redundancy_check, for example.
|
||||
///
|
||||
EASTDC_API uint32_t CRC32Reverse(const void* pData, size_t nLength, uint32_t nInitialValue = kCRC32InitialValue, bool bFinalize = true);
|
||||
|
||||
|
||||
/// CRC32Rwstdc
|
||||
/// Provided for compatibility with rwstdc. This version acts identically to the CRC32
|
||||
/// from the rwstdc package. Users are advised to switch to the regular CRC32 function,
|
||||
/// as it follows the CRC standard and thus is portable.
|
||||
EASTDC_API uint32_t CRC32Rwstdc(const void* pData, size_t nLength);
|
||||
|
||||
|
||||
|
||||
/// CRC64 (Cyclical Redundancy Code)
|
||||
///
|
||||
/// Classification:
|
||||
/// Binary hash
|
||||
/// CRC64 is not a cryptographic-level hash.
|
||||
///
|
||||
/// Description:
|
||||
/// This is a well-known standardized hash which does a decent
|
||||
/// job of generating non-colliding hash values.
|
||||
///
|
||||
/// You can use this function as a one-shot CRC or iteratively
|
||||
/// when your data is not contiguous. In the case of iterative
|
||||
/// calculation, you must set bFinalize to false for all but the
|
||||
/// final iteration. If you don't know that an iteration is the
|
||||
/// final iteration until after you have called this function,
|
||||
/// then you merely need to do this to 'finalize' the crc value:
|
||||
/// crc ^= UINT64_C(0xffffffffffffffff);
|
||||
///
|
||||
/// Example usage (single call):
|
||||
/// uint64_t crc = CRC64(pSomeData, nSomeDataLength);
|
||||
///
|
||||
/// Example usage (multiple successive calls):
|
||||
/// char pDataArray[32][1024];
|
||||
/// uint64_t crc = kCRC64InitialValue;
|
||||
///
|
||||
/// for(int i = 0; i < 32; i++) // Calculate the cumulative CRC for the data array.
|
||||
/// crc = CRC64(pDataArray[i], 1024, crc, i == 31);
|
||||
///
|
||||
const uint64_t kCRC64InitialValue = UINT64_C(0xffffffffffffffff);
|
||||
|
||||
EASTDC_API uint64_t CRC64(const void* pData, size_t nLength, uint64_t nInitialValue = kCRC64InitialValue, bool bFinalize = true);
|
||||
|
||||
|
||||
} // namespace StdC
|
||||
} // namespace EA
|
||||
|
||||
|
||||
#endif // Header include guard
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,247 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Electronic Arts Inc. All rights reserved.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Defines the following:
|
||||
// uint32_t FNV1 (const void* pData, size_t nLength, uint32_t nInitialValue = kFNV1InitialValue);
|
||||
// uint32_t FNV1_String8 (const char* pData, uint32_t nInitialValue = kFNV1InitialValue, CharCase charCase = kCharCaseAny);
|
||||
// uint32_t FNV1_String16(const char16_t* pData, uint32_t nInitialValue = kFNV1InitialValue, CharCase charCase = kCharCaseAny);
|
||||
//
|
||||
// uint64_t FNV64 (const void* pData, size_t nLength, uint64_t nInitialValue = kFNV1InitialValue);
|
||||
// uint64_t FNV64_String8 (const char* pData, uint64_t nInitialValue = kFNV1InitialValue, CharCase charCase = kCharCaseAny);
|
||||
// uint64_t FNV64_String16(const char16_t* pData, uint64_t nInitialValue = kFNV1InitialValue, CharCase charCase = kCharCaseAny);
|
||||
//
|
||||
// template<> class CTStringHash;
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#ifndef EASTDC_EAHASHSTRING_H
|
||||
#define EASTDC_EAHASHSTRING_H
|
||||
|
||||
|
||||
#include <EAStdC/internal/Config.h>
|
||||
#include <EABase/eabase.h>
|
||||
EA_DISABLE_ALL_VC_WARNINGS()
|
||||
#include <stddef.h>
|
||||
EA_RESTORE_ALL_VC_WARNINGS()
|
||||
|
||||
|
||||
namespace EA
|
||||
{
|
||||
namespace StdC
|
||||
{
|
||||
|
||||
/// CharCase
|
||||
///
|
||||
/// Defines an enumeration that refers to character case, such as
|
||||
/// upper case and lower case. Hashing functions that work on text
|
||||
/// (not necessarily just string hashes) use this enumeration.
|
||||
///
|
||||
enum CharCase
|
||||
{
|
||||
kCharCaseAny, /// Treat the text to be hashed as it is.
|
||||
kCharCaseLower, /// Treat the text to be hashed as lower-case.
|
||||
kCharCaseUpper /// Treat the text to be hashed as upper-case.
|
||||
};
|
||||
|
||||
|
||||
/// FNV1 (Fowler / Noll / Vo)
|
||||
///
|
||||
/// Reference:
|
||||
/// http://www.isthe.com/chongo/tech/comp/fnv/
|
||||
///
|
||||
/// Classification:
|
||||
/// String hash.
|
||||
/// FNV1 is not a cryptographic-level hash.
|
||||
///
|
||||
/// Description:
|
||||
/// FNV hashes are designed primarily to hash strings quickly.
|
||||
/// They are very fast, as the algorithm uses one multiply and one
|
||||
/// xor per charcter. These hashes are just a little bit slower than
|
||||
/// DJB2 hashes on most hardware but yield significantly better
|
||||
/// dispersion and thus lower collision rates.
|
||||
/// Note that we implement the FNV1 algorithm and not the FNV1a
|
||||
/// variation. For most data sets there is not a benefit with
|
||||
/// the FNV1a variation.
|
||||
///
|
||||
/// Notes:
|
||||
/// This string hash hashes the characters, not the binary bytes.
|
||||
/// As a result, the hash of "hello" and EA_CHAR16("hello") will yield the
|
||||
/// same result.
|
||||
///
|
||||
/// Algorithm:
|
||||
/// This hash is basically a loop like this:
|
||||
/// while(char c = *pData++)
|
||||
/// hash = (hash * 16777619) ^ c;
|
||||
///
|
||||
const uint32_t kFNV1InitialValue = 2166136261U;
|
||||
|
||||
EASTDC_API uint32_t FNV1 (const void* pData, size_t nLength, uint32_t nInitialValue = kFNV1InitialValue);
|
||||
EASTDC_API uint32_t FNV1_String8 (const char* pData, uint32_t nInitialValue = kFNV1InitialValue, CharCase charCase = kCharCaseAny);
|
||||
EASTDC_API uint32_t FNV1_String16(const char16_t* pData, uint32_t nInitialValue = kFNV1InitialValue, CharCase charCase = kCharCaseAny);
|
||||
EASTDC_API uint32_t FNV1_String32(const char32_t* pData, uint32_t nInitialValue = kFNV1InitialValue, CharCase charCase = kCharCaseAny);
|
||||
|
||||
|
||||
/// FNV64
|
||||
/// 64 bit version of the FNV1 algorithm.
|
||||
const uint64_t kFNV64InitialValue = UINT64_C(14695981039346656037);
|
||||
|
||||
EASTDC_API uint64_t FNV64 (const void* pData, size_t nLength, uint64_t nInitialValue = kFNV64InitialValue);
|
||||
EASTDC_API uint64_t FNV64_String8 (const char* pData, uint64_t nInitialValue = kFNV64InitialValue, CharCase charCase = kCharCaseAny);
|
||||
EASTDC_API uint64_t FNV64_String16(const char16_t* pData, uint64_t nInitialValue = kFNV64InitialValue, CharCase charCase = kCharCaseAny);
|
||||
EASTDC_API uint64_t FNV64_String32(const char32_t* pData, uint64_t nInitialValue = kFNV64InitialValue, CharCase charCase = kCharCaseAny);
|
||||
|
||||
|
||||
|
||||
// DJB2 function is deprecated, as FNV1 has been shown to be superior.
|
||||
const uint32_t kDJB2InitialValue = 5381;
|
||||
|
||||
EASTDC_API uint32_t DJB2 (const void* pData, size_t nLength, uint32_t nInitialValue = kDJB2InitialValue);
|
||||
EASTDC_API uint32_t DJB2_String8 (const char* pData, uint32_t nInitialValue = kDJB2InitialValue, CharCase charCase = kCharCaseAny);
|
||||
EASTDC_API uint32_t DJB2_String16(const char16_t* pData, uint32_t nInitialValue = kDJB2InitialValue, CharCase charCase = kCharCaseAny);
|
||||
|
||||
|
||||
} // namespace StdC
|
||||
} // namespace EA
|
||||
|
||||
|
||||
|
||||
#ifdef EA_COMPILER_MSVC
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4307) // Integer constant overflow.
|
||||
#pragma warning(disable: 4309) // Integer constant overflow.
|
||||
#pragma warning(disable: 4310) // Integer constant overflow.
|
||||
#endif
|
||||
|
||||
#define M(x) (x) // This is a placeholder for modification of x.
|
||||
#define H(x,h) ((uint32_t)(h * (uint64_t)16777619) ^ x)
|
||||
|
||||
|
||||
namespace EA
|
||||
{
|
||||
namespace StdC
|
||||
{
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
/// CTStringHash
|
||||
///
|
||||
/// Compile-time string hash. Generates FNV1 string hashes via compile-time
|
||||
/// template expansion. Both single byte and Unicode characters are accepted.
|
||||
///
|
||||
/// Example usage:
|
||||
/// const uint32_t h = CTStringHash<'T', 'e', 's', 't'>::value;
|
||||
///
|
||||
/// Example usage:
|
||||
/// void DoSomething(const char* pString) {
|
||||
/// switch(FNV1_String8(pString)) {
|
||||
/// case CTStringHash<'T', 'e', 's', 't'>::value:
|
||||
/// break;
|
||||
/// case CTStringHash<'H', 'e', 'l', 'l', 'o'>::value:
|
||||
/// break;
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// If many string constants need to be CTStringHash'd, the following Python script may be useful:
|
||||
/// def s(str):
|
||||
/// """
|
||||
/// Splits a string into a string of its constituent characters: "abc" --> "'a', 'b', 'c'"
|
||||
/// """
|
||||
/// splitStr = ""
|
||||
/// for c in str:
|
||||
/// splitStr += "'%s', " % c
|
||||
/// print splitStr[:-2]
|
||||
///
|
||||
/// Example usage:
|
||||
/// s("string") // yields 's', 't', 'r', 'i', 'n', 'g'
|
||||
///
|
||||
template<int c00=0, int c01=0, int c02=0, int c03=0, int c04=0, int c05=0, int c06=0, int c07=0,
|
||||
int c08=0, int c09=0, int c10=0, int c11=0, int c12=0, int c13=0, int c14=0, int c15=0,
|
||||
int c16=0, int c17=0, int c18=0, int c19=0, int c20=0, int c21=0, int c22=0, int c23=0,
|
||||
int c24=0, int c25=0, int c26=0, int c27=0, int c28=0, int c29=0, int c30=0, int c31=0>
|
||||
class CTStringHash
|
||||
{
|
||||
private:
|
||||
enum {
|
||||
V00=0x811c9dc5,
|
||||
V01=H(M(c00),V00), V02=H(M(c01),V01), V03=H(M(c02),V02), V04=H(M(c03),V03), V05=H(M(c04),V04),
|
||||
V06=H(M(c05),V05), V07=H(M(c06),V06), V08=H(M(c07),V07), V09=H(M(c08),V08), V10=H(M(c09),V09),
|
||||
V11=H(M(c10),V10), V12=H(M(c11),V11), V13=H(M(c12),V12), V14=H(M(c13),V13), V15=H(M(c14),V14),
|
||||
V16=H(M(c15),V15), V17=H(M(c16),V16), V18=H(M(c17),V17), V19=H(M(c18),V18), V20=H(M(c19),V19),
|
||||
V21=H(M(c20),V20), V22=H(M(c21),V21), V23=H(M(c22),V22), V24=H(M(c23),V23), V25=H(M(c24),V24),
|
||||
V26=H(M(c25),V25), V27=H(M(c26),V26), V28=H(M(c27),V27), V29=H(M(c28),V28), V30=H(M(c29),V29),
|
||||
V31=H(M(c30),V30), V32=H(M(c31),V31)
|
||||
};
|
||||
|
||||
public:
|
||||
static const uint32_t value = (uint32_t)
|
||||
((c00==0)?V00:((c01==0)?V01:((c02==0)?V02:((c03==0)?V03:((c04==0)?V04:((c05==0)?V05:
|
||||
((c06==0)?V06:((c07==0)?V07:((c08==0)?V08:((c09==0)?V09:((c10==0)?V10:((c11==0)?V11:
|
||||
((c12==0)?V12:((c13==0)?V13:((c14==0)?V14:((c15==0)?V15:((c16==0)?V16:((c17==0)?V17:
|
||||
((c18==0)?V18:((c19==0)?V19:((c20==0)?V20:((c21==0)?V21:((c22==0)?V22:((c23==0)?V23:
|
||||
((c24==0)?V24:((c25==0)?V25:((c26==0)?V26:((c27==0)?V27:((c28==0)?V28:((c29==0)?V29:
|
||||
((c30==0)?V30:((c31==0)?V31:V32))))))))))))))))))))))))))))))));
|
||||
};
|
||||
|
||||
|
||||
} // namespace StdC
|
||||
} // namespace EA
|
||||
|
||||
|
||||
#undef H
|
||||
#undef M
|
||||
|
||||
#ifdef EA_COMPILER_MSVC
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
// Edit-time string hash Visual Studio macro
|
||||
//
|
||||
// This macro replaces the currently selected text with an FNV1 hash of it.
|
||||
// It does not understand the concept of escaped characters such as \t, as it
|
||||
// merely computes the hash of the \ and t characters indepdendently here.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
// Sub FNV1_32Bit()
|
||||
// Dim i As Integer
|
||||
// Dim iEnd As Integer
|
||||
// Dim FNV1String As String
|
||||
// Dim FNV1Value As Long
|
||||
// Dim CharAscii As Integer
|
||||
// Dim sResult As String
|
||||
//
|
||||
// FNV1String = ActiveDocument().Selection.text()
|
||||
// FNV1Value = 2166136261
|
||||
//
|
||||
// For i = 1 To Len(FNV1String)
|
||||
// CharAscii = Asc(Mid(FNV1String, i, 1))
|
||||
// FNV1Value = (FNV1Value * 16777619) Xor CharAscii
|
||||
// FNV1Value = FNV1Value And 4294967295
|
||||
// Next
|
||||
//
|
||||
// sResult = "0x{0,8:x8}"
|
||||
// sResult = sResult.Format(sResult, FNV1Value)
|
||||
// ActiveDocument.Selection.Insert(sResult)
|
||||
// End Sub
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
#endif // header sentry
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,480 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Electronic Arts Inc. All rights reserved.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Implements fast, specialized scalar math primitives. This is not a general
|
||||
// purpose vector math library, but rather simply a portable version of some
|
||||
// basic FPU primitives.
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#ifndef EASTDC_EAMATHHELP_H
|
||||
#define EASTDC_EAMATHHELP_H
|
||||
|
||||
|
||||
#include <EAStdC/internal/Config.h>
|
||||
#include <EABase/eabase.h>
|
||||
|
||||
|
||||
namespace EA
|
||||
{
|
||||
namespace StdC
|
||||
{
|
||||
typedef float float32_t;
|
||||
typedef double float64_t;
|
||||
|
||||
union FloatUint32
|
||||
{
|
||||
uint32_t i;
|
||||
float f;
|
||||
};
|
||||
|
||||
union DoubleUint64
|
||||
{
|
||||
uint64_t i;
|
||||
double f;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Constants
|
||||
//
|
||||
const uint32_t kFloat32SignMask = UINT32_C(0x80000000);
|
||||
const uint32_t kFloat32ExponentMask = UINT32_C(0x7F800000);
|
||||
const uint32_t kFloat32MantissaMask = UINT32_C(0x007FFFFF);
|
||||
const uint32_t kFloat32SignAndExponentMask = UINT32_C(0xFF800000);
|
||||
const uint32_t kFloat32SignAndMantissaMask = UINT32_C(0x807FFFFF);
|
||||
const uint32_t kFloat32ExponentAndMantissaMask = UINT32_C(0x7FFFFFFF);
|
||||
const uint32_t kFloat32PositiveInfinityBits = UINT32_C(0x7F800000);
|
||||
const unsigned kFloat32SignBits = 1;
|
||||
const unsigned kFloat32ExponentBits = 8;
|
||||
const unsigned kFloat32MantissaBits = 23;
|
||||
const unsigned kFloat32BiasValue = 127;
|
||||
|
||||
const uint64_t kFloat64SignMask = UINT64_C(0x8000000000000000);
|
||||
const uint64_t kFloat64ExponentMask = UINT64_C(0x7FF0000000000000);
|
||||
const uint64_t kFloat64MantissaMask = UINT64_C(0x000FFFFFFFFFFFFF);
|
||||
const uint64_t kFloat64SignAndExponentMask = UINT64_C(0xFFF0000000000000);
|
||||
const uint64_t kFloat64SignAndMantissaMask = UINT64_C(0x800FFFFFFFFFFFFF);
|
||||
const uint64_t kFloat64ExponentAndMantissaMask = UINT64_C(0x7FFFFFFFFFFFFFFF);
|
||||
const uint64_t kFloat64PositiveInfinityBits = UINT64_C(0x7FF0000000000000);
|
||||
const unsigned kFloat64SignBits = 1;
|
||||
const unsigned kFloat64ExponentBits = 11;
|
||||
const unsigned kFloat64MantissaBits = 52;
|
||||
const unsigned kFloat64BiasValue = 1023;
|
||||
|
||||
const FloatUint32 kInfinityUnion32 = { kFloat32PositiveInfinityBits };
|
||||
const DoubleUint64 kInfinityUnion64 = { kFloat64PositiveInfinityBits };
|
||||
#define kFloat32Infinity kInfinityUnion32.f
|
||||
#define kFloat64Infinity kInfinityUnion64.f
|
||||
|
||||
|
||||
// bias to integer
|
||||
const float32_t kFToIBiasF32 = (float32_t)(uint32_t(3) << 22);
|
||||
const int32_t kFToIBiasS32 = 0x4B400000; // Same as ((int32_t&) kFToIBiasF32), but known to optimizer at compile time.
|
||||
const float64_t kFToIBiasF64 = (float64_t)(uint64_t(3) << 52);
|
||||
|
||||
// bias to 8-bit fraction
|
||||
const float32_t kFToI8BiasF32 = (float32_t)(uint32_t(3) << 14);
|
||||
const int32_t kFToI8BiasS32 = 0x47400000; // Same as ((int32_t&) kFToI8BiasF32), but known to optimizer at compile time.
|
||||
|
||||
// bias to 16-bit fraction
|
||||
const float32_t kFToI16BiasF32 = (float32_t)(uint32_t(3) << 6);
|
||||
const int32_t kFToI16BiasS32 = 0x43400000; // Same as ((int32_t&) kFToI16BiasF32), but known to optimizer at compile time.
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// MulDiv
|
||||
//
|
||||
// Returns the result of the expression a * b / divisor without loss of
|
||||
// precision. This is useful because any operation of two (e.g.) 32 bit
|
||||
// values can generate a 64 bit result.
|
||||
// divisor cannot be 0.
|
||||
//
|
||||
// Example usage:
|
||||
// int64_t result = MulDiv(INT64_C(0x8000000000000000),
|
||||
// INT64_C(0x8000000000000000),
|
||||
// INT64_C(0x4000000000000000));
|
||||
// // result -> 0x10000000000000000
|
||||
//
|
||||
int32_t MulDiv(int32_t a, int32_t b, int32_t divisor);
|
||||
uint32_t MulDiv(uint32_t a, uint32_t b, uint32_t divisor);
|
||||
int64_t MulDiv(int64_t a, int64_t b, int64_t divisor);
|
||||
uint64_t MulDiv(uint64_t a, uint64_t b, uint64_t divisor);
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// DivMod
|
||||
//
|
||||
// Returns the integer result of dividend / divisor and dividend % divisor
|
||||
// in a single operation.
|
||||
// This is useful because division is slow on some platforms and performing
|
||||
// a div and mod at the same time will be faster than performing both
|
||||
// separately. ARM processors don't have an integer divide function even
|
||||
// in 32 bit mode, and most processors don't have a 64 bit integer divide
|
||||
// function in 32 bit mode. In those cases this function is a potential 2x win.
|
||||
// This function is similar to the C Standard div, ldiv, and lldiv functions
|
||||
// except with unsigned versions and portable to all compilers/platforms.
|
||||
// Returns the result of the division. The modulus (remainder of the division)
|
||||
// is written to modResult.
|
||||
// divisor cannot be 0, and modResult must be valid.
|
||||
//
|
||||
// Example usage:
|
||||
// int64_t result, remainder;
|
||||
// result = DivMod(14, 3, &remainder);
|
||||
// // result -> 4, remainder-> 2.
|
||||
//
|
||||
int32_t DivMod(int32_t dividend, int32_t divisor, int32_t* modResult);
|
||||
uint32_t DivMod(uint32_t dividend, uint32_t divisor, uint32_t* modResult);
|
||||
int64_t DivMod(int64_t dividend, int64_t divisor, int64_t* modResult);
|
||||
uint64_t DivMod(uint64_t dividend, uint64_t divisor, uint64_t* modResult);
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// Full range conversion functions.
|
||||
//
|
||||
// These are good for floats within the full range of a float. Remember
|
||||
// that a single-precision float only has a 24-bit significand so
|
||||
// most integers |x| > 2^24 cannot be represented exactly.
|
||||
//
|
||||
// The result of converting an out-of-range number, infinity, or NaN
|
||||
// is undefined.
|
||||
//
|
||||
inline uint32_t RoundToUint32(float fValue);
|
||||
inline int32_t RoundToInt32(float fValue);
|
||||
inline int32_t FloorToInt32(float fValue);
|
||||
inline int32_t CeilToInt32(float fValue);
|
||||
inline int32_t TruncateToInt32(float fValue);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// Partial range conversion functions.
|
||||
//
|
||||
// These are only good for |x| <= 2^23. The result of converting an
|
||||
// out-of-range number, infinity, or NaN is undefined.
|
||||
//
|
||||
inline int32_t FastRoundToInt23(float fValue);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// Unit-to-byte functions.
|
||||
//
|
||||
// Converts real values in the range |x| <= 1 to unsigned 8-bit values
|
||||
// [0, 255]. The result of calling UnitFloatToUint8() with |x|>1 is
|
||||
// undefined.
|
||||
//
|
||||
inline uint8_t UnitFloatToUint8(float fValue);
|
||||
inline uint8_t ClampUnitFloatToUint8(float fValue);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// IsInvalid
|
||||
//
|
||||
// Returns true if a value does not obey normal arithmetic rules;
|
||||
// specifically, x != x. In the case of Visual C++ 2003, this is true
|
||||
// for NaNs and indefinites, and not for normalized finite values,
|
||||
// denormals, and infinities. Other compilers may return different
|
||||
// results or even false for all values.
|
||||
//
|
||||
// IsInvalid() is useful as a fast assert check that floats are
|
||||
// sane and won't poison computations as NaNs can with masked
|
||||
// exceptions.
|
||||
//
|
||||
inline bool IsInvalid(float32_t fValue);
|
||||
inline bool IsInvalid(float64_t fValue);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// IsNormal
|
||||
//
|
||||
// Returns true if the value is a normalized finite number. That is, it is neither
|
||||
// an infinite, nor a NaN (including indefinite NaN), nor a denormalized number.
|
||||
// You generally want to write math operation checking code that asserts for
|
||||
// IsNormal() as opposed to checking specifically for IsNaN, etc.
|
||||
//
|
||||
// Normal values are defined as any floating point value with an exponent in
|
||||
// the range of [1, 254], as 0 is reserved for denormalized (underflow) values
|
||||
// and 255 is reserved for infinite (overflow) and NaN values.
|
||||
//
|
||||
inline bool IsNormal(float32_t fValue);
|
||||
inline bool IsNormal(float64_t fValue);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// IsNAN
|
||||
//
|
||||
// A NaN is a special kind of number that is neither finite nor infinite.
|
||||
// It is the result of doing things like the following:
|
||||
// float x = 1 * NaN;
|
||||
// float x = NaN + NaN;
|
||||
// float x = 0 / 0;
|
||||
// float x = 0 / infinite;
|
||||
// float x = infinite - infinite
|
||||
// float x = sqrt(-1);
|
||||
// float x = cos(infinite);
|
||||
// Under the VC++ debugger, x will be displayed as 1.#QNAN00 or 1.#IND00 and
|
||||
// the bit representation of x will be 0x7fc00001 (in the case of 1 * NaN).
|
||||
// The 'Q' in front of NAN stands for "quiet" and means that use of that value
|
||||
// in expressions won't generate exceptions. A signaling NaN (SNAN) means that
|
||||
// use of the value would generate exceptions.
|
||||
//
|
||||
// NaNs are frequently generated in physics simulations and similar mathematical
|
||||
// situations when you are simulating an object moving or turning over time but
|
||||
// the time or distance differential in the calculation is very small.
|
||||
// Also, floating point roundoff error can generate NaNs if you do things
|
||||
// like call acos(x) where you didn't take care to clamp x to <= 1. You can
|
||||
// also get a NaN when memory used to store a floating point value is written
|
||||
// with random data.
|
||||
//
|
||||
// A curious property of NaNs is that all comparisons between NaNs return
|
||||
// false except the expression: NaN != NaN. This is so even if the bit
|
||||
// representation of the two compared NaNs are identical. Thus, with NaNs,
|
||||
// the following holds:
|
||||
// x == x is always false
|
||||
// x < y is always false
|
||||
// x > y is always false
|
||||
//
|
||||
// As a result, one simple way to test for a NaN without fiddling with bits is
|
||||
// to simply test for x == x. If this returns false, then you have a NaN.
|
||||
// Unfortunately, many C and C++ compilers don't obey this, so you are usually
|
||||
// stuck fiddling with bits.
|
||||
//
|
||||
// With a NaN, all exponent bits are 1 and the mantissa is not zero.
|
||||
// If the highest fraction bit is 1, the NAN is "quiet" -- it represents
|
||||
// and indeterminant operation rather than an invalid one.
|
||||
//
|
||||
inline bool IsNAN(float32_t fValue);
|
||||
inline bool IsNAN(float64_t fValue);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// IsInfinite
|
||||
//
|
||||
// A value is infinity if the exponent bits are all 1 and all the bits of the
|
||||
// mantissa (significand) are 0. The sign bit indicates positive or negative
|
||||
// infinity. Thus, for Float32, 0x7f800000 is positive infinity and 0xff800000
|
||||
// is negative infinity.
|
||||
//
|
||||
inline bool IsInfinite(float32_t fValue);
|
||||
inline bool IsInfinite(float64_t fValue);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// IsIndefinite
|
||||
//
|
||||
// An indefinite is a special kind of NaN that is used to signify that an
|
||||
// operation between non-NaNs generated a NaN. Other than that, it really is
|
||||
// simply another NaN.
|
||||
//
|
||||
inline bool IsIndefinite(float32_t fValue);
|
||||
inline bool IsIndefinite(float64_t fValue);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// IsDenormalized
|
||||
//
|
||||
// Much in the same way that infinite numbers represent an overflow,
|
||||
// denormalized numbers represent an underflow. A denormalized number is
|
||||
// indicated by an exponent with a value of zero. You get a denormalized
|
||||
// number when you do operations such as this:
|
||||
// float x = 1e-10 / 1e35;
|
||||
// Under the VC++ debugger, x will be displayed as 1.4e-045#DEN and the
|
||||
// bit representation of x will be 0x00000001. Unlike infinites and NaNs,
|
||||
// you can still do math with denormalized numbers. However, the results
|
||||
// of your math will likely have a lot of imprecision. You can also get a
|
||||
// denormalized value when memory used to store a floating point value is
|
||||
// written with random data.
|
||||
//
|
||||
inline bool IsDenormalized(float32_t fValue);
|
||||
inline bool IsDenormalized(float64_t fValue);
|
||||
|
||||
} // namespace StdC
|
||||
} // namespace EA
|
||||
|
||||
|
||||
|
||||
|
||||
// Common implementations.
|
||||
namespace EA
|
||||
{
|
||||
namespace StdC
|
||||
{
|
||||
const int32_t kFloat32LowestExponentBit = kFloat32ExponentMask & (~kFloat32ExponentMask << 1);
|
||||
const int64_t kFloat64LowestExponentBit = kFloat64ExponentMask & (~kFloat64ExponentMask << 1);
|
||||
|
||||
inline bool IsInvalid(float32_t fValue) {
|
||||
return fValue != fValue;
|
||||
}
|
||||
|
||||
inline bool IsInvalid(float64_t fValue) {
|
||||
return fValue != fValue;
|
||||
}
|
||||
|
||||
inline bool IsNormal(float32_t fValue) {
|
||||
const union {
|
||||
float32_t f;
|
||||
int32_t i;
|
||||
} converter = { fValue };
|
||||
|
||||
// An IEEE real value is normalized finite if its exponent field
|
||||
// is not either all zeroes or ones, or if the number is zero;
|
||||
return !(converter.i & ~kFloat32SignMask) || ((converter.i - (uint32_t)kFloat32LowestExponentBit) & kFloat32ExponentMask) < (kFloat32ExponentMask - kFloat32LowestExponentBit);
|
||||
}
|
||||
|
||||
inline bool IsNormal(float64_t fValue) {
|
||||
const union {
|
||||
float64_t f;
|
||||
int64_t i;
|
||||
} converter = { fValue };
|
||||
|
||||
// An IEEE real value is normalized finite if its exponent field
|
||||
// is not either all zeroes or ones.
|
||||
return !(converter.i & ~kFloat64SignMask) || ((converter.i - (uint64_t)kFloat64LowestExponentBit) & kFloat64ExponentMask) < (kFloat64ExponentMask - kFloat64LowestExponentBit);
|
||||
}
|
||||
|
||||
inline bool IsNAN(float32_t fValue) {
|
||||
const union {
|
||||
float32_t f;
|
||||
int32_t i;
|
||||
} converter = { fValue };
|
||||
|
||||
// An IEEE real value is a NaN if all exponent bits are one and
|
||||
// the mantissa is not zero.
|
||||
return (converter.i & ~kFloat32SignMask) > kFloat32ExponentMask;
|
||||
}
|
||||
|
||||
inline bool IsNAN(float64_t fValue) {
|
||||
const union {
|
||||
float64_t f;
|
||||
int64_t i;
|
||||
} converter = { fValue };
|
||||
|
||||
// An IEEE real value is a NaN if all exponent bits are one and
|
||||
// the mantissa is not zero.
|
||||
return (converter.i & ~kFloat64SignMask) > kFloat64ExponentMask;
|
||||
}
|
||||
|
||||
inline bool IsInfinite(float32_t fValue) {
|
||||
const union {
|
||||
float32_t f;
|
||||
int32_t i;
|
||||
} converter = { fValue };
|
||||
|
||||
// An IEEE real value is infinite if all exponent bits are one
|
||||
// and the mantissa is zero.
|
||||
return (converter.i & ~kFloat32SignMask) == kFloat32PositiveInfinityBits;
|
||||
}
|
||||
|
||||
inline bool IsInfinite(float64_t fValue) {
|
||||
const union {
|
||||
float64_t f;
|
||||
int64_t i;
|
||||
} converter = { fValue };
|
||||
|
||||
// An IEEE real value is infinite if all exponent bits are one
|
||||
// and the mantissa is zero.
|
||||
return (converter.i & ~kFloat64SignMask) == kFloat64PositiveInfinityBits;
|
||||
}
|
||||
|
||||
inline bool IsIndefinite(float32_t fValue) {
|
||||
const union {
|
||||
float32_t f;
|
||||
int32_t i;
|
||||
} converter = { fValue };
|
||||
|
||||
return converter.i == (int32_t)UINT32_C(0xFFC00000);
|
||||
}
|
||||
|
||||
inline bool IsIndefinite(float64_t fValue) {
|
||||
const union {
|
||||
float64_t f;
|
||||
int64_t i;
|
||||
} converter = { fValue };
|
||||
|
||||
return converter.i == (int64_t)UINT64_C(0xFFF8000000000000);
|
||||
}
|
||||
|
||||
inline bool IsDenormalized(float32_t fValue) {
|
||||
const union {
|
||||
float32_t f;
|
||||
int32_t i;
|
||||
} converter = { fValue };
|
||||
|
||||
// An IEEE real value is denormalized if its exponent is zero and the
|
||||
// mantissa is non-zero.
|
||||
return (uint32_t)((converter.i & ~kFloat32SignMask) - 1) < kFloat32MantissaMask;
|
||||
}
|
||||
|
||||
inline bool IsDenormalized(float64_t fValue) {
|
||||
const union {
|
||||
float64_t f;
|
||||
int64_t i;
|
||||
} converter = { fValue };
|
||||
|
||||
// An IEEE real value is denormalized if its exponent is zero and the
|
||||
// mantissa is non-zero.
|
||||
return (uint64_t)((converter.i & ~kFloat64SignMask) - 1) < kFloat64MantissaMask;
|
||||
}
|
||||
|
||||
} // namespace StdC
|
||||
} // namespace EA
|
||||
|
||||
|
||||
|
||||
#if defined(EA_PLATFORM_MICROSOFT)
|
||||
#include <EAStdC/Win32/EAMathHelpWin32.inl>
|
||||
#else
|
||||
#define EAMATHHELP_MODE_REFERENCE 1 // Use the reference implementation, which has no optimizations.
|
||||
#endif
|
||||
|
||||
#if defined(EAMATHHELP_MODE_REFERENCE) && EAMATHHELP_MODE_REFERENCE
|
||||
EA_DISABLE_ALL_VC_WARNINGS()
|
||||
#include <math.h>
|
||||
EA_RESTORE_ALL_VC_WARNINGS()
|
||||
|
||||
namespace EA
|
||||
{
|
||||
namespace StdC
|
||||
{
|
||||
EA_NO_UBSAN inline uint32_t RoundToUint32(float32_t fValue) {
|
||||
return (uint32_t)floorf(fValue + 0.5f);
|
||||
}
|
||||
|
||||
inline int32_t RoundToInt32(float32_t fValue) {
|
||||
return (int32_t)floorf(fValue + 0.5f);
|
||||
}
|
||||
|
||||
inline int32_t FloorToInt32(float32_t fValue) {
|
||||
return (int32_t)floorf(fValue);
|
||||
}
|
||||
|
||||
inline int32_t CeilToInt32(float32_t fValue) {
|
||||
return (int32_t)ceilf(fValue);
|
||||
}
|
||||
|
||||
inline int32_t TruncateToInt32(float32_t fValue) {
|
||||
return (int32_t)fValue;
|
||||
}
|
||||
|
||||
EA_NO_UBSAN inline uint8_t UnitFloatToUint8(float fValue) {
|
||||
return (uint8_t)floorf((fValue * 255.0f) + 0.5f);
|
||||
}
|
||||
|
||||
inline uint8_t ClampUnitFloatToUint8(float fValue) {
|
||||
if (fValue < 0.0f)
|
||||
fValue = 0.0f;
|
||||
else if (fValue > 1.0f)
|
||||
fValue = 1.0f;
|
||||
|
||||
return (uint8_t)floorf((fValue * 255.0f) + 0.5f);
|
||||
}
|
||||
|
||||
// This function is deprecated, as it's not very useful any more.
|
||||
inline int32_t FastRoundToInt23(float32_t fValue) {
|
||||
return (int32_t)floorf(fValue + 0.5f);
|
||||
}
|
||||
|
||||
} // namespace StdC
|
||||
} // namespace EA
|
||||
|
||||
|
||||
#endif // EAMATHHELP_MODE_REFERENCE
|
||||
|
||||
|
||||
#endif // HEader include guard
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,592 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Electronic Arts Inc. All rights reserved.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// This module implements the following functions which provide
|
||||
// implementations of basic memory manipulation functions.
|
||||
//
|
||||
// Note the presence of "C" functions below such as MemcpyC. These refer to
|
||||
// memory functions that operate only on cacheable memory, but are faster
|
||||
// than otherwise. Cacheable memory is standard system RAM used by applications
|
||||
// and is the memory you work with 98% of the time. However, on some hardware
|
||||
// systems (e.g. gaming console machines) there is uncacheable memory, such as
|
||||
// memory that is mapped to video addresses. This memory is typically called
|
||||
// write-through or write-combined memory and is useful for writing data in
|
||||
// one direction from system RAM to video memory. The "C" functions do not
|
||||
// work on this kind of memory and you can instead use the regular functions
|
||||
// such as Memcpy for uncacheable memory.
|
||||
//
|
||||
// char* Memcpy (void* pDestination, const void* pSource, size_t n);
|
||||
// char* MemcpyC (void* pDestination, const void* pSource, size_t n); // Faster version for cacheable memory (and not video memory).
|
||||
// char* MemcpyS (void* pDestination, const void* pSource, size_t n); // Streaming memory copy, doesn't invalidate the cache.
|
||||
// char* Memcpy128 (void* pDestination, const void* pSource, size_t n);
|
||||
// char* Memcpy128C (void* pDestination, const void* pSource, size_t n); // Faster version for cacheable memory (and not video memory).
|
||||
// char* Memmove (void* pDestination, const void* pSource, size_t n);
|
||||
// char* MemmoveC (void* pDestination, const void* pSource, size_t n); // Faster version for cacheable memory (and not video memory).
|
||||
//
|
||||
// const void* Memchr(const void* p, char c, size_t n);
|
||||
// int Memcmp(const void* p1, const void* p2, size_t n);
|
||||
// void* Memmem(const void* pMemory, size_t memorySize, const void* pFind, size_t findSize);
|
||||
//
|
||||
// uint8_t* Memset8 (void* pDestination, uint8_t c, size_t uint8Count);
|
||||
// uint8_t* Memset8C (void* pDestination, uint8_t c, size_t uint8Count); // Faster version for cacheable memory (and not video memory).
|
||||
// uint8_t* Memset8_128 (void* pDestination, uint8_t c, size_t uint8Count);
|
||||
// uint8_t* Memset8_128C (void* pDestination, uint8_t c, size_t uint8Count); // Faster version for cacheable memory (and not video memory).
|
||||
// uint16_t* Memset16 (void* pDestination, uint16_t c, size_t uint16Count);
|
||||
// uint32_t* Memset32 (void* pDestination, uint32_t c, size_t uint32Count);
|
||||
// uint64_t* Memset64 (void* pDestination, uint64_t c, size_t uint64Count);
|
||||
// void* MemsetPointer(void* pDestination, const void* const pValue, size_t ptrCount)
|
||||
// void* MemsetN (void* pDestination, const void* pSource, size_t sourceBytes, size_t nCount);
|
||||
// void Memclear (void* pDestination, size_t n);
|
||||
// void MemclearC (void* pDestination, size_t n); // Faster version for cacheable memory (and not video memory).
|
||||
//
|
||||
// void Memfill8 (void* pDestination, uint8_t c, size_t byteCount); // Same as Memset8
|
||||
// void Memfill16 (void* pDestination, uint16_t c, size_t byteCount);
|
||||
// void Memfill24 (void* pDestination, uint32_t c, size_t byteCount);
|
||||
// void Memfill32 (void* pDestination, uint32_t c, size_t byteCount);
|
||||
// void Memfill64 (void* pDestination, uint64_t c, size_t byteCount);
|
||||
// void MemfillSpecific(void* pDestination, const void* pSource, size_t destByteCount, size_t sourceByteCount);
|
||||
//
|
||||
// uint8_t* Memcheck8 (void* pDestination, uint8_t c, size_t byteCount);
|
||||
// uint16_t* Memcheck16 (void* pDestination, uint16_t c, size_t byteCount);
|
||||
// uint32_t* Memcheck32 (void* pDestination, uint32_t c, size_t byteCount);
|
||||
// uint64_t* Memcheck64 (void* pDestination, uint64_t c, size_t byteCount);
|
||||
//
|
||||
// bool TimingSafeMemEqual(const void* p1, const void* p2, size_t n);
|
||||
// int TimingSafeMemcmp(const void* p1, const void* p2, size_t n);
|
||||
// int TimingSafeMemIsClear(const void* p, size_t n);
|
||||
//
|
||||
// void* EAAlloca(size_t n);
|
||||
// void* EAMalloca(size_t n);
|
||||
// void EAFreea();
|
||||
//
|
||||
// template<size_t> StaticMemory;
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#ifndef EASTDC_EAMEMORY_H
|
||||
#define EASTDC_EAMEMORY_H
|
||||
|
||||
|
||||
#include <EABase/eabase.h>
|
||||
#include <EAStdC/internal/Config.h>
|
||||
|
||||
|
||||
EA_DISABLE_ALL_VC_WARNINGS()
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||
EA_DISABLE_ALL_VC_WARNINGS()
|
||||
// void* __cdecl _alloca(size_t n); To consider: directly forward declare _alloca in order to avoid #include of <malloc.h>, as it's slow to compile.
|
||||
#include <malloc.h> // For alloca.
|
||||
EA_RESTORE_ALL_VC_WARNINGS()
|
||||
#elif defined (EA_PLATFORM_BSD)
|
||||
// do nothing since alloca is in stdlib.h
|
||||
#elif (defined(__GNUC__) || defined(__clang__)) && !defined(EAAlloca)
|
||||
#define EAAlloca __builtin_alloca
|
||||
#endif
|
||||
|
||||
#if defined(EA_PLATFORM_MICROSOFT)
|
||||
#include <math.h> // VS2008 has an acknowledged bug that requires math.h (and possibly also string.h) to be #included before intrin.h.
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
|
||||
#if EA_SSE
|
||||
#include <xmmintrin.h>
|
||||
#endif
|
||||
|
||||
EA_RESTORE_ALL_VC_WARNINGS()
|
||||
|
||||
|
||||
|
||||
|
||||
namespace EA
|
||||
{
|
||||
namespace StdC
|
||||
{
|
||||
|
||||
#if defined(EA_PROCESSOR_X86)
|
||||
typedef uint32_t machine_word_t;
|
||||
const size_t kMachineWordSize = sizeof(machine_word_t);
|
||||
const size_t kMachineWordSizeMask = sizeof(machine_word_t) - 1;
|
||||
#if EA_SSE
|
||||
#define EA_CACHE_PREFETCH_128(addr) _mm_prefetch((const char*)(uintptr_t)(addr), _MM_HINT_NTA)
|
||||
#define EA_CACHE_ZERO_128(addr) memset(addr, 0, 128)
|
||||
#else
|
||||
#define EA_CACHE_PREFETCH_128(addr) // Need to support _mm_prefetch.
|
||||
#define EA_CACHE_ZERO_128(addr) memset(addr, 0, 128)
|
||||
#endif
|
||||
#elif defined(EA_PROCESSOR_X86_64)
|
||||
typedef uint64_t machine_word_t;
|
||||
const size_t kMachineWordSize = sizeof(machine_word_t);
|
||||
const size_t kMachineWordSizeMask = sizeof(machine_word_t) - 1;
|
||||
#define EA_CACHE_PREFETCH_128(addr) _mm_prefetch((const char*)(uintptr_t)(addr), _MM_HINT_NTA)
|
||||
#define EA_CACHE_ZERO_128(addr) memset(addr, 0, 128)
|
||||
|
||||
#elif defined(EA_PROCESSOR_ARM32)
|
||||
typedef uint32_t machine_word_t;
|
||||
const size_t kMachineWordSize = sizeof(machine_word_t);
|
||||
const size_t kMachineWordSizeMask = sizeof(machine_word_t) - 1;
|
||||
// Modern ARM CPUs have auto-prefetch functionality, though it's
|
||||
// not necessarily smart and has to be enabled by the OS.
|
||||
#if defined(__GNUC__) && (__GNUC__ >= 4)
|
||||
#define EA_CACHE_PREFETCH_128(addr) __builtin_prefetch(addr)
|
||||
#else
|
||||
#define EA_CACHE_PREFETCH_128(addr)
|
||||
#endif
|
||||
#define EA_CACHE_ZERO_128(addr) memset(addr, 0, 128)
|
||||
#elif (EA_PLATFORM_WORD_SIZE == 4)
|
||||
typedef uint32_t machine_word_t;
|
||||
const size_t kMachineWordSize = sizeof(machine_word_t);
|
||||
const size_t kMachineWordSizeMask = sizeof(machine_word_t) - 1;
|
||||
#define EA_CACHE_PREFETCH_128(addr)
|
||||
#define EA_CACHE_ZERO_128(addr) memset(addr, 0, 128)
|
||||
#else
|
||||
typedef uint64_t machine_word_t;
|
||||
const size_t kMachineWordSize = sizeof(machine_word_t);
|
||||
const size_t kMachineWordSizeMask = sizeof(machine_word_t) - 1;
|
||||
#define EA_CACHE_PREFETCH_128(addr)
|
||||
#define EA_CACHE_ZERO_128(addr) memset(addr, 0, 128)
|
||||
#endif
|
||||
|
||||
const size_t kCacheLineSize = EA_CACHE_LINE_SIZE;
|
||||
const size_t kCacheLineSizeMask = (EA_CACHE_LINE_SIZE-1);
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// EAAlloca / EAAllocaAligned
|
||||
///
|
||||
/// Platform-independent version of alloca. This is a #define instead of a
|
||||
/// function, and so it isn't used within a namespace. This #define is located
|
||||
/// within a namespace merely for file organization purposes.
|
||||
/// EAAlloca is prefixed with EA because it is (necessarily) a #define and
|
||||
/// the EA makes its usage less likely to collide with other defines.
|
||||
///
|
||||
/// As with alloca, emory allocated by EAAalloca must be used and freed within
|
||||
/// the same scope that it is allocated.
|
||||
///
|
||||
/// Return value:
|
||||
/// EAAlloca may return NULL with some implementations, may throw an
|
||||
/// exception in some implementations, and my return a non-NULL value
|
||||
/// which is usuable beyond an arbitrary number of bytes. Thus EAlloca/alloca is
|
||||
/// dangerous to use unless you are sure it will work for you, and that is very
|
||||
/// platform/compiler/ABI-dependent. Consider using EAMalloca/EAFreea instead.
|
||||
///
|
||||
/// If EAAlloca is not #defined for some platform then it is not supported by
|
||||
/// that platform. However, as of this writing EAAlloca/alloca is supported by all
|
||||
/// compilers and platforms that have been significant to EA, including console,
|
||||
/// mobile, desktop, and hand-held platforms.
|
||||
///
|
||||
/// Declaration:
|
||||
/// void* EAAlloca(size_t n);
|
||||
///
|
||||
/// Example usage:
|
||||
/// void Function() {
|
||||
/// char* buffer = (char*)EAAlloca(24);
|
||||
/// // Beware the return value behavior of EAAlloca (see above).
|
||||
/// sprintf(buffer, "hello world");
|
||||
/// // No need to try to free it.
|
||||
/// }
|
||||
///
|
||||
#if !defined(EAAlloca)
|
||||
#define EAAlloca alloca
|
||||
#endif
|
||||
|
||||
#if !defined(EAAllocaAligned)
|
||||
#define EAAllocaAligned(size, alignment) ((void*)(((uintptr_t)EAAlloca((size)+(alignment)) + ((alignment)-1)) & ~((alignment)-1)))
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// EAMalloca / EAFreea
|
||||
///
|
||||
/// Allocates memory from the stack if possible, otherwise allocates memory
|
||||
/// via malloc. It's not possible to determine ahead of time which of the two
|
||||
/// it will allocate from. EAMalloca must be eventually matched with an EAFreea.
|
||||
/// Memory allocated by EAMalloca must be used and freed within the same scope
|
||||
/// that it is allocated.
|
||||
///
|
||||
/// Return value:
|
||||
/// Returns NULL if the memory could not be allocated.
|
||||
///
|
||||
/// Declaration:
|
||||
/// void* EAMalloca(size_t n);
|
||||
/// void EAFreea(void *p);
|
||||
///
|
||||
/// Example usage:
|
||||
/// void Function() {
|
||||
/// char* buffer = (char*)EAMalloca(24);
|
||||
/// if(buffer) { // This will fail only if malloc fails for the given size.
|
||||
/// sprintf(buffer, "hello world");
|
||||
/// EAFreea(buffer);
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// To consider: Implement EAMallocaAligned
|
||||
///
|
||||
#if defined(EA_PLATFORM_MICROSOFT) // Microsoft platforms support larger stacks than many other platforms, as well as auto-growable stacks.
|
||||
#define EAMALLOCA_THRESHOLD 8192
|
||||
#else
|
||||
#define EAMALLOCA_THRESHOLD 1024
|
||||
#endif
|
||||
#define EAMALLOCA_TYPE_ALLOCA 0xaa
|
||||
#define EAMALLOCA_TYPE_MALLOC 0xbb
|
||||
#define EAMALLOCA_TYPE_SIZE EA_PLATFORM_MIN_MALLOC_ALIGNMENT
|
||||
|
||||
#if !defined(EAMalloca)
|
||||
#define EAMalloca(size) ((((size) + EAMALLOCA_TYPE_SIZE) < EAMALLOCA_THRESHOLD) ? \
|
||||
EA::StdC::EAMallocaSetType(EAAlloca((size) + EAMALLOCA_TYPE_SIZE), EAMALLOCA_TYPE_ALLOCA) : \
|
||||
EA::StdC::EAMallocaSetType(malloc((size) + EAMALLOCA_TYPE_SIZE), EAMALLOCA_TYPE_MALLOC))
|
||||
#endif
|
||||
#if !defined(EAFreea)
|
||||
#define EAFreea(p) EA::StdC::EAFreeaImpl(p)
|
||||
#endif
|
||||
|
||||
inline void* EAMallocaSetType(void* p, uint32_t type)
|
||||
{
|
||||
if(p) {
|
||||
*reinterpret_cast<uint32_t*>(p) = type;
|
||||
p = reinterpret_cast<char*>(p) + EAMALLOCA_TYPE_SIZE;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
inline void EAFreeaImpl(void* p)
|
||||
{
|
||||
if (p) {
|
||||
p = reinterpret_cast<char*>(p) - EAMALLOCA_TYPE_SIZE;
|
||||
uint32_t type = *reinterpret_cast<uint32_t*>(p);
|
||||
if(type == EAMALLOCA_TYPE_MALLOC)
|
||||
free(p);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
/// Memcpy
|
||||
///
|
||||
/// Copies nByteCount bytes from pSource to pDestination.
|
||||
/// The source and destination memory ranges must not overlap.
|
||||
/// Returns pDestination.
|
||||
/// There are no restrictions in the way of size, alignment, or memory type,
|
||||
/// though the source memory must be readable and the destination memory
|
||||
/// must be writable.
|
||||
/// Works with uncacheable memory, such as video memory.
|
||||
///
|
||||
EASTDC_API char* Memcpy(void* EA_RESTRICT pDestination, const void* EA_RESTRICT pSource, size_t nByteCount);
|
||||
|
||||
// Cacheable memory copy
|
||||
// Works only with cacheable memory (i.e. conventional system memory).
|
||||
// The source and destination memory must not overlap.
|
||||
// Cannot be relied on to work with uncachable memory, such as video memory.
|
||||
// There are no alignment restrictions on either pDestination or pSource.
|
||||
EASTDC_API char* MemcpyC(void* EA_RESTRICT pDestination, const void* EA_RESTRICT pSource, size_t nByteCount);
|
||||
|
||||
// Streaming memcpy
|
||||
// This function copies memory from source to destination without filling the cache with the memory.
|
||||
// This is useful for when you want to write memory that will not be read by the processor used
|
||||
// to write it, such as when the CPU writes to memory used by the GPU.
|
||||
// Works on both cacheable and uncacheable memory.
|
||||
// The source and destination memory must not overlap.
|
||||
// There are no alignment restrictions on either pDestination or pSource.
|
||||
EASTDC_API char* MemcpyS(void* EA_RESTRICT pDestination, const void* EA_RESTRICT pSource, size_t nByteCount);
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
/// Memcpy128
|
||||
///
|
||||
/// Copies nByteCount bytes from the source to the destination. nByteCount
|
||||
/// must be a multiple of 128, pDestination must be on at least a 128 byte
|
||||
/// boundary, and pSource must be on at least a 16 byte boundary.
|
||||
///
|
||||
/// Additionally, pDestination and pSource must refer to cacheable memory.
|
||||
/// Cacheable memory is standard RAM application memory as opposed to being
|
||||
/// video memory or IO-mapped memory. On XBox 360, uncacheable memory is
|
||||
/// that allocated with VirtualAlloc(..., PAGE_NOCACHE) or VirtualAlloc(..., PAGE_WRITECOMBINE).
|
||||
/// In fact, on XBox 360 this function is the same as XMemCpy128.
|
||||
///
|
||||
EASTDC_API char* Memcpy128(void* EA_RESTRICT pDestination, const void* EA_RESTRICT pSource, size_t nByteCount);
|
||||
|
||||
// cacheable 128 byte memcpy
|
||||
// This function is useful for higher performance memory copies when the requirements can be met.
|
||||
// The address pointed to by pDestination must be aligned on a 128-byte boundary, and uint8Count must be a multiple of 128.
|
||||
// Works only with cacheable memory (i.e. conventional system memory).
|
||||
// The source and destination memory must not overlap.
|
||||
EASTDC_API char* Memcpy128C(void* EA_RESTRICT pDestination, const void* EA_RESTRICT pSource, size_t nByteCount);
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
/// Memmove
|
||||
///
|
||||
/// Copies nByteCount bytes from pSource to pDestination.
|
||||
/// The source and destination memory ranges may overlap.
|
||||
/// Returns pDestination.
|
||||
/// There are no restrictions in the way of size, alignment, or memory type,
|
||||
/// though the source memory must be readable and the destination memory
|
||||
/// must be writable.
|
||||
/// Works with uncacheable memory, such as video memory.
|
||||
///
|
||||
EASTDC_API char* Memmove(void* pDestination, const void* pSource, size_t nByteCount);
|
||||
|
||||
// Cacheable memory move
|
||||
// Works only with cacheable memory (i.e. conventional system memory).
|
||||
// The source and destination memory may overlap.
|
||||
// Cannot be relied on to work with uncachable memory, such as video memory.
|
||||
EASTDC_API char* MemmoveC(void* pDestination, const void* pSource, size_t nByteCount);
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
/// Memchr
|
||||
///
|
||||
/// Same as memchr and wmemchr.
|
||||
/// Searches the first n characters (not necessarily bytes) of the memory block
|
||||
/// pointed to by pString for character c.
|
||||
/// Returns a pointer to the character or NULL if not found.
|
||||
/// There are no restrictions about the type of memory p refers
|
||||
/// to except that it be readable.
|
||||
///
|
||||
EASTDC_API const char* Memchr (const char* p, char c, size_t n);
|
||||
EASTDC_API const char16_t* Memchr16(const char16_t* p, char16_t c, size_t n);
|
||||
EASTDC_API const char32_t* Memchr32(const char32_t* p, char32_t c, size_t n);
|
||||
#if EA_WCHAR_UNIQUE
|
||||
inline const wchar_t* MemchrW(const wchar_t* p, wchar_t c, size_t n)
|
||||
{
|
||||
#if (EA_WCHAR_SIZE == 2)
|
||||
return reinterpret_cast<const wchar_t*>(Memchr16(reinterpret_cast<const char16_t*>(p), (char16_t)c, n));
|
||||
#else
|
||||
return reinterpret_cast<const wchar_t*>(Memchr32(reinterpret_cast<const char32_t*>(p), (char32_t)c, n));
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
/// Memcmp
|
||||
///
|
||||
/// Same as memcmp and wmemcmp.
|
||||
/// Compares the first n bytes of two memory blocks pointed by p1 and p2.
|
||||
/// The comparison is a bytewise compare and thus for strings it is case-sensitive.
|
||||
/// For a case-insensitive string comparison, use the Stricmp function.
|
||||
/// Bytes are treated as uint8_t for comparison purposes.
|
||||
/// Returns 0 if the memory is equal, < 0 if p1 < p2, and > 0 if p1 > p2.
|
||||
/// There are no restrictions about the type of memory p1 and p2 refer
|
||||
/// to except that they be readable.
|
||||
///
|
||||
EASTDC_API int Memcmp(const void* p1, const void* p2, size_t n);
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
/// Memmem
|
||||
///
|
||||
/// Search for pFind/findSize within pMemory/memorySize.
|
||||
/// Return NULL if not found, return the first found location within pMemory/memory size if found.
|
||||
/// If memorySize is 0, return value is NULL. Otherwise if the findSize is 0, then the return value is pMemory.
|
||||
/// If pMemory or pFind is NULL, their respective size must be 0.
|
||||
/// The return value is non-const because that's how C memmem (non-standard function) works.
|
||||
/// There are no restrictions about the type of memory pMemory and pFind refer
|
||||
/// to except that they be readable.
|
||||
/// There are no alignment restrictions on either pMemory or pFind.
|
||||
/// See http://linux.die.net/man/3/memmem
|
||||
///
|
||||
EASTDC_API void* Memmem(const void* pMemory, size_t memorySize, const void* pFind, size_t findSize);
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
/// Memclear
|
||||
///
|
||||
/// Has the same effect as memset(pDestination, 0, n) but may be faster.
|
||||
///
|
||||
/// Sets n bytes at pDestination to zero.
|
||||
/// Works with uncacheable memory, such as video memory.
|
||||
/// There are no alignment restrictions on either pDestination or pSource.
|
||||
///
|
||||
EASTDC_API void Memclear(void* pDestination, size_t n);
|
||||
|
||||
/// Sets n bytes at pDestination to zero.
|
||||
/// Works only with cacheable memory (i.e. conventional system memory).
|
||||
/// Cannot be relied on to clear uncachable memory, such as video memory.
|
||||
/// There are no alignment restrictions on either pDestination or pSource.
|
||||
EASTDC_API void MemclearC(void* pDestination, size_t n);
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
/// Memset8 / Memset16 / Memset32 / Memset64 / MemsetN / MemsetPointer
|
||||
///
|
||||
/// The standard memset function replicates a given 8 bit value into a memory
|
||||
/// block. However, we might want to replicate a 16 bit, 32 bit, or 64 bit value
|
||||
/// into a block. That's what these functions do. The MemsetN function is a
|
||||
/// generic version which can copy unusual sizes such as 24 bits (e.g. RGB fills).
|
||||
/// The count values for each of these is the count of uint16_t, count of uint32_t,
|
||||
/// count of pointers, etc.
|
||||
///
|
||||
/// Memset8 is the same as the C memset function. We provide additional variations
|
||||
/// of memset which set uint16_t values, uint32_t value, etc. instead of uint8_t
|
||||
/// values like Memset8. MemsetN writes a generic type of any size. In each case
|
||||
/// pDestination must point to enough memory to hold full values. Thus pDestination
|
||||
/// for Memset32 must have a capacity for at least (uint32Count * sizeof(uint32_t)) bytes.
|
||||
///
|
||||
/// The destination is required to be aligned to its type. Thus the destination
|
||||
/// of Memset32 must be 32 bit aligned.
|
||||
///
|
||||
/// There are no restrictions about the type of memory pDestination refers
|
||||
/// to except that it be writable.
|
||||
///
|
||||
/// Works with uncacheable memory, such as video memory, but also works on conventional
|
||||
/// cacheable system memory.
|
||||
EASTDC_API uint8_t* Memset8 (void* pDestination, uint8_t c, size_t uint8Count);
|
||||
EASTDC_API uint16_t* Memset16 (void* pDestination, uint16_t c, size_t uint16Count);
|
||||
EASTDC_API uint32_t* Memset32 (void* pDestination, uint32_t c, size_t uint32Count);
|
||||
EASTDC_API uint64_t* Memset64 (void* pDestination, uint64_t c, size_t uint64Count);
|
||||
EASTDC_API void* MemsetN (void* pDestination, const void* pSource, size_t sourceBytes, size_t nCount);
|
||||
EASTDC_API void* MemsetPointer(void* pDestination, const void* const pValue, size_t ptrCount);
|
||||
|
||||
// Works only with cacheable memory (i.e. conventional system memory).
|
||||
// Cannot be relied on to work with uncachable memory, such as video memory.
|
||||
// There are no alignment restrictions on either pDestination or pSource.
|
||||
EASTDC_API uint8_t* Memset8C(void* pDestination, uint8_t c, size_t uint8Count);
|
||||
|
||||
// Specialized memset for multiple of 128 byte sized blocks.
|
||||
EASTDC_API uint8_t* Memset8_128(void* pDestination, uint8_t c, size_t uint8Count);
|
||||
|
||||
// Cacheable 128 byte memory set
|
||||
// Works only with cacheable memory (i.e. conventional system memory).
|
||||
// Cannot be relied on to work with uncachable memory, such as video memory.
|
||||
// The address pointed to by pDestination must be aligned on a 128-byte boundary, and uint8Count must be a multiple of 128.
|
||||
EASTDC_API uint8_t* Memset8_128C(void* pDestination, uint8_t c, size_t uint8Count);
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
/// Memfill16 / Memfill24 / Memfill32 / Memfill64 / MemfillSpecific
|
||||
///
|
||||
/// Memfill is the same as memset except that the count parameter is a count
|
||||
/// of bytes and not (for example) a count of uint32_t values. Memfill supports
|
||||
/// byte counts that aren't an even multiple the value size. Thus a call to
|
||||
/// Memfill32(p, 0x00112233, 3) is valid and does what you would expect.
|
||||
/// MemfillSpecific fills (and potentially repeats) any source pattern into any destination space.
|
||||
/// There are no restrictions about the type of memory pDestination refers
|
||||
/// to except that it be writable.
|
||||
///
|
||||
EASTDC_API void Memfill8 (void* pDestination, uint8_t c, size_t byteCount);
|
||||
EASTDC_API void Memfill16 (void* pDestination, uint16_t c, size_t byteCount);
|
||||
EASTDC_API void Memfill24 (void* pDestination, uint32_t c, size_t byteCount);
|
||||
EASTDC_API void Memfill32 (void* pDestination, uint32_t c, size_t byteCount);
|
||||
EASTDC_API void Memfill64 (void* pDestination, uint64_t c, size_t byteCount);
|
||||
EASTDC_API void MemfillSpecific(void* pDestination, const void* pSource, size_t destByteCount, size_t sourceByteCount);
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
/// Memcheck8 / Memcheck16 / Memcheck32 / Memcheck64
|
||||
///
|
||||
/// This family of functions is like Memfill except it verifies that the
|
||||
/// memory is filled as per the value and byte count. Returns a pointer
|
||||
/// to the first mis-matching byte if there's a mismatch. Returns NULL if
|
||||
/// there are no mismatches.
|
||||
/// There are no restrictions about the type of memory pDestination refers
|
||||
/// to except that it be readable.
|
||||
|
||||
EASTDC_API const void* Memcheck8 (const void* p, uint8_t c, size_t byteCount);
|
||||
EASTDC_API const void* Memcheck16(const void* p, uint16_t c, size_t byteCount);
|
||||
EASTDC_API const void* Memcheck32(const void* p, uint32_t c, size_t byteCount);
|
||||
EASTDC_API const void* Memcheck64(const void* p, uint64_t c, size_t byteCount);
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
/// rwstdc compatibility
|
||||
///
|
||||
/// These are identical in behaviour to the EAStdC Memfill functions.
|
||||
/// There are no restrictions about the type of memory pDestination refers
|
||||
/// to except that it be writable.
|
||||
///
|
||||
EASTDC_API void MemFill16(void* pDestination, uint16_t c, unsigned int byteCount);
|
||||
EASTDC_API void MemFill32(void* pDestination, unsigned int value, unsigned int byteCount);
|
||||
EASTDC_API void MemFillSpecific(void* pDestination, const void* pSource, unsigned int destByteCount, unsigned int sourceByteCount);
|
||||
|
||||
|
||||
/// StaticMemory
|
||||
///
|
||||
/// Allows you to declare memory that's sized and aligned appropriately and
|
||||
/// is allocated outside of the dynamic heap. But also avoids declaring a
|
||||
/// class or struct, as that might be unfeasible in some circumstances.
|
||||
/// For example, if you have a class that you want to have a global variable
|
||||
/// for, but it's impossible for the class to be constructed before main or
|
||||
/// the compiler simply fails to do so, you can use StaticMemory.
|
||||
///
|
||||
/// Example usage:
|
||||
/// StaticMemory<sizeof(MyClass)> mStaticMemory;
|
||||
/// MyClass* pClass = new(mStaticMemory.Memory()) MyClass;
|
||||
///
|
||||
template <size_t n>
|
||||
struct StaticMemory
|
||||
{
|
||||
EA_PREFIX_ALIGN(8) uint64_t mMemory[(n + 7) / 8];
|
||||
inline void* Memory() { return mMemory; }
|
||||
};
|
||||
|
||||
|
||||
/// TimingSafeMemEqual
|
||||
///
|
||||
/// Similar to Memcmp but returns simply true or false. Performs slightly faster
|
||||
/// than TimingSafeMemcmp.
|
||||
/// Executes in a constant time for any given n value. The primary use case for this is
|
||||
/// for security in the presence of possible timing attacks.
|
||||
///
|
||||
EASTDC_API bool TimingSafeMemEqual(const void* p1, const void* p2, size_t n);
|
||||
|
||||
|
||||
/// TimingSafeMemcmp
|
||||
///
|
||||
/// Behaves the same as Memcmp, but executes in a constant time for any given n value.
|
||||
/// The primary use case for this is for security in the presence of possible timing attacks.
|
||||
///
|
||||
EASTDC_API int TimingSafeMemcmp(const void* p1, const void* p2, size_t n);
|
||||
|
||||
|
||||
/// TimingSafeMemIsClear
|
||||
///
|
||||
/// Returns true if the given n bytes of memory are all zero in value, as would be the case
|
||||
/// if the memory was cleared with Memclear.
|
||||
/// Executes in a constant time for any given n value and regardless of whether the return
|
||||
/// value is true or valse. The primary use case for this is for security in the presence of
|
||||
/// possible timing attacks.
|
||||
///
|
||||
EASTDC_API bool TimingSafeMemIsClear(const void* p, size_t n);
|
||||
|
||||
|
||||
} // namespace StdC
|
||||
} // namespace EA
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Inlines
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// In optimized non-debug builds, we inline various functions.
|
||||
// We don't inline these functions in debug builds because in debug builds they
|
||||
// contain diagnostic code that can't be exposed in headers because that would the
|
||||
// user of this header to #include all the debug functionality headers, which isn't
|
||||
// feasible.
|
||||
#if EASTDC_MEMORY_INLINE_ENABLED
|
||||
#include <EAStdC/internal/EAMemory.inl>
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#endif // Header include guard
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,385 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Electronic Arts Inc. All rights reserved.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// This module defines functions for process spawning and query.
|
||||
// Normally this functionality is present only on platforms that support
|
||||
// multiple processes, such as desktop and server platforms.
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#ifndef EASTDC_EAPROCESS_H
|
||||
#define EASTDC_EAPROCESS_H
|
||||
|
||||
|
||||
#include <EABase/eabase.h>
|
||||
#include <EAStdC/internal/Config.h>
|
||||
#include <EAStdC/EAString.h>
|
||||
EA_DISABLE_ALL_VC_WARNINGS()
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
EA_RESTORE_ALL_VC_WARNINGS()
|
||||
|
||||
|
||||
namespace EA
|
||||
{
|
||||
namespace StdC
|
||||
{
|
||||
#if defined(EA_PLATFORM_WINDOWS) && EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
|
||||
const int kMaxPathLength = _MAX_PATH;
|
||||
const int kMaxDirectoryLength = _MAX_PATH;
|
||||
#elif defined(EA_PLATFORM_XBOXONE) || defined(CS_UNDEFINED_STRING)
|
||||
const int kMaxPathLength = 260;
|
||||
const int kMaxDirectoryLength = 260;
|
||||
#elif defined(EA_PLATFORM_SONY)
|
||||
const int kMaxPathLength = 1024;
|
||||
const int kMaxDirectoryLength = 1024;
|
||||
#elif defined(EA_PLATFORM_OSX)
|
||||
const int kMaxPathLength = 1024;
|
||||
const int kMaxDirectoryLength = 1024;
|
||||
#elif defined(EA_PLATFORM_LINUX)
|
||||
const int kMaxPathLength = 1024;
|
||||
const int kMaxDirectoryLength = 1024;
|
||||
#else
|
||||
const int kMaxPathLength = 512;
|
||||
const int kMaxDirectoryLength = 512;
|
||||
#endif
|
||||
|
||||
|
||||
/// PathFlags
|
||||
///
|
||||
enum PathFlags
|
||||
{
|
||||
kPathFlagNone = 0x00,
|
||||
kPathFlagBundlePath = 0x01 // Apple-specific: Return the path to the bundle instead of its inner contents. https://developer.apple.com/library/ios/documentation/CoreFoundation/Conceptual/CFBundles/AboutBundles/AboutBundles.html
|
||||
};
|
||||
|
||||
|
||||
/// GetCurrentProcessPath
|
||||
///
|
||||
/// Returns the file path to the current process.
|
||||
/// The output parameter pPath must be big enough to hold the largest
|
||||
/// possible path (i.e. IO::kMaxPathLength) for the given platform.
|
||||
/// The return value is the strlen of the path in the argument pPath.
|
||||
/// The return value is 0 upon failure, but there should never be failure
|
||||
/// as long as the function is implemented on the given platform.
|
||||
/// The pathCapacity must be at least 1.
|
||||
///
|
||||
/// Example usage:
|
||||
/// char16_t path[IO::kMaxPathLength];
|
||||
///
|
||||
/// GetCurrentProcessPath(path, IO::kMaxPathLength);
|
||||
/// printf("Path: %ls\n", path);
|
||||
///
|
||||
EASTDC_API size_t GetCurrentProcessPath(char* pPath, int pathCapacity = kMaxPathLength, int pathFlags = kPathFlagNone);
|
||||
EASTDC_API size_t GetCurrentProcessPath(char16_t* pPath, int pathCapacity = kMaxPathLength, int pathFlags = kPathFlagNone);
|
||||
EASTDC_API size_t GetCurrentProcessPath(char32_t* pPath, int pathCapacity = kMaxPathLength, int pathFlags = kPathFlagNone);
|
||||
#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
|
||||
size_t GetCurrentProcessPath(wchar_t* pPath, int pathCapacity = kMaxPathLength, int pathFlags = kPathFlagNone);
|
||||
#endif
|
||||
|
||||
|
||||
/// SetCurrentProcessPath
|
||||
///
|
||||
/// Specifies the process path, for platforms in which it's not possible for this library
|
||||
/// to know it without being told. Subsequent calls to GetCurrentProcessPath with flags other
|
||||
/// than kPathFlagNone will ignore such flags and simply return this path.
|
||||
///
|
||||
EASTDC_API void SetCurrentProcessPath(const char* pPath);
|
||||
|
||||
|
||||
/// GetCurrentProcessDirectory
|
||||
///
|
||||
/// Returns the directory path to the current process.
|
||||
/// The output parameter pPath must be big enough to hold the largest
|
||||
/// possible directory path (i.e. IO::kMaxDirectoryLength) for the given platform.
|
||||
/// The return value is the strlen of the path in the argument pPath.
|
||||
/// The return value is 0 upon failure, but there should never be failure
|
||||
/// as long as the function is implemented on the given platform.
|
||||
/// The pathCapacity must be at least 1.
|
||||
///
|
||||
/// The return value will have a trailing directory separator, as with
|
||||
/// other directory paths in this system.
|
||||
///
|
||||
/// Example usage:
|
||||
/// char16_t dir[IO::kMaxDirectoryLength];
|
||||
///
|
||||
/// GetCurrentProcessDirectory(dir, IO::kMaxDirectoryLength);
|
||||
/// printf("Directory: %ls\n", dir);
|
||||
///
|
||||
EASTDC_API size_t GetCurrentProcessDirectory(char* pDirectory, int pathCapacity = kMaxDirectoryLength, int pathFlags = kPathFlagNone);
|
||||
EASTDC_API size_t GetCurrentProcessDirectory(char16_t* pDirectory, int pathCapacity = kMaxDirectoryLength, int pathFlags = kPathFlagNone);
|
||||
EASTDC_API size_t GetCurrentProcessDirectory(char32_t* pDirectory, int pathCapacity = kMaxDirectoryLength, int pathFlags = kPathFlagNone);
|
||||
#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
|
||||
size_t GetCurrentProcessDirectory(wchar_t* pDirectory, int pathCapacity = kMaxDirectoryLength, int pathFlags = kPathFlagNone);
|
||||
#endif
|
||||
|
||||
|
||||
/// GetEnvironmentVar
|
||||
///
|
||||
/// Environment variables are per-process global variables. Their advantage
|
||||
/// is that they can be read or written at any time during the process execution
|
||||
/// and are available from any part of the executing process, including from
|
||||
/// dynamic libraries (e.g. DLLs on the Windows platform).
|
||||
///
|
||||
/// The input pName is a 0-terminated string.
|
||||
/// The output sValue will hold the resulting environment variable string.
|
||||
/// The return value is the strlen of the required string. Thus a return value
|
||||
/// that is less than valueCapacity was able to write the entire result string.
|
||||
/// A return value of zero means that the variable exists but it is empty.
|
||||
/// A return value of size_t(-1) means the variable doesn't exist.
|
||||
///
|
||||
/// This function is named GetEnvironmentVar instead of GetEnvironmentVariable
|
||||
/// because the windows.h file #defines the latter to something else.
|
||||
///
|
||||
/// As of this writing, another version of GetEnvironmentVar exists in the
|
||||
/// EAEnvironmentVariable module and which uses char buffers instead of string
|
||||
/// objects. You may prefer the char buffers if you are avoiding memory allocations.
|
||||
///
|
||||
/// Example usage:
|
||||
/// wchar_t pValue[64];
|
||||
///
|
||||
/// if(GetEnvironmentVar(L"UserName", pValue, 64) < 64)
|
||||
/// printf("Path: %ls\n", pValue);
|
||||
///
|
||||
EASTDC_API size_t GetEnvironmentVar(const char* pName, char* pValue, size_t valueCapacity);
|
||||
EASTDC_API size_t GetEnvironmentVar(const char16_t* pName, char16_t* pValue, size_t valueCapacity);
|
||||
EASTDC_API size_t GetEnvironmentVar(const char32_t* pName, char32_t* pValue, size_t valueCapacity);
|
||||
#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
|
||||
size_t GetEnvironmentVar(const wchar_t* pName, wchar_t* pValue, size_t valueCapacity);
|
||||
#endif
|
||||
|
||||
|
||||
/// SetEnvironmentVar
|
||||
///
|
||||
/// Sets the given environment variable. Returns true if it could be set.
|
||||
/// A return value of false means the variable didn't previously exist.
|
||||
/// To remove an environment variable, set pValue to NULL. Removing is different
|
||||
/// from seting to an empty string, as the latter will result in a successful
|
||||
/// return from GetEnvironmentVar, whereas the former will result in an error return.
|
||||
/// This function is named SetEnvironmentVar instead of SetEnvironmentVariable
|
||||
/// because the windows.h file #defines the latter to something else.
|
||||
///
|
||||
/// Example usage:
|
||||
/// if(SetEnvironmentVar("User Name", "Lance Armstrong"))
|
||||
/// printf("Success setting user name.\n");
|
||||
///
|
||||
EASTDC_API bool SetEnvironmentVar(const char* pName, const char* pValue);
|
||||
EASTDC_API bool SetEnvironmentVar(const char16_t* pName, const char16_t* pValue);
|
||||
EASTDC_API bool SetEnvironmentVar(const char32_t* pName, const char32_t* pValue);
|
||||
#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
|
||||
bool SetEnvironmentVar(const wchar_t* pName, const wchar_t* pValue);
|
||||
#endif
|
||||
|
||||
|
||||
/// Spawn
|
||||
///
|
||||
/// This function spawns a process whose path is 'pPath'. The arguments for the
|
||||
/// process are specified as an array of pointers to strings.
|
||||
///
|
||||
/// It is customary for the application that spawns another application to pass
|
||||
/// the spawned application path as the first argument. However, we, like the
|
||||
/// standard C spawn function, do not enforce this. It is up to the caller to
|
||||
/// follow this convention.
|
||||
///
|
||||
/// The return value is the exit status of the process, or -1 on error.
|
||||
///
|
||||
/// Given that this is a low-level C runtime replacement function, this function
|
||||
/// does not do string encoding conversion of the input path. The caller must
|
||||
/// do such a conversion manually with a call to:
|
||||
/// EA::TextUtil::ConvertStringEncoding(sSomePathSource, sSomePathDestination, kCodePageSystem);
|
||||
///
|
||||
/// If 'wait' is true, the function does not return until the spawned process completes.
|
||||
///
|
||||
/// This function works only on systems that support multiple concurrent
|
||||
/// processes. Usually that means desktop and server operating systems.
|
||||
///
|
||||
/// Example usage:
|
||||
/// const char16_t* ptrArray[4] = { EA_CHAR16("/System/Utilities/PingSomeAddresses.exe"), EA_CHAR16("www.bozo.com"), EA_CHAR16("www.nifty.com"), NULL };
|
||||
/// int nReturnValue = Spawn("/System/Utilities/PingSomeAddresses.exe", ptrArray, true);
|
||||
///
|
||||
EASTDC_API int Spawn(const char* pPath, const char* const* pArgumentArray, bool wait = false);
|
||||
EASTDC_API int Spawn(const char16_t* pPath, const char16_t* const* pArgumentArray, bool wait = false);
|
||||
EASTDC_API int Spawn(const char32_t* pPath, const char32_t* const* pArgumentArray, bool wait = false);
|
||||
#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
|
||||
int Spawn(const wchar_t* pPath, const wchar_t* const* pArgumentArray, bool wait = false);
|
||||
#endif
|
||||
|
||||
|
||||
/// ExecuteShellCommand
|
||||
///
|
||||
/// Similar to the C runtime "system()" function present on some platforms.
|
||||
/// Multiple commands can be executed by separating them with newline characters.
|
||||
///
|
||||
/// This function works only on systems that support system-level command execution.
|
||||
/// Usually that means desktop and server operating systems.
|
||||
///
|
||||
/// Example usage:
|
||||
/// ExecuteShellCommand("su root");
|
||||
/// ExecuteShellCommand("rm /* -r");
|
||||
///
|
||||
/// ExecuteShellCommand("su root\nrm /* -r");
|
||||
///
|
||||
EASTDC_API int ExecuteShellCommand(const char* pCommand);
|
||||
EASTDC_API int ExecuteShellCommand(const char16_t* pCommand);
|
||||
EASTDC_API int ExecuteShellCommand(const char32_t* pCommand);
|
||||
#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
|
||||
int ExecuteShellCommand(const wchar_t* pCommand);
|
||||
#endif
|
||||
|
||||
|
||||
/// SearchEnvironmentPath
|
||||
///
|
||||
/// Searches the system application path set for the named application.
|
||||
///
|
||||
/// Returns true and the full path to the file if found. Desktop operating
|
||||
/// systems will often have a PATH variable or similar setting which lists
|
||||
/// a set of directories in which to look for executable programs when the
|
||||
/// programs are executed by file name (and not directory) alone.
|
||||
/// This function searches the system path set for the given file name.
|
||||
/// The returned path will have surrounding quotes removed if they were present.
|
||||
///
|
||||
/// The pFileName and pPath parameters may not be NULL.
|
||||
///
|
||||
/// If the input pEnvironmentVar is non-NULL, the environment variable
|
||||
/// identified by pEnvironmentVar is assumed to refer to a set of
|
||||
/// paths to search and it is used instead of the default system path set.
|
||||
/// The environment variable should be of the form "<directory>;<directory>;..."
|
||||
/// where individual paths may be quoted. Note that pEnvironmentVar specifies
|
||||
/// the environment variable name (e.g. "PATH") and not its value.
|
||||
///
|
||||
/// Example usage:
|
||||
/// char16_t fullPath[IO::kMaxPathLength];
|
||||
///
|
||||
/// if(SearchEnvironmentPath("perforce.exe", fullPath, "PATH"))
|
||||
/// printf("Full path to Perforce is "%ls\n", fullPath);
|
||||
///
|
||||
EASTDC_API bool SearchEnvironmentPath(const char* pFileName, char* pPath, const char* pEnvironmentVar = NULL);
|
||||
EASTDC_API bool SearchEnvironmentPath(const char16_t* pFileName, char16_t* pPath, const char16_t* pEnvironmentVar = NULL);
|
||||
EASTDC_API bool SearchEnvironmentPath(const char32_t* pFileName, char32_t* pPath, const char32_t* pEnvironmentVar = NULL);
|
||||
#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
|
||||
bool SearchEnvironmentPath(const wchar_t* pFileName, wchar_t* pPath, const wchar_t* pEnvironmentVar = NULL);
|
||||
#endif
|
||||
|
||||
|
||||
/// OpenFile
|
||||
///
|
||||
/// Opens a file via the default system application
|
||||
/// For example, a .doc file would be opened by your word processor.
|
||||
/// The input pPath must point to a valid path string.
|
||||
///
|
||||
/// This function works only on systems that support multiple concurrent
|
||||
/// processes. Usually that means desktop and server operating systems.
|
||||
///
|
||||
/// Example usage:
|
||||
/// OpenFile("/system/settings/somefile.txt");
|
||||
/// OpenFile("/system/settings/somefile.html");
|
||||
/// OpenFile("http://www.bozo.com/somefile.html");
|
||||
///
|
||||
EASTDC_API bool OpenFile(const char* pPath);
|
||||
EASTDC_API bool OpenFile(const char16_t* pPath);
|
||||
EASTDC_API bool OpenFile(const char32_t* pPath);
|
||||
#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
|
||||
bool OpenFile(const wchar_t* pPath);
|
||||
#endif
|
||||
|
||||
|
||||
} // namespace StdC
|
||||
|
||||
} // namespace EA
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Inlines
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace EA
|
||||
{
|
||||
namespace StdC
|
||||
{
|
||||
|
||||
#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
|
||||
|
||||
#if !defined(EASTDC_UNICODE_CONST_CHAR_PTR_CONST_CHAR_PTR_CAST)
|
||||
#if (EA_WCHAR_SIZE == 2)
|
||||
#define EASTDC_UNICODE_CONST_CHAR_PTR_CONST_CHAR_PTR_CAST(x) reinterpret_cast<const char16_t* const*>(reinterpret_cast<const void *>(x))
|
||||
#else
|
||||
#define EASTDC_UNICODE_CONST_CHAR_PTR_CONST_CHAR_PTR_CAST(x) reinterpret_cast<const char32_t* const*>(x)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
inline size_t GetCurrentProcessPath(wchar_t* pPath, int pathCapacity, int pathFlags)
|
||||
{
|
||||
return GetCurrentProcessPath(EASTDC_UNICODE_CHAR_PTR_CAST(pPath), pathCapacity, pathFlags);
|
||||
}
|
||||
|
||||
inline size_t GetCurrentProcessDirectory(wchar_t* pDirectory, int pathCapacity, int pathFlags)
|
||||
{
|
||||
return GetCurrentProcessDirectory(EASTDC_UNICODE_CHAR_PTR_CAST(pDirectory), pathCapacity, pathFlags);
|
||||
}
|
||||
|
||||
inline size_t GetEnvironmentVar(const wchar_t* pName, wchar_t* pValue, size_t valueCapacity)
|
||||
{
|
||||
return GetEnvironmentVar(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pName),
|
||||
EASTDC_UNICODE_CHAR_PTR_CAST(pValue),
|
||||
valueCapacity);
|
||||
}
|
||||
|
||||
inline bool SetEnvironmentVar(const wchar_t* pName, const wchar_t* pValue)
|
||||
{
|
||||
return SetEnvironmentVar(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pName),
|
||||
EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pValue));
|
||||
}
|
||||
|
||||
inline int Spawn(const wchar_t* pPath, const wchar_t* const* pArgumentArray, bool wait)
|
||||
{
|
||||
return Spawn(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pPath),
|
||||
EASTDC_UNICODE_CONST_CHAR_PTR_CONST_CHAR_PTR_CAST(pArgumentArray),
|
||||
wait);
|
||||
}
|
||||
|
||||
inline int ExecuteShellCommand(const wchar_t* pCommand)
|
||||
{
|
||||
return ExecuteShellCommand(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pCommand));
|
||||
}
|
||||
|
||||
inline bool SearchEnvironmentPath(const wchar_t* pFileName, wchar_t* pPath, const wchar_t* pEnvironmentVar)
|
||||
{
|
||||
return SearchEnvironmentPath(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pFileName),
|
||||
EASTDC_UNICODE_CHAR_PTR_CAST(pPath),
|
||||
EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pEnvironmentVar));
|
||||
}
|
||||
|
||||
inline bool OpenFile(const wchar_t* pPath)
|
||||
{
|
||||
return OpenFile(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pPath)); // EASTDC_UNICODE_CONST_CHAR_PTR_CAST is defined in EAString.h
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
} // namespace StdC
|
||||
|
||||
} // namespace EA
|
||||
|
||||
|
||||
#endif // Header include guard
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,583 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Electronic Arts Inc. All rights reserved.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// This file implements a basic set of random number generators suitable for game
|
||||
// development usage.
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#ifndef EASTDC_EARANDOM_H
|
||||
#define EASTDC_EARANDOM_H
|
||||
|
||||
|
||||
#include <EAStdC/internal/Config.h>
|
||||
#include <EABase/eabase.h>
|
||||
EA_DISABLE_ALL_VC_WARNINGS()
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
EA_RESTORE_ALL_VC_WARNINGS()
|
||||
|
||||
|
||||
namespace EA
|
||||
{
|
||||
namespace StdC
|
||||
{
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
/// Introduction
|
||||
///
|
||||
/// This code includes basic random number generator functionality. It is good for
|
||||
/// most game development uses with the exception of cryptographic uses and legally
|
||||
/// controlled gambling mechanisms. For example, cryptographic random number generator
|
||||
/// requirements have additional considerations regarding protection against
|
||||
/// cryptographic attacks.
|
||||
///
|
||||
/// Purpose
|
||||
/// There are many free C/C++ random number packages available today. Many of them
|
||||
/// are rather comprehensive and some are fairly flexible. The design of this package
|
||||
/// is based on the needs of game programmers and intentionally avoids some of the
|
||||
/// complications of other packages. This package is designed first and foremost to
|
||||
/// be fast and to offer at least the option of low footprint. Secondly this package
|
||||
/// is designed to be very easily understood with a minimum of effort; experience has
|
||||
/// shown that game programmers will not use a library unless they can figure it out
|
||||
/// on their own in a matter of a few minutes. This last consideration rules out the
|
||||
/// possibility of using a heavily templated library such as that found in Boost.
|
||||
///
|
||||
/// Distributions
|
||||
/// The generation of random number generation distributions other than linear
|
||||
/// distributions is part of an optional layer that sits on top of this core
|
||||
/// generation layer. One implementation can be found in earandom_distribution.h.
|
||||
///
|
||||
/// Random Generator Misuses
|
||||
/// It is worth mentioning that it is surprisingly common for users of random number
|
||||
/// generator classes to come to the belief that the generator is broken when in fact
|
||||
/// they are misusing the generator. Common misuses of generators include:
|
||||
/// - Seeding a generator with the same seed every time it's used.
|
||||
/// - Seeding two generators at the same time via the system clock and
|
||||
/// finding that they produce idential values.
|
||||
/// - Using 'RandomUint32Uniform() % 5000' instead of 'RandomUint32Uniform(5000)'.
|
||||
/// - Inventing flawed distribution generators.
|
||||
/// - Misusing the results of a generator but assuming the generator is
|
||||
/// yielding bad values.
|
||||
/// - Creating a random number generator for a single use right when it
|
||||
/// is needed. This is usually bad because the first generated value is
|
||||
/// no more random than the seed used to generate the number.
|
||||
///
|
||||
/// Weaknesses of the C Library rand Function
|
||||
/// The C library rand function does an OK job as a basic random number generator.
|
||||
/// for the purposes of writing applets of various types. However, it is not an
|
||||
/// optimal generic solution. Some reasons include:
|
||||
/// - Generates only numbers between 0 and RAND_MAX, which is usually 32767.
|
||||
/// - Doesn't generate floating point numbers.
|
||||
/// - Doesn't generate random numbers within a prescribed range. Using
|
||||
/// the % operator to rectify this is slow (requires integer division) and
|
||||
/// produces a lopsided distribution unless N is evenly divisble into RAND_MAX.
|
||||
/// - Generates rather poor random numbers. In particular, they tend to have
|
||||
/// obvious patterns in the low bits.
|
||||
/// - A single instance of a generator is shared with the entire application
|
||||
/// and there is no way to make another instance.
|
||||
/// - Is slow. Generating a random number via rand() was measured as 70% slower
|
||||
/// than the fast generator provided here (Intel Pentium P4 / MSVC++).
|
||||
///
|
||||
/// How to fill a container or sequence with random uint32_t values:
|
||||
/// #include <eastl/algorithm.h> // or #include <algorithm> to use std STL.
|
||||
///
|
||||
/// EA::StdC::Random rand(someSeedValue); // We can just use EA::StdC::Random directly because
|
||||
/// eastl::generate(myVector.begin(), myVector.end(), rand); // it has an operator() that returns uint32_t.
|
||||
///
|
||||
/// How to randomize (shuffle) an existing container or sequence of uint32_t values:
|
||||
/// EA::StdC::Random rand(someSeedValue);
|
||||
/// eastl::random_shuffle(myVector.begin(), myVector.end(), rand);
|
||||
///
|
||||
/// How to fill a container or sequence with random double:
|
||||
/// struct GenerateDouble {
|
||||
/// EA::StdC::Random mRand; // We need to make a tiny struct that simply
|
||||
/// GenerateDouble(uint32_t seed) : mRand(seed){} // has an operator() that returns double.
|
||||
/// double operator(){ mRand.RandomDoubleUniform(); }
|
||||
/// };
|
||||
/// GenerateDouble rand(someSeedValue);
|
||||
/// eastl::generate(myVector.begin(), myVector.end(), rand);
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
/// GetRandomSeed
|
||||
///
|
||||
/// This type of function is also known as an "entropy collector".
|
||||
/// This function generates a pseudorandom number generator seed. As such, this function is
|
||||
/// a random number generator itself. However, this random number generator is likely to be
|
||||
/// much slower than a standard pseudorandom number generator on most systems. Some systems
|
||||
/// such as Linux support seeding natively via reading from the /dev/random file. Other
|
||||
/// systems that don't support that natively implement it by reading sources of randomness
|
||||
/// on the system such as the system clock, input device state, processor state, etc.
|
||||
EASTDC_API void GetRandomSeed(void* pSeed, size_t nLength);
|
||||
|
||||
|
||||
// Random number generator prototype
|
||||
// For ease of readability, we display a condensed version of a random number generator.
|
||||
// Each function is present for a reason. For example, the functions that take a limit
|
||||
// argument are provide an efficient and reliable implementation of generator with a
|
||||
// limited return value. It is both inefficient and unreliable to generate a random
|
||||
// integer within a range by using the % operator, as is commonly done.
|
||||
//
|
||||
// class Random
|
||||
// {
|
||||
// public:
|
||||
// Random(uint32_t nSeed = 0xffffffff);
|
||||
// Random(const Random& random);
|
||||
// Random& operator=(const Random& random);
|
||||
//
|
||||
// uint32_t GetSeed() const;
|
||||
// void SetSeed(uint32_t nSeed = 0xffffffff);
|
||||
//
|
||||
// uint32_t operator()(uint32_t nLimit = 0);
|
||||
// uint32_t RandomUint32Uniform();
|
||||
// uint32_t RandomUint32Uniform(uint32_t nLimit);
|
||||
// double RandomDoubleUniform();
|
||||
// double RandomDoubleUniform(double limit);
|
||||
// };
|
||||
|
||||
|
||||
|
||||
/// RandomLinearCongruential
|
||||
///
|
||||
/// Implements a random number generator via the linear congruential algorithm.
|
||||
/// This algorithm generates good enough pseudorandom numbers for most simulation
|
||||
/// uses. Its biggest weakness is that there are some patterns that occur in the
|
||||
/// lower bits.
|
||||
///
|
||||
/// This generator optimizes speed and size at the cost of randomness.
|
||||
///
|
||||
class EASTDC_API RandomLinearCongruential
|
||||
{
|
||||
public:
|
||||
typedef uint32_t result_type;
|
||||
|
||||
/// RandomLinearCongruential
|
||||
/// Constructs the random number generator with a given initial state (seed).
|
||||
/// If the seed is 0xffffffff (MAX_UINT32), then the seed is generated automatically
|
||||
/// by a semi-random internal mechanism such as reading the system clock. Note that
|
||||
/// seeding by this mechanism can yield unexpected poor results if you create multiple
|
||||
/// instances of this class within a short period of time, as they will all get the
|
||||
/// same seed due to the system clock having not advanced.
|
||||
RandomLinearCongruential(uint32_t nSeed = 0xffffffff);
|
||||
|
||||
/// RandomLinearCongruential
|
||||
/// Copy constructor
|
||||
RandomLinearCongruential(const RandomLinearCongruential& randomLC);
|
||||
|
||||
/// operator =
|
||||
RandomLinearCongruential& operator=(const RandomLinearCongruential& randomLC);
|
||||
|
||||
/// GetSeed
|
||||
/// Gets the state of the random number generator, which can be entirely
|
||||
/// defined by a single uint32_t.
|
||||
uint32_t GetSeed() const;
|
||||
|
||||
/// SetSeed
|
||||
/// Sets the current state of the random number generator, which can be
|
||||
/// entirely defined by a single uint32_t. If you want random number generation
|
||||
/// to appear random, the seeds that you supply must themselves be randomly
|
||||
/// selected.
|
||||
void SetSeed(uint32_t nSeed = 0xffffffff);
|
||||
|
||||
/// operator ()
|
||||
/// Generates a random uint32 with an optional limit. Acts the same as the
|
||||
/// RandomUint32Uniform(uint32_t nLimit) function. This function is useful for
|
||||
/// some templated uses whereby you want the class to act as a function object.
|
||||
/// If the input nLimit is 0, then the return value is from 0 to MAX_UINT32 inclusively.
|
||||
uint32_t operator()(uint32_t nLimit = 0);
|
||||
|
||||
/// RandomUint32Uniform
|
||||
/// Return value is from 0 to MAX_UINT32 inclusively, with uniform probability.
|
||||
/// This is the most basic random integer generator for this class; it has no
|
||||
/// extra options but is also the fastest. Note that if you want a random
|
||||
/// integer between 0 and some value, you should use RandomUint32Uniform(nLimit)
|
||||
/// and not use RandomUint32Uniform() % nLimit. The former is both faster and
|
||||
/// more random; using % to achieve a truly random distribution fails unless
|
||||
/// nLimit is evenly divisible into MAX_UINT32.
|
||||
uint32_t RandomUint32Uniform();
|
||||
|
||||
/// RandomUint32Uniform (with limit)
|
||||
/// Return value is from 0 to nLimit-1 inclusively, with uniform probability.
|
||||
uint32_t RandomUint32Uniform(uint32_t nLimit);
|
||||
|
||||
/// RandomDoubleUniform
|
||||
/// Output is in range of [0, 1) with uniform numeric (not bit) distribution.
|
||||
double RandomDoubleUniform();
|
||||
|
||||
/// RandomDoubleUniform (with limit)
|
||||
/// Output is in range of [0, limit) with uniform numeric (not bit) distribution.
|
||||
/// limit is a value > 0.
|
||||
double RandomDoubleUniform(double limit);
|
||||
|
||||
protected:
|
||||
uint32_t mnSeed;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// RandomTaus
|
||||
///
|
||||
/// P. L'Ecuyer, "Maximally Equidistributed Combined Tausworthe Generators",
|
||||
/// Mathematics of Computation, 65, 213 (1996), 203-213.
|
||||
///
|
||||
/// RandomTaus is slower than the other EARandom generators but has only 12 bytes of
|
||||
/// state data. RandomLinearCongruental has only 4 bytes of data but is not as
|
||||
/// random as RandomTaus. RandomMersenneTwister is more random than RandomTaus but
|
||||
/// has about 2500 bytes of state data. Thus RandomTaus is a tradeoff.
|
||||
///
|
||||
/// This generator optimizes randomness and and to some degree size at the cost of speed.
|
||||
///
|
||||
class EASTDC_API RandomTaus
|
||||
{
|
||||
public:
|
||||
typedef uint32_t result_type;
|
||||
|
||||
RandomTaus(uint32_t nSeed = 0xffffffff);
|
||||
RandomTaus(const uint32_t* pSeedArray); // Array of 3 uint32_t values.
|
||||
|
||||
RandomTaus(const RandomTaus& randomT);
|
||||
RandomTaus& operator=(const RandomTaus& randomT);
|
||||
|
||||
// Single uint32_t version, for compatibility.
|
||||
// Use the seed array version for best behavior.
|
||||
// Not guaranteed to return the uint32_t set by SetSeed(uint32_t).
|
||||
uint32_t GetSeed() const;
|
||||
void SetSeed(uint32_t nSeed = 0xffffffff);
|
||||
|
||||
void GetSeed(uint32_t* pSeedArray) const; // Array of 3 uint32_t values.
|
||||
void SetSeed(const uint32_t* pSeedArray); // Array of 3 uint32_t values.
|
||||
|
||||
/// Output is in range of [0, nLimit) with uniform distribution.
|
||||
uint32_t operator()(uint32_t nLimit = 0);
|
||||
|
||||
/// Output is in range of [0, UINT32_MAX] with uniform distribution.
|
||||
uint32_t RandomUint32Uniform();
|
||||
|
||||
/// Output is in range of [0, nLimit) with uniform distribution.
|
||||
uint32_t RandomUint32Uniform(uint32_t nLimit);
|
||||
|
||||
/// Output is in range of [0, 1) with uniform numeric (not bit) distribution.
|
||||
double RandomDoubleUniform();
|
||||
|
||||
/// Output is in range of [0, limit) with uniform numeric (not bit) distribution.
|
||||
/// limit is a value > 0.
|
||||
double RandomDoubleUniform(double limit);
|
||||
|
||||
protected:
|
||||
uint32_t mState[3];
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// \class RandomMersenneTwister
|
||||
/// \brief Implements a random number generator via the Mersenne Twister algorithm.
|
||||
///
|
||||
/// This algorithm is popular for its very high degree of randomness (period of 2^19937-1
|
||||
/// with 623-dimensional equidistribution) while achieving good speed.
|
||||
///
|
||||
/// This generator optimizes randomness and to some degree speed at the cost of size.
|
||||
///
|
||||
/// Algorithm Reference:
|
||||
/// M. Matsumoto and T. Nishimura, "Mersenne Twister: A 623-Dimensionally
|
||||
/// Equidistributed Uniform Pseudo-Random Number Generator", ACM Transactions
|
||||
/// on Modeling and Computer Simulation, Vol. 8, No. 1, January 1998, pp 3-30.
|
||||
/// See http://www.math.keio.ac.jp/~matumoto/emt.html
|
||||
///
|
||||
/// The Mersenne Twister is an algorithm for generating random numbers.
|
||||
/// It was designed with consideration of the flaws in various other
|
||||
/// generators. It has a period of 2^19937-1 and the order of equidistribution
|
||||
/// of 623 dimensions. It is also quite fast; it avoids multiplication and
|
||||
/// division.
|
||||
///
|
||||
/// License:
|
||||
/// Permission of Commercial Use of Mersenne Twister
|
||||
/// We Makoto Matsumoto and Takuji Nishimura decided to
|
||||
/// let MT be used in commercial products freely.
|
||||
///
|
||||
class EASTDC_API RandomMersenneTwister
|
||||
{
|
||||
public:
|
||||
/// enum kSeedArrayCount
|
||||
/// This enum is public because it allows the user to know how much
|
||||
/// data or space to provide for the GetSeed and SetSeed functions.
|
||||
enum { kSeedArrayCount = 625 }; // 624 + 1.
|
||||
|
||||
RandomMersenneTwister(uint32_t nSeed = 0xffffffff);
|
||||
RandomMersenneTwister(const uint32_t seedArray[], unsigned nSeedArraySize);
|
||||
RandomMersenneTwister(const RandomMersenneTwister& randomMT);
|
||||
|
||||
RandomMersenneTwister& operator=(const RandomMersenneTwister& randomMT);
|
||||
|
||||
// GetSeed retrieves the current seed. The nSeedArraySize parameter should
|
||||
// how many values the seedArray holds. Normally it should be kSeedArrayCount values.
|
||||
// The return value is the number of items written to the seedArray, which will
|
||||
// be min(nSeedArraySize, kSeedArrayCount).
|
||||
unsigned GetSeed(uint32_t seedArray[], unsigned nSeedArraySize = kSeedArrayCount) const;
|
||||
|
||||
// Sets the seed to be used for random number generation. Using GetSeed and SetSeed
|
||||
// allows for saving the state of the random number generator between application runs.
|
||||
// nSeedArraySize must be at least two.
|
||||
void SetSeed(const uint32_t seedArray[], unsigned nSeedArraySize = kSeedArrayCount);
|
||||
|
||||
// This is a simple seed specification function. It will work OK for many cases but
|
||||
// doesn't provide the direct mapping of state that the other SetSeed function does.
|
||||
// A seed of 0xffffffff results in the generation of a random seed from system information.
|
||||
void SetSeed(uint32_t nSeed = 0xffffffff);
|
||||
|
||||
/// Output is in range of [0, nLimit) with uniform distribution.
|
||||
uint32_t operator()(uint32_t nLimit = 0);
|
||||
|
||||
/// Output is in range of [0, UINT32_MAX] with uniform distribution.
|
||||
uint32_t RandomUint32Uniform();
|
||||
|
||||
/// Output is in range of [0, nLimit) with uniform distribution.
|
||||
uint32_t RandomUint32Uniform(uint32_t nLimit);
|
||||
|
||||
/// Output is in range of [0, 1) with uniform numeric (not bit) distribution.
|
||||
double RandomDoubleUniform();
|
||||
|
||||
/// Output is in range of [0, limit) with uniform numeric (not bit) distribution.
|
||||
/// limit is a value > 0.
|
||||
double RandomDoubleUniform(double limit);
|
||||
|
||||
protected:
|
||||
void Reload();
|
||||
uint32_t Hash(int t, int c);
|
||||
|
||||
protected:
|
||||
enum { kStateCount = 624 };
|
||||
|
||||
uint32_t mState[kStateCount]; // State data.
|
||||
uint32_t* mpNextState; // Next state data.
|
||||
int32_t mnCountRemaining; // Count of remaining entries before reloading.
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Typedefs
|
||||
|
||||
/// class Random
|
||||
/// Default random number generator. We use the RandomLinearCongruential
|
||||
/// generator as random because for most uses it is random enough and it
|
||||
/// uses up very little space and is fairly fast.
|
||||
typedef RandomLinearCongruential Random;
|
||||
|
||||
/// class RandomSmall
|
||||
/// Implements a random number generator with a small footprint.
|
||||
/// The tradeoff is that it is not as highly random as other generators
|
||||
/// and perhaps not as fast as other generators.
|
||||
typedef RandomLinearCongruential RandomSmall;
|
||||
|
||||
/// class RandomFast
|
||||
/// Implements a random number generator optimized for speed.
|
||||
/// The tradeoff is that it is not as highly random as other generators
|
||||
/// and perhaps not as fast as other generators.
|
||||
typedef RandomLinearCongruential RandomFast;
|
||||
|
||||
/// class RandomQuality
|
||||
/// Implements a random number generator optimized for high randomness.
|
||||
/// The tradeoff for being highly random is that the space used by the
|
||||
/// class is not small.
|
||||
typedef RandomMersenneTwister RandomQuality;
|
||||
|
||||
|
||||
} // namespace StdC
|
||||
} // namespace EA
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Inlines
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace EA
|
||||
{
|
||||
namespace StdC
|
||||
{
|
||||
////////////////////////////////
|
||||
// RandomLinearCongruential //
|
||||
////////////////////////////////
|
||||
|
||||
// Inlined because an inlined implementation would be no larger than
|
||||
// non-inline implementation due to the fact that the code here is but
|
||||
// a single function call.
|
||||
inline RandomLinearCongruential::RandomLinearCongruential(uint32_t nSeed)
|
||||
{
|
||||
SetSeed(nSeed);
|
||||
}
|
||||
|
||||
|
||||
// Inlined because an inlined implementation would be no larger than
|
||||
// non-inline implementation due to the fact that the code here is but
|
||||
// a single function call.
|
||||
inline RandomLinearCongruential::RandomLinearCongruential(const RandomLinearCongruential& randomLC)
|
||||
{
|
||||
SetSeed(randomLC.GetSeed());
|
||||
}
|
||||
|
||||
|
||||
// Inlined because an inlined implementation would be no larger than
|
||||
// non-inline implementation due to the fact that the code here is but
|
||||
// a single function call.
|
||||
inline RandomLinearCongruential& RandomLinearCongruential::operator=(const RandomLinearCongruential& randomLC)
|
||||
|
||||
{
|
||||
SetSeed(randomLC.GetSeed());
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
// Inlined because an inlined implementation would be smaller than
|
||||
// a function call, as it is simply a reading of a variable.
|
||||
inline uint32_t RandomLinearCongruential::GetSeed() const
|
||||
{
|
||||
return mnSeed;
|
||||
}
|
||||
|
||||
|
||||
// Inlined because an inlined implementation would be no larger than
|
||||
// non-inline implementation due to the fact that the code here is but
|
||||
// a single function call.
|
||||
inline uint32_t RandomLinearCongruential::operator()(uint32_t nLimit)
|
||||
{
|
||||
return RandomUint32Uniform(nLimit);
|
||||
}
|
||||
|
||||
|
||||
inline uint32_t RandomLinearCongruential::RandomUint32Uniform()
|
||||
{
|
||||
// If mnSeed == 0, then we would have a problem. But in practice you
|
||||
// will never get an mnSeed of zero from an mnSeed of non-zero.
|
||||
const uint64_t nResult64 = mnSeed * (uint64_t)1103515245 + 12345;
|
||||
mnSeed = (uint32_t)nResult64;
|
||||
return (uint32_t)(nResult64 >> 16);
|
||||
}
|
||||
|
||||
|
||||
// Inlined because an inlined implementation would be hardly larger than
|
||||
// non-inline implementation due to the fact that the code here is but
|
||||
// a single function call. Since the only operation beyond the function
|
||||
// call is a multiply, we can feel safe that inlining this is a win.
|
||||
inline double RandomLinearCongruential::RandomDoubleUniform(double limit)
|
||||
{
|
||||
// For the time being, we simply return rand * limit. This is
|
||||
// however not an ideal solution because in going from a range
|
||||
// of [0,1) to a range of [0,2000) you are expanding the bit
|
||||
// information and thus not yielding all possible values
|
||||
// between 0 and 2000. At least you will have a distribution
|
||||
// that is largely still uniform between 0 and 2000. Ideally
|
||||
// we can find an algorithm that generates more possible bit
|
||||
// patterns between 0 and 2000 (for example).
|
||||
return RandomDoubleUniform() * limit;
|
||||
}
|
||||
|
||||
|
||||
|
||||
////////////////////////////////
|
||||
// RandomTaus //
|
||||
////////////////////////////////
|
||||
|
||||
inline RandomTaus::RandomTaus(uint32_t nSeed)
|
||||
{
|
||||
SetSeed(nSeed);
|
||||
}
|
||||
|
||||
inline RandomTaus::RandomTaus(const uint32_t* pSeedArray)
|
||||
{
|
||||
SetSeed(pSeedArray);
|
||||
}
|
||||
|
||||
inline RandomTaus::RandomTaus(const RandomTaus& randomT)
|
||||
{
|
||||
memcpy(mState, randomT.mState, sizeof(mState));
|
||||
}
|
||||
|
||||
inline RandomTaus& RandomTaus::operator=(const RandomTaus& randomT)
|
||||
{
|
||||
memcpy(mState, randomT.mState, sizeof(mState));
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline void RandomTaus::GetSeed(uint32_t* pSeedArray) const
|
||||
{
|
||||
memcpy(pSeedArray, mState, sizeof(mState));
|
||||
}
|
||||
|
||||
inline uint32_t RandomTaus::operator()(uint32_t nLimit)
|
||||
{
|
||||
return RandomUint32Uniform(nLimit);
|
||||
}
|
||||
|
||||
|
||||
|
||||
////////////////////////////////
|
||||
// RandomMersenneTwister //
|
||||
////////////////////////////////
|
||||
|
||||
// Inlined because an inlined implementation would be no larger than
|
||||
// non-inline implementation due to the fact that the code here is but
|
||||
// a single function call.
|
||||
inline RandomMersenneTwister::RandomMersenneTwister(const RandomMersenneTwister& randomMT)
|
||||
{
|
||||
operator=(randomMT);
|
||||
}
|
||||
|
||||
|
||||
// Inlined because an inlined implementation would be no larger than
|
||||
// non-inline implementation due to the fact that the code here is but
|
||||
// a single function call.
|
||||
inline uint32_t RandomMersenneTwister::operator()(uint32_t nLimit)
|
||||
{
|
||||
return RandomUint32Uniform(nLimit);
|
||||
}
|
||||
|
||||
|
||||
// Inlined because an inlined implementation would be no larger than
|
||||
// non-inline implementation due to the fact that the code here is but
|
||||
// a single function call.
|
||||
inline double RandomMersenneTwister::RandomDoubleUniform(double limit)
|
||||
{
|
||||
// For the time being, we simply return rand * limit. This is
|
||||
// however not an ideal solution because in going from a range
|
||||
// of [0,1) to a range of [0,2000) you are expanding the bit
|
||||
// information and thus not yielding all possible values
|
||||
// between 0 and 2000. At least you will have a distribution
|
||||
// that is largely still uniform between 0 and 2000. Ideally
|
||||
// we can find an algorithm that generates more possible bit
|
||||
// patterns between 0 and 2000 (for example).
|
||||
return RandomDoubleUniform() * limit;
|
||||
}
|
||||
|
||||
} // namespace StdC
|
||||
|
||||
} // namespace EA
|
||||
|
||||
|
||||
#endif // Header include guard
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,445 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Electronic Arts Inc. All rights reserved.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// This file implements a basic set of random number generators suitable for game
|
||||
// development usage.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
// This code includes basic random number generator functionality. It is good for
|
||||
// most game development uses with the exception of cryptographic uses and legally
|
||||
// controlled gambling mechanisms. For example, cryptographic random number generator
|
||||
// requirements have additional considerations regarding protection against
|
||||
// cryptographic attacks.
|
||||
//
|
||||
// There are many free C/C++ random number packages available today. Many of them
|
||||
// are rather comprehensive and some are fairly flexible. The design of this package
|
||||
// is based on the needs of game programmers and intentionally avoids some of the
|
||||
// complications of other packages. This package is designed first and foremost to
|
||||
// be fast and to offer at least the option of low footprint. Secondly this package
|
||||
// is designed to be very easily understood with a minimum of effort; experience has
|
||||
// shown that game programmers will not use a library unless they can figure it out
|
||||
// on their own in a matter of a few minutes. This last consideration rules out the
|
||||
// possibility of using a heavily templated library such as that found in Boost.
|
||||
//
|
||||
// Functions:
|
||||
// bool RandomBool(Random& r);
|
||||
// int32_t Random2(Random& r);
|
||||
// int32_t Random4(Random& r);
|
||||
// int32_t Random8(Random& r);
|
||||
// int32_t Random16(Random& r);
|
||||
// int32_t Random32(Random& r);
|
||||
// int32_t Random64(Random& r);
|
||||
// int32_t Random128(Random& r);
|
||||
// int32_t Random256(Random& r);
|
||||
// uint32_t RandomLimit(Random& r, uint32_t nLimit);
|
||||
// int32_t RandomPowerOfTwo(Random& r, unsigned nPowerOfTwo);
|
||||
// int32_t RandomInt32UniformRange(Random& r, int32_t nBegin, int32_t nEnd);
|
||||
// double RandomDoubleUniformRange(Random& r, double begin, double end);
|
||||
// uint32_t RandomUint32WeightedChoice(Random& r, uint32_t nLimit, float weights[]);
|
||||
// int32_t RandomInt32GaussianRange(Random& r, int32_t nBegin, int32_t nEnd);
|
||||
// Float RandomFloatGaussianRange(Random& r, Float fBegin, Float fEnd);
|
||||
// int32_t RandomInt32TriangleRange(Random& r, int32_t nBegin, int32_t nEnd);
|
||||
// Float RandomFloatTriangleRange(Random& r, Float fBegin, Float fEnd);
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#ifndef EASTDC_EARANDOMDISTRIBUTION_H
|
||||
#define EASTDC_EARANDOMDISTRIBUTION_H
|
||||
|
||||
|
||||
#include <EAStdC/internal/Config.h>
|
||||
#include <EABase/eabase.h>
|
||||
#include <EAAssert/eaassert.h>
|
||||
#include <EAStdC/EARandom.h>
|
||||
#include <math.h>
|
||||
|
||||
|
||||
namespace EA
|
||||
{
|
||||
namespace StdC
|
||||
{
|
||||
|
||||
/// RandomBool
|
||||
/// Returns true or false.
|
||||
template <typename Random>
|
||||
bool RandomBool(Random& r)
|
||||
{
|
||||
return (r.RandomUint32Uniform() & 0x80000000) != 0;
|
||||
}
|
||||
|
||||
|
||||
/// Random2
|
||||
/// Returns a value between 0 and 1, inclusively.
|
||||
template <typename Random>
|
||||
int32_t Random2(Random& r)
|
||||
{ // Don't trust the low bits, as some generators don't have good low bits.
|
||||
return (int32_t)(r.RandomUint32Uniform() >> 31);
|
||||
}
|
||||
|
||||
|
||||
/// Random4
|
||||
/// Returns a value between 0 and 3, inclusively.
|
||||
template <typename Random>
|
||||
int32_t Random4(Random& r)
|
||||
{
|
||||
return (int32_t)(r.RandomUint32Uniform() >> 30);
|
||||
}
|
||||
|
||||
|
||||
/// Random8
|
||||
/// Returns a value between 0 and 7, inclusively.
|
||||
template <typename Random>
|
||||
int32_t Random8(Random& r)
|
||||
{
|
||||
return (int32_t)(r.RandomUint32Uniform() >> 29);
|
||||
}
|
||||
|
||||
|
||||
/// Random16
|
||||
/// Returns a value between 0 and 15, inclusively.
|
||||
template <typename Random>
|
||||
int32_t Random16(Random& r)
|
||||
{
|
||||
return (int32_t)(r.RandomUint32Uniform() >> 28);
|
||||
}
|
||||
|
||||
|
||||
/// Random32
|
||||
/// Returns a value between 0 and 31, inclusively.
|
||||
template <typename Random>
|
||||
int32_t Random32(Random& r)
|
||||
{
|
||||
return (int32_t)(r.RandomUint32Uniform() >> 27);
|
||||
}
|
||||
|
||||
|
||||
/// Random64
|
||||
/// Returns a value between 0 and 63, inclusively.
|
||||
template <typename Random>
|
||||
int32_t Random64(Random& r)
|
||||
{
|
||||
return (int32_t)(r.RandomUint32Uniform() >> 26);
|
||||
}
|
||||
|
||||
|
||||
/// Random128
|
||||
/// Returns a value between 0 and 127, inclusively.
|
||||
template <typename Random>
|
||||
int32_t Random128(Random& r)
|
||||
{
|
||||
return (int32_t)(r.RandomUint32Uniform() >> 25);
|
||||
}
|
||||
|
||||
|
||||
/// Random256
|
||||
/// Returns a value between 0 and 255, inclusively.
|
||||
template <typename Random>
|
||||
int32_t Random256(Random& r)
|
||||
{
|
||||
return (int32_t)(r.RandomUint32Uniform() >> 24);
|
||||
}
|
||||
|
||||
|
||||
/// RandomLimit
|
||||
/// Return value is from 0 to nLimit-1 inclusively, with uniform probability.
|
||||
template <typename Random>
|
||||
uint32_t RandomLimit(Random& r, uint32_t nLimit)
|
||||
{
|
||||
if((nLimit & (nLimit - 1)) == 0) // If nLimit is an unsigned power of 2...
|
||||
return (uint32_t)((r.RandomUint32Uniform() * (uint64_t)nLimit) >> 32); // Scales the value from [0, MAX_UINT32] to a range of [0, nLimit).
|
||||
|
||||
uint32_t bits, returnValue;
|
||||
do
|
||||
{
|
||||
bits = r.RandomUint32Uniform();
|
||||
returnValue = (bits % nLimit);
|
||||
} // Ignore the highest bits of the representation that are remainder bits.
|
||||
while((bits + ((nLimit - 1) - returnValue) < bits)); // This is a faster way of testing that (bits < (0xFFFFFFFF - (0xFFFFFFFF % nLimit))).
|
||||
// This depends on unsigned integer wraparound occurring.
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
|
||||
/// RandomLimitFastWithBias
|
||||
/// Return value is from 0 to nLimit-1 inclusively, but with a small amount of bias towards
|
||||
/// some values. This function is not suitable for uses where you absolutely need perfectly
|
||||
/// uniform distribution, and probably not suitable for use with limits greater than ~2^20.
|
||||
/// However, this function is significantly faster than the RandomLimit function and is useful
|
||||
/// for generating many values quickly where truly uniform results aren't critical.
|
||||
/// The bias is roughly one in a billion for smaller numbers, but larger for huge numbers (e.g. numbers > 2^20).
|
||||
/// If you were to call RandomUint32Uniform(0xfffffff0) 2^32 times, you'd find that 16 of the numbers
|
||||
/// would be returned twice, while the rest would be returned once.
|
||||
template <typename Random>
|
||||
uint32_t RandomLimitFastBiased(Random& r, uint32_t nLimit)
|
||||
{
|
||||
const uint32_t nRandNoLimit = r.RandomUint32Uniform();
|
||||
const uint32_t nReturnValue = (uint32_t)((nRandNoLimit * (uint64_t)nLimit) >> 32);
|
||||
return nReturnValue;
|
||||
}
|
||||
|
||||
|
||||
/// RandomPowerOfTwo
|
||||
/// Returns a value between 0 and 2 ^ nPowerOfTwo - 1, inclusively.
|
||||
/// This is a generalized form of the RandomN set of functions.
|
||||
template <typename Random>
|
||||
int32_t RandomPowerOfTwo(Random& r, unsigned nPowerOfTwo)
|
||||
{
|
||||
//assert(nPowerOfTwo <= 32);
|
||||
return (int32_t)(r.RandomUint32Uniform() >> (32 - nPowerOfTwo));
|
||||
}
|
||||
|
||||
|
||||
/// RandomInt32UniformRange
|
||||
/// Return value is from nBegin to nEnd-1 inclusively, with uniform probability.
|
||||
template <typename Random>
|
||||
int32_t RandomInt32UniformRange(Random& r, int32_t nBegin, int32_t nEnd)
|
||||
{
|
||||
return nBegin + (int32_t)r.RandomUint32Uniform((uint32_t)(nEnd - nBegin));
|
||||
}
|
||||
|
||||
|
||||
/// RandomDoubleUniformRange
|
||||
/// Return value is in range of [nBegin, nEnd) with uniform probability.
|
||||
template <typename Random>
|
||||
double RandomDoubleUniformRange(Random& r, double begin, double end)
|
||||
{
|
||||
const double result = begin + r.RandomDoubleUniform(end - begin);
|
||||
|
||||
if(result >= end) // FPU roundoff errors can cause the result to
|
||||
return end; // go slightly outside the range. We deal with
|
||||
if(result < begin) // the possibility of this.
|
||||
return begin;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/// RandomUint32WeightedChoice
|
||||
///
|
||||
/// Return value is from 0 to nLimit-1 inclusively, with probabilities proportional to weights.
|
||||
/// The input array weights must be of length <nLimit>. These values are used to
|
||||
/// determine the probability of each choice. That is, weight[i] is proportional
|
||||
/// to the probability that this function will return i. Negative values are ignored.
|
||||
/// This function is useful in generating a custom distribution.
|
||||
///
|
||||
/// Example usage:
|
||||
/// const float weights[7] = { 128, 64, 32, 16, 8, 4, 2 }; // Create a logarithmic distribution in the range of [0, 6).
|
||||
///
|
||||
/// uint32_t x = RandomUint32WeightedChoice(random, 7, weights);
|
||||
///
|
||||
template <typename Random>
|
||||
uint32_t RandomUint32WeightedChoice(Random& r, uint32_t nLimit, const float weights[])
|
||||
{
|
||||
if(nLimit >= 2)
|
||||
{
|
||||
float weightSum = 0;
|
||||
|
||||
for(uint32_t i = 0; i < nLimit; ++i)
|
||||
{
|
||||
const float weight = weights[i];
|
||||
|
||||
if(weight > 0)
|
||||
weightSum += weight;
|
||||
}
|
||||
|
||||
if(weightSum > 0)
|
||||
{
|
||||
float value = (float)RandomDoubleUniformRange(r, 0, weightSum);
|
||||
|
||||
// Do a linear search. A binary search would be faster for
|
||||
// cases where the array size is > ~10.
|
||||
for(uint32_t j = 0; j < nLimit; ++j)
|
||||
{
|
||||
const float weight = weights[j];
|
||||
|
||||
if(weight > 0)
|
||||
{
|
||||
if(value < weight)
|
||||
return j;
|
||||
value -= weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
return r.RandomUint32Uniform(nLimit);
|
||||
}
|
||||
|
||||
// Normally we shouldn't get here, but we might due to rounding errors.
|
||||
return nLimit - 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// RandomInt32GaussianRange
|
||||
///
|
||||
/// Creates an approximation to a normal distribution (a.k.a. "Gaussian",
|
||||
/// "bell-curve") in the range of [nBegin, nEnd).
|
||||
///
|
||||
/// |
|
||||
/// | ****
|
||||
/// | * *
|
||||
/// | * *
|
||||
/// | * *
|
||||
/// | * *
|
||||
/// | * *
|
||||
/// | * *
|
||||
/// | * *
|
||||
/// | * * * *
|
||||
/// ----------------------------------------
|
||||
/// | |
|
||||
/// begin end
|
||||
///
|
||||
template <typename Random>
|
||||
int32_t RandomInt32GaussianRange(Random& r, int32_t nBegin, int32_t nEnd)
|
||||
{
|
||||
const uint32_t t0 = r.RandomUint32Uniform();
|
||||
const uint32_t t1 = r.RandomUint32Uniform();
|
||||
const uint32_t t2 = r.RandomUint32Uniform();
|
||||
const uint32_t t3 = r.RandomUint32Uniform();
|
||||
const uint64_t tcubic = ((((uint64_t)t0 + t1) + ((uint64_t)t2 + t3) + 2) >> 2);
|
||||
|
||||
return nBegin + (int32_t)((tcubic * (uint32_t)(nEnd - nBegin)) >> 32);
|
||||
}
|
||||
|
||||
|
||||
/// RandomFloatGaussianRange
|
||||
///
|
||||
/// Generates a floating point value with an approximated
|
||||
/// Guassian distribution in the range of [fBegin, fEnd).
|
||||
///
|
||||
/// Example usage:
|
||||
/// float x = RandomFloatGaussianRange(random, 0.f, 100.f);
|
||||
///
|
||||
template <typename Random, typename Float>
|
||||
Float RandomFloatGaussianRange(Random& r, Float fBegin, Float fEnd)
|
||||
{
|
||||
const Float x0 = (Float)r.RandomDoubleUniform();
|
||||
const Float x1 = (Float)r.RandomDoubleUniform();
|
||||
const Float x2 = (Float)r.RandomDoubleUniform();
|
||||
|
||||
return fBegin + ((fEnd - fBegin) * Float(0.33333333) * (x0 + x1 + x2));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// RandomInt32TriangleRange
|
||||
///
|
||||
/// Creates a "triangle" distribution in the range of [nBegin, nEnd).
|
||||
///
|
||||
/// |
|
||||
/// | *
|
||||
/// |
|
||||
/// | * *
|
||||
/// |
|
||||
/// | * *
|
||||
/// |
|
||||
/// | * *
|
||||
/// |
|
||||
/// | * *
|
||||
/// --------------------------------------
|
||||
/// | |
|
||||
/// begin end
|
||||
///
|
||||
template <typename Random>
|
||||
int32_t RandomInt32TriangleRange(Random& r, int32_t nBegin, int32_t nEnd)
|
||||
{
|
||||
const uint32_t t0 = r.RandomUint32Uniform();
|
||||
const uint32_t t1 = r.RandomUint32Uniform();
|
||||
const uint64_t ttri = (t0 >> 1) + (t1 >> 1) + (t0 & t1 & 1); // triangular from 0...2^31-1
|
||||
|
||||
return nBegin + (int32_t)((ttri * (uint32_t)(nEnd - nBegin)) >> 32);
|
||||
}
|
||||
|
||||
|
||||
/// RandomFloatTriangleRange
|
||||
///
|
||||
/// Generates a floating point value with a triangular distribution
|
||||
/// in the range of [fBegin, fEnd).
|
||||
///
|
||||
/// Example usage:
|
||||
/// double x = RandomFloatTriangleRange(random, 0.0, 100.0);
|
||||
///
|
||||
template <typename Random, typename Float>
|
||||
Float RandomFloatTriangleRange(Random& r, Float fBegin, Float fEnd)
|
||||
{
|
||||
const Float u0 = (Float)r.RandomDoubleUniform();
|
||||
const Float u1 = (Float)r.RandomDoubleUniform();
|
||||
|
||||
return fBegin + (fEnd - fBegin) * Float(0.5) * (u0 + u1);
|
||||
}
|
||||
|
||||
/// Devroye, Luc (1986). "Discrete Univariate Distributions" (PDF).
|
||||
/// Non-Uniform Random Variate Generation. New York: Springer-Verlag. p. 505.
|
||||
///
|
||||
/// Poisson generator based upon the inversion by sequential search.
|
||||
///
|
||||
/// This Poisson Random generator only works for a mean that is <= 100.
|
||||
///
|
||||
/// "x" is a uniform distributed float in the range of 0.0f and 1.0f.
|
||||
/// "mean" is the expected number of occurrences during a given interval.
|
||||
///
|
||||
inline int32_t RandomInt32Poisson(float x, float mean)
|
||||
{
|
||||
EA_ASSERT(x >= 0.0f && x <= 1.0f);
|
||||
EA_ASSERT_MSG(mean >= 0.f && mean <= 100.0f, "Poisson random generator only works for means that are <= 100.0f");
|
||||
|
||||
// clamp x value
|
||||
if (x < 0.f) x = 0.f;
|
||||
else if (x > 1.f) x = 1.f;
|
||||
|
||||
// clamp mean value
|
||||
if (mean < 0.f) mean = 0.f;
|
||||
else if (mean >= 100.f) mean = 100.f;
|
||||
|
||||
int32_t k = 0; // Counter
|
||||
const int32_t max_k = 1000; // k upper limit
|
||||
float P = (float)::exp(-mean); // probability
|
||||
float sum = P; // cumulant
|
||||
if (sum >= x) return 0; // done already
|
||||
for (k = 1; k < max_k; ++k)
|
||||
{ // Loop over all k:s
|
||||
P *= mean / (float)k; // Calc next prob
|
||||
sum += P; // Increase cumulant
|
||||
if (sum >= x) break; // Leave loop
|
||||
}
|
||||
|
||||
return k; // return random number
|
||||
}
|
||||
|
||||
|
||||
} // namespace StdC
|
||||
} // namespace EA
|
||||
|
||||
|
||||
|
||||
#endif // Header include guard
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,217 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Electronic Arts Inc. All rights reserved.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// This module implements the following functions.
|
||||
// int Cscanf(ReadFunction pReadFunction, void* pContext, const char_t* pFormat, ...);
|
||||
// int Fscanf(FILE* pFile, const char_t* pFormat, ...);
|
||||
// int Scanf(const char_t* pFormat, ...);
|
||||
// int Sscanf(char_t* pDestination, const char_t* pFormat, ...);
|
||||
//
|
||||
// int Vcscanf(ReadFunction pReadFunction, void* pContext, const char_t* pFormat, va_list arguments);
|
||||
// int Vfscanf(FILE* pFile, const char_t* pFormat, va_list arguments);
|
||||
// int Vscanf(const char_t* pFormat, va_list arguments);
|
||||
// int Vsscanf(char_t* pDestination, const char_t* pFormat, va_list arguments);
|
||||
//
|
||||
// Limitations
|
||||
// As of this writing (September 2007), the %[] modifier supports only single-byte
|
||||
// characters in the 8 bit version and supports only the first 256 characters in
|
||||
// the 16/32 bit versions. If you need support for additional characters, consult the
|
||||
// maintainer of this package and you should be able to get it within 48 hours.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#ifndef EASTDC_EASCANF_H
|
||||
#define EASTDC_EASCANF_H
|
||||
|
||||
|
||||
#include <EABase/eabase.h>
|
||||
#include <EAStdC/internal/Config.h>
|
||||
#include <EAStdC/internal/stdioEA.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push, 0)
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
namespace EA
|
||||
{
|
||||
namespace StdC
|
||||
{
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// ReadAction
|
||||
//
|
||||
enum ReadAction
|
||||
{
|
||||
kReadActionBegin, // Called upon first entering scanf. The value param will be 1 for UTF8 and 2 for UCS2. This allows the ReadFunction to take any setup actions.
|
||||
kReadActionEnd, // Called upon just exiting scanf. This allows the ReadFunction to take any cleanup actions. The value param is unused.
|
||||
kReadActionRead, // Read and return a single UCS2 Unicode character, very much like the fgetc function. Return -1 upon error or EOF; GetLastError will be used to distinguish between the two. The read function may need to convert the data source to Unicode if the data source is not UCS-encoded. The value param is unused.
|
||||
kReadActionUnread, // Push back the given UCS2 Unicode value. Return -1 upon error, 0 on success. The read function may need to convert the data source from Unicode if the data source is not UCS-encoded. The value param is unused.
|
||||
kReadActionGetAtEnd, // Return 1 if at end of data, 0 if not. The value param is unused.
|
||||
kReadActionGetLastError // Return the last file read error value. Zero means no error. The value param is unused.
|
||||
};
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// kReadError
|
||||
//
|
||||
const int kReadError = -1;
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// ReadFunction8
|
||||
//
|
||||
// This a multi-purpose callback function that's provided by the user for
|
||||
// the scanf function and similar data reading functions. It is called with
|
||||
// one of the ReadAction enumerations, and the expected behaviour depends
|
||||
// on the enumeration.
|
||||
//
|
||||
// The meaning of the value parameter depends on the ReadAction. See ReadAction
|
||||
// for documentation of each case.
|
||||
//
|
||||
// The pContext parameter is the value the user originally provided to
|
||||
// scanf or similar data reading function.
|
||||
//
|
||||
// The function is expected to convert the actual source data into individual
|
||||
// Unicode code points during kReadActionRead. For ASCII text there is no
|
||||
// conversion involved, but for multi-byte text the ReadFunction will need to
|
||||
// convert any such text to UCS Unicode.
|
||||
//
|
||||
// Returns the number of chars read on success. Returns 0 when at end of buffer.
|
||||
// Upon error, returns -1 (kReadError, same as EOF).
|
||||
//
|
||||
// The scanf functions will not use kReadActionUnread multiple times in
|
||||
// succession; only at most unread action will be outstanding. Due to the
|
||||
// specification for scanf, there is no practical way to avoid the requirement
|
||||
// of being able to unread characters.
|
||||
//
|
||||
// See the source code to the scanf function in order to see some examples
|
||||
// of ReadFunction implementations.
|
||||
//
|
||||
// UTF8 multi-byte characters should be returned as their unsigned value.
|
||||
// Thus even though char is signed, all characters are read and written
|
||||
// as if they are uint8_t. The only time a negative value should be used is
|
||||
// in the case of a -1 return value. This situation exists because it
|
||||
// exists as such with the C Standard Library, for better or worse.
|
||||
//
|
||||
typedef int (*ReadFunction8)(ReadAction readAction, int value, void* pContext);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// ReadFunction16
|
||||
//
|
||||
// This function is currently identical to ReadFunction8 with the exception
|
||||
// that kReadActionBegin will specify char16_t characters instead of char.
|
||||
//
|
||||
typedef int (*ReadFunction16)(ReadAction readAction, int value, void* pContext);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// ReadFunction32
|
||||
//
|
||||
// This function is currently identical to ReadFunction8 with the exception
|
||||
// that kReadActionBegin will specify char32_t characters instead of char.
|
||||
//
|
||||
typedef int (*ReadFunction32)(ReadAction readAction, int value, void* pContext);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// ReadFunctionW
|
||||
//
|
||||
// This function is currently identical to ReadFunction8 with the exception
|
||||
// that kReadActionBegin will specify wchar_t characters instead of char.
|
||||
//
|
||||
typedef int (*ReadFunctionW)(ReadAction readAction, int value, void* pContext);
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// EAScanf configuration parameters
|
||||
//
|
||||
#ifndef EASCANF_FIELD_MAX // Defines the maximum supported length of a field,
|
||||
#define EASCANF_FIELD_MAX 1024 // except string fields, which have no size limit.
|
||||
#endif // This value relates to the size of buffers used in the stack space.
|
||||
|
||||
#ifndef EASCANF_MS_STYLE_S_FORMAT // Microsoft uses a non-standard interpretation of the %s field type.
|
||||
#define EASCANF_MS_STYLE_S_FORMAT 1 // For wsprintf MSVC interprets %s as a wchar_t string and %S as a char string.
|
||||
#endif // You can make your code portable by using %hs and %ls to force the type.
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// Scanf
|
||||
///
|
||||
EASTDC_API int Cscanf(ReadFunction8 pReadFunction8, void* pContext, const char* pFormat, ...);
|
||||
EASTDC_API int Fscanf(FILE* pFile, const char* pFormat, ...);
|
||||
EASTDC_API int Scanf(const char* pFormat, ...);
|
||||
EASTDC_API int Sscanf(const char* pTextBuffer, const char* pFormat, ...);
|
||||
|
||||
EASTDC_API int Cscanf(ReadFunction16 pReadFunction16, void* pContext, const char16_t* pFormat, ...);
|
||||
EASTDC_API int Fscanf(FILE* pFile, const char16_t* pFormat, ...);
|
||||
EASTDC_API int Scanf(const char16_t* pFormat, ...);
|
||||
EASTDC_API int Sscanf(const char16_t* pTextBuffer, const char16_t* pFormat, ...);
|
||||
|
||||
EASTDC_API int Cscanf(ReadFunction32 pReadFunction32, void* pContext, const char32_t* pFormat, ...);
|
||||
EASTDC_API int Fscanf(FILE* pFile, const char32_t* pFormat, ...);
|
||||
EASTDC_API int Scanf(const char32_t* pFormat, ...);
|
||||
EASTDC_API int Sscanf(const char32_t* pTextBuffer, const char32_t* pFormat, ...);
|
||||
|
||||
#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
|
||||
EASTDC_API int Cscanf(ReadFunctionW pReadFunctionW, void* pContext, const wchar_t* pFormat, ...);
|
||||
EASTDC_API int Fscanf(FILE* pFile, const wchar_t* pFormat, ...);
|
||||
EASTDC_API int Scanf(const wchar_t* pFormat, ...);
|
||||
EASTDC_API int Sscanf(const wchar_t* pTextBuffer, const wchar_t* pFormat, ...);
|
||||
#endif
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// Vscanf
|
||||
///
|
||||
EASTDC_API int Vcscanf(ReadFunction8 pReadFunction8, void* pContext, const char* pFormat, va_list arguments);
|
||||
EASTDC_API int Vfscanf(FILE* pFile, const char* pFormat, va_list arguments);
|
||||
EASTDC_API int Vscanf(const char* pFormat, va_list arguments);
|
||||
EASTDC_API int Vsscanf(const char* pTextBuffer, const char* pFormat, va_list arguments);
|
||||
|
||||
EASTDC_API int Vcscanf(ReadFunction16 pReadFunction16, void* pContext, const char16_t* pFormat, va_list arguments);
|
||||
EASTDC_API int Vfscanf(FILE* pFile, const char16_t* pFormat, va_list arguments);
|
||||
EASTDC_API int Vscanf(const char16_t* pFormat, va_list arguments);
|
||||
EASTDC_API int Vsscanf(const char16_t* pTextBuffer, const char16_t* pFormat, va_list arguments);
|
||||
|
||||
EASTDC_API int Vcscanf(ReadFunction32 pReadFunction32, void* pContext, const char32_t* pFormat, va_list arguments);
|
||||
EASTDC_API int Vfscanf(FILE* pFile, const char32_t* pFormat, va_list arguments);
|
||||
EASTDC_API int Vscanf(const char32_t* pFormat, va_list arguments);
|
||||
EASTDC_API int Vsscanf(const char32_t* pTextBuffer, const char32_t* pFormat, va_list arguments);
|
||||
|
||||
#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
|
||||
EASTDC_API int Vcscanf(ReadFunctionW pReadFunctionW, void* pContext, const wchar_t* pFormat, va_list arguments);
|
||||
EASTDC_API int Vfscanf(FILE* pFile, const wchar_t* pFormat, va_list arguments);
|
||||
EASTDC_API int Vscanf(const wchar_t* pFormat, va_list arguments);
|
||||
EASTDC_API int Vsscanf(const wchar_t* pTextBuffer, const wchar_t* pFormat, va_list arguments);
|
||||
#endif
|
||||
|
||||
|
||||
} // namespace StdC
|
||||
} // namespace EA
|
||||
|
||||
|
||||
#endif // Header include guard
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Electronic Arts Inc. All rights reserved.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef EASTDC_EASINGLETON_H
|
||||
#define EASTDC_EASINGLETON_H
|
||||
|
||||
#include <EAStdC/internal/Config.h>
|
||||
#include <EAAssert/eaassert.h>
|
||||
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4127) // conditional expression is constant
|
||||
#endif
|
||||
|
||||
|
||||
/// The standard Electronic Arts namespace.
|
||||
namespace EA
|
||||
{
|
||||
namespace StdC
|
||||
{
|
||||
/// \class Singleton
|
||||
///
|
||||
/// Singleton adds singleton semantics to derived classes. It provides
|
||||
/// singleton-style instance accessors and will assert if more than one
|
||||
/// instance of the derived class is constructed.
|
||||
///
|
||||
/// \code
|
||||
/// class UniqueWidget : public EA::StdC::Singleton<UniqueWidget> { };
|
||||
/// UniqueWidget *pWidget = UniqueWidget::GetInstance();
|
||||
/// \endcode
|
||||
///
|
||||
/// \param T The classname from which to create the singleton.
|
||||
/// \param kId Multiple unique singleton instances of class \a T
|
||||
/// can be created if they are given unique \a kId
|
||||
/// numbers.
|
||||
///
|
||||
template <typename T, unsigned int kId = 0>
|
||||
class Singleton
|
||||
{
|
||||
public:
|
||||
typedef T value_type;
|
||||
typedef Singleton<T, kId> this_type;
|
||||
|
||||
/// Return the pointer to the singleton instance.
|
||||
static T* GetInstance()
|
||||
{
|
||||
return static_cast<T*>(spInstance);
|
||||
}
|
||||
|
||||
protected:
|
||||
/// Constructor
|
||||
/// The singleton instance is assigned at construction time.
|
||||
Singleton()
|
||||
{
|
||||
EA_ASSERT_FORMATTED(!spInstance, ("Singleton instance (%p) has already been created", static_cast<T*>(spInstance)));
|
||||
spInstance = this;
|
||||
}
|
||||
|
||||
/// Destructor
|
||||
/// This destructor is intentionally not marked 'virtual'. We don't
|
||||
/// want to force virtual-ness on our derived class.
|
||||
~Singleton()
|
||||
{
|
||||
spInstance = NULL;
|
||||
}
|
||||
|
||||
private:
|
||||
/// Private (disabled) copy constructor
|
||||
Singleton(const this_type&);
|
||||
|
||||
/// Private (disabled) assignment operator
|
||||
Singleton & operator=(const this_type&);
|
||||
|
||||
/// Static pointer to this singleton's instance.
|
||||
static this_type* spInstance;
|
||||
};
|
||||
|
||||
|
||||
/// \class SingletonAdapter
|
||||
///
|
||||
/// SingletonAdapter adds singleton semantics to an existing class by
|
||||
/// extending its public interface. This is useful for creating
|
||||
/// singletons from existing (and potentially externally maintained)
|
||||
/// classes without modifiying the original code directly.
|
||||
///
|
||||
/// To use this class, derive a new class with a unique name from the base
|
||||
/// class (since the static instance pointer will be unique only for this
|
||||
/// class name).
|
||||
///
|
||||
/// \code
|
||||
/// class WidgetSingleton : public EA::StdC::SingletonAdapter<Widget> { };
|
||||
/// \endcode
|
||||
///
|
||||
/// If implicit creation was requested, the singleton's instance will be
|
||||
/// created the first time it is accessed. Otherwise, CreateInstance() or
|
||||
/// SetInstance() must be called.
|
||||
///
|
||||
/// The singleton's instance can be destructed and freed using Destroy().
|
||||
///
|
||||
/// SingletonAdapter<> is not thread-safe.
|
||||
///
|
||||
/// If you're going to be using \a T exclusively through the
|
||||
/// SingletonAdapter interface, you should consider making its constructor
|
||||
/// and destructor protected members. That will ensure that instances of
|
||||
/// \a T can only be created by the SingletonAdapter layer.
|
||||
///
|
||||
/// \param T The classname from which to create the singleton.
|
||||
/// \param bImplicitCreation If implicit creation is requested, the
|
||||
/// singleton instance will be created on the first
|
||||
/// attempt to access it.
|
||||
/// \param kId Multiple unique singleton instances of class \a T
|
||||
/// can be created if they are given unique \a kId
|
||||
/// numbers.
|
||||
///
|
||||
template <typename T, bool bImplicitCreation = false, unsigned int kId = 0>
|
||||
class SingletonAdapter : public T
|
||||
{
|
||||
public:
|
||||
typedef T value_type;
|
||||
typedef SingletonAdapter<T, bImplicitCreation, kId> this_type;
|
||||
|
||||
/// Return the pointer to the singleton instance.
|
||||
/// If \a bImplicitCreation was requested, an instance will be created
|
||||
/// if one does not already exist.
|
||||
static T* GetInstance()
|
||||
{
|
||||
if (bImplicitCreation && !spInstance)
|
||||
return CreateInstance(EASTDC_ALLOC_PREFIX "SingletonAdapter");
|
||||
|
||||
return static_cast<T*>(spInstance);
|
||||
}
|
||||
|
||||
/// This allows you to manually set the instance, which is useful
|
||||
/// if you want to allocate the memory for it yourself.
|
||||
/// \return A pointer to the previous instance.
|
||||
static T* SetInstance(T* pInstance)
|
||||
{
|
||||
T* const pPrevious = spInstance;
|
||||
spInstance = pInstance;
|
||||
return pPrevious;
|
||||
}
|
||||
|
||||
/// Create this singleton's instance. If the instance has already
|
||||
/// been created, a pointer to it will simply be returned.
|
||||
/// \return A pointer to the singleton's instance.
|
||||
static T* CreateInstance(const char* pName)
|
||||
{
|
||||
if (!spInstance)
|
||||
spInstance = EASTDC_NEW(pName) SingletonAdapter;
|
||||
|
||||
return spInstance;
|
||||
}
|
||||
|
||||
/// Destroy this singleton's instance.
|
||||
static void DestroyInstance()
|
||||
{
|
||||
if (spInstance)
|
||||
{
|
||||
EASTDC_DELETE spInstance;
|
||||
spInstance = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
/// Constructor
|
||||
SingletonAdapter() { }
|
||||
|
||||
/// Destructor
|
||||
~SingletonAdapter() { }
|
||||
|
||||
private:
|
||||
/// Private (disabled) copy constructor
|
||||
SingletonAdapter(const this_type&);
|
||||
|
||||
/// Private (disabled) assignment operator
|
||||
SingletonAdapter& operator=(const this_type&);
|
||||
|
||||
/// Static pointer to this singleton's instance.
|
||||
static this_type* spInstance;
|
||||
};
|
||||
|
||||
} // namespace StdC
|
||||
} // namespace EA
|
||||
|
||||
|
||||
// Initialize the singletons' static instance pointers to NULL.
|
||||
template <typename T, unsigned int kId> EA::StdC::Singleton<T, kId> *EA::StdC::Singleton<T, kId>::spInstance = NULL;
|
||||
template <typename T, bool bImplicitCreation, unsigned int kId> EA::StdC::SingletonAdapter<T, bImplicitCreation, kId> *EA::StdC::SingletonAdapter<T, bImplicitCreation, kId>::spInstance = NULL;
|
||||
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
|
||||
#endif // Header include guard
|
||||
@@ -0,0 +1,315 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Electronic Arts Inc. All rights reserved.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// This module implements the functions listed below.
|
||||
//
|
||||
// Variable argument versions:
|
||||
// int Cprintf(WriteFunction pFunction, void* pContext, const char_t* pFormat, ...);
|
||||
// int Fprintf(FILE* pFile, const char_t* pFormat, ...);
|
||||
// int Printf(cconst char_t* pFormat, ...);
|
||||
// int Sprintf(char_t* pDestination, const char_t* pFormat, ...);
|
||||
// int Snprintf(char_t* pDestination, size_t n, const char_t* pFormat, ...);
|
||||
// int Dprintf(char_t* pDestination, size_t n, const char_t* pFormat, ...);
|
||||
// int StringPrintf(String& s, const typename String::value_type* EA_RESTRICT pFormat, ...);
|
||||
//
|
||||
// Vararg versions:
|
||||
// int Vcprintf(WriteFunction pFunction, void* pContext, const char_t* pFormat, va_list arguments);
|
||||
// int Vfprintf(FILE* pFile, const char_t* pFormat, va_list arguments);
|
||||
// int Vprintf(const char_t* pFormat, va_list arguments);
|
||||
// int Vsprintf(char_t* pDestination, const char_t* pFormat, va_list arguments);
|
||||
// int Vsnprintf(char_t* pDestination, size_t n, const char_t* pFormat, va_list arguments);
|
||||
// int Vscprintf(const char* pFormat, va_list arguments);
|
||||
// int Vdprintf(const char* pFormat, va_list arguments);
|
||||
// int StringVcprintf(String& s, const typename String::value_type* EA_RESTRICT pFormat, va_list arguments)
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#ifndef EASTDC_EASPRINTF_H
|
||||
#define EASTDC_EASPRINTF_H
|
||||
|
||||
|
||||
#include <EABase/eabase.h>
|
||||
#include <EAStdC/internal/Config.h>
|
||||
#include <EAStdC/EAScanf.h>
|
||||
#include <EAStdC/internal/stdioEA.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push, 0)
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
|
||||
namespace EA
|
||||
{
|
||||
namespace StdC
|
||||
{
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// WriteFunctionState
|
||||
//
|
||||
// This is used to help the WriteFunction know what state its being called
|
||||
// in. Our sprintf family of functions work by repeatedly calling the
|
||||
// WriteFunction until all of the string is complete. But due to platform
|
||||
// quirks and limitations, some platforms require the WriteFunction to know
|
||||
// if this is the last write, because they use it to complete a write that
|
||||
// needs to do the complete string all at once.
|
||||
//
|
||||
enum WriteFunctionState
|
||||
{
|
||||
kWFSBegin, /// Called once before any data is written, including if there will be no data written.
|
||||
kWFSIntermediate, /// Called zero or more times, with partial data. UTF8 sequences will always be whole and not split between calls.
|
||||
kWFSEnd /// Called once after any data is written, including if there was no data written.
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// WriteFunction8
|
||||
//
|
||||
// The input string for WriteFunction8 is assumed to be UTF8 or ASCII-encoded.
|
||||
// Returns number of chars actually written to the destination.
|
||||
// Returns -1 upon error.
|
||||
//
|
||||
// UTF8 sequences will always be whole and not split between calls.
|
||||
// The input pData is *not* guaranteed to be 0-terminated and won't be so for
|
||||
// some cases, as it's impractical to make it always be so due to the fact
|
||||
// that users can print truncated read-only string data memory. However, you are
|
||||
// guaranteed to be able to read the character at pData[nCount], as it will
|
||||
// always be valid readable memory. This may be useful to do because you can
|
||||
// write an optimized pathway for the case that pData is in fact 0-terminated,
|
||||
// which will often be the case.
|
||||
//
|
||||
typedef int (*WriteFunction8)(const char* EA_RESTRICT pData, size_t nCount, void* EA_RESTRICT pContext, WriteFunctionState wfs);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// WriteFunction16
|
||||
//
|
||||
// The input string is UCS2 encoded.
|
||||
// Otherwise this is the same as WriteFunction8.
|
||||
//
|
||||
typedef int (*WriteFunction16)(const char16_t* EA_RESTRICT pData, size_t nCount, void* EA_RESTRICT pContext, WriteFunctionState wfs);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// WriteFunction32
|
||||
//
|
||||
// The input string is UCS4 encoded.
|
||||
// Otherwise this is the same as WriteFunction8.
|
||||
//
|
||||
typedef int (*WriteFunction32)(const char32_t* EA_RESTRICT pData, size_t nCount, void* EA_RESTRICT pContext, WriteFunctionState wfs);
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// WriteFunctionW
|
||||
//
|
||||
// The input string is wchar_t, with wchar_t being UCS2 or UCS4-encoded, depending on its size.
|
||||
// Otherwise this is the same as WriteFunction8.
|
||||
//
|
||||
#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
|
||||
typedef int (*WriteFunctionW)(const wchar_t* EA_RESTRICT pData, size_t nCount, void* EA_RESTRICT pContext, WriteFunctionState wfs);
|
||||
#endif
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// WriteFunctionString
|
||||
//
|
||||
// This allows the implementation of a Sprintf that writes directly into
|
||||
// a C++ string class instance. This approach may be better because otherwise
|
||||
// you'd have to do an approach where you call Sprintf once to find the length,
|
||||
// resize your string, then call Sprintf again with the string address.
|
||||
// See StringSprintf below for a generic implementation of direct string writing.
|
||||
//
|
||||
template <typename String>
|
||||
int WriteFunctionString(const typename String::value_type* EA_RESTRICT pData, size_t nCount, void* EA_RESTRICT pContext, WriteFunctionState /*wfs*/)
|
||||
{
|
||||
String* pString = static_cast<String*>(pContext);
|
||||
pString->append(pData, (uint32_t)nCount);
|
||||
return (int)nCount;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// Vprintf
|
||||
///
|
||||
/// Note that vsnprintf was not added to the C standard until C99.
|
||||
/// Here we follow the C99 standard and have the return value of vsnprintf
|
||||
/// return the number of required characters to complete the formatted string.
|
||||
/// This is more useful than the way some libraries implement vsnprintf by
|
||||
/// returning -1 if there isn't enough space. The return value is -1 if there
|
||||
/// was an "encoding error" or the implementation was unable to return a
|
||||
/// value > n upon lack of sufficient space as per the C99 standard.
|
||||
///
|
||||
/// Official specification:
|
||||
/// The vsnprintf function is equivalent to snprintf, with the variable
|
||||
/// argument list replaced by arguments, which shall have been initialized
|
||||
/// by the va_start macro (and possibly subsequent va_arg calls).
|
||||
/// The vsnprintf function does not invoke the va_end macro. If copying
|
||||
/// takes place between objects that overlap, the behavior is undefined.
|
||||
///
|
||||
/// The vsnprintf function returns the number of characters that would
|
||||
/// have been written had n been sufficiently large, not counting the
|
||||
/// terminating null character, or a neg ative value if an encoding error
|
||||
/// occurred. Thus, the null-terminated output has been completely written
|
||||
/// if and only if the returned value is nonnegative and less than n.
|
||||
///
|
||||
/// The vscprintf function returns the number of chars that are needed for
|
||||
/// a printf operation. It writes nothing to any destination.
|
||||
///
|
||||
/// See also http://www.cplusplus.com/reference/clibrary/cstdio/printf.html
|
||||
///
|
||||
/// Description:
|
||||
/// Vcprintf: Print to a user-supplied WriteFunction. Can form the foundation for other printfs.
|
||||
/// Vfprintf: Print to a specific FILE, same as the C vfprintf function. Acts like Vdprintf for platforms where stdout doesn't exist (e.g. consoles) when the FILE is stdout.
|
||||
/// Vprintf: Print to the stdout FILE, same as the C vfprintf function. Acts like Vdprintf for platforms where stdout doesn't exist (e.g. consoles).
|
||||
/// Vsprintf: Print to a string, with no capacity specified, same as the C vsprintf function.
|
||||
/// Vsnprintf: Print to a string, with capacity specified, same as the C vsnprintf function.
|
||||
/// Vdprintf: Print to a debug output destination (e.g. OutputDebugString on Microsoft platforms).
|
||||
///
|
||||
EASTDC_API int Vcprintf(WriteFunction8 pWriteFunction8, void* EA_RESTRICT pContext, const char* EA_RESTRICT pFormat, va_list arguments);
|
||||
EASTDC_API int Vfprintf(FILE* EA_RESTRICT pFile, const char* EA_RESTRICT pFormat, va_list arguments);
|
||||
EASTDC_API int Vprintf(const char* EA_RESTRICT pFormat, va_list arguments);
|
||||
EASTDC_API int Vsprintf(char* EA_RESTRICT pDestination, const char* EA_RESTRICT pFormat, va_list arguments);
|
||||
EASTDC_API int Vsnprintf(char* EA_RESTRICT pDestination, size_t n, const char* EA_RESTRICT pFormat, va_list arguments);
|
||||
EASTDC_API int Vscprintf(const char* EA_RESTRICT pFormat, va_list arguments);
|
||||
EASTDC_API int Vdprintf(const char* EA_RESTRICT pFormat, va_list arguments);
|
||||
|
||||
EASTDC_API int Vcprintf(WriteFunction16 pWriteFunction16, void* EA_RESTRICT pContext, const char16_t* EA_RESTRICT pFormat, va_list arguments);
|
||||
EASTDC_API int Vfprintf(FILE* EA_RESTRICT pFile, const char16_t* EA_RESTRICT pFormat, va_list arguments);
|
||||
EASTDC_API int Vprintf(const char16_t* EA_RESTRICT pFormat, va_list arguments);
|
||||
EASTDC_API int Vsprintf(char16_t* EA_RESTRICT pDestination, const char16_t* EA_RESTRICT pFormat, va_list arguments);
|
||||
EASTDC_API int Vsnprintf(char16_t* EA_RESTRICT pDestination, size_t n, const char16_t* EA_RESTRICT pFormat, va_list arguments);
|
||||
EASTDC_API int Vscprintf(const char16_t* EA_RESTRICT pFormat, va_list arguments);
|
||||
|
||||
EASTDC_API int Vcprintf(WriteFunction32 pWriteFunction32, void* EA_RESTRICT pContext, const char32_t* EA_RESTRICT pFormat, va_list arguments);
|
||||
EASTDC_API int Vfprintf(FILE* EA_RESTRICT pFile, const char32_t* EA_RESTRICT pFormat, va_list arguments);
|
||||
EASTDC_API int Vprintf(const char32_t* EA_RESTRICT pFormat, va_list arguments);
|
||||
EASTDC_API int Vsprintf(char32_t* EA_RESTRICT pDestination, const char32_t* EA_RESTRICT pFormat, va_list arguments);
|
||||
EASTDC_API int Vsnprintf(char32_t* EA_RESTRICT pDestination, size_t n, const char32_t* EA_RESTRICT pFormat, va_list arguments);
|
||||
EASTDC_API int Vscprintf(const char32_t* EA_RESTRICT pFormat, va_list arguments);
|
||||
|
||||
#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
|
||||
EASTDC_API int Vcprintf(WriteFunctionW pWriteFunctionW, void* EA_RESTRICT pContext, const wchar_t* EA_RESTRICT pFormat, va_list arguments);
|
||||
EASTDC_API int Vfprintf(FILE* EA_RESTRICT pFile, const wchar_t* EA_RESTRICT pFormat, va_list arguments);
|
||||
EASTDC_API int Vprintf(const wchar_t* EA_RESTRICT pFormat, va_list arguments);
|
||||
EASTDC_API int Vsprintf(wchar_t* EA_RESTRICT pDestination, const wchar_t* EA_RESTRICT pFormat, va_list arguments);
|
||||
EASTDC_API int Vsnprintf(wchar_t* EA_RESTRICT pDestination, size_t n, const wchar_t* EA_RESTRICT pFormat, va_list arguments);
|
||||
EASTDC_API int Vscprintf(const wchar_t* EA_RESTRICT pFormat, va_list arguments);
|
||||
#endif
|
||||
|
||||
// StringVcprintf
|
||||
// Writes directly into a C++ string object. e.g. StringVcprintf(strObject, "%d", arguments);
|
||||
// Will be faster than eastl::string::printf() as long as the supplied string has a capacity that
|
||||
// doesn't need to keep increasing while being written into.
|
||||
template <typename String>
|
||||
int StringVcprintf(String& s, const typename String::value_type* EA_RESTRICT pFormat, va_list arguments)
|
||||
{
|
||||
return Vcprintf(WriteFunctionString<String>, &s, pFormat, arguments);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// Printf
|
||||
///
|
||||
/// The printf family implements the printf family of functions strictly to
|
||||
/// the C99 standard. As of this writing, all C99 functionality is supported
|
||||
/// except the unusual %a field type, which is treated as %g for the time being.
|
||||
/// Additionally, extra functionality such as the following field types and
|
||||
/// modifiers are supported:
|
||||
/// b Binary output field type (joins d, i, x, o, etc.). Example: printf("%b", 255) prints "11111111"
|
||||
/// I8 8 bit integer field modifier. Example: printf("%I8d", 0xff) prints "-1"
|
||||
/// I16 16 bit integer field modifier. Example: printf("%I16d", 0xffff) prints "-1"
|
||||
/// I32 32 bit integer field modifier. Example: printf("%I32d", 0xffffffff) prints "-1"
|
||||
/// I64 64 bit integer field modifier. Example: printf("%I64d", 0xffffffffffffffff) prints "-1"
|
||||
/// I128 128 bit integer field modifier. Example: printf("%I128d", 0xffffffffffffffffffffffffffffffff) prints "-1"
|
||||
/// ' Display a thousands separator. Example: printf("%'I16u", 0xffff); prints 65,535
|
||||
///
|
||||
/// See also http://www.cplusplus.com/reference/clibrary/cstdio/printf.html
|
||||
///
|
||||
/// Description:
|
||||
/// Cprintf: Print to a user-supplied WriteFunction. Can form the foundation for other printfs.
|
||||
/// Fprintf: Print to a specific FILE, same as the C fprintf function. Acts like Dprintf for platforms where stdout doesn't exist (e.g. consoles) when the FILE is stdout.
|
||||
/// Printf: Print to the stdout FILE, same as the C fprintf function. Acts like Dprintf for platforms where stdout doesn't exist (e.g. consoles).
|
||||
/// Sprintf: Print to a string, with no capacity specified, same as the C sprintf function.
|
||||
/// Snprintf: Print to a string, with capacity specified, same as the C snprintf function.
|
||||
/// Dprintf: Print to a debug output destination (e.g. OutputDebugString on Microsoft platforms).
|
||||
///
|
||||
EASTDC_API int Cprintf(WriteFunction8 pWriteFunction, void* EA_RESTRICT pContext, const char* EA_RESTRICT pFormat, ...);
|
||||
EASTDC_API int Fprintf(FILE* EA_RESTRICT pFile, const char* EA_RESTRICT pFormat, ...);
|
||||
EASTDC_API int Printf(const char* EA_RESTRICT pFormat, ...);
|
||||
EASTDC_API int Sprintf(char* EA_RESTRICT pDestination, const char* EA_RESTRICT pFormat, ...);
|
||||
EASTDC_API int Snprintf(char* EA_RESTRICT pDestination, size_t n, const char* EA_RESTRICT pFormat, ...);
|
||||
EASTDC_API int Dprintf(const char* EA_RESTRICT pFormat, ...);
|
||||
|
||||
EASTDC_API int Cprintf(WriteFunction16 pWriteFunction, void* EA_RESTRICT pContext, const char16_t* EA_RESTRICT pFormat, ...);
|
||||
EASTDC_API int Fprintf(FILE* EA_RESTRICT pFile, const char16_t* EA_RESTRICT pFormat, ...);
|
||||
EASTDC_API int Printf(const char16_t* EA_RESTRICT pFormat, ...);
|
||||
EASTDC_API int Sprintf(char16_t* EA_RESTRICT pDestination, const char16_t* EA_RESTRICT pFormat, ...);
|
||||
EASTDC_API int Snprintf(char16_t* EA_RESTRICT pDestination, size_t n, const char16_t* EA_RESTRICT pFormat, ...);
|
||||
|
||||
EASTDC_API int Cprintf(WriteFunction32 pWriteFunction, void* EA_RESTRICT pContext, const char32_t* EA_RESTRICT pFormat, ...);
|
||||
EASTDC_API int Fprintf(FILE* EA_RESTRICT pFile, const char32_t* EA_RESTRICT pFormat, ...);
|
||||
EASTDC_API int Printf(const char32_t* EA_RESTRICT pFormat, ...);
|
||||
EASTDC_API int Sprintf(char32_t* EA_RESTRICT pDestination, const char32_t* EA_RESTRICT pFormat, ...);
|
||||
EASTDC_API int Snprintf(char32_t* EA_RESTRICT pDestination, size_t n, const char32_t* EA_RESTRICT pFormat, ...);
|
||||
|
||||
#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
|
||||
EASTDC_API int Cprintf(WriteFunctionW pWriteFunction, void* EA_RESTRICT pContext, const wchar_t* EA_RESTRICT pFormat, ...);
|
||||
EASTDC_API int Fprintf(FILE* EA_RESTRICT pFile, const wchar_t* EA_RESTRICT pFormat, ...);
|
||||
EASTDC_API int Printf(const wchar_t* EA_RESTRICT pFormat, ...);
|
||||
EASTDC_API int Sprintf(wchar_t* EA_RESTRICT pDestination, const wchar_t* EA_RESTRICT pFormat, ...);
|
||||
EASTDC_API int Snprintf(wchar_t* EA_RESTRICT pDestination, size_t n, const wchar_t* EA_RESTRICT pFormat, ...);
|
||||
#endif
|
||||
|
||||
// StringPrintf
|
||||
// Writes directly into a C++ string object. e.g. StringPrintf(strObject, "%d", 37);
|
||||
// Will be faster than eastl::string::printf() as long as the supplied string has a capacity that
|
||||
// doesn't need to keep increasing while being written into.
|
||||
template <typename String>
|
||||
int StringPrintf(String& s, const typename String::value_type* EA_RESTRICT pFormat, ...)
|
||||
{
|
||||
va_list arguments;
|
||||
va_start(arguments, pFormat);
|
||||
int result = Vcprintf(WriteFunctionString<String>, &s, pFormat, arguments);
|
||||
va_end(arguments);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Deprecated functionality
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if EASTDC_VSNPRINTF8_ENABLED
|
||||
EASTDC_API int Vsnprintf8(char* EA_RESTRICT pDestination, size_t n, const char* EA_RESTRICT pFormat, va_list arguments);
|
||||
EASTDC_API int Vsnprintf16(char16_t* EA_RESTRICT pDestination, size_t n, const char16_t* EA_RESTRICT pFormat, va_list arguments);
|
||||
EASTDC_API int Vsnprintf32(char32_t* EA_RESTRICT pDestination, size_t n, const char32_t* EA_RESTRICT pFormat, va_list arguments);
|
||||
#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
|
||||
EASTDC_API int VsnprintfW(wchar_t* EA_RESTRICT pDestination, size_t n, const wchar_t* EA_RESTRICT pFormat, va_list arguments);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
} // namespace StdC
|
||||
} // namespace EA
|
||||
|
||||
|
||||
#endif // Header include guard
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Electronic Arts Inc. All rights reserved.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Ordered printf
|
||||
//
|
||||
// The module provides ordered versions of the printf family of functions:
|
||||
// int OCprintf(WriteFunction pFunction, void* pContext, const char_t* pFormat, ...);
|
||||
// int OFprintf(FILE* pFile, const char_t* pFormat, ...);
|
||||
// int OPrintf(cconst char_t* pFormat, ...);
|
||||
// int OSprintf(char_t* pDestination, const char_t* pFormat, ...);
|
||||
// int OSnprintf(char_t* pDestination, size_t n, const char_t* pFormat, ...);
|
||||
//
|
||||
// int OVcprintf(WriteFunction pFunction, void* pContext, const char_t* pFormat, va_list arguments);
|
||||
// int OVfprintf(FILE* pFile, const char_t* pFormat, va_list arguments);
|
||||
// int OVprintf(const char_t* pFormat, va_list arguments);
|
||||
// int OVsprintf(char_t* pDestination, const char_t* pFormat, va_list arguments);
|
||||
// int OVsnprintf(char_t* pDestination, size_t n, const char_t* pFormat, va_list arguments);
|
||||
// int OVscprintf(const char* pFormat, va_list arguments);
|
||||
//
|
||||
// Ordered printf is like printf except it works on "ordered" printf specifiers.
|
||||
// This means that instead of using "%4d %f" we give an order to the arguments via
|
||||
// an index and colon, as with "%1:4d %2:f". The point, however, is that you can
|
||||
// reorder the indexes, as with "%2:f %1:4d". This is particularly useful for
|
||||
// formatted string localization, as different locales use subjects and verbs
|
||||
// in different orders.
|
||||
//
|
||||
// User indexes can start at either 0 or 1. Oprintf detects which is in use as
|
||||
// it goes. So the following have identical effect:
|
||||
// OPrintf("%1:s" %0:f", 3.0, "hello");
|
||||
// OPrintf("%2:s" %1:f", 3.0, "hello");
|
||||
//
|
||||
// User indexes must be contiguous and the behaviour of Oprintf is undefined
|
||||
// if the ordering is non-contiguous. There are debug checks to validate
|
||||
// contiguity, so debug tests should catch mistakes. The following is an
|
||||
// example of non-contiguous ordering, as it is missing a "3:" format:
|
||||
// OPrintf("%1:d" %0:d %3:d", 17, 18, 19, 20);
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#ifndef EASTDC_EASPRINTFORDERED_H
|
||||
#define EASTDC_EASPRINTFORDERED_H
|
||||
|
||||
|
||||
#include <EAStdC/EASprintf.h>
|
||||
|
||||
|
||||
namespace EA
|
||||
{
|
||||
namespace StdC
|
||||
{
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// Printf
|
||||
///
|
||||
/// See EASprintf.h for documentation on the Printf family.
|
||||
/// See also http://www.cplusplus.com/reference/clibrary/cstdio/printf.html
|
||||
///
|
||||
EASTDC_API int OCprintf(WriteFunction8 pWriteFunction, void* EA_RESTRICT pContext, const char* EA_RESTRICT pFormat, ...);
|
||||
EASTDC_API int OFprintf(FILE* EA_RESTRICT pFile, const char* EA_RESTRICT pFormat, ...);
|
||||
EASTDC_API int OPrintf(const char* EA_RESTRICT pFormat, ...);
|
||||
EASTDC_API int OSprintf(char* EA_RESTRICT pDestination, const char* EA_RESTRICT pFormat, ...);
|
||||
EASTDC_API int OSnprintf(char* EA_RESTRICT pDestination, size_t n, const char* EA_RESTRICT pFormat, ...);
|
||||
|
||||
EASTDC_API int OCprintf(WriteFunction16 pWriteFunction, void* EA_RESTRICT pContext, const char16_t* EA_RESTRICT pFormat, ...);
|
||||
EASTDC_API int OFprintf(FILE* EA_RESTRICT pFile, const char16_t* EA_RESTRICT pFormat, ...);
|
||||
EASTDC_API int OPrintf(const char16_t* EA_RESTRICT pFormat, ...);
|
||||
EASTDC_API int OSprintf(char16_t* EA_RESTRICT pDestination, const char16_t* EA_RESTRICT pFormat, ...);
|
||||
EASTDC_API int OSnprintf(char16_t* EA_RESTRICT pDestination, size_t n, const char16_t* EA_RESTRICT pFormat, ...);
|
||||
|
||||
EASTDC_API int OCprintf(WriteFunction32 pWriteFunction, void* EA_RESTRICT pContext, const char32_t* EA_RESTRICT pFormat, ...);
|
||||
EASTDC_API int OFprintf(FILE* EA_RESTRICT pFile, const char32_t* EA_RESTRICT pFormat, ...);
|
||||
EASTDC_API int OPrintf(const char32_t* EA_RESTRICT pFormat, ...);
|
||||
EASTDC_API int OSprintf(char32_t* EA_RESTRICT pDestination, const char32_t* EA_RESTRICT pFormat, ...);
|
||||
EASTDC_API int OSnprintf(char32_t* EA_RESTRICT pDestination, size_t n, const char32_t* EA_RESTRICT pFormat, ...);
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// OVprintf
|
||||
///
|
||||
/// See EASprintf.h for documentation on the Vprintf family.
|
||||
/// See also http://www.cplusplus.com/reference/clibrary/cstdio/printf.html
|
||||
///
|
||||
EASTDC_API int OVcprintf(WriteFunction8 pWriteFunction8, void* EA_RESTRICT pContext, const char* EA_RESTRICT pFormat, va_list arguments);
|
||||
EASTDC_API int OVfprintf(FILE* EA_RESTRICT pFile, const char* EA_RESTRICT pFormat, va_list arguments);
|
||||
EASTDC_API int OVprintf(const char* EA_RESTRICT pFormat, va_list arguments);
|
||||
EASTDC_API int OVsprintf(char* EA_RESTRICT pDestination, const char* EA_RESTRICT pFormat, va_list arguments);
|
||||
EASTDC_API int OVsnprintf(char* EA_RESTRICT pDestination, size_t n, const char* EA_RESTRICT pFormat, va_list arguments);
|
||||
EASTDC_API int OVscprintf(const char* EA_RESTRICT pFormat, va_list arguments);
|
||||
|
||||
EASTDC_API int OVcprintf(WriteFunction16 pWriteFunction16, void* EA_RESTRICT pContext, const char16_t* EA_RESTRICT pFormat, va_list arguments);
|
||||
EASTDC_API int OVfprintf(FILE* EA_RESTRICT pFile, const char16_t* EA_RESTRICT pFormat, va_list arguments);
|
||||
EASTDC_API int OVprintf(const char16_t* EA_RESTRICT pFormat, va_list arguments);
|
||||
EASTDC_API int OVsprintf(char16_t* EA_RESTRICT pDestination, const char16_t* EA_RESTRICT pFormat, va_list arguments);
|
||||
EASTDC_API int OVsnprintf(char16_t* EA_RESTRICT pDestination, size_t n, const char16_t* EA_RESTRICT pFormat, va_list arguments);
|
||||
EASTDC_API int OVscprintf(const char16_t* EA_RESTRICT pFormat, va_list arguments);
|
||||
|
||||
EASTDC_API int OVcprintf(WriteFunction32 pWriteFunction32, void* EA_RESTRICT pContext, const char32_t* EA_RESTRICT pFormat, va_list arguments);
|
||||
EASTDC_API int OVfprintf(FILE* EA_RESTRICT pFile, const char32_t* EA_RESTRICT pFormat, va_list arguments);
|
||||
EASTDC_API int OVprintf(const char32_t* EA_RESTRICT pFormat, va_list arguments);
|
||||
EASTDC_API int OVsprintf(char32_t* EA_RESTRICT pDestination, const char32_t* EA_RESTRICT pFormat, va_list arguments);
|
||||
EASTDC_API int OVsnprintf(char32_t* EA_RESTRICT pDestination, size_t n, const char32_t* EA_RESTRICT pFormat, va_list arguments);
|
||||
EASTDC_API int OVscprintf(const char32_t* EA_RESTRICT pFormat, va_list arguments);
|
||||
|
||||
|
||||
} // namespace StdC
|
||||
} // namespace EA
|
||||
|
||||
|
||||
#endif // Header include guard
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Electronic Arts Inc. All rights reserved.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#include <EAStdC/internal/Config.h>
|
||||
|
||||
|
||||
#ifndef EASTDC_EASTDC_H
|
||||
#define EASTDC_EASTDC_H
|
||||
|
||||
|
||||
namespace EA
|
||||
{
|
||||
namespace StdC
|
||||
{
|
||||
/// Does any startup initialization that EAStdC might need. Usually this
|
||||
/// means allocating structures that can't otherwise be automatically
|
||||
/// auto-initialized (e.g. due to thread safety problems).
|
||||
/// Memory may be allocated via EASTDC_NEW within this function.
|
||||
EASTDC_API void Init();
|
||||
|
||||
/// Undoes any initialization that Init did.
|
||||
/// Memory may be freed via EASTDC_DELETE within this function.
|
||||
EASTDC_API void Shutdown();
|
||||
|
||||
|
||||
|
||||
/// SetAssertionsEnabled
|
||||
/// If enabled then debug builds execute EA_ASSERT and EA_FAIL statements
|
||||
/// for serious failures that are otherwise silent. For example, the C strtoul
|
||||
/// function silently fails by default according the the C99 language standard.
|
||||
///
|
||||
/// The assertions this applies to are assertions that check user parameters
|
||||
/// and thus detect user bugs, in particular those that could otherwise go
|
||||
/// silently undected. This doesn't apply to assertions that are internal
|
||||
/// sanity checks.
|
||||
///
|
||||
/// By default assertions are disabled (for compatibility with C99 behavior),
|
||||
/// but they also require EA_ASSERT to be enabled for the given build.
|
||||
EASTDC_API void SetAssertionsEnabled(bool enabled);
|
||||
|
||||
/// GetAssertionsEnabled
|
||||
/// Returns whether assertions are enabled, as described in SetAssertionsEnabled.
|
||||
EASTDC_API bool GetAssertionsEnabled();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // Header include guard
|
||||
|
||||
|
||||
@@ -0,0 +1,874 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Electronic Arts Inc. All rights reserved.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Implements a stopwatch-style timer. This is useful for both benchmarking
|
||||
// and for implementing runtime timing.
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#ifndef EASTDC_EASTOPWATCH_H
|
||||
#define EASTDC_EASTOPWATCH_H
|
||||
|
||||
|
||||
#include <EAStdC/internal/Config.h>
|
||||
#include <EABase/eabase.h>
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// EASTDC_STOPWATCH_USE_GETTIMEOFDAY / EASTDC_STOPWATCH_USE_CLOCK_GETTIME
|
||||
//
|
||||
// Defined as 0 or 1.
|
||||
// Identifies if we are using clock_gettime or gettimeofday for GetCPUCycle.
|
||||
//
|
||||
#if defined(EA_PLATFORM_POSIX) // Posix means Linux, Unix, and Macintosh OSX, among others (including Linux-based mobile platforms).
|
||||
#include <time.h>
|
||||
|
||||
// If using Dinkumware's standard library instead of GlibC and similar, then use clock_gettime (which is theoretically better).
|
||||
// To try to do: Use clock_gettime whenever possible in preference over gettimeofday.
|
||||
#if (defined(CLOCK_REALTIME) || defined(CLOCK_MONOTONIC))
|
||||
#define EASTDC_STOPWATCH_USE_GETTIMEOFDAY 0
|
||||
#define EASTDC_STOPWATCH_USE_CLOCK_GETTIME 1
|
||||
#else
|
||||
#define EASTDC_STOPWATCH_USE_GETTIMEOFDAY 1
|
||||
#define EASTDC_STOPWATCH_USE_CLOCK_GETTIME 0
|
||||
#endif
|
||||
#else
|
||||
#define EASTDC_STOPWATCH_USE_GETTIMEOFDAY 0
|
||||
#define EASTDC_STOPWATCH_USE_CLOCK_GETTIME 0
|
||||
#endif
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// EASTDC_STOPWATCH_DISABLE_CPU_CALIBRATION()
|
||||
///
|
||||
/// Use this macro in a module within a Win32 DLL or EXE to disable CPU
|
||||
/// calibration on startup, which takes about half a second. Doing this causes
|
||||
/// GetCPUFrequency() and CPU cycle based stopwatches to return placeholder
|
||||
/// timing, although GetCPUCycle() and stopwatch cycle based measurements are
|
||||
/// unaffected.
|
||||
///
|
||||
/// The initializer declared by this macro must be called before the default
|
||||
/// stopwatch initializer executes. In VC++, the easiest way to do this is to
|
||||
/// declare #pragma init_seg(compiler) before the auto-initializer. For this
|
||||
/// reason, it is best to place this initializer in its own .cpp file.
|
||||
///
|
||||
/// Example usage:
|
||||
/// #if defined(_MSC_VER)
|
||||
/// #pragma warning(disable: 4074)
|
||||
/// #pragma init_seg(compiler)
|
||||
/// #endif
|
||||
///
|
||||
/// EASTDC_STOPWATCH_DISABLE_CPU_CALIBRATION()
|
||||
///
|
||||
/// int main(int argc, char** argv)
|
||||
/// {
|
||||
/// . . .
|
||||
/// }
|
||||
///
|
||||
#ifndef EASTDC_STOPWATCH_DISABLE_CPU_CALIBRATION
|
||||
// To consider: provide a means for the user to #define the CPU frequency to use
|
||||
// before using this macro. Otherwise the user has to accept the default (0) or
|
||||
// would have to manually call EAStdCStopwatchDisableCPUCalibration instead of
|
||||
// using this macro.
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define EASTDC_STOPWATCH_DISABLE_CPU_CALIBRATION() \
|
||||
EASTDC_API void EAStdCStopwatchDisableCPUCalibration(uint64_t cpuFrequency = 0);\
|
||||
\
|
||||
namespace EA \
|
||||
{ \
|
||||
namespace StdC \
|
||||
{ \
|
||||
void AutoStopwatchDisableCPUCalibration() __attribute__((constructor)); \
|
||||
void AutoStopwatchDisableCPUCalibration() \
|
||||
{ \
|
||||
EAStdCStopwatchDisableCPUCalibration(0); \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
#else
|
||||
#define EASTDC_STOPWATCH_DISABLE_CPU_CALIBRATION() \
|
||||
EASTDC_API void EAStdCStopwatchDisableCPUCalibration(uint64_t cpuFrequency = 0);\
|
||||
\
|
||||
namespace EA \
|
||||
{ \
|
||||
namespace StdC \
|
||||
{ \
|
||||
struct AutoStopwatchDisableCPUCalibration \
|
||||
{ \
|
||||
AutoStopwatchDisableCPUCalibration() \
|
||||
{ EAStdCStopwatchDisableCPUCalibration(0); } \
|
||||
} gAutoStopwatchDisableCPUCalibration; \
|
||||
} \
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
namespace EA
|
||||
{
|
||||
namespace StdC
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// Stopwatch
|
||||
///
|
||||
/// The Stopwatch class acts very much like a hand-held stopwatch. You can start
|
||||
/// it, stop it, start it again, reset it, and get the elapsed time. Elapsed time
|
||||
/// acts just like a stopwatch -- if the stopwatch is running, elapsed time is the
|
||||
/// current stopwatch time; if the stopwatch is stopped, elapsed time is the
|
||||
/// time up till the stop.
|
||||
///
|
||||
/// Important things to know about Stopwatch:
|
||||
/// - There is a distinction between stopwatch cycles and CPU cycles. While it
|
||||
/// may be the case that the stopwatch uses a CPU cycle counter as its basis,
|
||||
/// this also may not be the case. In fact, using a CPU cycles counter
|
||||
/// as the basis for a stopwatch is often a dangerous thing to do because
|
||||
/// processors these days will sometimes switch frequencies on the fly.
|
||||
///
|
||||
/// - You won't get very accurate timings if you use a millisecond stopwatch
|
||||
/// repeatedly to time tiny sections of code that take only nanoseconds.
|
||||
///
|
||||
/// - You can start and stop a stopwatch at various times and it will do the
|
||||
/// right thing, which is sum up the times during which it was running.
|
||||
///
|
||||
/// - Timing CPU cycles (clock ticks) accurately can be hard if you are
|
||||
/// trying to time very small pieces of code.
|
||||
///
|
||||
/// - You don't have to stop a stopwatch that is running; it doesn't take
|
||||
/// up CPU time to be running. It is not an error to not stop a stopwatch.
|
||||
///
|
||||
/// - You don't have to worry about multi-processing issues, even when
|
||||
/// measuring clock ticks. If you start a stopwatch while your thread is
|
||||
/// running on one CPU, you can stop it while running on a different CPU.
|
||||
///
|
||||
/// - You can call GetElapsedTime while the stopwatch is running and it will
|
||||
/// act as you expect.
|
||||
///
|
||||
/// - The construction and destruction instances of Stopwatch is fast; don't
|
||||
/// worry about it. Similarly, a Stopwatch instance is small in size.
|
||||
///
|
||||
/// Example usage:
|
||||
/// Stopwatch stopwatch(Stopwatch::kUnitsMilliseconds);
|
||||
/// stopwatch.Start();
|
||||
/// DoSomethingThatYouWantToMeasure();
|
||||
/// printf("Time to do something: %u.\n", stopwatch.GetElapsedTime());
|
||||
///
|
||||
/// Example usage:
|
||||
/// Stopwatch stopwatch(Stopwatch::kUnitsCycles);
|
||||
/// stopwatch.Start();
|
||||
/// DoSomethingSmall();
|
||||
/// printf("Time to do something small: %u.\n", stopwatch.GetElapsedTime());
|
||||
/// stopwatch.Restart();
|
||||
/// DoSomethingElseSmall();
|
||||
/// printf("Time to do something else small: %d.\n", stopwatch.GetElapsedTime());
|
||||
///
|
||||
/// Example usage:
|
||||
/// Stopwatch stopwatch(Stopwatch::kUnitsMilliseconds);
|
||||
/// stopwatch.Start();
|
||||
/// stopwatch.SetElapsedTime(100);
|
||||
/// printf("This should print out 100 (or maybe 101): %u.\n", stopwatch.GetElapsedTime());
|
||||
///
|
||||
/// Don't do this!:
|
||||
/// It seems that quite often people unfamiliar with a C++ stopwatch tend to revert away from
|
||||
/// using the stopwatch as it was designed and try to do timings manually, like shown below.
|
||||
/// The code below is more complicated and is less precise, as the high internal resolution
|
||||
/// of the stopwatch is lost.
|
||||
/// Stopwatch stopwatch(Stopwatch::kUnitsMilliseconds);
|
||||
/// stopwatch.Start();
|
||||
/// uint64_t timeStart = stopwatch.GetElapsedTime();
|
||||
/// DoSomething();
|
||||
/// uint64_t timeElapsed = stopwatch.GetElapsedTime() - time;
|
||||
///
|
||||
/// Todo: Change all floats to doubles due to precision issues, especially when the Stopwatch
|
||||
/// is improperly used. Probably retain as float for PS2 platform.
|
||||
///
|
||||
class EASTDC_API Stopwatch
|
||||
{
|
||||
public:
|
||||
/// Units
|
||||
/// Defines common timing units plus a user-definable set of units.
|
||||
enum Units
|
||||
{
|
||||
kUnitsCycles = 0, /// Stopwatch clock ticks. May or may not match CPU clock ticks 1:1, depending on your hardware and operating system. Some CPUs' low level cycle count instruction counts every 16 cycles instead of every cycle.
|
||||
kUnitsCPUCycles = 1, /// CPU clock ticks (or similar equivalent for the platform). Not recommended for use in shipping softare as many systems alter their CPU frequencies at runtime.
|
||||
kUnitsNanoseconds = 2, /// For a 1GHz processor, 1 nanosecond is the same as 1 clock tick.
|
||||
kUnitsMicroseconds = 3, /// For a 1GHz processor, 1 microsecond is the same as 1,000 clock ticks.
|
||||
kUnitsMilliseconds = 4, /// For a 1GHz processor, 1 millisecond is the same as 1,000,000 clock ticks.
|
||||
kUnitsSeconds = 5, /// For a 1GHz processor, 1 second is the same as 1,000,000,000 clock ticks.
|
||||
kUnitsMinutes = 6, /// For a 1GHz processor, 1 minute is the same as 60,000,000,000 clock ticks.
|
||||
kUnitsUserDefined = 1000 /// User defined units, such as animation frames, video vertical retrace, etc.
|
||||
};
|
||||
|
||||
public:
|
||||
/// Stopwatch
|
||||
/// Constructor for Stopwatch, allows user to specify units.
|
||||
/// If units are kUnitsUserDefined, you'll need to either subclass
|
||||
/// Stopwatch and implement GetUserDefinedStopwatchCycle or call
|
||||
/// SetUserDefinedUnitsToStopwatchCyclesRatio in order to allow it
|
||||
/// to know how to convert units.
|
||||
explicit Stopwatch(int nUnits = kUnitsCycles, bool bStartImmediately = false);
|
||||
|
||||
/// Stopwatch
|
||||
/// Copy constructor.
|
||||
Stopwatch(const Stopwatch& stopwatch);
|
||||
|
||||
/// ~Stopwatch
|
||||
/// Destructor.
|
||||
~Stopwatch();
|
||||
|
||||
/// operator=
|
||||
/// Assignment operator.
|
||||
Stopwatch& operator=(const Stopwatch& stopwatch);
|
||||
|
||||
/// GetUnits
|
||||
/// Gets the current units. Returns one of enum Units or kUnitsUserDefined+.
|
||||
int GetUnits() const;
|
||||
|
||||
/// SetUnits
|
||||
/// Sets the current units. One of enum Units or kUnitsUserDefined+.
|
||||
void SetUnits(int nUnits);
|
||||
|
||||
/// Start
|
||||
/// Starts the stopwatch. Continues where it was last stopped.
|
||||
/// Does nothing if the stopwatch is already started.
|
||||
void Start();
|
||||
|
||||
/// Stop
|
||||
/// Stops the stopwatch it it was running and retaines the elasped time.
|
||||
void Stop();
|
||||
|
||||
/// Reset
|
||||
/// Stops the stopwatch if it was running and clears the elapsed time.
|
||||
void Reset();
|
||||
|
||||
/// Restart
|
||||
/// Clears the elapsed time and starts the stopwatch if it was not
|
||||
/// already running. Has the same effect as Reset(), Start().
|
||||
void Restart();
|
||||
|
||||
/// IsRunning
|
||||
/// Returns true if the stopwatch is running.
|
||||
bool IsRunning() const;
|
||||
|
||||
/// GetElapsedTime
|
||||
/// Gets the elapsed time, which properly takes into account any
|
||||
/// intervening stops and starts. Works properly whether the stopwatch
|
||||
/// is running or not.
|
||||
uint64_t GetElapsedTime() const;
|
||||
|
||||
/// SetElapsedTime
|
||||
/// Allows you to set the elapsed time. Erases whatever is current.
|
||||
/// Works properly whether the stopwatch is running or not.
|
||||
void SetElapsedTime(uint64_t nElapsedTime);
|
||||
|
||||
/// GetElapsedTimeFloat
|
||||
/// Float version, which is useful for counting fractions of
|
||||
/// seconds or possibly milliseconds.
|
||||
float GetElapsedTimeFloat() const;
|
||||
|
||||
/// SetElapsedTimeFloat
|
||||
/// Allows you to set the elapsed time. Erases whatever is current.
|
||||
/// Works properly whether the stopwatch is running or not.
|
||||
void SetElapsedTimeFloat(float fElapsedTime);
|
||||
|
||||
/// SetCyclesPerUnit
|
||||
/// Allows the user to manually override the frequency of the
|
||||
/// timer.
|
||||
void SetCyclesPerUnit(float fCyclesPerUnit);
|
||||
|
||||
/// GetCyclesPerUnit
|
||||
/// Returns the value number of cycles per unit. If the user
|
||||
/// set a manual value via SetCyclesPerUnit, this function returns
|
||||
/// that value.
|
||||
float GetCyclesPerUnit() const;
|
||||
|
||||
/// GetStopwatchCycle
|
||||
/// Gets the current stopwatch cycle on the current machine.
|
||||
/// Note that a stopwatch cycle may or may not be the same thing
|
||||
/// as a CPU cycle. We provide the distinction between stopwatch
|
||||
/// cycles and CPU cycles in order to accommodate platforms (e.g.
|
||||
/// desktop platforms) in which CPU cycle counting is unreliable.
|
||||
static uint64_t GetStopwatchCycle();
|
||||
|
||||
/// GetStopwatchFrequency
|
||||
/// Note that the stopwatch frequency may or may not be the same thing
|
||||
/// as the CPU frequency. We provide the distinction between stopwatch
|
||||
/// cycles and CPU cycles in order to accommodate platforms (e.g.
|
||||
/// desktop platforms) in which CPU cycle counting is unreliable.
|
||||
static uint64_t GetStopwatchFrequency();
|
||||
|
||||
/// GetUnitsPerStopwatchCycle
|
||||
/// Returns the number of specified units per cycle.
|
||||
/// If the unit is seconds, the return value would be the frequency of
|
||||
/// the stopwatch timer and thus be the same value as returned by
|
||||
/// GetStopwatchFrequency().
|
||||
///
|
||||
/// Example usage:
|
||||
/// const float fFrequencyInverse = Stopwatch::GetUnitsPerStopwatchCycle(Stopwatch::kUnitsSeconds);
|
||||
/// const uint64_t nStartCycle = Stopwatch::GetStopwatchCycle();
|
||||
/// <Do something>
|
||||
/// const uint64_t nElapsedTime = (Stopwatch::GetStopwatchCycle() - nStartStopwatchCycle) * fFrequencyInverse;
|
||||
///
|
||||
static float GetUnitsPerStopwatchCycle(Units units);
|
||||
|
||||
/// GetCPUCycle
|
||||
/// Gets the current CPU-based timer cycle on the current processor
|
||||
/// (if in a multiprocessor system). Note that this doesn't necessarily
|
||||
/// get the actual machine CPU clock cycle; rather it returns the
|
||||
/// CPU-based timer cycle. One some platforms the CPU-based timer is
|
||||
/// a 1:1 relation to the CPU clock, while on others it is some multiple
|
||||
/// of it.
|
||||
/// Note that on some systems you can't rely on kUnitsCycles being consistent
|
||||
/// at runtime, especially on x86 PCs with their multiple desynchronized CPUs
|
||||
/// and variable runtime clock speed.
|
||||
static uint64_t GetCPUCycle();
|
||||
|
||||
/// GetCPUFrequency
|
||||
/// Gets the frequency of the CPU-based timer. Note that this doesn't
|
||||
/// necessarily get the actual machine CPU clock frequency; rather it returns
|
||||
/// the CPU-based timer frequency. One some platforms the CPU-based timer is
|
||||
/// a 1:1 relation to the CPU clock, while on others it is some multiple of it.
|
||||
static uint64_t GetCPUFrequency();
|
||||
|
||||
/// GetUnitsPerCPUCycle
|
||||
/// Returns the number of CPU cycles per the given unit.
|
||||
/// If the unit is seconds, the return value would be the frequency
|
||||
/// of the CPU-based timer.
|
||||
///
|
||||
/// Example usage:
|
||||
/// const float fFrequencyInverse = Stopwatch::GetUnitsPerCPUCycle(Stopwatch::kUnitsSeconds);
|
||||
/// const uint64_t nStartCycle = Stopwatch::GetCPUCycle();
|
||||
/// <Do something>
|
||||
/// const uint64_t nElapsedTime = (Stopwatch::GetCPUCycle() - nStartStopwatchCycle) * fFrequencyInverse;
|
||||
///
|
||||
static float GetUnitsPerCPUCycle(Units units);
|
||||
|
||||
protected:
|
||||
uint64_t mnStartTime; /// Start time; always in cycles.
|
||||
uint64_t mnTotalElapsedTime; /// Elapsed time; always in cycles.
|
||||
int mnUnits; /// Stopwatch units. One of enum Units or kUnitsUserDefined+.
|
||||
float mfStopwatchCyclesToUnitsCoefficient; /// Coefficient is defined seconds per cycle (assuming you want to measure seconds). This is the inverse of the frequency, done this way for speed. Time passed = cycle count * coefficient.
|
||||
|
||||
}; // class Stopwatch
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// LimitStopwatch
|
||||
///
|
||||
/// Implements a stopwatch whose purpose is to tell whether a given amount of
|
||||
/// time has passed. This has the same effect as using a Stopwatch and checking
|
||||
/// that its elapsed time >= its start time plus delta time. However, it is more
|
||||
/// efficient to have a LimitStopwatch because it can precalculate the end
|
||||
/// condition and its IsTimeUp function merely compares the current tick with
|
||||
/// the end tick and thus no multiplication, division or other calculations need occur.
|
||||
///
|
||||
/// Example usage:
|
||||
/// LimitStopwatch limitStopwatch(Stopwatch::kUnitsMilliseconds, 1000, true);
|
||||
/// while(!limitStopwatch.IsTimeUp())
|
||||
/// printf("waiting\n");
|
||||
///
|
||||
/// Example usage of re-using a LimitStopwatch:
|
||||
/// LimitStopwatch limitStopwatch(Stopwatch::kUnitsSeconds);
|
||||
/// limitStopwatch.SetTimeLimit(10, true);
|
||||
/// while(!limitStopwatch.IsTimeUp()
|
||||
/// { }
|
||||
/// limitStopwatch.SetTimeLimit(10, true);
|
||||
/// while(!limitStopwatch.IsTimeUp()
|
||||
/// { }
|
||||
///
|
||||
class EASTDC_API LimitStopwatch : public Stopwatch
|
||||
{
|
||||
public:
|
||||
/// LimitStopwatch
|
||||
/// Constructor
|
||||
LimitStopwatch(int nUnits = kUnitsCycles, uint64_t nLimit = 0, bool bStartImmediately = false);
|
||||
|
||||
/// SetTimeLimit
|
||||
/// Sets the time limit and lets you start the stopwatch at the same time.
|
||||
void SetTimeLimit(uint64_t nLimit, bool bStartImmediately = false);
|
||||
|
||||
/// IsTimeUp
|
||||
/// Returns true if the limit has been reached. Highly efficient.
|
||||
bool IsTimeUp() const;
|
||||
|
||||
/// GetTimeRemaining
|
||||
/// More expensive than IsTimeUp, as it returns a value.
|
||||
int64_t GetTimeRemaining() const;
|
||||
|
||||
/// GetTimeRemainingFloat
|
||||
/// More expensive than IsTimeUp, as it returns a value.
|
||||
float GetTimeRemainingFloat() const;
|
||||
|
||||
protected:
|
||||
uint64_t mnEndTime; /// The precomputed end time used by limit timing functions.
|
||||
|
||||
}; // class LimitStopwatch
|
||||
|
||||
} // namespace StdC
|
||||
|
||||
} // namespace EA
|
||||
|
||||
|
||||
|
||||
#if defined(EA_PLATFORM_MICROSOFT) && (defined(EA_PROCESSOR_ARM) || defined(EA_PLATFORM_WINDOWS_PHONE))
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define EASTDC_WIN32_LEAN_AND_MEAN_DEFINED
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#ifdef EASTDC_WIN32_LEAN_AND_MEAN_DEFINED
|
||||
#undef EASTDC_WIN32_LEAN_AND_MEAN_DEFINED
|
||||
#undef WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
inline uint64_t EA::StdC::Stopwatch::GetCPUCycle()
|
||||
{
|
||||
LARGE_INTEGER perfCounter;
|
||||
QueryPerformanceCounter(&perfCounter);
|
||||
return static_cast<uint64_t>(perfCounter.QuadPart);
|
||||
}
|
||||
|
||||
|
||||
// x86-64/VC++
|
||||
// EA_PROCESSOR_X86 is the processor for standard Windows PCs.
|
||||
#elif defined(EA_PROCESSOR_X86_64) && defined(EA_COMPILER_MSVC)
|
||||
#pragma warning(push, 0)
|
||||
#include <math.h> // VS2008 has an acknowledged bug that requires math.h (and possibly also string.h) to be #included before intrin.h.
|
||||
#if (_MSC_VER >= 1500) // if VS2008 or later... Earlier versions of intrin.h are acknowledged as broken by Microsoft.
|
||||
#include <intrin.h>
|
||||
#else
|
||||
extern "C" unsigned __int64 __rdtsc(void);
|
||||
#pragma intrinsic(__rdtsc)
|
||||
#endif
|
||||
#pragma warning(pop)
|
||||
|
||||
#define EASTDC_CPU_FREQ_CALCULATED
|
||||
|
||||
__forceinline uint64_t EA::StdC::Stopwatch::GetCPUCycle()
|
||||
{
|
||||
return __rdtsc();
|
||||
}
|
||||
|
||||
|
||||
// x86/VC++
|
||||
// EA_PROCESSOR_X86 is the processor for standard Windows PCs.
|
||||
#elif defined(EA_PROCESSOR_X86) && defined(EA_ASM_STYLE_INTEL)
|
||||
#define EASTDC_CPU_FREQ_CALCULATED
|
||||
|
||||
#if defined(EA_COMPILER_MSVC)
|
||||
#if(EA_COMPILER_VERSION >= 1300) // VC7.0 or later
|
||||
__forceinline
|
||||
#else
|
||||
__declspec(naked) inline // A problem with __declsped(naked) under VC6 and
|
||||
#endif // earlier is that it makes a function non-inlinable.
|
||||
#else
|
||||
inline
|
||||
#endif
|
||||
|
||||
uint64_t EA::StdC::Stopwatch::GetCPUCycle()
|
||||
{
|
||||
// Note that we do not call cpuid before rdtsc to serialize the instruction queue. If you are doing tiny measurements and
|
||||
// need this serialization to prevent instructions from effectively executing after rdtsc, then use asm cpuid or use rdtscp (newer CPUs or XBox One platform).
|
||||
#if !defined(EA_COMPILER_MSVC) || (EA_COMPILER_VERSION >= 1200)
|
||||
__asm rdtsc // Read the CPU time stamp counter into edx:eax.
|
||||
#else
|
||||
__asm __emit 0x0f // 0x0f31 -> rdtsc for older compilers.
|
||||
__asm __emit 0x31
|
||||
#endif
|
||||
#if defined(EA_COMPILER_MSVC) && (EA_COMPILER_VERSION <= 1200)
|
||||
__asm ret // Problem: need to deal with stdcall vs. cdecl calling convention.
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Apple OSs
|
||||
#elif defined(EA_PLATFORM_APPLE)
|
||||
#include <mach/mach_time.h>
|
||||
inline uint64_t EA::StdC::Stopwatch::GetCPUCycle()
|
||||
{
|
||||
return mach_absolute_time();
|
||||
}
|
||||
|
||||
|
||||
#elif defined(EA_PLATFORM_SONY)
|
||||
// According to Sony, there is a possible ~100 clock tick discrepancy between cores.
|
||||
// Thus GetCPUCycle values could go "back in time" if called on two cores, one very soon
|
||||
// after the other. https://ps4.scedev.net/forums/thread/18192/
|
||||
|
||||
// sceKernelGetProcessTimeCounter is a userland function in a library and thus occurs
|
||||
// a small amount of overhead. Our measurements show the overhead is ~6 cycles
|
||||
// compared to the previously implementation which utilized rdtsc directly.
|
||||
|
||||
// Previous implementation for reference:
|
||||
//
|
||||
// inline
|
||||
// uint64_t EA::StdC::Stopwatch::GetCPUCycle()
|
||||
// {
|
||||
// // Note that we do not call cpuid before rdtsc to serialize the instruction queue. If you are doing tiny measurements
|
||||
// // and need this serialization to prevent instructions from effectively executing after rdtsc then use rdtscp.
|
||||
// // Note that as of this writing clang doesn't support __builtin_ia32_rdtsc().
|
||||
// #if EA_COMPILER_HAS_BUILTIN(__builtin_ia32_rdtsc)
|
||||
// return __builtin_ia32_rdtsc();
|
||||
// #else
|
||||
// uint32_t edxHigh32;
|
||||
// uint64_t raxLow32;
|
||||
|
||||
// asm volatile("rdtsc" : "=a" (raxLow32), "=d" (edxHigh32)); // rdtsc clears the high 32 bits of rax and so we can use result directly here.
|
||||
// return ((uint64_t)edxHigh32 << 32) | raxLow32;
|
||||
// #endif
|
||||
// }
|
||||
|
||||
#include <kernel.h>
|
||||
inline uint64_t EA::StdC::Stopwatch::GetCPUCycle()
|
||||
{
|
||||
return sceKernelGetProcessTimeCounter();
|
||||
}
|
||||
|
||||
|
||||
// x86,x86-64/GNUC or Clang
|
||||
#elif (defined(EA_PROCESSOR_X86) || defined(EA_PROCESSOR_X86_64)) && (defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_CLANG))
|
||||
#define EASTDC_CPU_FREQ_CALCULATED
|
||||
|
||||
inline
|
||||
uint64_t EA::StdC::Stopwatch::GetCPUCycle()
|
||||
{
|
||||
// Note that we do not call cpuid before rdtsc to serialize the instruction queue. If you are doing tiny measurements and
|
||||
// need this serialization to prevent instructions from effectively executing after rdtsc, then use asm cpuid or use rdtscp (newer CPUs).
|
||||
#if EA_COMPILER_HAS_BUILTIN(__builtin_ia32_rdtsc)
|
||||
return __builtin_ia32_rdtsc();
|
||||
#else
|
||||
uint32_t eaxLow32, edxHigh32;
|
||||
uint64_t result;
|
||||
|
||||
asm volatile("rdtsc" : "=a" (eaxLow32), "=d" (edxHigh32));
|
||||
result = ((uint64_t)edxHigh32 << 32) | ((uint64_t)eaxLow32);
|
||||
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
// PowerPC64 and GCC
|
||||
#elif defined(EA_PROCESSOR_POWERPC) && (EA_PLATFORM_WORD_SIZE == 8) && defined(EA_COMPILER_GNUC)
|
||||
#define EASTDC_CPU_FREQ_CALCULATED
|
||||
|
||||
// This is a generic GCC version for 64 bit PowerPC machines.
|
||||
inline uint64_t EA::StdC::Stopwatch::GetCPUCycle()
|
||||
{
|
||||
uint64_t nTimeBase;
|
||||
__asm__ __volatile__ ("mftb %0" : "=r" (nTimeBase));
|
||||
return nTimeBase;
|
||||
}
|
||||
|
||||
// PowerPC32 and GCC
|
||||
#elif defined(EA_PROCESSOR_POWERPC) && (EA_PLATFORM_WORD_SIZE == 4) && defined(EA_COMPILER_GNUC)
|
||||
#define EASTDC_CPU_FREQ_CALCULATED
|
||||
|
||||
// This is a generic GCC version for 32 bit PowerPC machines.
|
||||
// We need to loop because we are reading two registers and the high one might change.
|
||||
inline uint64_t EA::StdC::Stopwatch::GetCPUCycle()
|
||||
{
|
||||
uint32_t high1, high2, low;
|
||||
|
||||
__asm__ __volatile__
|
||||
(
|
||||
"1: mftbu %0\n"
|
||||
"mftb %1\n"
|
||||
"mftbu %2\n"
|
||||
"cmpw %3,%4\n"
|
||||
"bne- 1b\n"
|
||||
: "=r" (high1), "=r" (low), "=r" (high2)
|
||||
: "0" (high1), "2" (high2)
|
||||
);
|
||||
|
||||
return (uint64_t(high1) << 32) | low;
|
||||
}
|
||||
#elif defined(EA_PLATFORM_POSIX) // Posix means Linux, Unix, and Macintosh OSX, among others (including Linux-based mobile platforms).
|
||||
#include <time.h>
|
||||
|
||||
#if EASTDC_STOPWATCH_USE_CLOCK_GETTIME
|
||||
// http://linux.die.net/man/3/clock_gettime
|
||||
#include <errno.h>
|
||||
|
||||
inline uint64_t EA::StdC::Stopwatch::GetCPUCycle()
|
||||
{
|
||||
timespec ts;
|
||||
int result = clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
|
||||
if(result == EINVAL
|
||||
)
|
||||
result = clock_gettime(CLOCK_REALTIME, &ts);
|
||||
|
||||
const uint64_t nNanoseconds = (uint64_t)ts.tv_nsec + ((uint64_t)ts.tv_sec * UINT64_C(1000000000));
|
||||
return nNanoseconds;
|
||||
}
|
||||
|
||||
#else // EASTDC_STOPWATCH_USE_GETTIMEOFDAY
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
inline uint64_t EA::StdC::Stopwatch::GetCPUCycle()
|
||||
{
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
const uint64_t nMicroseconds = (uint64_t)tv.tv_usec + ((uint64_t)tv.tv_sec * 1000000);
|
||||
return nMicroseconds;
|
||||
}
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#error EA::StdC::Stopwatch::GetCPUCycle not implemented.
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#if defined(EA_PLATFORM_MICROSOFT) && !EASTDC_STOPWATCH_FORCE_CPU_CYCLE_USAGE
|
||||
// PC computers (x86 and otherwise) have the property whereby they change
|
||||
// CPU frequencies on the fly. As such, you cannot safely use GetCPUCycle
|
||||
// on these computers to tell you how much time has passed. This is not the
|
||||
// case with XBOX because it is specifically designed not to do this.
|
||||
// You can disable usage of QueryPerformanceCounter below by defining
|
||||
// EASTDC_STOPWATCH_FORCE_CPU_CYCLE_USAGE as 1.
|
||||
// hardcode prototype here so we don't pull in <windows.h>
|
||||
extern "C" __declspec(dllimport) int __stdcall QueryPerformanceCounter(_Out_ union _LARGE_INTEGER *lpPerformanceCount);
|
||||
|
||||
inline uint64_t EA::StdC::Stopwatch::GetStopwatchCycle()
|
||||
{
|
||||
uint64_t nReturnValue;
|
||||
QueryPerformanceCounter((union _LARGE_INTEGER*)&nReturnValue);
|
||||
return nReturnValue;
|
||||
}
|
||||
|
||||
|
||||
// Apple OSs
|
||||
#elif defined(__APPLE__)
|
||||
#include <mach/mach_time.h>
|
||||
inline uint64_t EA::StdC::Stopwatch::GetStopwatchCycle()
|
||||
{
|
||||
return mach_absolute_time();
|
||||
}
|
||||
|
||||
|
||||
#elif defined(EA_PLATFORM_SONY)
|
||||
// See the discussion above in GetCPUCycle for EA_PLATFORM_SONY.
|
||||
// Previous implementation for reference:
|
||||
//
|
||||
// inline uint64_t EA::StdC::Stopwatch::GetStopwatchCycle()
|
||||
// {
|
||||
// uint32_t eaxLow32, edxHigh32;
|
||||
// uint64_t result;
|
||||
//
|
||||
// asm volatile("rdtsc" : "=a" (eaxLow32), "=d" (edxHigh32));
|
||||
// result = ((uint64_t)edxHigh32 << 32) | ((uint64_t)eaxLow32);
|
||||
//
|
||||
// return result;
|
||||
// }
|
||||
|
||||
#include <kernel.h>
|
||||
inline uint64_t EA::StdC::Stopwatch::GetStopwatchCycle()
|
||||
{
|
||||
return sceKernelGetProcessTimeCounter();
|
||||
}
|
||||
|
||||
|
||||
#elif defined(EA_PLATFORM_POSIX)
|
||||
#include <time.h>
|
||||
|
||||
#if EASTDC_STOPWATCH_USE_CLOCK_GETTIME
|
||||
// http://linux.die.net/man/3/clock_gettime
|
||||
#include <errno.h>
|
||||
|
||||
inline uint64_t EA::StdC::Stopwatch::GetStopwatchCycle()
|
||||
{
|
||||
timespec ts;
|
||||
int result = clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
|
||||
if(result == EINVAL
|
||||
)
|
||||
result = clock_gettime(CLOCK_REALTIME, &ts);
|
||||
|
||||
const uint64_t nNanoseconds = (uint64_t)ts.tv_nsec + ((uint64_t)ts.tv_sec * UINT64_C(1000000000));
|
||||
return nNanoseconds;
|
||||
}
|
||||
|
||||
#elif EASTDC_STOPWATCH_USE_GETTIMEOFDAY
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
inline uint64_t EA::StdC::Stopwatch::GetStopwatchCycle()
|
||||
{
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
const uint64_t nMicroseconds = (uint64_t)tv.tv_usec + ((uint64_t)tv.tv_sec * 1000000);
|
||||
return nMicroseconds;
|
||||
}
|
||||
#else
|
||||
inline uint64_t EA::StdC::Stopwatch::GetStopwatchCycle()
|
||||
{
|
||||
return GetCPUCycle();
|
||||
}
|
||||
#endif
|
||||
|
||||
#else
|
||||
// Other supported processors have fixed-frequency CPUs and thus can
|
||||
// directly use the GetCPUCycle functionality for maximum precision
|
||||
// and speed.
|
||||
inline uint64_t EA::StdC::Stopwatch::GetStopwatchCycle()
|
||||
{
|
||||
return GetCPUCycle();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Inline functions
|
||||
//
|
||||
// We choose to make these functions inline because they are so small that
|
||||
// making them inline would likely have no effect on code size but would
|
||||
// significantly improve speed and possibly instruction cache friendliness.
|
||||
// Also, the PS2 compiler is picky about ordering of inline functions, so
|
||||
// the StopWatch class inlines are placed after the GetCPUCycle functions.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
inline
|
||||
EA::StdC::Stopwatch::Stopwatch(const Stopwatch& stopwatch)
|
||||
{
|
||||
mnUnits = stopwatch.mnUnits;
|
||||
mnStartTime = stopwatch.mnStartTime;
|
||||
mnTotalElapsedTime = stopwatch.mnTotalElapsedTime;
|
||||
mfStopwatchCyclesToUnitsCoefficient = stopwatch.mfStopwatchCyclesToUnitsCoefficient;
|
||||
}
|
||||
|
||||
|
||||
inline
|
||||
EA::StdC::Stopwatch::~Stopwatch()
|
||||
{
|
||||
// Empty
|
||||
}
|
||||
|
||||
|
||||
inline
|
||||
EA::StdC::Stopwatch& EA::StdC::Stopwatch::operator =(const Stopwatch& stopwatch)
|
||||
{
|
||||
mnUnits = stopwatch.mnUnits;
|
||||
mnStartTime = stopwatch.mnStartTime;
|
||||
mnTotalElapsedTime = stopwatch.mnTotalElapsedTime;
|
||||
mfStopwatchCyclesToUnitsCoefficient = stopwatch.mfStopwatchCyclesToUnitsCoefficient;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
inline
|
||||
int EA::StdC::Stopwatch::GetUnits() const
|
||||
{
|
||||
return mnUnits;
|
||||
}
|
||||
|
||||
|
||||
inline
|
||||
void EA::StdC::Stopwatch::Start()
|
||||
{
|
||||
if(!mnStartTime) // If not already started...
|
||||
{
|
||||
if(mnUnits == kUnitsCPUCycles)
|
||||
mnStartTime = GetCPUCycle();
|
||||
else
|
||||
mnStartTime = GetStopwatchCycle();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inline
|
||||
void EA::StdC::Stopwatch::Reset()
|
||||
{
|
||||
mnStartTime = 0;
|
||||
mnTotalElapsedTime = 0;
|
||||
}
|
||||
|
||||
|
||||
inline
|
||||
void EA::StdC::Stopwatch::Restart()
|
||||
{
|
||||
mnStartTime = 0;
|
||||
mnTotalElapsedTime = 0;
|
||||
Start();
|
||||
}
|
||||
|
||||
|
||||
inline
|
||||
bool EA::StdC::Stopwatch::IsRunning() const
|
||||
{
|
||||
return (mnStartTime != 0);
|
||||
}
|
||||
|
||||
|
||||
inline
|
||||
void EA::StdC::Stopwatch::SetCyclesPerUnit(float fCyclesPerUnit)
|
||||
{
|
||||
mfStopwatchCyclesToUnitsCoefficient = fCyclesPerUnit;
|
||||
}
|
||||
|
||||
|
||||
inline
|
||||
float EA::StdC::Stopwatch::GetCyclesPerUnit() const
|
||||
{
|
||||
return mfStopwatchCyclesToUnitsCoefficient;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
inline
|
||||
EA::StdC::LimitStopwatch::LimitStopwatch(int nUnits, uint64_t nLimit, bool bStartImmediately)
|
||||
: Stopwatch(nUnits, false)
|
||||
{
|
||||
SetTimeLimit(nLimit, bStartImmediately);
|
||||
}
|
||||
|
||||
|
||||
inline
|
||||
bool EA::StdC::LimitStopwatch::IsTimeUp() const
|
||||
{
|
||||
const uint64_t nCurrentTime = GetStopwatchCycle();
|
||||
return (int64_t)(mnEndTime - nCurrentTime) < 0; // We do this instead of a straight compare to deal with possible integer wraparound issues.
|
||||
}
|
||||
|
||||
|
||||
inline
|
||||
int64_t EA::StdC::LimitStopwatch::GetTimeRemaining() const
|
||||
{
|
||||
const uint64_t nCurrentTime = GetStopwatchCycle();
|
||||
const int64_t nTimeRemaining = (int64_t)((int64_t)(mnEndTime - nCurrentTime) * mfStopwatchCyclesToUnitsCoefficient);
|
||||
return nTimeRemaining;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif // header guard
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,508 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Electronic Arts Inc. All rights reserved.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#ifndef EASTDC_EATEXTUTIL_H
|
||||
#define EASTDC_EATEXTUTIL_H
|
||||
|
||||
|
||||
#include <EABase/eabase.h>
|
||||
#include <EAStdC/internal/Config.h>
|
||||
#include <EAStdC/EAString.h>
|
||||
|
||||
|
||||
namespace EA
|
||||
{
|
||||
namespace StdC
|
||||
{
|
||||
|
||||
/// kLengthNull
|
||||
/// Defines a value to be used for string conversion functions which means
|
||||
/// that the string length is specified by a wherever the terminating null
|
||||
/// character is. For the copying or converting of strings, the terminating
|
||||
/// null character is also copied to the destination.
|
||||
const size_t kLengthNull = (size_t)-1;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// UTF8 utilities
|
||||
///
|
||||
EASTDC_API bool UTF8Validate(const char* p, size_t nLength);
|
||||
EASTDC_API char* UTF8Increment(const char* p, size_t n);
|
||||
EASTDC_API char* UTF8Decrement(const char* p, size_t n);
|
||||
EASTDC_API size_t UTF8Length(const char* p);
|
||||
EASTDC_API size_t UTF8Length(const char16_t* p);
|
||||
EASTDC_API size_t UTF8Length(const char32_t* p);
|
||||
EASTDC_API size_t UTF8CharSize(const char* p);
|
||||
EASTDC_API size_t UTF8CharSize(char16_t c);
|
||||
EASTDC_API size_t UTF8CharSize(char32_t c);
|
||||
EASTDC_API char16_t UTF8ReadChar(const char* p, const char** ppEnd = NULL);
|
||||
EASTDC_API char* UTF8WriteChar(char* p, char16_t c);
|
||||
EASTDC_API char* UTF8WriteChar(char* p, char32_t c);
|
||||
EASTDC_API size_t UTF8TrimPartialChar(char* p, size_t nLength);
|
||||
EASTDC_API char* UTF8ReplaceInvalidChar(const char* pIn, size_t nLength, char* pOut, char replaceWith);
|
||||
inline bool UTF8IsSoloByte(char c) { return ((uint8_t)c < 0x80); }
|
||||
inline bool UTF8IsLeadByte(char c) { return (0xc0 <= (uint8_t)c) && ((uint8_t)c <= 0xef); } // This tests for lead bytes for 2 and 3 byte UTF8 sequences, which map to char16_t code points. If we were to support 4, 5, 6 byte code sequences (char32_t code points), we'd test for <= 0xfd.
|
||||
inline bool UTF8IsFollowByte(char c) { return (0x80 <= (uint8_t)c) && ((uint8_t)c <= 0xbf); } // This assumes that the char is part of a valid UTF8 sequence.
|
||||
|
||||
#if EA_CHAR8_UNIQUE
|
||||
// We simply forward the UTF8 overload to maintain backwards compatbility.
|
||||
// Eventually, we should leverage the type system instead of UTF8 specific functions.
|
||||
//
|
||||
inline size_t UTF8Length(const char8_t* p) { return UTF8Length((const char*)p); }
|
||||
inline char8_t* UTF8ReplaceInvalidChar(const char8_t* pIn, size_t nLength, char8_t* pOut, char8_t replaceWith)
|
||||
{ return (char8_t*)UTF8ReplaceInvalidChar((const char*)pIn, nLength, (char*)pOut, (char)replaceWith); }
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// WildcardMatch
|
||||
///
|
||||
/// These functions match source strings to wildcard patterns like those used
|
||||
/// in file specifications. '*' in the pattern means match zero or more
|
||||
/// consecutive source characters. '?' in the pattern means match exactly one
|
||||
/// source character. Here are some examples:
|
||||
///
|
||||
/// Source Pattern Result
|
||||
/// -------------------------------------------
|
||||
/// abcde *e true
|
||||
/// abcde *f false
|
||||
/// abcde ???de true
|
||||
/// abcde ????g false
|
||||
/// abcde *c?? true
|
||||
/// abcde *e?? false
|
||||
/// abcde *???? true
|
||||
/// abcde bcdef false
|
||||
/// abcde *????? true
|
||||
///
|
||||
/// Multiple * and ? characters may be used. Two consecutive * characters are
|
||||
/// treated as if they were one.
|
||||
///
|
||||
EASTDC_API bool WildcardMatch(const char* pString, const char* pPattern, bool bCaseSensitive);
|
||||
EASTDC_API bool WildcardMatch(const char16_t* pString, const char16_t* pPattern, bool bCaseSensitive);
|
||||
EASTDC_API bool WildcardMatch(const char32_t* pString, const char32_t* pPattern, bool bCaseSensitive);
|
||||
|
||||
#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
|
||||
inline bool WildcardMatch(const wchar_t* pString, const wchar_t* pPattern, bool bCaseSensitive)
|
||||
{ return WildcardMatch(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pString), EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pPattern), bCaseSensitive); }
|
||||
#endif
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// ParseDelimitedText
|
||||
///
|
||||
/// Given a line of text (e.g. like this:)
|
||||
/// 342.5, "This is a string", test, "This is a string, with a comma"
|
||||
/// This function parses it into separate fields (e.g. like this:)
|
||||
/// 342.5
|
||||
/// This is a string
|
||||
/// test
|
||||
/// This is a string, with a comma
|
||||
/// This function lets you dynamically specify the delimiter. The delimiter
|
||||
/// can be any char (e.g. space, tab, semicolon) except the quote char
|
||||
/// itself, which is reserved for the purpose of grouping. See the source
|
||||
/// code comments for more details. However, in the case of text that is
|
||||
/// UTF8-encoded, you need to make sure the delimiter char is a value
|
||||
/// that is less than 127, so as not to collide with UTF8 encoded chars.
|
||||
///
|
||||
/// The input is a pointer to text and the text length. For ASCII, MBCS, and
|
||||
/// UTF8, this is the number of bytes or chars. For UTF16 (Unicode) it is
|
||||
/// the number of characters. There are two bytes (two chars) per character
|
||||
/// in UTF16.
|
||||
///
|
||||
/// The input nTextLength can be -1 (kLengthNull) to indicate that the string
|
||||
/// is null-terminated.
|
||||
///
|
||||
/// See the other version of ParseDelimitedText below for some additional documentation.
|
||||
///
|
||||
EASTDC_API bool ParseDelimitedText(const char* pText, const char* pTextEnd, char cDelimiter,
|
||||
const char*& pToken, const char*& pTokenEnd, const char** ppNewText);
|
||||
|
||||
EASTDC_API bool ParseDelimitedText(const char16_t* pText, const char16_t* pTextEnd, char16_t cDelimiter,
|
||||
const char16_t*& pToken, const char16_t*& pTokenEnd, const char16_t** ppNewText);
|
||||
|
||||
EASTDC_API bool ParseDelimitedText(const char32_t* pText, const char32_t* pTextEnd, char32_t cDelimiter,
|
||||
const char32_t*& pToken, const char32_t*& pTokenEnd, const char32_t** ppNewText);
|
||||
|
||||
#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
|
||||
inline bool ParseDelimitedText(const wchar_t* pText, const wchar_t* pTextEnd, wchar_t cDelimiter,
|
||||
const wchar_t*& pToken, const wchar_t*& pTokenEnd, const wchar_t** ppNewText)
|
||||
{ return ParseDelimitedText(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pText), EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pTextEnd), EASTDC_UNICODE_CHAR_CAST(cDelimiter),
|
||||
EASTDC_UNICODE_CONST_CHAR_PTR_REF_CAST(pToken), EASTDC_UNICODE_CONST_CHAR_PTR_REF_CAST(pTokenEnd), EASTDC_UNICODE_CONST_CHAR_PTR_PTR_CAST(ppNewText)); }
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
/// ConvertBinaryDataToASCIIArray
|
||||
///
|
||||
/// These functions convert an array of binary characters into an encoded ASCII
|
||||
/// format that can be later converted back to binary. You might want to do this
|
||||
/// if you are trying to embed binary data into a text file (e.g. .ini file)
|
||||
/// and need a way to encode the binary data as text.
|
||||
///
|
||||
/// Example input:
|
||||
/// { 0x12, 0x34, 0x56, 0x78 }
|
||||
/// Resulting output:
|
||||
/// "12345678"
|
||||
///
|
||||
EASTDC_API void ConvertBinaryDataToASCIIArray(const void* pBinaryData, size_t nBinaryDataLength, char* pASCIIArray);
|
||||
EASTDC_API void ConvertBinaryDataToASCIIArray(const void* pBinaryData, size_t nBinaryDataLength, char16_t* pASCIIArray);
|
||||
EASTDC_API void ConvertBinaryDataToASCIIArray(const void* pBinaryData, size_t nBinaryDataLength, char32_t* pASCIIArray);
|
||||
|
||||
#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
|
||||
inline void ConvertBinaryDataToASCIIArray(const void* pBinaryData, size_t nBinaryDataLength, wchar_t* pASCIIArray)
|
||||
{ ConvertBinaryDataToASCIIArray(pBinaryData, nBinaryDataLength, EASTDC_UNICODE_CHAR_PTR_CAST(pASCIIArray)); }
|
||||
#endif
|
||||
|
||||
|
||||
/// ConvertASCIIArrayToBinaryData
|
||||
///
|
||||
/// Takes an ASCII string of text and converts it to binary data. This is the reverse
|
||||
/// of the ConvertBinaryDataToASCIIArray function. If an invalid hexidecimal character
|
||||
/// is encountered, it is replaced with a '0' character.
|
||||
/// Returns true if the input was entirely valid hexadecimal data.
|
||||
///
|
||||
/// Example input:
|
||||
/// "12345678"
|
||||
/// Resulting output:
|
||||
/// { 0x12, 0x34, 0x56, 0x78 }
|
||||
///
|
||||
EASTDC_API bool ConvertASCIIArrayToBinaryData(const char* pASCIIArray, size_t nASCIIArrayLength, void* pBinaryData);
|
||||
EASTDC_API bool ConvertASCIIArrayToBinaryData(const char16_t* pASCIIArray, size_t nASCIIArrayLength, void* pBinaryData);
|
||||
EASTDC_API bool ConvertASCIIArrayToBinaryData(const char32_t* pASCIIArray, size_t nASCIIArrayLength, void* pBinaryData);
|
||||
|
||||
#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
|
||||
inline bool ConvertASCIIArrayToBinaryData(const wchar_t* pASCIIArray, size_t nASCIIArrayLength, void* pBinaryData)
|
||||
{ return ConvertASCIIArrayToBinaryData(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pASCIIArray), nASCIIArrayLength, pBinaryData); }
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// GetTextLine
|
||||
///
|
||||
/// Given a block of text, this function reads a line of text and moves to the
|
||||
/// beginning of the next line. The return value is the end of the current line,
|
||||
/// previous to the newline characters.
|
||||
/// Lines are defined as ending in \n, \r, \r\n, or \n\r sequences.
|
||||
/// If ppNewText is supplied, it holds the start of the new line, which will often
|
||||
/// be different from the return value, as the start of the new line is after any
|
||||
/// newline characters. The length of the current line is pTextEnd - pText.
|
||||
///
|
||||
/// Example usage (assumes that :
|
||||
/// cosnt char buffer[30] = "line1\nLine2\r\nLine3\rLine4\nLine5"; // strlen(buffer) == 30.
|
||||
/// const char* pLineNext(buffer);
|
||||
/// const char* pLine;
|
||||
///
|
||||
/// do{
|
||||
/// pLine = pLineNext;
|
||||
/// const char* pLineEnd = GetTextLine(pLine, buffer + EAArrayCount(buffer), &pLineNext);
|
||||
/// // Use pLine - pLineEnd
|
||||
/// }while(pLineNext != (buffer + 256));
|
||||
///
|
||||
EASTDC_API const char* GetTextLine(const char* pText, const char* pTextEnd, const char** ppNewText);
|
||||
EASTDC_API const char16_t* GetTextLine(const char16_t* pText, const char16_t* pTextEnd, const char16_t** ppNewText);
|
||||
EASTDC_API const char32_t* GetTextLine(const char32_t* pText, const char32_t* pTextEnd, const char32_t** ppNewText);
|
||||
|
||||
#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
|
||||
inline const wchar_t* GetTextLine(const wchar_t* pText, const wchar_t* pTextEnd, const wchar_t** ppNewText)
|
||||
{ return reinterpret_cast<const wchar_t *>(GetTextLine(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pText), EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pTextEnd), EASTDC_UNICODE_CONST_CHAR_PTR_PTR_CAST(ppNewText))); }
|
||||
#endif
|
||||
|
||||
|
||||
/// GetTextLine
|
||||
///
|
||||
/// Retrieves the line in the pLine output argument. Lines are defined as ending in
|
||||
/// \n, \r, \r\n, or \n\r sequences.
|
||||
/// Returns true if there is any line to retrieve. Returns false only if sSource is empty.
|
||||
/// The retrieved pLine does not have the final newline character(s).
|
||||
/// sSource is modified to have the line removed. This gets increasingly less efficient
|
||||
/// as the length of sSource increases for the case that sSource is a typical STL-like
|
||||
/// linear string.
|
||||
/// If pLine is NULL then the behavior is the same except no copy is done to pLine.
|
||||
///
|
||||
/// Requires that String support the following STL-like data functions:
|
||||
/// data(), length(), erase(pos, count), assign(begin, end).
|
||||
///
|
||||
/// Example usage:
|
||||
/// eastl::stringW s(L"line1\nLine2\r\nLine3\rLine4\nLine5");
|
||||
/// eastl::stringW line;
|
||||
///
|
||||
/// while(GetTextLine(s, &line))
|
||||
/// printf("%ls\n", line.c_str());
|
||||
///
|
||||
template<typename String>
|
||||
bool GetTextLine(String& sSource, String* pLine);
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// SplitTokenDelimited
|
||||
///
|
||||
/// This function returns tokens that are delimited by a single character --
|
||||
/// repetitions of that character will result in empty tokens returned.
|
||||
/// This is most commonly useful when you want to parse a string of text
|
||||
/// delimited by commas or spaces.
|
||||
///
|
||||
/// Returns true whenever it extracts a token. Note however that the extracted
|
||||
/// token may be empty. Note that the return value will be true if the source
|
||||
/// has length and will be false if the source is empty.
|
||||
/// If the input pToken is non-null, the text before the delimiter is copied to it.
|
||||
///
|
||||
/// Examples (delimiter is comma):
|
||||
/// source token new source return
|
||||
/// -----------------------------------------
|
||||
/// "a,b" "a" "b" true
|
||||
/// " a , b " " a " " b " true
|
||||
/// "ab,b" "ab" "b" true
|
||||
/// ",a,b" "" "a,b" true
|
||||
/// ",b" "" "b" true
|
||||
/// ",,b" "" ",b" true
|
||||
/// ",a," "" "a," true
|
||||
/// "a," "a" "" true
|
||||
/// "," "" "" true
|
||||
/// ", " "" " " true
|
||||
/// "a" "a" "" true
|
||||
/// " " " " "" true
|
||||
/// "" "" "" false
|
||||
/// NULL "" NULL false
|
||||
///
|
||||
/// Example usage:
|
||||
/// const char16_t* pString = EA_CHAR16("a, b, c, d");
|
||||
/// char16_t pToken[16];
|
||||
///
|
||||
/// while(SplitTokenDelimited(pString, kLengthNull, ',', pToken, 16, &pString))
|
||||
/// printf("%s\n", pToken);
|
||||
///
|
||||
EASTDC_API bool SplitTokenDelimited(const char* pSource, size_t nSourceLength, char cDelimiter, char* pToken, size_t nTokenLength, const char** ppNewSource = NULL);
|
||||
EASTDC_API bool SplitTokenDelimited(const char16_t* pSource, size_t nSourceLength, char16_t cDelimiter, char16_t* pToken, size_t nTokenLength, const char16_t** ppNewSource = NULL);
|
||||
EASTDC_API bool SplitTokenDelimited(const char32_t* pSource, size_t nSourceLength, char32_t cDelimiter, char32_t* pToken, size_t nTokenLength, const char32_t** ppNewSource = NULL);
|
||||
|
||||
#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
|
||||
inline const wchar_t* SplitTokenDelimited(const wchar_t* pSource, size_t nSourceLength, wchar_t cDelimiter, wchar_t* pToken, size_t nTokenLength, const wchar_t** ppNewSource = NULL)
|
||||
{ return reinterpret_cast<const wchar_t*>(SplitTokenDelimited(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pSource), nSourceLength, EASTDC_UNICODE_CHAR_CAST(cDelimiter), EASTDC_UNICODE_CHAR_PTR_CAST(pToken), nTokenLength, EASTDC_UNICODE_CONST_CHAR_PTR_PTR_CAST(ppNewSource))); }
|
||||
#endif
|
||||
|
||||
template<typename String, typename Char>
|
||||
bool SplitTokenDelimited(String& sSource, Char cDelimiter, String* pToken);
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// SplitTokenSeparated
|
||||
///
|
||||
/// This function returns tokens that are separated by one or more instances
|
||||
/// of a character. Returns true whenever it extracts a token.
|
||||
///
|
||||
/// Examples (delimiter is space):
|
||||
/// source token new source return
|
||||
/// ---------------------------------------
|
||||
/// "a" "a" "" true
|
||||
/// "a b" "a" "b" true
|
||||
/// "a b" "a" "b" true
|
||||
/// " a b" "a" "b" true
|
||||
/// " a b " "a" "b " true
|
||||
/// " a " "a" "" true
|
||||
/// " a " "a" "" true
|
||||
/// "" "" "" false
|
||||
/// " " "" "" false
|
||||
/// NULL "" NULL false
|
||||
///
|
||||
EASTDC_API bool SplitTokenSeparated(const char* pSource, size_t nSourceLength, char cDelimiter, char* pToken, size_t nTokenLength, const char** ppNewSource = NULL);
|
||||
EASTDC_API bool SplitTokenSeparated(const char16_t* pSource, size_t nSourceLength, char16_t cDelimiter, char16_t* pToken, size_t nTokenLength, const char16_t** ppNewSource = NULL);
|
||||
EASTDC_API bool SplitTokenSeparated(const char32_t* pSource, size_t nSourceLength, char32_t cDelimiter, char32_t* pToken, size_t nTokenLength, const char32_t** ppNewSource = NULL);
|
||||
|
||||
#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE
|
||||
inline const wchar_t* SplitTokenSeparated(const wchar_t* pSource, size_t nSourceLength, wchar_t cDelimiter, wchar_t* pToken, size_t nTokenLength, const wchar_t** ppNewSource = NULL)
|
||||
{ return reinterpret_cast<const wchar_t*>(SplitTokenSeparated(EASTDC_UNICODE_CONST_CHAR_PTR_CAST(pSource), nSourceLength, EASTDC_UNICODE_CHAR_CAST(cDelimiter), EASTDC_UNICODE_CHAR_PTR_CAST(pToken), nTokenLength, EASTDC_UNICODE_CONST_CHAR_PTR_PTR_CAST(ppNewSource))); }
|
||||
#endif
|
||||
|
||||
template<typename String, typename Char>
|
||||
bool SplitTokenSeparated(String& sSource, Char c, String* pToken);
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// Boyer-Moore string search
|
||||
///
|
||||
/// This is the "turbo" implementation defined at http://www-igm.univ-mlv.fr/~lecroq/string/node14.html#SECTION00140.
|
||||
/// Boyer-Moore is a very fast string search compared to most others, including
|
||||
/// those in the STL. However, you need to be searching a string of at least 100
|
||||
/// chars and have a search pattern of at least 3 characters for the speed to show,
|
||||
/// as Boyer-Moore has a startup precalculation that costs some cycles.
|
||||
/// This startup precalculation is proportional to the size of your search pattern
|
||||
/// and the size of the alphabet in use. Thus, doing Boyer-Moore searches on the
|
||||
/// entire Unicode alphabet is going to incur a fairly expensive precalculation cost.
|
||||
///
|
||||
/// patternBuffer1 is a user-supplied buffer and must be at least as long as the search pattern.
|
||||
/// patternBuffer2 is a user-supplied buffer and must be at least as long as the search pattern.
|
||||
/// alphabetBuffer is a user-supplied buffer and must be at least as long as the highest character value used in the searched string and search pattern.
|
||||
///
|
||||
EASTDC_API int BoyerMooreSearch(const char* pPattern, int nPatternLength, const char* pSearchString, int nSearchStringLength,
|
||||
int* pPatternBuffer1, int* pPatternBuffer2, int* pAlphabetBuffer, int nAlphabetBufferSize);
|
||||
|
||||
|
||||
} // namespace StdC
|
||||
} // namespace EA
|
||||
|
||||
|
||||
|
||||
namespace EA
|
||||
{
|
||||
namespace StdC
|
||||
{
|
||||
|
||||
template<typename String>
|
||||
bool GetTextLine(String& sSource, String* pLine)
|
||||
{
|
||||
typedef typename String::value_type Char;
|
||||
|
||||
bool bReturnValue = false;
|
||||
const Char* pText = sSource.data();
|
||||
const Char* pTextEnd = pText + sSource.length();
|
||||
|
||||
if(pText < pTextEnd)
|
||||
{
|
||||
// Read until the first \r or \n.
|
||||
while((pText < pTextEnd) && (*pText != '\r') && (*pText != '\n'))
|
||||
++pText;
|
||||
|
||||
if(pLine)
|
||||
pLine->assign(sSource.data(), pText);
|
||||
|
||||
// Find the end of the line, which will be 0, 1, or 2 chars past pText.
|
||||
const Char* pTextCurrent = pText;
|
||||
|
||||
if(pTextCurrent < pTextEnd)
|
||||
{
|
||||
// Read past a second newline character, e.g. as part of a /r/n sequence.
|
||||
if((++pTextCurrent < pTextEnd) && (*pTextCurrent ^ *pText) == ('\r' ^ '\n'))
|
||||
++pTextCurrent;
|
||||
}
|
||||
|
||||
sSource.erase(0, pTextCurrent - sSource.data());
|
||||
|
||||
bReturnValue = true;
|
||||
}
|
||||
|
||||
return bReturnValue;
|
||||
}
|
||||
|
||||
|
||||
template<typename String, typename Char>
|
||||
bool SplitTokenDelimited(String& sSource, Char cDelimiter, String* pToken)
|
||||
{
|
||||
if(pToken)
|
||||
pToken->clear();
|
||||
|
||||
if(!sSource.empty())
|
||||
{
|
||||
// find the first occurence of the delimiter
|
||||
const size_t nIndex = sSource.find(cDelimiter);
|
||||
|
||||
if(nIndex == String::npos) // If we didn't find the delimiter...
|
||||
{
|
||||
if(pToken) // If we have the token
|
||||
pToken->swap(sSource); // swap it with the source string ( it will clear the source )
|
||||
else
|
||||
sSource.erase(); // simply clear the source
|
||||
}
|
||||
else
|
||||
{
|
||||
if(pToken) // If we found a delimiter - move the string before it to the token, if present
|
||||
pToken->assign(sSource, 0, (typename String::size_type)nIndex);
|
||||
|
||||
sSource.erase(0, (typename String::size_type)nIndex + 1); // remove the token + delimiter from the source
|
||||
}
|
||||
|
||||
return true; // there are tokens remaining
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename String, typename Char>
|
||||
bool SplitTokenSeparated(String& sSource, Char c, String* pToken)
|
||||
{
|
||||
// loop until we are done
|
||||
for(;;)
|
||||
{
|
||||
// look for first occurance of the separator
|
||||
const size_t nIndex1 = sSource.find(c);
|
||||
|
||||
// didn't find any
|
||||
if(nIndex1 == String::npos)
|
||||
{
|
||||
// no text available
|
||||
if(sSource.empty())
|
||||
{
|
||||
// clear token
|
||||
if(pToken)
|
||||
pToken->erase();
|
||||
|
||||
// no token found - return false
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// no separators, but we are not empty - fill the token and clear the source
|
||||
if(pToken)
|
||||
{
|
||||
// clear it
|
||||
pToken->erase();
|
||||
|
||||
// swap it with the source string ( it will clear the source )
|
||||
pToken->swap(sSource);
|
||||
}
|
||||
else
|
||||
{
|
||||
// simply clear the source
|
||||
sSource.erase();
|
||||
}
|
||||
|
||||
// token found - return true
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// find first ( or second ) occurence of a non-separator character
|
||||
const size_t nIndex2 = sSource.find_first_not_of(c, (typename String::size_type)nIndex1);
|
||||
|
||||
// if we found a non-empty string in front of separators, extract it and exit
|
||||
if(nIndex1 > 0)
|
||||
{
|
||||
// add it to the token
|
||||
if(pToken)
|
||||
pToken->assign(sSource, 0, (typename String::size_type)nIndex1);
|
||||
|
||||
// remove all we found so far from the source
|
||||
sSource.erase(0, (typename String::size_type)nIndex2);
|
||||
|
||||
// token found - return true
|
||||
return true;
|
||||
}
|
||||
|
||||
// Remove initial separator and loop back to try again
|
||||
sSource.erase(0, (typename String::size_type)nIndex2);
|
||||
}
|
||||
|
||||
// return false; // This should never get executed, but some compilers might not be smart enough to realize it.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#endif // Header include guard
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,544 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Electronic Arts Inc. All rights reserved.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Defines the following:
|
||||
// Integer data types int128_t and uint128_t
|
||||
// INT128_MIN, INT128_MAX, UINT128_MIN, UINT128_MAX
|
||||
// INT128_C, UINT128_C
|
||||
//
|
||||
// Issues:
|
||||
// * Automatic float and double conversion to int128_t is only partially supported.
|
||||
// * Some problems exist with respect to different compiler's view of what types
|
||||
// like 'long' and 'long long' are. Some see these as 32 and 64 bits respectively
|
||||
// and some don't.
|
||||
//
|
||||
// Note that GCC defines the __int128_t and __uint128_t data types when building
|
||||
// for 64 bit platforms. These types are binary-compatible with the int128_t and
|
||||
// uint128_t types defined here.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#ifndef EASTDC_INT128_H
|
||||
#define EASTDC_INT128_H
|
||||
|
||||
|
||||
#include <EAStdC/internal/Config.h>
|
||||
#include <EABase/eabase.h>
|
||||
|
||||
EA_DISABLE_GCC_WARNING(-Wtype-limits)
|
||||
#include <wchar.h>
|
||||
EA_RESTORE_GCC_WARNING()
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// EA_INT128_USE_INT64
|
||||
//
|
||||
#ifndef EA_INT128_USE_INT64
|
||||
#if (EA_PLATFORM_WORD_SIZE >= 8)
|
||||
#define EA_INT128_USE_INT64 1
|
||||
#else
|
||||
#define EA_INT128_USE_INT64 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// INT128_INT_TYPE / INT128_UINT_TYPE
|
||||
//
|
||||
// int32_t may be equivalent to int or it may be equivalent to long, depending
|
||||
// on the compiler / platform. In order for integer expressions involving
|
||||
// int128_t to work for all cases, we need to provide support for whichever
|
||||
// type is not handled by int32_t.
|
||||
|
||||
#if defined(EA_PLATFORM_MICROSOFT) // Microsoft always defines long as 32 bits, and int32_t as int.
|
||||
#define INT128_INT_TYPE long
|
||||
#define INT128_UINT_TYPE unsigned long
|
||||
#elif(EA_PLATFORM_PTR_SIZE == 4) // Other compilers define long as 32 or 64 bits, and int32_t as int and int64_t as long or long long.
|
||||
#define INT128_INT_TYPE long
|
||||
#define INT128_UINT_TYPE unsigned long
|
||||
#elif defined(__have_long64) // Some Standard C libraries use this to indicate the int64_t type.
|
||||
#define INT128_INT_TYPE long long
|
||||
#define INT128_UINT_TYPE unsigned long long
|
||||
#elif defined(__have_longlong64) // Some Standard C libraries use this to indicate the int64_t type.
|
||||
#define INT128_INT_TYPE long
|
||||
#define INT128_UINT_TYPE unsigned long
|
||||
#elif defined(EA_PLATFORM_APPLE) // Apple always sets int64_t as long long on 64 bit platforms.
|
||||
#define INT128_INT_TYPE long
|
||||
#define INT128_UINT_TYPE unsigned long
|
||||
#elif defined(EA_PLATFORM_UNIX) // Unix always sets int64_t as long on 64 bit platforms.
|
||||
#define INT128_INT_TYPE long long
|
||||
#define INT128_UINT_TYPE unsigned long long
|
||||
#elif defined(__WORDSIZE) && (__WORDSIZE == 64) // When __WORDSIZE is 64 bit, the compiler sets int64_t to be long. Except on Apple platforms.
|
||||
#define INT128_INT_TYPE long long
|
||||
#define INT128_UINT_TYPE unsigned long long
|
||||
#else
|
||||
// To do: we need to detect whether the compiler/stdlib is defining int64_t as long or long long.
|
||||
// In the case that it's using long, we use long long here. In the case that it's using long long, we use long here.
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
namespace EA
|
||||
{
|
||||
namespace StdC
|
||||
{
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Forward declarations
|
||||
//
|
||||
class int128_t_base;
|
||||
class int128_t;
|
||||
class uint128_t;
|
||||
|
||||
|
||||
/// \class int128_t_base
|
||||
/// \brief Base class upon which int128_t and uint128_t build.
|
||||
///
|
||||
class EASTDC_API int128_t_base
|
||||
{
|
||||
public:
|
||||
// Constructors / destructors
|
||||
int128_t_base();
|
||||
int128_t_base(uint32_t nPart0, uint32_t nPart1, uint32_t nPart2, uint32_t nPart3);
|
||||
int128_t_base(uint64_t nPart0, uint64_t nPart1);
|
||||
int128_t_base(uint8_t value);
|
||||
int128_t_base(uint16_t value);
|
||||
int128_t_base(uint32_t value);
|
||||
#if defined(INT128_UINT_TYPE)
|
||||
int128_t_base(INT128_UINT_TYPE value);
|
||||
#endif
|
||||
int128_t_base(uint64_t value);
|
||||
int128_t_base(const int128_t_base& value);
|
||||
|
||||
// To do: Add an int128_t_base(unsigned int) constructor just for Metrowerks,
|
||||
// as Metrowerks defines uint32_t to be unsigned long and not unsigned int.
|
||||
|
||||
// Assignment operator
|
||||
int128_t_base& operator=(const int128_t_base& value);
|
||||
|
||||
// Math operators
|
||||
static void operatorPlus (const int128_t_base& value1, const int128_t_base& value2, int128_t_base& result);
|
||||
static void operatorMinus(const int128_t_base& value1, const int128_t_base& value2, int128_t_base& result);
|
||||
static void operatorMul (const int128_t_base& value1, const int128_t_base& value2, int128_t_base& result);
|
||||
|
||||
// Shift operators
|
||||
static void operatorShiftRight(const int128_t_base& value, int nShift, int128_t_base& result);
|
||||
static void operatorShiftLeft (const int128_t_base& value, int nShift, int128_t_base& result);
|
||||
|
||||
// Unary arithmetic/logic operators
|
||||
bool operator!() const;
|
||||
|
||||
// Logical operators
|
||||
static void operatorXOR(const int128_t_base& value1, const int128_t_base& value2, int128_t_base& result);
|
||||
static void operatorOR (const int128_t_base& value1, const int128_t_base& value2, int128_t_base& result);
|
||||
static void operatorAND(const int128_t_base& value1, const int128_t_base& value2, int128_t_base& result);
|
||||
|
||||
// Operators to convert back to basic types
|
||||
// We do not provide casting operators (e.g. operator float()) because doing so would
|
||||
// cause the compiler to complian about multiple conversion choices while doing freeform
|
||||
// math. That's standard C++ behaviour and the conventional solution is to not provide
|
||||
// implicit casting operators but to provide functions such as AsFloat() to allow the
|
||||
// user to do explicit casts.
|
||||
bool AsBool() const;
|
||||
uint8_t AsUint8() const;
|
||||
uint16_t AsUint16() const;
|
||||
uint32_t AsUint32() const;
|
||||
uint64_t AsUint64() const;
|
||||
|
||||
// Misc. Functions
|
||||
// The index values below start with zero, and zero means the lowest part.
|
||||
// For example, calling SetPartUint32(3, 0x00000000) zeros the high 32 bits of the int128.
|
||||
int GetBit(int nIndex) const;
|
||||
void SetBit(int nIndex, int value);
|
||||
uint8_t GetPartUint8 (int nIndex) const;
|
||||
uint16_t GetPartUint16(int nIndex) const;
|
||||
uint32_t GetPartUint32(int nIndex) const;
|
||||
uint64_t GetPartUint64(int nIndex) const;
|
||||
void SetPartUint8 (int nIndex, uint8_t value);
|
||||
void SetPartUint16(int nIndex, uint16_t value);
|
||||
void SetPartUint32(int nIndex, uint32_t value);
|
||||
void SetPartUint64(int nIndex, uint64_t value);
|
||||
|
||||
bool IsZero() const;
|
||||
void SetZero();
|
||||
void TwosComplement();
|
||||
void InverseTwosComplement();
|
||||
|
||||
enum LeadingZeroes
|
||||
{
|
||||
kLZDefault, // Do the default for the base. By default there are leading zeroes only with base 16.
|
||||
kLZEnable,
|
||||
kLZDisable
|
||||
};
|
||||
|
||||
enum Prefix
|
||||
{
|
||||
kPrefixDefault, // Do the default for the base. By default there is a prefix only with base 16.
|
||||
kPrefixEnable,
|
||||
kPrefixDisable
|
||||
};
|
||||
|
||||
protected:
|
||||
void DoubleToUint128(double value);
|
||||
|
||||
protected:
|
||||
#if EA_INT128_USE_INT64
|
||||
#ifdef EA_SYSTEM_BIG_ENDIAN
|
||||
uint64_t mPart1; // Most significant byte.
|
||||
uint64_t mPart0; // Least significant byte.
|
||||
#else
|
||||
uint64_t mPart0; // Most significant byte.
|
||||
uint64_t mPart1; // Least significant byte.
|
||||
#endif
|
||||
#else
|
||||
#ifdef EA_SYSTEM_BIG_ENDIAN
|
||||
uint32_t mPart3; // Most significant byte.
|
||||
uint32_t mPart2;
|
||||
uint32_t mPart1;
|
||||
uint32_t mPart0; // Least significant byte.
|
||||
#else
|
||||
uint32_t mPart0; // Most significant byte.
|
||||
uint32_t mPart1;
|
||||
uint32_t mPart2;
|
||||
uint32_t mPart3; // Least significant byte.
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
/// \class int128_t
|
||||
/// \brief Implements signed 128 bit integer.
|
||||
///
|
||||
class EASTDC_API int128_t : public int128_t_base
|
||||
{
|
||||
public:
|
||||
// Constructors / destructors
|
||||
int128_t();
|
||||
int128_t(uint32_t nPart0, uint32_t nPart1, uint32_t nPart2, uint32_t nPart3);
|
||||
int128_t(uint64_t nPart0, uint64_t nPart1);
|
||||
|
||||
int128_t(int8_t value);
|
||||
int128_t(uint8_t value);
|
||||
|
||||
int128_t(int16_t value);
|
||||
int128_t(uint16_t value);
|
||||
|
||||
int128_t(int32_t value);
|
||||
int128_t(uint32_t value);
|
||||
|
||||
#if defined(INT128_INT_TYPE)
|
||||
int128_t(INT128_INT_TYPE value);
|
||||
int128_t(INT128_UINT_TYPE value);
|
||||
#endif
|
||||
|
||||
int128_t(int64_t value);
|
||||
int128_t(uint64_t value);
|
||||
|
||||
int128_t(const int128_t& value);
|
||||
//int128_t(const uint128_t& value); // Not defined because doing so would make the compiler unable to decide how to choose binary functions involving int128/uint128.
|
||||
|
||||
int128_t(const float value);
|
||||
int128_t(const double value);
|
||||
|
||||
int128_t(const char* pValue, int nBase = 10);
|
||||
int128_t(const wchar_t* pValue, int nBase = 10);
|
||||
|
||||
// To do: Add an int128_t(int) constructor just for Metrowerks,
|
||||
// as Metrowerks defines uint32_t to be unsigned long and not unsigned int.
|
||||
|
||||
// Assignment operator
|
||||
int128_t& operator=(const int128_t_base& value);
|
||||
|
||||
// Unary arithmetic/logic operators
|
||||
int128_t operator-() const;
|
||||
int128_t& operator++();
|
||||
int128_t& operator--();
|
||||
int128_t operator++(int);
|
||||
int128_t operator--(int);
|
||||
int128_t operator~() const;
|
||||
int128_t operator+() const;
|
||||
|
||||
// Math operators
|
||||
friend EASTDC_API int128_t operator+(const int128_t& value1, const int128_t& value2);
|
||||
friend EASTDC_API int128_t operator-(const int128_t& value1, const int128_t& value2);
|
||||
friend EASTDC_API int128_t operator*(const int128_t& value1, const int128_t& value2);
|
||||
friend EASTDC_API int128_t operator/(const int128_t& value1, const int128_t& value2);
|
||||
friend EASTDC_API int128_t operator%(const int128_t& value1, const int128_t& value2);
|
||||
|
||||
int128_t& operator+=(const int128_t& value);
|
||||
int128_t& operator-=(const int128_t& value);
|
||||
int128_t& operator*=(const int128_t& value);
|
||||
int128_t& operator/=(const int128_t& value);
|
||||
int128_t& operator%=(const int128_t& value);
|
||||
|
||||
// Shift operators
|
||||
int128_t operator>> (int nShift) const;
|
||||
int128_t operator<< (int nShift) const;
|
||||
int128_t& operator>>=(int nShift);
|
||||
int128_t& operator<<=(int nShift);
|
||||
|
||||
// Logical operators
|
||||
friend EASTDC_API int128_t operator^(const int128_t& value1, const int128_t& value2);
|
||||
friend EASTDC_API int128_t operator|(const int128_t& value1, const int128_t& value2);
|
||||
friend EASTDC_API int128_t operator&(const int128_t& value1, const int128_t& value2);
|
||||
|
||||
int128_t& operator^= (const int128_t& value);
|
||||
int128_t& operator|= (const int128_t& value);
|
||||
int128_t& operator&= (const int128_t& value);
|
||||
|
||||
// Equality operators
|
||||
friend EASTDC_API int compare (const int128_t& value1, const int128_t& value2);
|
||||
friend EASTDC_API bool operator==(const int128_t& value1, const int128_t& value2);
|
||||
friend EASTDC_API bool operator!=(const int128_t& value1, const int128_t& value2);
|
||||
friend EASTDC_API bool operator> (const int128_t& value1, const int128_t& value2);
|
||||
friend EASTDC_API bool operator>=(const int128_t& value1, const int128_t& value2);
|
||||
friend EASTDC_API bool operator< (const int128_t& value1, const int128_t& value2);
|
||||
friend EASTDC_API bool operator<=(const int128_t& value1, const int128_t& value2);
|
||||
|
||||
// Operators to convert back to basic types
|
||||
// We do not provide casting operators (e.g. operator float()) because doing so would
|
||||
// cause the compiler to complian about multiple conversion choices while doing freeform
|
||||
// math. That's standard C++ behaviour and the conventional solution is to not provide
|
||||
// implicit casting operators but to provide functions such as AsFloat() to allow the
|
||||
// user to do explicit casts.
|
||||
int8_t AsInt8() const;
|
||||
int16_t AsInt16() const;
|
||||
int32_t AsInt32() const;
|
||||
int64_t AsInt64() const;
|
||||
float AsFloat() const;
|
||||
double AsDouble() const;
|
||||
|
||||
// Misc. Functions
|
||||
void Negate();
|
||||
bool IsNegative() const; // Returns true for value < 0
|
||||
bool IsPositive() const; // Returns true for value >= 0
|
||||
void Modulus(const int128_t& divisor, int128_t& quotient, int128_t& remainder) const;
|
||||
|
||||
// String conversion functions
|
||||
static int128_t StrToInt128(const char* pValue, char** ppEnd, int base);
|
||||
static int128_t StrToInt128(const wchar_t* pValue, wchar_t** ppEnd, int base);
|
||||
|
||||
void Int128ToStr(char* pValue, char** ppEnd, int base, LeadingZeroes lz = kLZDefault, Prefix prefix = kPrefixDefault) const;
|
||||
void Int128ToStr(wchar_t* pValue, wchar_t** ppEnd, int base, LeadingZeroes lz = kLZDefault, Prefix pPrefix = kPrefixDefault) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// \class uint128_t
|
||||
/// \brief Implements unsigned 128 bit integer.
|
||||
///
|
||||
class EASTDC_API uint128_t : public int128_t_base
|
||||
{
|
||||
public:
|
||||
// Constructors / destructors
|
||||
uint128_t();
|
||||
uint128_t(uint32_t nPart0, uint32_t nPart1, uint32_t nPart2, uint32_t nPart3);
|
||||
uint128_t(uint64_t nPart0, uint64_t nPart1);
|
||||
|
||||
uint128_t(int8_t value);
|
||||
uint128_t(uint8_t value);
|
||||
|
||||
uint128_t(int16_t value);
|
||||
uint128_t(uint16_t value);
|
||||
|
||||
uint128_t(int32_t value);
|
||||
uint128_t(uint32_t value);
|
||||
|
||||
#if defined(INT128_INT_TYPE)
|
||||
uint128_t(INT128_INT_TYPE value);
|
||||
uint128_t(INT128_UINT_TYPE value);
|
||||
#endif
|
||||
|
||||
uint128_t(int64_t value);
|
||||
uint128_t(uint64_t value);
|
||||
|
||||
uint128_t(const int128_t& value);
|
||||
uint128_t(const uint128_t& value);
|
||||
|
||||
uint128_t(const float value);
|
||||
uint128_t(const double value);
|
||||
|
||||
uint128_t(const char* pValue, int nBase = 10);
|
||||
uint128_t(const wchar_t* pValue, int nBase = 10);
|
||||
|
||||
// To do: Add a uint128_t(unsigned int) constructor just for Metrowerks,
|
||||
// as Metrowerks defines uint32_t to be unsigned long and not unsigned int.
|
||||
|
||||
// Assignment operator
|
||||
uint128_t& operator=(const int128_t_base& value);
|
||||
|
||||
// Unary arithmetic/logic operators
|
||||
uint128_t operator-() const;
|
||||
uint128_t& operator++();
|
||||
uint128_t& operator--();
|
||||
uint128_t operator++(int);
|
||||
uint128_t operator--(int);
|
||||
uint128_t operator~() const;
|
||||
uint128_t operator+() const;
|
||||
|
||||
// Math operators
|
||||
friend EASTDC_API uint128_t operator+(const uint128_t& value1, const uint128_t& value2);
|
||||
friend EASTDC_API uint128_t operator-(const uint128_t& value1, const uint128_t& value2);
|
||||
friend EASTDC_API uint128_t operator*(const uint128_t& value1, const uint128_t& value2);
|
||||
friend EASTDC_API uint128_t operator/(const uint128_t& value1, const uint128_t& value2);
|
||||
friend EASTDC_API uint128_t operator%(const uint128_t& value1, const uint128_t& value2);
|
||||
|
||||
uint128_t& operator+=(const uint128_t& value);
|
||||
uint128_t& operator-=(const uint128_t& value);
|
||||
uint128_t& operator*=(const uint128_t& value);
|
||||
uint128_t& operator/=(const uint128_t& value);
|
||||
uint128_t& operator%=(const uint128_t& value);
|
||||
|
||||
// Shift operators
|
||||
uint128_t operator>> (int nShift) const;
|
||||
uint128_t operator<< (int nShift) const;
|
||||
uint128_t& operator>>=(int nShift);
|
||||
uint128_t& operator<<=(int nShift);
|
||||
|
||||
// Logical operators
|
||||
friend EASTDC_API uint128_t operator^(const uint128_t& value1, const uint128_t& value2);
|
||||
friend EASTDC_API uint128_t operator|(const uint128_t& value1, const uint128_t& value2);
|
||||
friend EASTDC_API uint128_t operator&(const uint128_t& value1, const uint128_t& value2);
|
||||
|
||||
uint128_t& operator^= (const uint128_t& value);
|
||||
uint128_t& operator|= (const uint128_t& value);
|
||||
uint128_t& operator&= (const uint128_t& value);
|
||||
|
||||
// Equality operators
|
||||
friend EASTDC_API int compare (const uint128_t& value1, const uint128_t& value2);
|
||||
friend EASTDC_API bool operator==(const uint128_t& value1, const uint128_t& value2);
|
||||
friend EASTDC_API bool operator!=(const uint128_t& value1, const uint128_t& value2);
|
||||
friend EASTDC_API bool operator> (const uint128_t& value1, const uint128_t& value2);
|
||||
friend EASTDC_API bool operator>=(const uint128_t& value1, const uint128_t& value2);
|
||||
friend EASTDC_API bool operator< (const uint128_t& value1, const uint128_t& value2);
|
||||
friend EASTDC_API bool operator<=(const uint128_t& value1, const uint128_t& value2);
|
||||
|
||||
// Operators to convert back to basic types
|
||||
// We do not provide casting operators (e.g. operator float()) because doing so would
|
||||
// cause the compiler to complian about multiple conversion choices while doing freeform
|
||||
// math. That's standard C++ behaviour and the conventional solution is to not provide
|
||||
// implicit casting operators but to provide functions such as AsFloat() to allow the
|
||||
// user to do explicit casts.
|
||||
int8_t AsInt8() const;
|
||||
int16_t AsInt16() const;
|
||||
int32_t AsInt32() const;
|
||||
int64_t AsInt64() const;
|
||||
float AsFloat() const;
|
||||
double AsDouble() const;
|
||||
|
||||
// Misc. Functions
|
||||
void Negate();
|
||||
bool IsNegative() const; // Returns true for value < 0
|
||||
bool IsPositive() const; // Returns true for value >= 0
|
||||
void Modulus(const uint128_t& divisor, uint128_t& quotient, uint128_t& remainder) const;
|
||||
|
||||
// String conversion functions
|
||||
static uint128_t StrToInt128(const char* pValue, char** ppEnd, int base);
|
||||
static uint128_t StrToInt128(const wchar_t* pValue, wchar_t** ppEnd, int base);
|
||||
|
||||
void Int128ToStr(char* pValue, char** ppEnd, int base, LeadingZeroes lz = kLZDefault, Prefix prefix = kPrefixDefault) const;
|
||||
void Int128ToStr(wchar_t* pValue, wchar_t** ppEnd, int base, LeadingZeroes lz = kLZDefault, Prefix pPrefix = kPrefixDefault) const;
|
||||
};
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// binary operators
|
||||
//
|
||||
// Operators involving two operands are made as independent functions instead
|
||||
// of member functions because if they were member functions then an expression
|
||||
// involving both operands would always have to be written in a specific order.
|
||||
//
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// int128_t
|
||||
//
|
||||
EASTDC_API int128_t operator+(const int128_t& value1, const int128_t& value2);
|
||||
EASTDC_API int128_t operator-(const int128_t& value1, const int128_t& value2);
|
||||
EASTDC_API int128_t operator*(const int128_t& value1, const int128_t& value2);
|
||||
EASTDC_API int128_t operator/(const int128_t& value1, const int128_t& value2);
|
||||
EASTDC_API int128_t operator%(const int128_t& value1, const int128_t& value2);
|
||||
|
||||
EASTDC_API int128_t operator^(const int128_t& value1, const int128_t& value2);
|
||||
EASTDC_API int128_t operator|(const int128_t& value1, const int128_t& value2);
|
||||
EASTDC_API int128_t operator&(const int128_t& value1, const int128_t& value2);
|
||||
|
||||
EASTDC_API int compare (const int128_t& value1, const int128_t& value2);
|
||||
EASTDC_API bool operator==(const int128_t& value1, const int128_t& value2);
|
||||
EASTDC_API bool operator!=(const int128_t& value1, const int128_t& value2);
|
||||
EASTDC_API bool operator> (const int128_t& value1, const int128_t& value2);
|
||||
EASTDC_API bool operator>=(const int128_t& value1, const int128_t& value2);
|
||||
EASTDC_API bool operator< (const int128_t& value1, const int128_t& value2);
|
||||
EASTDC_API bool operator<=(const int128_t& value1, const int128_t& value2);
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// uint128_t
|
||||
//
|
||||
EASTDC_API uint128_t operator+(const uint128_t& value1, const uint128_t& value2);
|
||||
EASTDC_API uint128_t operator-(const uint128_t& value1, const uint128_t& value2);
|
||||
EASTDC_API uint128_t operator*(const uint128_t& value1, const uint128_t& value2);
|
||||
EASTDC_API uint128_t operator/(const uint128_t& value1, const uint128_t& value2);
|
||||
EASTDC_API uint128_t operator%(const uint128_t& value1, const uint128_t& value2);
|
||||
|
||||
EASTDC_API uint128_t operator^(const uint128_t& value1, const uint128_t& value2);
|
||||
EASTDC_API uint128_t operator|(const uint128_t& value1, const uint128_t& value2);
|
||||
EASTDC_API uint128_t operator&(const uint128_t& value1, const uint128_t& value2);
|
||||
|
||||
EASTDC_API int compare (const uint128_t& value1, const uint128_t& value2);
|
||||
EASTDC_API bool operator==(const uint128_t& value1, const uint128_t& value2);
|
||||
EASTDC_API bool operator!=(const uint128_t& value1, const uint128_t& value2);
|
||||
EASTDC_API bool operator> (const uint128_t& value1, const uint128_t& value2);
|
||||
EASTDC_API bool operator>=(const uint128_t& value1, const uint128_t& value2);
|
||||
EASTDC_API bool operator< (const uint128_t& value1, const uint128_t& value2);
|
||||
EASTDC_API bool operator<=(const uint128_t& value1, const uint128_t& value2);
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Min / Max
|
||||
//
|
||||
// The C99 language standard defines macros for sized types, such as INT32_MAX.
|
||||
// Usually such macros are defined like this:
|
||||
// #define INT32_MAX 2147483647
|
||||
// We cannot do this with int128, so instead we define a const variable that
|
||||
// can be linked to which has the desirable property.
|
||||
//
|
||||
extern EASTDC_API const int128_t EASTDC_INT128_MIN;
|
||||
extern EASTDC_API const int128_t EASTDC_INT128_MAX;
|
||||
|
||||
extern EASTDC_API const uint128_t EASTDC_UINT128_MIN;
|
||||
extern EASTDC_API const uint128_t EASTDC_UINT128_MAX;
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// INT128_C / UINT128_C
|
||||
//
|
||||
// The C99 language defines macros for portably defining constants of
|
||||
// sized numeric types. For example, there might be:
|
||||
// #define UINT64_C(x) x##ULL
|
||||
// Since our int128 data type is not a built-in type, we can't define a
|
||||
// UINT128_C macro as something that pastes ULLL at the end of the digits.
|
||||
// Instead we define it to create a temporary that is constructed from a
|
||||
// string of the digits. This will work in most cases that suffix pasting
|
||||
// would work.
|
||||
//
|
||||
#define EASTDC_INT128_C(x) EA::StdC::int128_t(#x)
|
||||
#define EASTDC_UINT128_C(x) EA::StdC::uint128_t(#x)
|
||||
|
||||
|
||||
} // namespace StdC
|
||||
} // namespace EA
|
||||
|
||||
|
||||
#endif // Header include guard
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,257 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Electronic Arts Inc. All rights reserved.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Win32 implementation of fast, specialized scalar math primitives
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
// EAMathHelp.h (or possibly the build file) would have set none or one of the
|
||||
// following (usually none). If none were defined then we auto-detect.
|
||||
#if !defined(EAMATHHELP_MODE_SSE) && !defined(EAMATHHELP_MODE_X86ASM) && !defined(EAMATHHELP_MODE_REFERENCE)
|
||||
#if !defined(EAMATHHELP_MODE_SSE) && defined(EA_PROCESSOR_X86_64) && defined(EA_COMPILER_MSVC)
|
||||
#define EAMATHHELP_MODE_SSE 1
|
||||
#elif !defined(EAMATHHELP_MODE_X86ASM) && defined(EA_PROCESSOR_X86) && defined(EA_ASM_STYLE_INTEL)
|
||||
#define EAMATHHELP_MODE_X86ASM 1
|
||||
#else
|
||||
#define EAMATHHELP_MODE_REFERENCE 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if EAMATHHELP_MODE_SSE
|
||||
#pragma warning(push, 0)
|
||||
#include <xmmintrin.h>
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
namespace EA
|
||||
{
|
||||
namespace StdC
|
||||
{
|
||||
|
||||
#if EAMATHHELP_MODE_SSE
|
||||
|
||||
inline uint32_t RoundToUint32(float32_t fValue) {
|
||||
// We accomplish unsigned 32-bit conversion here by wrapping the
|
||||
// value to [-2^31, 2^31] before doing a signed 32-bit conversion.
|
||||
// This is necessary because SSE cannot do unsigned 32-bit itof
|
||||
// conversion and cannot store 64-bit values like x87 can.
|
||||
|
||||
const __m128 fValue128 = _mm_set_ss(fValue);
|
||||
return (uint32_t)_mm_cvtss_si32(
|
||||
_mm_add_ss(fValue128,
|
||||
_mm_and_ps(
|
||||
_mm_cmpgt_ss(
|
||||
fValue128,
|
||||
_mm_set_ss(2147483648.0f)
|
||||
),
|
||||
_mm_set_ss(-4294967296.0f)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
inline int32_t RoundToInt32(float32_t fValue) {
|
||||
return _mm_cvtss_si32(_mm_set_ss(fValue));
|
||||
}
|
||||
|
||||
inline int32_t FloorToInt32(float32_t fValue) {
|
||||
__m128 fValue128 = _mm_set_ss(fValue);
|
||||
int32_t iValue = _mm_cvtss_si32(fValue128);
|
||||
int32_t correction = _mm_cvtss_si32( // correction = iValue > fValue128 ? -1.0f : 0.0f
|
||||
_mm_and_ps(
|
||||
_mm_cmplt_ss(
|
||||
fValue128,
|
||||
_mm_cvtsi32_ss(_mm_setzero_ps(), iValue)
|
||||
),
|
||||
_mm_set_ss(-1.0f)
|
||||
)
|
||||
);
|
||||
|
||||
return iValue + correction;
|
||||
}
|
||||
|
||||
inline int32_t CeilToInt32(float32_t fValue) {
|
||||
__m128 fValue128 = _mm_set_ss(fValue);
|
||||
int32_t iValue = _mm_cvtss_si32(fValue128);
|
||||
int32_t correction = _mm_cvtss_si32( // correction = iValue < fValue128 ? +1.0f : 0.0f
|
||||
_mm_and_ps(
|
||||
_mm_cmplt_ss(
|
||||
_mm_cvtsi32_ss(_mm_setzero_ps(), iValue),
|
||||
fValue128
|
||||
),
|
||||
_mm_set_ss(+1.0f)
|
||||
)
|
||||
);
|
||||
|
||||
return iValue + correction;
|
||||
}
|
||||
|
||||
// This shouldn't be necessary with /arch:SSE, but if VC7.1 is doing calcs
|
||||
// in x87 -- which it will sometimes do if the SSE ops or data types are
|
||||
// too awkward for the expression -- it will resort to good old slow-as-@&$
|
||||
// fldcw + fistp.
|
||||
inline int32_t TruncateToInt32(float32_t fValue) {
|
||||
#if (defined(_MSC_VER) && (_MSC_VER < 1500)) || !EA_SSE2 // If using VC++ < VS2008 or if SSE2+ is not available...
|
||||
return _mm_cvttss_si32(_mm_set_ss(fValue));
|
||||
#else
|
||||
return (int32_t)fValue;
|
||||
#endif
|
||||
}
|
||||
|
||||
// This function is deprecated, as it's not very useful any more.
|
||||
inline int32_t FastRoundToInt23(float32_t fValue) {
|
||||
return _mm_cvtss_si32(_mm_set_ss(fValue));
|
||||
}
|
||||
|
||||
inline uint8_t UnitFloatToUint8(float fValue) {
|
||||
return (uint8_t)_mm_cvtss_si32(_mm_set_ss(fValue * 255.0f));
|
||||
}
|
||||
|
||||
inline uint8_t ClampUnitFloatToUint8(float fValue) {
|
||||
return (uint8_t)_mm_cvtss_si32(
|
||||
_mm_max_ss(
|
||||
_mm_min_ss(
|
||||
_mm_set_ss(fValue * 255.0f),
|
||||
_mm_set_ss(255.0f)
|
||||
),
|
||||
_mm_set_ss(0.0f)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#elif EAMATHHELP_MODE_X86ASM
|
||||
|
||||
inline uint32_t RoundToUint32(float32_t fValue) {
|
||||
EA_PREFIX_ALIGN(8) int64_t v;
|
||||
__asm {
|
||||
fld fValue
|
||||
fistp v
|
||||
}
|
||||
return (uint32_t)v;
|
||||
}
|
||||
|
||||
inline int32_t RoundToInt32(float32_t fValue) {
|
||||
int32_t iv;
|
||||
__asm {
|
||||
fld fValue
|
||||
fistp iv
|
||||
}
|
||||
return iv;
|
||||
}
|
||||
|
||||
inline int32_t FloorToInt32(float32_t fValue) {
|
||||
int32_t iv, correct;
|
||||
|
||||
__asm {
|
||||
fld fValue ;load fp value
|
||||
fist iv ;store as integer (rounded to nearest even)
|
||||
fild iv ;reload rounded value
|
||||
fsub ;compute v - round(v)
|
||||
fstp correct ;store v - round(v)
|
||||
cmp correct, 80000001h ;set carry if (v >= round(v)) (watch for -0!)
|
||||
mov eax, iv ;load rounded value
|
||||
adc eax, -1 ;subtract 1 if v < round(v)
|
||||
}
|
||||
}
|
||||
|
||||
inline int32_t CeilToInt32(float32_t fValue) {
|
||||
int32_t iv, correct;
|
||||
|
||||
__asm {
|
||||
fld fValue ;load fp value
|
||||
fist iv ;store as integer (rounded to nearest even)
|
||||
fild iv ;reload rounded value
|
||||
fsubr ;compute round(v) - v
|
||||
fstp correct ;store round(v) - v
|
||||
cmp correct, 80000001h ;set carry if (round(v) >= v) (watch for -0!)
|
||||
mov eax, iv ;load rounded value
|
||||
sbb eax, -1 ;add 1 if round(v) < v
|
||||
}
|
||||
}
|
||||
|
||||
// This function is not much faster than VC7.1's improved _ftol()
|
||||
// and is actually slower than fldcw on a Pentium 4, but is likely
|
||||
// to be faster on a PIII, which does not cache fpucw changes.
|
||||
inline int32_t TruncateToInt32(float32_t fValue) {
|
||||
int32_t iv, correct;
|
||||
|
||||
__asm {
|
||||
fld fValue ;load fp value
|
||||
fabs ;compute |value|
|
||||
fist iv ;store |value| as integer using round-to-nearest-even
|
||||
mov eax, fValue ;load fp value as integer
|
||||
cdq ;extract sign bit
|
||||
fild iv ;load rounded fp value
|
||||
fsub ;compute |v| - round(|v|)
|
||||
fstp correct ;store |v| - round(|v|)
|
||||
mov eax, iv ;load rounded value
|
||||
cmp correct, 80000001h ;set carry if (v >= round(|v|)) (watch for -0!)
|
||||
adc eax, -1 ;add 1 if |v| < round(|v|)
|
||||
xor eax, edx ;compute ~round(|v|) if v<0
|
||||
sub eax, edx ;compute ~round(|v|)+1 == -round(|v|) if v<0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// The FastRoundToInt23() and UnitFloatToUint8() functions are somewhat fast
|
||||
// by themselves, but what really makes them shine is the integer addition
|
||||
// that can easily be folded into other additions done on the value.
|
||||
// In particular, a table lookup on the result is likely to have the bias
|
||||
// subtracted incorporated into the address displacement for free on x86.
|
||||
|
||||
// This function is deprecated, as it's not very useful any more.
|
||||
inline int32_t FastRoundToInt23(float32_t fValue) {
|
||||
const union {
|
||||
float32_t f;
|
||||
int32_t i;
|
||||
} converter = { fValue + kFToIBiasF32 };
|
||||
|
||||
return converter.i - kFToIBiasS32;
|
||||
}
|
||||
|
||||
inline uint8_t UnitFloatToUint8(float fValue) {
|
||||
const union {
|
||||
float32_t f;
|
||||
int32_t i;
|
||||
} converter = { fValue * (255.0f/256.0f) + kFToI8BiasF32 };
|
||||
|
||||
return (uint8_t)(int8_t)(converter.i - kFToI8BiasS32);
|
||||
}
|
||||
|
||||
|
||||
inline uint8_t ClampUnitFloatToUint8(float fValue) {
|
||||
const union {
|
||||
float32_t f;
|
||||
int32_t i;
|
||||
} converter = { kFToI8BiasF32 + fValue * (255.0f/256.0f) };
|
||||
|
||||
const int32_t i = converter.i - kFToI8BiasS32;
|
||||
|
||||
// ~(i>>31) == (i<0 ) ? 0 : 0xFFFFFFFF (low clamp)
|
||||
// (255-i)>>31 == (i>255) ? 0xFFFFFFFF : 0 (high clamp)
|
||||
|
||||
return uint8_t((i & ~(i>>31)) | ((255-i)>>31));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
} // namespace StdC
|
||||
} // namespace EA
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,699 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Electronic Arts Inc. All rights reserved.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef EASTDC_INTERNAL_CONFIG_H
|
||||
#define EASTDC_INTERNAL_CONFIG_H
|
||||
|
||||
|
||||
#include <EABase/eabase.h>
|
||||
#include <stddef.h>
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// EASTDC_VERSION
|
||||
//
|
||||
// We more or less follow the conventional EA packaging approach to versioning
|
||||
// here. A primary distinction here is that minor versions are defined as two
|
||||
// digit entities (e.g. .03") instead of minimal digit entities ".3"). The logic
|
||||
// here is that the value is a counter and not a floating point fraction.
|
||||
// Note that the major version doesn't have leading zeros.
|
||||
//
|
||||
// Example version strings:
|
||||
// "0.91.00" // Major version 0, minor version 91, patch version 0.
|
||||
// "1.00.00" // Major version 1, minor and patch version 0.
|
||||
// "3.10.02" // Major version 3, minor version 10, patch version 02.
|
||||
// "12.03.01" // Major version 12, minor version 03, patch version
|
||||
//
|
||||
// Example usage:
|
||||
// printf("EASTDC version: %s", EASTDC_VERSION);
|
||||
// printf("EASTDC version: %d.%d.%d", EASTDC_VERSION_N / 10000 % 100, EASTDC_VERSION_N / 100 % 100, EASTDC_VERSION_N % 100);
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef EASTDC_VERSION
|
||||
#define EASTDC_VERSION "1.26.07"
|
||||
#define EASTDC_VERSION_N 12607
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// EA_XBDM_ENABLED
|
||||
//
|
||||
// Defined as 0 or 1, with 1 being the default for debug builds.
|
||||
// This controls whether xbdm library usage is enabled on XBox 360. This library
|
||||
// allows for runtime debug functionality. But shipping applications are not
|
||||
// allowed to use xbdm.
|
||||
//
|
||||
#if !defined(EA_XBDM_ENABLED)
|
||||
#if defined(EA_DEBUG)
|
||||
#define EA_XBDM_ENABLED 1
|
||||
#else
|
||||
#define EA_XBDM_ENABLED 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// EA_SCEDBG_ENABLED
|
||||
//
|
||||
// Defined as 0 or 1, with 1 being the default for debug builds.
|
||||
// This controls whether sceDbg library usage is enabled on Sony platforms. This library
|
||||
// allows for runtime debug functionality. But shipping applications are not
|
||||
// allowed to use sceDbg. You can define EA_SCEDBG_ENABLED=1 in your nant build
|
||||
// properties to enable EA_SCEDBG_ENABLED in any build; the .build file for
|
||||
// this package will pick it up and define it for the compile of this package.
|
||||
//
|
||||
#if !defined(EA_SCEDBG_ENABLED)
|
||||
#if defined(EA_DEBUG)
|
||||
#define EA_SCEDBG_ENABLED 1
|
||||
#else
|
||||
#define EA_SCEDBG_ENABLED 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// EASTDC_PRINTF_DEBUG_ENABLED
|
||||
//
|
||||
// Defined as 0 or 1. Enabled by default in debug builds for platforms which
|
||||
// don't support stdout.
|
||||
// Has the effect of causing writes to stdout to be redirected to debug output
|
||||
// (same as Dprintf) on platforms where stdout is a no-op (e.g. consoles).
|
||||
//
|
||||
#if !defined(EASTDC_PRINTF_DEBUG_ENABLED)
|
||||
#if defined(EA_PLATFORM_CONSOLE) || defined(EA_PLATFORM_MOBILE)
|
||||
#define EASTDC_PRINTF_DEBUG_ENABLED 1
|
||||
#else
|
||||
#define EASTDC_PRINTF_DEBUG_ENABLED 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// EASTDC_OUTPUTDEBUGSTRING_ENABLED
|
||||
//
|
||||
// Defined as 0 or 1. Enabled of the platform supports OutputDebugString and
|
||||
// if it's allowed to in the current build. Consider that Microsoft disallows
|
||||
// using OutputDebugString in published store applications.
|
||||
//
|
||||
#if defined(EA_PLATFORM_MICROSOFT) && (defined(EA_PLATFORM_DESKTOP) || EA_XBDM_ENABLED)
|
||||
#define EASTDC_OUTPUTDEBUGSTRING_ENABLED 1
|
||||
#else
|
||||
#define EASTDC_OUTPUTDEBUGSTRING_ENABLED 0
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// EASTDC_DLL
|
||||
//
|
||||
// Defined as 0 or 1. The default is dependent on the definition of EA_DLL.
|
||||
// If EA_DLL is defined, then EASTDC_DLL is 1, else EASTDC_DLL is 0.
|
||||
// EA_DLL is a define that controls DLL builds within the EAConfig build system.
|
||||
// EASTDC_DLL controls whether EASTDC is built and used as a DLL.
|
||||
// Normally you wouldn't do such a thing, but there are use cases for such
|
||||
// a thing, particularly in the case of embedding C++ into C# applications.
|
||||
//
|
||||
#ifndef EASTDC_DLL
|
||||
#if defined(EA_DLL)
|
||||
#define EASTDC_DLL 1
|
||||
#else
|
||||
#define EASTDC_DLL 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// EASTDC_API
|
||||
//
|
||||
// This is used to label functions as DLL exports under Microsoft platforms.
|
||||
// If EA_DLL is defined, then the user is building EASTDC as a DLL and EASTDC's
|
||||
// non-templated functions will be exported. EASTDC template functions are not
|
||||
// labelled as EASTDC_API (and are thus not exported in a DLL build). This is
|
||||
// because it's not possible (or at least unsafe) to implement inline templated
|
||||
// functions in a DLL.
|
||||
//
|
||||
// Example usage of EASTDC_API:
|
||||
// EASTDC_API int someVariable = 10; // Export someVariable in a DLL build.
|
||||
//
|
||||
// struct EASTDC_API SomeClass{ // Export SomeClass and its member functions in a DLL build.
|
||||
// EASTDC_LOCAL void PrivateMethod(); // Not exported.
|
||||
// };
|
||||
//
|
||||
// EASTDC_API void SomeFunction(); // Export SomeFunction in a DLL build.
|
||||
//
|
||||
// For GCC, see http://gcc.gnu.org/wiki/Visibility
|
||||
//
|
||||
#ifndef EASTDC_API // If the build file hasn't already defined this to be dllexport...
|
||||
#if EASTDC_DLL
|
||||
#if defined(_MSC_VER)
|
||||
#define EASTDC_API __declspec(dllimport)
|
||||
#define EASTDC_LOCAL
|
||||
#elif defined(__CYGWIN__)
|
||||
#define EASTDC_API __attribute__((dllimport))
|
||||
#define EASTDC_LOCAL
|
||||
#elif (defined(__GNUC__) && (__GNUC__ >= 4))
|
||||
#define EASTDC_API __attribute__ ((visibility("default")))
|
||||
#define EASTDC_LOCAL __attribute__ ((visibility("hidden")))
|
||||
#else
|
||||
#define EASTDC_API
|
||||
#define EASTDC_LOCAL
|
||||
#endif
|
||||
#else
|
||||
#define EASTDC_API
|
||||
#define EASTDC_LOCAL
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// EASTDC_MEMORY_INLINE_ENABLED
|
||||
//
|
||||
// Defined as 0 or 1, with 1 being the default.
|
||||
// This controls whether EAMemory functions are inlined or not in builds.
|
||||
// The advantage of them being inlined is that they can pass straight through
|
||||
// to inlinable code. The disadvantage is increased code size and lack of
|
||||
// diagnostic functionality that's available when not inlined.
|
||||
//
|
||||
#if !defined(EASTDC_MEMORY_INLINE_ENABLED)
|
||||
#define EASTDC_MEMORY_INLINE_ENABLED 1
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// EASTDC_VSNPRINTF8_ENABLED
|
||||
//
|
||||
// *** This option is deprecated. ***
|
||||
// Defined as 0 or 1. Default is 0.
|
||||
// If enabled then Vsnprintf8 and Vsnprintf16 are enabled. These are functions
|
||||
// which simply call Vsnprintf(char8_t..) and Vsnprintf(char16_t). Vsnprintf8
|
||||
// is the older name for this function and is deprecated. However, some existing
|
||||
// code uses Vsnprintf8 (most notably the EASTL package).
|
||||
//
|
||||
#ifndef EASTDC_VSNPRINTF8_ENABLED
|
||||
#define EASTDC_VSNPRINTF8_ENABLED 0
|
||||
#endif
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// EASTDC_CHAR32_SUPPORT_ENABLED
|
||||
//
|
||||
// Defined as 0 or 1. Default is 1 except on platforms where it would never be used.
|
||||
// Defines whether functions that use char32_t are supported. If not then there
|
||||
// are no available declarations or definitions of char32_t functions (e.g. Sprintf(char32_t*,...)
|
||||
// Note that some char32_t functionality might nevertheless be enabled even if
|
||||
// EASTDC_CHAR32_SUPPORT_ENABLED is 0. In these cases it's expected that the
|
||||
// C/C++ linker will link away such functions.
|
||||
//
|
||||
#ifndef EASTDC_CHAR32_SUPPORT_ENABLED
|
||||
#define EASTDC_CHAR32_SUPPORT_ENABLED 1
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// EASTDC_GLOBALPTR_SUPPORT_ENABLED
|
||||
//
|
||||
// Defined as 0 or 1. Default is 1.
|
||||
// See include/EAStdC/EAGlobal.h for a full description on this feature.
|
||||
#ifndef EASTDC_GLOBALPTR_SUPPORT_ENABLED
|
||||
#define EASTDC_GLOBALPTR_SUPPORT_ENABLED 1
|
||||
#endif
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// EASprintf configuration parameters
|
||||
//
|
||||
#ifndef EASPRINTF_FIELD_MAX // Defines the maximum supported length of a field, except string fields, which have no size limit.
|
||||
#if defined(EA_PLATFORM_UNIX) // This value relates to the size of buffers used in the stack space.
|
||||
#define EASPRINTF_FIELD_MAX 4096
|
||||
#elif defined(EA_PLATFORM_DESKTOP)
|
||||
#define EASPRINTF_FIELD_MAX 3600 // Much higher than this and it could risk problems with stack space.
|
||||
#else
|
||||
#define EASPRINTF_FIELD_MAX 1024 // The reason it is 1024 and not the 4095 that the C99 Standard specifies is that 4095 can blow up the stack on some platforms (e.g. PS3).
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef EASPRINTF_MS_STYLE_S_FORMAT // Microsoft uses a non-standard interpretation of the %s field type.
|
||||
#define EASPRINTF_MS_STYLE_S_FORMAT 1 // For wsprintf MSVC interprets %s as a wchar_t string and %S as a char string.
|
||||
#endif // You can make your code portable by using %hs and %ls to force the type.
|
||||
|
||||
#ifndef EASPRINTF_SNPRINTF_C99_RETURN // The C99 standard specifies that snprintf returns the required strlen
|
||||
#define EASPRINTF_SNPRINTF_C99_RETURN 1 // of the output, which may or may not be less than the supplied buffer size.
|
||||
#endif // Some snprintf implementations instead return -1 if the supplied buffer is too small.
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// EASTDC_THREADING_SUPPORTED
|
||||
//
|
||||
// Defined as 0 or 1. Default is 1 (enabled).
|
||||
// Specifies if code that uses multithreading is available. If enabled then
|
||||
// this package is dependent on the EAThread package.
|
||||
//
|
||||
#ifndef EASTDC_THREADING_SUPPORTED
|
||||
#define EASTDC_THREADING_SUPPORTED 1
|
||||
#endif
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// EASTDC_VALGRIND_ENABLED
|
||||
//
|
||||
// Defined as 0 or 1. It's value depends on the compile environment.
|
||||
// Specifies whether the code is being built with Valgrind instrumentation.
|
||||
// Note that you can detect valgrind at runtime via getenv("RUNNING_ON_VALGRIND")
|
||||
//
|
||||
#if !defined(EASTDC_VALGRIND_ENABLED)
|
||||
#define EASTDC_VALGRIND_ENABLED 0
|
||||
#endif
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// EASTDC_ASAN_ENABLED
|
||||
//
|
||||
// Defined as 0 or 1. It's value depends on the compile environment.
|
||||
// Specifies whether the code is being built with Clang's Address Sanitizer.
|
||||
//
|
||||
#if defined(__has_feature)
|
||||
#if __has_feature(address_sanitizer)
|
||||
#define EASTDC_ASAN_ENABLED 1
|
||||
#else
|
||||
#define EASTDC_ASAN_ENABLED 0
|
||||
#endif
|
||||
#else
|
||||
#define EASTDC_ASAN_ENABLED 0
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// EASTDC_STATIC_ANALYSIS_ENABLED
|
||||
//
|
||||
// Defined as 0 or 1. It's value depends on the compile environment. This is
|
||||
// generic compile-time flag that indicates EAStdc is running under static
|
||||
// analysis environment. This is important because specific string
|
||||
// optimizations are disabled which are correctly flagged as errors but in
|
||||
// practice do not cause any harm.
|
||||
//
|
||||
#if !defined(EASTDC_STATIC_ANALYSIS_ENABLED)
|
||||
#if EASTDC_ASAN_ENABLED || EASTDC_VALGRIND_ENABLED
|
||||
#define EASTDC_STATIC_ANALYSIS_ENABLED 1
|
||||
#else
|
||||
#define EASTDC_STATIC_ANALYSIS_ENABLED 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// operator new
|
||||
//
|
||||
// A user-provided operator new of the following types is required.
|
||||
// Note that the array versions of this are the same operator new used/required by EASTL.
|
||||
//
|
||||
// Example usage:
|
||||
// SomeClass* pObject = new("SomeClass") SomeClass(1, 2, 3);
|
||||
// SomeClass* pObject = new("SomeClass", 0, 0, __FILE__, __LINE__) SomeClass(1, 2, 3);
|
||||
// SomeClass* pObject = new("SomeClass", EA::Allocator::kFlagNormal, 1 << EA::Allocator::GeneralAllocatorDebug::kDebugDataIdCallStack) SomeClass(1, 2, 3);
|
||||
//
|
||||
/// Example usage:
|
||||
/// SomeClass* pArray = new("SomeClass", EA::Allocator::kFlagPermanent, __FILE__, __LINE__) SomeClass(1, 2, 3)[4];
|
||||
/// SomeClass* pArray = new("SomeClass", EA::Allocator::kFlagNormal, 1 << EA::Allocator::GeneralAllocatorDebug::kDebugDataIdCallStack, __FILE__, __LINE__) SomeClass(1, 2, 3)[4];
|
||||
//
|
||||
void* operator new (size_t size, const char* name, int flags, unsigned debugFlags, const char* file, int line);
|
||||
void* operator new[] (size_t size, const char* name, int flags, unsigned debugFlags, const char* file, int line);
|
||||
void* operator new (size_t size, size_t alignment, size_t alignmentOffset, const char* name, int flags, unsigned debugFlags, const char* file, int line);
|
||||
void* operator new[] (size_t size, size_t alignment, size_t alignmentOffset, const char* name, int flags, unsigned debugFlags, const char* file, int line);
|
||||
|
||||
void operator delete (void* p, const char* name, int flags, unsigned debugFlags, const char* file, int line);
|
||||
void operator delete[](void* p, const char* name, int flags, unsigned debugFlags, const char* file, int line);
|
||||
void operator delete (void* p, size_t alignment, size_t alignmentOffset, const char* name, int flags, unsigned debugFlags, const char* file, int line);
|
||||
void operator delete[](void* p, size_t alignment, size_t alignmentOffset, const char* name, int flags, unsigned debugFlags, const char* file, int line);
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// EASTDC_ALLOC_PREFIX
|
||||
//
|
||||
// Defined as a string literal. Defaults to this package's name.
|
||||
// Can be overridden by the user by predefining it or by editing this file.
|
||||
// This define is used as the default name used by this package for naming
|
||||
// memory allocations and memory allocators.
|
||||
//
|
||||
// All allocations names follow the same naming pattern:
|
||||
// <package>/<module>[/<specific usage>]
|
||||
//
|
||||
// Example usage:
|
||||
// void* p = pCoreAllocator->Alloc(37, EASTDC_ALLOC_PREFIX, 0);
|
||||
//
|
||||
// Example usage:
|
||||
// gMessageServer.GetMessageQueue().get_allocator().set_name(EASTDC_ALLOC_PREFIX "MessageSystem/Queue");
|
||||
//
|
||||
#ifndef EASTDC_ALLOC_PREFIX
|
||||
#define EASTDC_ALLOC_PREFIX "EAStdC/"
|
||||
#endif
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// EASTDC_USE_STANDARD_NEW
|
||||
//
|
||||
// Defines whether we use the basic standard operator new or the named
|
||||
// extended version of operator new, as per the EASTL package.
|
||||
//
|
||||
#ifndef EASTDC_USE_STANDARD_NEW
|
||||
#if EASTDC_DLL // A DLL must provide its own implementation of new, so we just use built-in new.
|
||||
#define EASTDC_USE_STANDARD_NEW 1
|
||||
#else
|
||||
#define EASTDC_USE_STANDARD_NEW 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// EASTDC_NEW
|
||||
//
|
||||
// This is merely a wrapper for operator new which can be overridden and
|
||||
// which has debug/release forms.
|
||||
//
|
||||
// Example usage:
|
||||
// SomeClass* pObject = EASTDC_NEW("SomeClass") SomeClass(1, 2, 3);
|
||||
//
|
||||
#ifndef EASTDC_NEW
|
||||
#if EASTDC_USE_STANDARD_NEW
|
||||
#define EASTDC_NEW(name) new
|
||||
#define EASTDC_NEW_ALIGNED(alignment, offset, name) new
|
||||
#define EASTDC_DELETE delete
|
||||
#else
|
||||
#if defined(EA_DEBUG)
|
||||
#define EASTDC_NEW(name) new(name, 0, 0, __FILE__, __LINE__)
|
||||
#define EASTDC_NEW_ALIGNED(alignment, offset, name) new(alignment, offset, name, 0, 0, __FILE__, __LINE__)
|
||||
#define EASTDC_DELETE delete
|
||||
#else
|
||||
#define EASTDC_NEW(name) new(name, 0, 0, 0, 0)
|
||||
#define EASTDC_NEW_ALIGNED(alignment, offset, name) new(alignment, offset, name, 0, 0, 0, 0)
|
||||
#define EASTDC_DELETE delete
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
#if !defined(EA_ASSERT_HEADER)
|
||||
#define EA_ASSERT_HEADER <EAAssert/eaassert.h>
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// EASTDC_STOPWATCH_FORCE_CPU_CYCLE_USAGE
|
||||
//
|
||||
// Defined as 0 or 1.
|
||||
// If 1 then CPU cycle counts are used instead of system timer counts.
|
||||
// For systems where CPU frequencies are stable, this should be defined.
|
||||
//
|
||||
#ifndef EASTDC_STOPWATCH_FORCE_CPU_CYCLE_USAGE
|
||||
#if defined(EA_PROCESSOR_X86) || defined(EA_PROCESSOR_X86_64)
|
||||
#define EASTDC_STOPWATCH_FORCE_CPU_CYCLE_USAGE 0 // x86 rdtsc is unreliable.
|
||||
#else
|
||||
#define EASTDC_STOPWATCH_FORCE_CPU_CYCLE_USAGE 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// EASTDC_STOPWATCH_OVERHEAD_ENABLED
|
||||
//
|
||||
// Defined as 0 or 1. Default depends on the platform.
|
||||
// When defined as 1 the overhead is esimated on startup and applied to
|
||||
// timing events.
|
||||
// On some systems the overhead of reading the current time is small
|
||||
// enough that we consider it insiginificant, but on some others it is not.
|
||||
// We consider significance to mean more than ~100 CPU clock ticks.
|
||||
//
|
||||
#ifndef EASTDC_STOPWATCH_OVERHEAD_ENABLED
|
||||
#if defined(EA_PLATFORM_MICROSOFT) || defined(EA_PLATFORM_DESKTOP)
|
||||
#define EASTDC_STOPWATCH_OVERHEAD_ENABLED 1
|
||||
#else
|
||||
#define EASTDC_STOPWATCH_OVERHEAD_ENABLED 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// EASTDC_SCANF_WARNINGS_ENABLED
|
||||
//
|
||||
// Defined as 0 or 1. Default is 0 (disabled).
|
||||
// If enabled then scanf execution that is not an error but could be considered
|
||||
// a warning is reported. An example is a character format conversion that loses
|
||||
// information.
|
||||
//
|
||||
#ifndef EASTDC_SCANF_WARNINGS_ENABLED
|
||||
#define EASTDC_SCANF_WARNINGS_ENABLED 0
|
||||
#endif
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// EASTDC_PRINTF_WARNINGS_ENABLED
|
||||
//
|
||||
// Defined as 0 or 1. Default is 0 (disabled).
|
||||
// If enabled then printf execution that is not an error but could be considered
|
||||
// a warning is reported. An example is a character format conversion that loses
|
||||
// information.
|
||||
//
|
||||
#ifndef EASTDC_PRINTF_WARNINGS_ENABLED
|
||||
#define EASTDC_PRINTF_WARNINGS_ENABLED 0
|
||||
#endif
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// EASTDC_XXXXX_AVAILABLE
|
||||
// Similar functionality is present in recent versions of EABase via <eahave.h>.
|
||||
// To do: Migrate to using EABase versions of these.
|
||||
//
|
||||
// Defined as 0 or 1.
|
||||
// Tells if various headers are available. Some platforms/compile targets don't
|
||||
// fully support standard C/C++ libraries.
|
||||
//
|
||||
#ifndef EASTDC_TIME_H_AVAILABLE // time.h header and associated functionality.
|
||||
#define EASTDC_TIME_H_AVAILABLE 1
|
||||
#endif
|
||||
|
||||
#ifndef EASTDC_SYS_TIME_H_AVAILABLE // sys/time.h header and associated functionality.
|
||||
#if defined(EA_PLATFORM_POSIX) || defined(__APPLE__)
|
||||
#define EASTDC_SYS_TIME_H_AVAILABLE 1
|
||||
#else
|
||||
#define EASTDC_SYS_TIME_H_AVAILABLE 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef EASTDC_SYS__TIMEVAL_H_AVAILABLE // sys/_timeval.h header and associated functionality.
|
||||
#if defined(EA_PLATFORM_FREEBSD)
|
||||
#define EASTDC_SYS__TIMEVAL_H_AVAILABLE 1
|
||||
#else
|
||||
#define EASTDC_SYS__TIMEVAL_H_AVAILABLE 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef EASTDC_LOCALE_H_AVAILABLE // locale.h header and associated functionality.
|
||||
#define EASTDC_LOCALE_H_AVAILABLE 1
|
||||
#endif
|
||||
|
||||
#ifndef EASTDC_SYS_MMAN_H_AVAILABLE // sys/mman.h header and associated functionality
|
||||
#if defined(EA_PLATFORM_POSIX)
|
||||
#define EASTDC_SYS_MMAN_H_AVAILABLE 1
|
||||
#else
|
||||
#define EASTDC_SYS_MMAN_H_AVAILABLE 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef EASTDC_SYS_WAIT_H_AVAILABLE // sys/wait.h header and associated functionality
|
||||
#if defined(EA_PLATFORM_UNIX)
|
||||
#define EASTDC_SYS_WAIT_H_AVAILABLE 1
|
||||
#else
|
||||
#define EASTDC_SYS_WAIT_H_AVAILABLE 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef EASTDC_FILE_AVAILABLE // FILE io, such as fopen, fread.
|
||||
#define EASTDC_FILE_AVAILABLE 1
|
||||
#endif
|
||||
|
||||
#ifndef EASTDC_UNIX_TZNAME_AVAILABLE // The global tzname variable.
|
||||
#if defined(EA_PLATFORM_UNIX)
|
||||
#define EASTDC_UNIX_TZNAME_AVAILABLE 1
|
||||
#else
|
||||
#define EASTDC_UNIX_TZNAME_AVAILABLE 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef EASTDC_CLOCK_GETTIME_AVAILABLE // The clock_gettime function, which is an alternative to gettimeofday.
|
||||
//#if defined(CLOCK_REALTIME) // Disabled until we can get usage of this better.
|
||||
// #define EASTDC_CLOCK_GETTIME_AVAILABLE 1
|
||||
//#else
|
||||
#define EASTDC_CLOCK_GETTIME_AVAILABLE 0
|
||||
//#endif
|
||||
#endif
|
||||
|
||||
|
||||
// EA_COMPILER_HAS_BUILTIN
|
||||
//
|
||||
// Present in recent versions of EABase; provided here for backward compatibility.
|
||||
//
|
||||
#ifndef EA_COMPILER_HAS_BUILTIN
|
||||
#if defined(__clang__)
|
||||
#define EA_COMPILER_HAS_BUILTIN(x) __has_builtin(x)
|
||||
#else
|
||||
#define EA_COMPILER_HAS_BUILTIN(x) 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// EASTDC_SSE_POPCNT
|
||||
//
|
||||
// Defined as 1 or undefined.
|
||||
// Implements support for x86 POPCNT instruction. We do not use __builtin_popcnt()
|
||||
// from gcc/clang for example as those will compile to a table-based lookup or
|
||||
// some other software implementation on processors with SSE version < 4.2
|
||||
// but we have our own software implementation we want to fall back on
|
||||
// popcnt instruction was added in SSE4.2 starting with Nehalem on Intel and
|
||||
// SSE4A starting with Barcelona on AMD
|
||||
//
|
||||
// x86 Android and OSX require popcnt target feature enabled on clang inorder to compile
|
||||
// which is why they are excluded for now
|
||||
#ifndef EASTDC_SSE_POPCNT
|
||||
#if ((defined(EA_SSE4_2) && EA_SSE4_2) || (defined(EA_SSE4A) && EA_SSE4A)) && \
|
||||
(!defined(EA_PLATFORM_OSX) && !defined(EA_PLATFORM_ANDROID))
|
||||
#define EASTDC_SSE_POPCNT 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// EABase fallbacks
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// EA_CHAR16
|
||||
// Present in recent versions of EABase.
|
||||
//
|
||||
// EA_CHAR16 is defined in EABase 2.0.20 and later. If we are using an earlier
|
||||
// version of EABase then we replicate what EABase 2.0.20 does.
|
||||
//
|
||||
#ifndef EA_CHAR16
|
||||
#if !defined(EA_CHAR16_NATIVE)
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1600) && defined(_HAS_CHAR16_T_LANGUAGE_SUPPORT) && _HAS_CHAR16_T_LANGUAGE_SUPPORT // VS2010+
|
||||
#define EA_CHAR16_NATIVE 1
|
||||
#elif defined(__GNUC__) && ((__GNUC__ * 100 + __GNUC_MINOR__) >= 404) && (defined(__GXX_EXPERIMENTAL_CXX0X__) || defined(__STDC_VERSION__)) // g++ (C++ compiler) 4.4+ with -std=c++0x or gcc (C compiler) 4.4+ with -std=gnu99
|
||||
#define EA_CHAR16_NATIVE 1
|
||||
#else
|
||||
#define EA_CHAR16_NATIVE 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if EA_CHAR16_NATIVE && !defined(_MSC_VER) // Microsoft doesn't support char16_t string literals.
|
||||
#define EA_CHAR16(s) u ## s
|
||||
#elif (EA_WCHAR_SIZE == 2)
|
||||
#define EA_CHAR16(s) L ## s
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// EA_UNUSED
|
||||
// Present in recent versions of EABase.
|
||||
//
|
||||
// Makes compiler warnings about unused variables go away.
|
||||
//
|
||||
// Example usage:
|
||||
// void Function(int x)
|
||||
// {
|
||||
// int y;
|
||||
// EA_UNUSED(x);
|
||||
// EA_UNUSED(y);
|
||||
// }
|
||||
//
|
||||
#ifndef EA_UNUSED
|
||||
// The EDG solution below is pretty weak and needs to be augmented or replaced.
|
||||
// It can't handle the C language, is limited to places where template declarations
|
||||
// can be used, and requires the type x to be usable as a functions reference argument.
|
||||
#if defined(__cplusplus) && defined(__EDG__)
|
||||
template <typename T>
|
||||
inline void EABaseUnused(T const volatile & x) { (void)x; }
|
||||
#define EA_UNUSED(x) EABaseUnused(x)
|
||||
#else
|
||||
#define EA_UNUSED(x) (void)x
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// EA_DISABLE_CLANG_WARNING / EA_RESTORE_CLANG_WARNING
|
||||
// Present in recent versions of EABase.
|
||||
//
|
||||
// Example usage:
|
||||
// // Only one warning can be ignored per statement, due to how clang works.
|
||||
// EA_DISABLE_CLANG_WARNING(-Wuninitialized)
|
||||
// EA_DISABLE_CLANG_WARNING(-Wunused)
|
||||
// <code>
|
||||
// EA_RESTORE_CLANG_WARNING()
|
||||
// EA_RESTORE_CLANG_WARNING()
|
||||
//
|
||||
#ifndef EA_DISABLE_CLANG_WARNING
|
||||
#if defined(EA_COMPILER_CLANG)
|
||||
#define EACLANGWHELP0(x) #x
|
||||
#define EACLANGWHELP1(x) EACLANGWHELP0(clang diagnostic ignored x)
|
||||
#define EACLANGWHELP2(x) EACLANGWHELP1(#x)
|
||||
|
||||
#define EA_DISABLE_CLANG_WARNING(w) \
|
||||
_Pragma("clang diagnostic push") \
|
||||
_Pragma(EACLANGWHELP2(w))
|
||||
#else
|
||||
#define EA_DISABLE_CLANG_WARNING(w)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef EA_RESTORE_CLANG_WARNING
|
||||
#if defined(EA_COMPILER_CLANG)
|
||||
#define EA_RESTORE_CLANG_WARNING() \
|
||||
_Pragma("clang diagnostic pop")
|
||||
#else
|
||||
#define EA_RESTORE_CLANG_WARNING()
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// EA_HAVE_WCHAR_IMPL
|
||||
//
|
||||
#if !defined(EA_HAVE_localtime_DECL) && !defined(EA_NO_HAVE_localtime_DECL)
|
||||
#define EA_HAVE_localtime_DECL 1
|
||||
#endif
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// EA_DISABLE_ALL_VC_WARNINGS
|
||||
//
|
||||
// This is defined in newer versions of EABase, but we temporarily define it here for backward compatibility.
|
||||
// To do: Remove this in July 2014, at which point the EABase version will be two years old.
|
||||
//
|
||||
#ifndef EA_DISABLE_ALL_VC_WARNINGS
|
||||
#if defined(_MSC_VER)
|
||||
#define EA_DISABLE_ALL_VC_WARNINGS() \
|
||||
__pragma(warning(push, 0)) \
|
||||
__pragma(warning(disable: 4244 4267 4350 4509 4548 4710 4985 6320)) // Some warnings need to be explicitly called out.
|
||||
#else
|
||||
#define EA_DISABLE_ALL_VC_WARNINGS()
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef EA_RESTORE_ALL_VC_WARNINGS
|
||||
#if defined(_MSC_VER)
|
||||
#define EA_RESTORE_ALL_VC_WARNINGS() \
|
||||
__pragma(warning(pop))
|
||||
#else
|
||||
#define EA_RESTORE_ALL_VC_WARNINGS()
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif // Header include guard
|
||||
@@ -0,0 +1,158 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Electronic Arts Inc. All rights reserved.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
// No include guards should be necessary since this is an internal file which
|
||||
// is only ever #included in one place.
|
||||
|
||||
|
||||
#include <EAAssert/eaassert.h>
|
||||
|
||||
|
||||
// If EASTDC_MEMORY_INLINE_ENABLED is 1 (usually optimized builds) then this .inl file is
|
||||
// #included from EAMemory.h and thus we want the functions to be declared inline and don't
|
||||
// want them ever declared with DLL-export tags like EASTDC_API does. But otherwise (usually
|
||||
// debug builds) this .inl file is #included from EAMemory.cpp and we don't want 'inline'
|
||||
// but do want EASTDC_API.
|
||||
#if EASTDC_MEMORY_INLINE_ENABLED
|
||||
#define EASTDC_EAMEMORY_DECL inline
|
||||
#else
|
||||
#define EASTDC_EAMEMORY_DECL EASTDC_API // Maps to __declspec(dllexport) in DLL-builds (with VC++).
|
||||
#endif
|
||||
|
||||
|
||||
namespace EA
|
||||
{
|
||||
namespace StdC
|
||||
{
|
||||
|
||||
EASTDC_EAMEMORY_DECL void Memclear(void* pDestination, size_t n)
|
||||
{
|
||||
memset(pDestination, 0, n);
|
||||
}
|
||||
|
||||
|
||||
EASTDC_EAMEMORY_DECL void MemclearC(void* pDestination, size_t n)
|
||||
{
|
||||
memset(pDestination, 0, n);
|
||||
}
|
||||
|
||||
|
||||
EASTDC_EAMEMORY_DECL uint8_t* Memset8(void* pDestination, uint8_t c, size_t uint8Count)
|
||||
{
|
||||
return (uint8_t*)memset(pDestination, c, uint8Count);
|
||||
}
|
||||
|
||||
|
||||
EASTDC_EAMEMORY_DECL uint8_t* Memset8C(void* pDestination, uint8_t c, size_t uint8Count)
|
||||
{
|
||||
if(c == 0)
|
||||
{
|
||||
Memclear(pDestination, uint8Count);
|
||||
return (uint8_t*)pDestination;
|
||||
}
|
||||
|
||||
return (uint8_t*)memset(pDestination, c, uint8Count);
|
||||
}
|
||||
|
||||
|
||||
EASTDC_EAMEMORY_DECL uint8_t* Memset8_128(void* pDestination, uint8_t c, size_t uint8Count)
|
||||
{
|
||||
// To do: Make an optimized version of this.
|
||||
return (uint8_t*)memset(pDestination, c, uint8Count);
|
||||
}
|
||||
|
||||
|
||||
EASTDC_EAMEMORY_DECL uint8_t* Memset8_128C(void* pDestination, uint8_t c, size_t uint8Count)
|
||||
{
|
||||
if(c == 0)
|
||||
{
|
||||
Memclear(pDestination, uint8Count);
|
||||
return (uint8_t*)pDestination;
|
||||
}
|
||||
|
||||
return (uint8_t*)memset(pDestination, c, uint8Count);
|
||||
}
|
||||
|
||||
|
||||
EASTDC_EAMEMORY_DECL void* MemsetPointer(void* pDestination, const void* const pValue, size_t ptrCount)
|
||||
{
|
||||
#if (EA_PLATFORM_PTR_SIZE == 8)
|
||||
return Memset64(pDestination, (uint64_t)(uintptr_t)pValue, ptrCount);
|
||||
#else
|
||||
return Memset32(pDestination, (uint32_t)(uintptr_t)pValue, ptrCount);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
EASTDC_EAMEMORY_DECL char* Memcpy(void* EA_RESTRICT pDestination, const void* EA_RESTRICT pSource, size_t nByteCount)
|
||||
{
|
||||
EA_ASSERT((pSource >= (const uint8_t*)pDestination + nByteCount) || // Verify the memory doesn't overlap.
|
||||
(pDestination >= (const uint8_t*)pSource + nByteCount));
|
||||
|
||||
// Some compilers offer __builtin_memcpy, but we haven't found it to be faster than memcpy for any platforms
|
||||
// and it's significantly slower than memcpy for some platform/compiler combinations (e.g. SN compiler on PS3).
|
||||
return (char*)memcpy(pDestination, pSource, nByteCount);
|
||||
}
|
||||
|
||||
|
||||
EASTDC_EAMEMORY_DECL char* MemcpyC(void* EA_RESTRICT pDestination, const void* EA_RESTRICT pSource, size_t nByteCount)
|
||||
{
|
||||
EA_ASSERT((pSource >= (const uint8_t*)pDestination + nByteCount) || // Verify the memory doesn't overlap.
|
||||
(pDestination >= (const uint8_t*)pSource + nByteCount));
|
||||
|
||||
return (char*)memcpy(pDestination, pSource, nByteCount);
|
||||
}
|
||||
|
||||
|
||||
EASTDC_EAMEMORY_DECL char* MemcpyS(void* EA_RESTRICT pDestination, const void* EA_RESTRICT pSource, size_t nByteCount)
|
||||
{
|
||||
EA_ASSERT((pSource >= (const uint8_t*)pDestination + nByteCount) || // Verify the memory doesn't overlap.
|
||||
(pDestination >= (const uint8_t*)pSource + nByteCount));
|
||||
|
||||
|
||||
return (char*)memcpy(pDestination, pSource, nByteCount);
|
||||
}
|
||||
|
||||
|
||||
EASTDC_EAMEMORY_DECL char* Memcpy128(void* EA_RESTRICT pDestination, const void* EA_RESTRICT pSource, size_t nByteCount)
|
||||
{
|
||||
EA_ASSERT((pSource >= (const uint8_t*)pDestination + nByteCount) || // Verify the memory doesn't overlap.
|
||||
(pDestination >= (const uint8_t*)pSource + nByteCount));
|
||||
|
||||
// This is expected to work with both cacheable and uncacheable memory,
|
||||
// thus we can't use all alternative optimized functions that exist for memcpy.
|
||||
return (char*)memcpy(pDestination, pSource, nByteCount);
|
||||
}
|
||||
|
||||
|
||||
EASTDC_EAMEMORY_DECL char* Memcpy128C(void* EA_RESTRICT pDestination, const void* EA_RESTRICT pSource, size_t nByteCount)
|
||||
{
|
||||
EA_ASSERT((pSource >= (const uint8_t*)pDestination + nByteCount) || // Verify the memory doesn't overlap.
|
||||
(pDestination >= (const uint8_t*)pSource + nByteCount));
|
||||
|
||||
return (char*)memcpy(pDestination, pSource, nByteCount);
|
||||
}
|
||||
|
||||
|
||||
EASTDC_EAMEMORY_DECL char* Memmove(void* pDestination, const void* pSource, size_t nByteCount)
|
||||
{
|
||||
// Some compilers offer __builtin_memmove, but we haven't found it to be faster than memcpy for any platforms
|
||||
// and it's significantly slower than memcpy for some platform/compiler combinations (e.g. SN compiler on PS3).
|
||||
return (char*)memmove(pDestination, pSource, nByteCount);
|
||||
}
|
||||
|
||||
|
||||
EASTDC_EAMEMORY_DECL char* MemmoveC(void* pDestination, const void* pSource, size_t nByteCount)
|
||||
{
|
||||
return (char*)memmove(pDestination, pSource, nByteCount);
|
||||
}
|
||||
|
||||
} // namespace StdC
|
||||
} // namespace EA
|
||||
|
||||
|
||||
// See the top of this file for #define EASTDC_API and an explanation of it.
|
||||
#undef EASTDC_EAMEMORY_DECL
|
||||
|
||||
@@ -0,0 +1,706 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Electronic Arts Inc. All rights reserved.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// This is a curtailed version of eastl::intrusive_list.
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#ifndef EASTDC_INTRUSIVELIST_H
|
||||
#define EASTDC_INTRUSIVELIST_H
|
||||
|
||||
|
||||
#include <EABase/eabase.h>
|
||||
EA_DISABLE_ALL_VC_WARNINGS()
|
||||
#include <stddef.h>
|
||||
EA_RESTORE_ALL_VC_WARNINGS()
|
||||
|
||||
|
||||
// Disabled because we want to remove our dependence on STL.
|
||||
//
|
||||
//#ifdef _MSC_VER
|
||||
// #pragma warning(push, 0)
|
||||
//#endif
|
||||
//#include <iterator>
|
||||
//#ifdef _MSC_VER
|
||||
// #pragma warning(pop)
|
||||
//#endif
|
||||
|
||||
|
||||
namespace EA
|
||||
{
|
||||
namespace StdC
|
||||
{
|
||||
|
||||
template <typename Category, typename T, typename Distance = ptrdiff_t,
|
||||
typename Pointer = T*, typename Reference = T&>
|
||||
struct iterator
|
||||
{
|
||||
typedef Category iterator_category;
|
||||
typedef T value_type;
|
||||
typedef Distance difference_type;
|
||||
typedef Pointer pointer;
|
||||
typedef Reference reference;
|
||||
};
|
||||
|
||||
|
||||
/* Disabled because we want to remove our dependence on STL.
|
||||
|
||||
// struct iterator_traits
|
||||
template <typename Iterator>
|
||||
struct iterator_traits
|
||||
{
|
||||
typedef typename Iterator::iterator_category iterator_category;
|
||||
typedef typename Iterator::value_type value_type;
|
||||
typedef typename Iterator::difference_type difference_type;
|
||||
typedef typename Iterator::pointer pointer;
|
||||
typedef typename Iterator::reference reference;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct iterator_traits<T*>
|
||||
{
|
||||
typedef std::random_access_iterator_tag iterator_category;
|
||||
typedef T value_type;
|
||||
typedef ptrdiff_t difference_type;
|
||||
typedef T* pointer;
|
||||
typedef T& reference;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct iterator_traits<const T*>
|
||||
{
|
||||
typedef std::random_access_iterator_tag iterator_category;
|
||||
typedef T value_type;
|
||||
typedef ptrdiff_t difference_type;
|
||||
typedef const T* pointer;
|
||||
typedef const T& reference;
|
||||
};
|
||||
|
||||
|
||||
/// reverse_iterator
|
||||
///
|
||||
/// From the C++ standard:
|
||||
/// Bidirectional and random access iterators have corresponding reverse
|
||||
/// iterator adaptors that iterate through the data structure in the
|
||||
/// opposite direction. They have the same signatures as the corresponding
|
||||
/// iterators. The fundamental relation between a reverse iterator and its
|
||||
/// corresponding iterator i is established by the identity:
|
||||
/// &*(reverse_iterator(i)) == &*(i - 1).
|
||||
/// This mapping is dictated by the fact that while there is always a pointer
|
||||
/// past the end of an array, there might not be a valid pointer before the
|
||||
/// beginning of an array.
|
||||
///
|
||||
template <typename Iterator>
|
||||
class reverse_iterator : public iterator<typename EA::StdC::iterator_traits<Iterator>::iterator_category,
|
||||
typename EA::StdC::iterator_traits<Iterator>::value_type,
|
||||
typename EA::StdC::iterator_traits<Iterator>::difference_type,
|
||||
typename EA::StdC::iterator_traits<Iterator>::pointer,
|
||||
typename EA::StdC::iterator_traits<Iterator>::reference>
|
||||
{
|
||||
public:
|
||||
typedef Iterator iterator_type;
|
||||
typedef typename EA::StdC::iterator_traits<Iterator>::pointer pointer;
|
||||
typedef typename EA::StdC::iterator_traits<Iterator>::reference reference;
|
||||
typedef typename EA::StdC::iterator_traits<Iterator>::difference_type difference_type;
|
||||
|
||||
protected:
|
||||
Iterator mIterator;
|
||||
|
||||
public:
|
||||
reverse_iterator() // It's important that we construct mIterator, because if Iterator
|
||||
: mIterator() { } // is a pointer, there's a difference between doing it and not.
|
||||
|
||||
explicit reverse_iterator(iterator_type i)
|
||||
: mIterator(i) { }
|
||||
|
||||
reverse_iterator(const reverse_iterator& ri)
|
||||
: mIterator(ri.mIterator) { }
|
||||
|
||||
template <typename U>
|
||||
reverse_iterator(const reverse_iterator<U>& ri)
|
||||
: mIterator(ri.base()) { }
|
||||
|
||||
// This operator= isn't in the standard, but the the C++
|
||||
// library working group has tentatively approved it, as it
|
||||
// allows const and non-const reverse_iterators to interoperate.
|
||||
template <typename U>
|
||||
reverse_iterator<Iterator>& operator=(const reverse_iterator<U>& ri)
|
||||
{ mIterator = ri.base(); return *this; }
|
||||
|
||||
iterator_type base() const
|
||||
{ return mIterator; }
|
||||
|
||||
reference operator*() const
|
||||
{
|
||||
iterator_type i(mIterator);
|
||||
return *--i;
|
||||
}
|
||||
|
||||
pointer operator->() const
|
||||
{ return &(operator*()); }
|
||||
|
||||
reverse_iterator& operator++()
|
||||
{ --mIterator; return *this; }
|
||||
|
||||
reverse_iterator operator++(int)
|
||||
{
|
||||
reverse_iterator ri(*this);
|
||||
--mIterator;
|
||||
return ri;
|
||||
}
|
||||
|
||||
reverse_iterator& operator--()
|
||||
{ ++mIterator; return *this; }
|
||||
|
||||
reverse_iterator operator--(int)
|
||||
{
|
||||
reverse_iterator ri(*this);
|
||||
++mIterator;
|
||||
return ri;
|
||||
}
|
||||
|
||||
reverse_iterator operator+(difference_type n) const
|
||||
{ return reverse_iterator(mIterator - n); }
|
||||
|
||||
reverse_iterator& operator+=(difference_type n)
|
||||
{ mIterator -= n; return *this; }
|
||||
|
||||
reverse_iterator operator-(difference_type n) const
|
||||
{ return reverse_iterator(mIterator + n); }
|
||||
|
||||
reverse_iterator& operator-=(difference_type n)
|
||||
{ mIterator += n; return *this; }
|
||||
|
||||
reference operator[](difference_type n) const
|
||||
{ return mIterator[-n - 1]; }
|
||||
};
|
||||
*/
|
||||
|
||||
|
||||
/// intrusive_list_node
|
||||
///
|
||||
struct intrusive_list_node
|
||||
{
|
||||
intrusive_list_node* mpNext;
|
||||
intrusive_list_node* mpPrev;
|
||||
};
|
||||
|
||||
|
||||
/// intrusive_list_iterator
|
||||
///
|
||||
template <typename T, typename Pointer, typename Reference>
|
||||
class intrusive_list_iterator
|
||||
{
|
||||
public:
|
||||
typedef intrusive_list_iterator<T, Pointer, Reference> this_type;
|
||||
typedef intrusive_list_iterator<T, T*, T&> iterator;
|
||||
typedef intrusive_list_iterator<T, const T*, const T&> const_iterator;
|
||||
typedef T value_type;
|
||||
typedef T node_type;
|
||||
typedef ptrdiff_t difference_type;
|
||||
typedef Pointer pointer;
|
||||
typedef Reference reference;
|
||||
//typedef std::bidirectional_iterator_tag iterator_category;
|
||||
|
||||
public:
|
||||
pointer mpNode; // Needs to be public for operator==() to work
|
||||
|
||||
public:
|
||||
intrusive_list_iterator();
|
||||
explicit intrusive_list_iterator(pointer pNode); // Note that you can also construct an iterator from T via this, since value_type == node_type.
|
||||
intrusive_list_iterator(const iterator& x);
|
||||
|
||||
reference operator*() const;
|
||||
pointer operator->() const;
|
||||
|
||||
intrusive_list_iterator& operator++();
|
||||
intrusive_list_iterator& operator--();
|
||||
|
||||
intrusive_list_iterator operator++(int);
|
||||
intrusive_list_iterator operator--(int);
|
||||
|
||||
}; // class intrusive_list_iterator
|
||||
|
||||
|
||||
|
||||
/// intrusive_list_base
|
||||
///
|
||||
class intrusive_list_base
|
||||
{
|
||||
public:
|
||||
typedef size_t size_type; // See config.h for the definition of this, which defaults to uint32_t.
|
||||
typedef ptrdiff_t difference_type;
|
||||
|
||||
protected:
|
||||
intrusive_list_node mAnchor; ///< Sentinel node (end). All data nodes are linked in a ring from this node.
|
||||
|
||||
public:
|
||||
intrusive_list_base();
|
||||
|
||||
bool empty() const;
|
||||
size_t size() const; ///< Returns the number of elements in the list; O(n).
|
||||
void clear(); ///< Clears the list; O(1). No deallocation occurs.
|
||||
void pop_front(); ///< Removes an element from the front of the list; O(1). The element must exist, but is not deallocated.
|
||||
void pop_back(); ///< Removes an element from the back of the list; O(1). The element must exist, but is not deallocated.
|
||||
|
||||
}; // class intrusive_list_base
|
||||
|
||||
|
||||
|
||||
template <typename T = intrusive_list_node>
|
||||
class intrusive_list : public intrusive_list_base
|
||||
{
|
||||
public:
|
||||
typedef intrusive_list<T> this_type;
|
||||
typedef intrusive_list_base base_type;
|
||||
typedef T node_type;
|
||||
typedef T value_type;
|
||||
typedef typename base_type::size_type size_type;
|
||||
typedef typename base_type::difference_type difference_type;
|
||||
typedef T& reference;
|
||||
typedef const T& const_reference;
|
||||
typedef T* pointer;
|
||||
typedef const T* const_pointer;
|
||||
typedef intrusive_list_iterator<T, T*, T&> iterator;
|
||||
typedef intrusive_list_iterator<T, const T*, const T&> const_iterator;
|
||||
//typedef EA::StdC::reverse_iterator<iterator> reverse_iterator;
|
||||
//typedef EA::StdC::reverse_iterator<const_iterator> const_reverse_iterator;
|
||||
|
||||
public:
|
||||
intrusive_list(); ///< Creates an empty list.
|
||||
intrusive_list(const this_type& x); ///< Creates an empty list; ignores the argument.
|
||||
this_type& operator=(const this_type& x); ///< Clears the list; ignores the argument.
|
||||
|
||||
iterator begin(); ///< Returns an iterator pointing to the first element in the list.
|
||||
const_iterator begin() const; ///< Returns a const_iterator pointing to the first element in the list.
|
||||
iterator end(); ///< Returns an iterator pointing one-after the last element in the list.
|
||||
const_iterator end() const; ///< Returns a const_iterator pointing one-after the last element in the list.
|
||||
//reverse_iterator rbegin(); ///< Returns a reverse_iterator pointing at the end of the list (start of the reverse sequence).
|
||||
//const_reverse_iterator rbegin() const; ///< Returns a const_reverse_iterator pointing at the end of the list (start of the reverse sequence).
|
||||
//reverse_iterator rend(); ///< Returns a reverse_iterator pointing at the start of the list (end of the reverse sequence).
|
||||
//const_reverse_iterator rend() const; ///< Returns a const_reverse_iterator pointing at the start of the list (end of the reverse sequence).
|
||||
|
||||
reference front(); ///< Returns a reference to the first element. The list must be empty.
|
||||
const_reference front() const; ///< Returns a const reference to the first element. The list must be empty.
|
||||
reference back(); ///< Returns a reference to the last element. The list must be empty.
|
||||
const_reference back() const; ///< Returns a const reference to the last element. The list must be empty.
|
||||
|
||||
void push_front(T& x); ///< Adds an element to the front of the list; O(1). The element is not copied. The element must not be in any other list.
|
||||
void push_back(T& x); ///< Adds an element to the back of the list; O(1). The element is not copied. The element must not be in any other list.
|
||||
|
||||
bool contains(const T& x) const; ///< Returns true if the given element is in the list; O(n). Equivalent to (locate(x) != end()).
|
||||
|
||||
iterator locate(T& x); ///< Converts a reference to an object in the list back to an iterator, or returns end() if it is not part of the list. O(n)
|
||||
const_iterator locate(const T& x) const; ///< Converts a const reference to an object in the list back to a const iterator, or returns end() if it is not part of the list. O(n)
|
||||
|
||||
iterator insert(iterator pos, T& x); ///< Inserts an element before the element pointed to by the iterator. O(1)
|
||||
iterator erase(iterator pos); ///< Erases the element pointed to by the iterator. O(1)
|
||||
iterator erase(iterator pos, iterator last); ///< Erases elements within the iterator range [pos, last). O(1)
|
||||
void swap(intrusive_list&); ///< Swaps the contents of two intrusive lists; O(1).
|
||||
|
||||
static void remove(T& value); ///< Erases an element from a list; O(1). Note that this is static so you don't need to know which list the element, although it must be in some list.
|
||||
|
||||
}; // intrusive_list
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// intrusive_list_iterator
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <typename T, typename Pointer, typename Reference>
|
||||
inline intrusive_list_iterator<T, Pointer, Reference>::intrusive_list_iterator()
|
||||
{
|
||||
// Empty
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename Pointer, typename Reference>
|
||||
inline intrusive_list_iterator<T, Pointer, Reference>::intrusive_list_iterator(pointer pNode)
|
||||
: mpNode(pNode)
|
||||
{
|
||||
// Empty
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename Pointer, typename Reference>
|
||||
inline intrusive_list_iterator<T, Pointer, Reference>::intrusive_list_iterator(const iterator& x)
|
||||
: mpNode(x.mpNode)
|
||||
{
|
||||
// Empty
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename Pointer, typename Reference>
|
||||
inline typename intrusive_list_iterator<T, Pointer, Reference>::reference
|
||||
intrusive_list_iterator<T, Pointer, Reference>::operator*() const
|
||||
{
|
||||
return *mpNode;
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename Pointer, typename Reference>
|
||||
inline typename intrusive_list_iterator<T, Pointer, Reference>::pointer
|
||||
intrusive_list_iterator<T, Pointer, Reference>::operator->() const
|
||||
{
|
||||
return mpNode;
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename Pointer, typename Reference>
|
||||
inline typename intrusive_list_iterator<T, Pointer, Reference>::this_type&
|
||||
intrusive_list_iterator<T, Pointer, Reference>::operator++()
|
||||
{
|
||||
mpNode = static_cast<node_type*>(mpNode->mpNext);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename Pointer, typename Reference>
|
||||
inline typename intrusive_list_iterator<T, Pointer, Reference>::this_type
|
||||
intrusive_list_iterator<T, Pointer, Reference>::operator++(int)
|
||||
{
|
||||
intrusive_list_iterator it(*this);
|
||||
mpNode = static_cast<node_type*>(mpNode->mpNext);
|
||||
return it;
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename Pointer, typename Reference>
|
||||
inline typename intrusive_list_iterator<T, Pointer, Reference>::this_type&
|
||||
intrusive_list_iterator<T, Pointer, Reference>::operator--()
|
||||
{
|
||||
mpNode = static_cast<node_type*>(mpNode->mpPrev);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename Pointer, typename Reference>
|
||||
inline typename intrusive_list_iterator<T, Pointer, Reference>::this_type
|
||||
intrusive_list_iterator<T, Pointer, Reference>::operator--(int)
|
||||
{
|
||||
intrusive_list_iterator it(*this);
|
||||
mpNode = static_cast<node_type*>(mpNode->mpPrev);
|
||||
return it;
|
||||
}
|
||||
|
||||
|
||||
// The C++ defect report #179 requires that we support comparisons between const and non-const iterators.
|
||||
// Thus we provide additional template paremeters here to support this. The defect report does not
|
||||
// require us to support comparisons between reverse_iterators and const_reverse_iterators.
|
||||
template <typename T, typename PointerA, typename ReferenceA, typename PointerB, typename ReferenceB>
|
||||
inline bool operator==(const intrusive_list_iterator<T, PointerA, ReferenceA>& a,
|
||||
const intrusive_list_iterator<T, PointerB, ReferenceB>& b)
|
||||
{
|
||||
return a.mpNode == b.mpNode;
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename PointerA, typename ReferenceA, typename PointerB, typename ReferenceB>
|
||||
inline bool operator!=(const intrusive_list_iterator<T, PointerA, ReferenceA>& a,
|
||||
const intrusive_list_iterator<T, PointerB, ReferenceB>& b)
|
||||
{
|
||||
return a.mpNode != b.mpNode;
|
||||
}
|
||||
|
||||
|
||||
// We provide a version of operator!= for the case where the iterators are of the
|
||||
// same type. This helps prevent ambiguity errors in the presence of rel_ops.
|
||||
template <typename T, typename Pointer, typename Reference>
|
||||
inline bool operator!=(const intrusive_list_iterator<T, Pointer, Reference>& a,
|
||||
const intrusive_list_iterator<T, Pointer, Reference>& b)
|
||||
{
|
||||
return a.mpNode != b.mpNode;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// intrusive_list_base
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
inline intrusive_list_base::intrusive_list_base()
|
||||
{
|
||||
mAnchor.mpNext = mAnchor.mpPrev = &mAnchor;
|
||||
}
|
||||
|
||||
|
||||
inline bool intrusive_list_base::empty() const
|
||||
{
|
||||
return mAnchor.mpPrev == &mAnchor;
|
||||
}
|
||||
|
||||
|
||||
inline intrusive_list_base::size_type intrusive_list_base::size() const
|
||||
{
|
||||
const intrusive_list_node* p = &mAnchor;
|
||||
size_type n = (size_type)-1;
|
||||
|
||||
do {
|
||||
++n;
|
||||
p = p->mpNext;
|
||||
} while(p != &mAnchor);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
inline void intrusive_list_base::clear()
|
||||
{
|
||||
mAnchor.mpNext = mAnchor.mpPrev = &mAnchor;
|
||||
}
|
||||
|
||||
|
||||
inline void intrusive_list_base::pop_front()
|
||||
{
|
||||
mAnchor.mpNext->mpNext->mpPrev = &mAnchor;
|
||||
mAnchor.mpNext = mAnchor.mpNext->mpNext;
|
||||
}
|
||||
|
||||
|
||||
inline void intrusive_list_base::pop_back()
|
||||
{
|
||||
mAnchor.mpPrev->mpPrev->mpNext = &mAnchor;
|
||||
mAnchor.mpPrev = mAnchor.mpPrev->mpPrev;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// intrusive_list
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <typename T>
|
||||
inline intrusive_list<T>::intrusive_list()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
inline intrusive_list<T>::intrusive_list(const this_type& /*x*/)
|
||||
{
|
||||
// We intentionally ignore argument x.
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
inline typename intrusive_list<T>::this_type& intrusive_list<T>::operator=(const this_type& /*x*/)
|
||||
{
|
||||
return *this; // We intentionally ignore argument x.
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
inline typename intrusive_list<T>::iterator intrusive_list<T>::begin()
|
||||
{
|
||||
return iterator(static_cast<T*>(mAnchor.mpNext));
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
inline typename intrusive_list<T>::const_iterator intrusive_list<T>::begin() const
|
||||
{
|
||||
return const_iterator(static_cast<T*>(mAnchor.mpNext));
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
inline typename intrusive_list<T>::iterator intrusive_list<T>::end()
|
||||
{
|
||||
return iterator(static_cast<T*>(&mAnchor));
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
inline typename intrusive_list<T>::const_iterator intrusive_list<T>::end() const
|
||||
{
|
||||
return const_iterator(static_cast<const T*>(&mAnchor));
|
||||
}
|
||||
|
||||
|
||||
//template <typename T>
|
||||
//inline typename intrusive_list<T>::reverse_iterator intrusive_list<T>::rbegin()
|
||||
//{
|
||||
// return reverse_iterator(iterator(static_cast<T*>(&mAnchor)));
|
||||
//}
|
||||
|
||||
|
||||
//template <typename T>
|
||||
//inline typename intrusive_list<T>::const_reverse_iterator intrusive_list<T>::rbegin() const
|
||||
//{
|
||||
// return const_reverse_iterator(const_iterator(static_cast<const T*>(&mAnchor)));
|
||||
//}
|
||||
|
||||
|
||||
//template <typename T>
|
||||
//inline typename intrusive_list<T>::reverse_iterator intrusive_list<T>::rend()
|
||||
//{
|
||||
// return reverse_iterator(iterator(static_cast<T*>(mAnchor.mpNext)));
|
||||
//}
|
||||
|
||||
|
||||
//template <typename T>
|
||||
//inline typename intrusive_list<T>::const_reverse_iterator intrusive_list<T>::rend() const
|
||||
//{
|
||||
// return const_reverse_iterator(const_iterator(static_cast<const T*>(mAnchor.mpNext)));
|
||||
//}
|
||||
|
||||
|
||||
template <typename T>
|
||||
inline typename intrusive_list<T>::reference intrusive_list<T>::front()
|
||||
{
|
||||
return *static_cast<T*>(mAnchor.mpNext);
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
inline typename intrusive_list<T>::const_reference intrusive_list<T>::front() const
|
||||
{
|
||||
return *static_cast<const T*>(mAnchor.mpNext);
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
inline typename intrusive_list<T>::reference intrusive_list<T>::back()
|
||||
{
|
||||
return *static_cast<T*>(mAnchor.mpPrev);
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
inline typename intrusive_list<T>::const_reference intrusive_list<T>::back() const
|
||||
{
|
||||
return *static_cast<const T*>(mAnchor.mpPrev);
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
inline void intrusive_list<T>::push_front(T& x)
|
||||
{
|
||||
x.mpNext = mAnchor.mpNext;
|
||||
x.mpPrev = &mAnchor;
|
||||
mAnchor.mpNext = &x;
|
||||
x.mpNext->mpPrev = &x;
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
inline void intrusive_list<T>::push_back(T& x)
|
||||
{
|
||||
x.mpPrev = mAnchor.mpPrev;
|
||||
x.mpNext = &mAnchor;
|
||||
mAnchor.mpPrev = &x;
|
||||
x.mpPrev->mpNext = &x;
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
inline bool intrusive_list<T>::contains(const T& x) const
|
||||
{
|
||||
for(const intrusive_list_node* p = mAnchor.mpNext; p != &mAnchor; p = p->mpNext)
|
||||
{
|
||||
if(p == &x)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
inline typename intrusive_list<T>::iterator intrusive_list<T>::locate(T& x)
|
||||
{
|
||||
for(intrusive_list_node* p = (T*)mAnchor.mpNext; p != &mAnchor; p = p->mpNext)
|
||||
{
|
||||
if(p == &x)
|
||||
return iterator(static_cast<T*>(p));
|
||||
}
|
||||
|
||||
return iterator((T*)&mAnchor);
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
inline typename intrusive_list<T>::const_iterator intrusive_list<T>::locate(const T& x) const
|
||||
{
|
||||
for(const intrusive_list_node* p = mAnchor.mpNext; p != &mAnchor; p = p->mpNext)
|
||||
{
|
||||
if(p == &x)
|
||||
return const_iterator(static_cast<const T*>(p));
|
||||
}
|
||||
|
||||
return const_iterator((T*)&mAnchor);
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
inline typename intrusive_list<T>::iterator intrusive_list<T>::insert(iterator pos, T& x)
|
||||
{
|
||||
node_type& next = *pos;
|
||||
node_type& prev = *static_cast<node_type*>(next.mpPrev);
|
||||
prev.mpNext = next.mpPrev = &x;
|
||||
x.mpPrev = &prev;
|
||||
x.mpNext = &next;
|
||||
return iterator(&x);
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
inline typename intrusive_list<T>::iterator intrusive_list<T>::erase(iterator pos)
|
||||
{
|
||||
node_type& prev = *static_cast<node_type*>(pos.mpNode->mpPrev);
|
||||
node_type& next = *static_cast<node_type*>(pos.mpNode->mpNext);
|
||||
prev.mpNext = &next;
|
||||
next.mpPrev = &prev;
|
||||
return iterator(&next);
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
inline typename intrusive_list<T>::iterator intrusive_list<T>::erase(iterator pos, iterator last)
|
||||
{
|
||||
node_type& prev = *static_cast<node_type*>(pos.mpNode->mpPrev);
|
||||
node_type& next = *last.mpNode;
|
||||
prev.mpNext = &next;
|
||||
next.mpPrev = &prev;
|
||||
return last;
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
void intrusive_list<T>::swap(intrusive_list& x)
|
||||
{
|
||||
// swap anchors
|
||||
const intrusive_list_node temp(mAnchor);
|
||||
mAnchor = x.mAnchor;
|
||||
x.mAnchor = temp;
|
||||
|
||||
// Fixup node pointers into the anchor, since the addresses of
|
||||
// the anchors must stay the same with each list.
|
||||
if(mAnchor.mpNext == &x.mAnchor)
|
||||
mAnchor.mpNext = mAnchor.mpPrev = &mAnchor;
|
||||
else
|
||||
mAnchor.mpNext->mpPrev = mAnchor.mpPrev->mpNext = &mAnchor;
|
||||
|
||||
if(x.mAnchor.mpNext == &mAnchor)
|
||||
x.mAnchor.mpNext = x.mAnchor.mpPrev = &x.mAnchor;
|
||||
else
|
||||
x.mAnchor.mpNext->mpPrev = x.mAnchor.mpPrev->mpNext = &x.mAnchor;
|
||||
}
|
||||
|
||||
|
||||
} // namespace StdC
|
||||
|
||||
} // namespace EA
|
||||
|
||||
|
||||
|
||||
|
||||
#endif // Header include guard
|
||||
|
||||
@@ -0,0 +1,321 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Electronic Arts Inc. All rights reserved.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#ifndef EASTDC_INTERNAL_SCANFCORE_H
|
||||
#define EASTDC_INTERNAL_SCANFCORE_H
|
||||
|
||||
|
||||
#include <EABase/eabase.h>
|
||||
#include <EAStdC/internal/Config.h>
|
||||
#include <EAStdC/internal/stdioEA.h>
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <float.h>
|
||||
#include <math.h>
|
||||
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4127) // conditional expression is constant.
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
namespace EA
|
||||
{
|
||||
namespace StdC
|
||||
{
|
||||
namespace ScanfLocal
|
||||
{
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Constants
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
typedef float float32_t;
|
||||
typedef double float64_t;
|
||||
|
||||
union FloatUint32
|
||||
{
|
||||
uint32_t i;
|
||||
float f;
|
||||
};
|
||||
|
||||
union DoubleUint64
|
||||
{
|
||||
uint64_t i;
|
||||
double f;
|
||||
};
|
||||
|
||||
const int kConversionBufferSize = EASCANF_FIELD_MAX + 8; // This is the size for a single field's representation, and not the entire formatted string representation. Multiple references say that this value must be at least 509, but I can't find that in the C99 standard.
|
||||
const int kMaxWidth = kConversionBufferSize - 8;
|
||||
const int kMaxPrecision = kConversionBufferSize - 8;
|
||||
const int kNoPrecisionLimit = INT_MAX;
|
||||
const int kNoWidthLimit = INT_MAX;
|
||||
const int kMinDoubleExponent = DBL_MIN_10_EXP; // From <float.h>
|
||||
const int kMaxDoubleExponent = DBL_MAX_10_EXP; // From <float.h>
|
||||
const int kFormatError = 0;
|
||||
const int kMaxSignificandDigits = 24;
|
||||
const uint32_t kFloat32PositiveInfinityBits = UINT32_C(0x7F800000);
|
||||
const uint64_t kFloat64PositiveInfinityBits = UINT64_C(0x7FF0000000000000);
|
||||
const FloatUint32 kInfinityUnion32 = { kFloat32PositiveInfinityBits };
|
||||
const DoubleUint64 kInfinityUnion64 = { kFloat64PositiveInfinityBits };
|
||||
//const float32_t kFloat32Infinity = kInfinityUnion32.f;
|
||||
//const float64_t kFloat64Infinity = kInfinityUnion64.f;
|
||||
const uint32_t kFloat32NANBits = UINT32_C(0x7FFFFFFF);
|
||||
const uint64_t kFloat64NANBits = UINT64_C(0x7FFFFFFFFFFFFFFF);
|
||||
const FloatUint32 kNANUnion32 = { kFloat32NANBits };
|
||||
const DoubleUint64 kNANUnion64 = { kFloat64NANBits };
|
||||
//const float32_t kFloat32NAN = kNANUnion32.f;
|
||||
const float64_t kFloat64NAN = kNANUnion64.f;
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Enumerations
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
enum Alignment // The C99 standard incorrectly uses the term "justification" when it means to use "alignment". There is no such thing as left or right justification, but thinking so is a common mistake for typography newbies.
|
||||
{ //
|
||||
kAlignmentLeft, //
|
||||
kAlignmentRight, //
|
||||
kAlignmentZeroFill //
|
||||
};
|
||||
|
||||
enum Sign
|
||||
{
|
||||
kSignNone, // Never show any sign.
|
||||
kSignMinus, // Only show sign if minus (this is default).
|
||||
kSignMinusPlus, // Show sign if plus or minus.
|
||||
kSignSpace // Show space in place of a plus sign.
|
||||
};
|
||||
|
||||
enum Modifier
|
||||
{
|
||||
kModifierNone, // No modifier, use the type as-is.
|
||||
kModifierChar, // Use char instead of int. Specified by hh in front of d, i, o, u, x, or X.
|
||||
kModifierShort, // Use short instead of int. Specified by h in front of d, i, o, u, x, or X.
|
||||
kModifierInt, // This is a placeholder, as integer is the default fd for integral types.
|
||||
kModifierLong, // Use long instead of int. Specified by l in front of d, i, o, u, x, or X.
|
||||
kModifierLongLong, // Use long long instead of int. Specified by ll in front of d, i, o, u, x, or X.
|
||||
kModifierMax_t, // Use intmax_t argument. Specified by 'j' in front of d, i, o, u, x, or X.
|
||||
kModifierSize_t, // Use size_t argument. Specified by 'z' in front of d, i, o, u, x, or X.
|
||||
kModifierPtrdiff_t, // Use ptrdiff_t argument. Specified by 't' in front of d, i, o, u, x, or X.
|
||||
kModifierDouble, // Use double instead of float. Specified by nothing in front of e, E, f, F, g, G for printf and l for scanf.
|
||||
kModifierLongDouble, // Use long double instead of double. Specified by l in front of e, E, f, F, g, G for printf and L for scanf.
|
||||
kModifierWChar, // Use wide char instead of char. Specified by l (in front of c).
|
||||
kModifierInt8, // Use int8_t or uint8_t. Specified by I8 in front of d, i, o, u.
|
||||
kModifierInt16, // Use int16_t or uint16_t. Specified by I16 in front of d, i, o, u.
|
||||
kModifierInt32, // Use int32_t or uint32_t. Specified by I32 in front of d, i, o, u.
|
||||
kModifierInt64, // Use int64_t or uint64_t. Specified by I64 in front of d, i, o, u.
|
||||
kModifierInt128 // Use int128_t or uint128_t. Specified by I128 in front of d, i, o, u.
|
||||
};
|
||||
|
||||
|
||||
enum ReadIntegerState // The ^ chars below indicate what part of the string the state refers to.
|
||||
{ // " -00123456"
|
||||
kRISLeadingSpace = 0x0001, // ^
|
||||
kRISZeroTest = 0x0002, // ^
|
||||
kRISAfterZero = 0x0004, // ^
|
||||
kRISReadFirstDigit = 0x0008, // ^
|
||||
kRISReadDigits = 0x0010, // ^
|
||||
kRISEnd = 0x0020, // ^
|
||||
kRISError = 0x0040 // ^
|
||||
};
|
||||
|
||||
enum ReadDoubleState // The ^ chars below indicate what part of the string the state refers to.
|
||||
{ // " -123.345e-0023"
|
||||
kRDSLeadingSpace = 0x0001, // ^
|
||||
kRDSSignificandBegin = 0x0002, // ^
|
||||
kRDSSignificandLeading = 0x0004, // ^
|
||||
kRDSIntegerDigits = 0x0008, // ^
|
||||
kRDSFractionBegin = 0x0010, // ^
|
||||
kRDSFractionLeading = 0x0020, // ^
|
||||
kRDSFractionDigits = 0x0040, // ^
|
||||
kRDSSignificandEnd = 0x0080, // ^
|
||||
kRDSExponentBegin = 0x0100, // ^
|
||||
kRDSExponentBeginDigits = 0x0200, // ^
|
||||
kRDSExponentLeading = 0x0400, // ^
|
||||
kRDSExponentDigits = 0x0800, // ^
|
||||
kRDSInfinity = 0x1000, // ^
|
||||
kRDSNAN = 0x2000, // ^
|
||||
kRDSEnd = 0x4000, // ^
|
||||
kRDSError = 0x8000 // ^
|
||||
};
|
||||
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// CharBitmap
|
||||
//
|
||||
// Used to do fast char set tests.
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct CharBitmap
|
||||
{
|
||||
uint32_t mBits[8]; // 32 bits per uint32_t times 8 values => 256 bits.
|
||||
|
||||
CharBitmap()
|
||||
{ memset(mBits, 0, sizeof(mBits)); }
|
||||
|
||||
int Get(char c) const
|
||||
{ return (int)mBits[(uint8_t)(unsigned)c >> 5] & (1 << (c & 31)); }
|
||||
|
||||
// This isn't correct. To do this completely right for all uses of scanf,
|
||||
// we need to make a 16 bit bitmap which handles 65536 bits. However, this
|
||||
// would entail rather obscure scanf %[...]s usage for Unicode text.
|
||||
int Get(char16_t c) const // If c >= 256, we return whatever the first bit is, since it will be equal to what bits 256 - 65536 are meant to be.
|
||||
{ if(c < 256) return (int)mBits[(uint8_t)(unsigned)c >> 5] & (1 << (c & 31)); else return (int)(mBits[0] & 0x00000001); }
|
||||
|
||||
int Get(char32_t c) const // If c >= 256, we return whatever the first bit is, since it will be equal to what bits 256 - 2^32 are meant to be.
|
||||
{ if(c < 256) return (int)mBits[(uint8_t)(unsigned)c >> 5] & (1 << (c & 31)); else return (int)(mBits[0] & 0x00000001); }
|
||||
|
||||
void Set(char c)
|
||||
{ mBits[(uint8_t)(unsigned)c >> 5] |= (1 << (c & 31)); }
|
||||
|
||||
void Set(char16_t c)
|
||||
{ if((uint16_t)c < 256) mBits[(uint8_t)(uint16_t)c >> 5] |= (1 << (c & 31)); }
|
||||
|
||||
void Set(char32_t c)
|
||||
{ if((uint32_t)c < 256) mBits[(uint8_t)(uint32_t)c >> 5] |= (1 << (c & 31)); }
|
||||
|
||||
void SetAll()
|
||||
{ memset(mBits, 0xffffffff, sizeof(mBits)); }
|
||||
|
||||
void NegateAll()
|
||||
{ for(size_t i = 0; i < 8; i++) mBits[i] = ~mBits[i]; }
|
||||
|
||||
void ClearAll()
|
||||
{ memset(mBits, 0, sizeof(mBits)); }
|
||||
|
||||
void Clear(int c)
|
||||
{ mBits[(uint8_t)(unsigned)c >> 5] &= ~(1 << (c & 31)); }
|
||||
};
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// DoubleValue
|
||||
//
|
||||
// Used as the lowest level string representation of a double.
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct DoubleValue
|
||||
{
|
||||
char mSigStr[kMaxSignificandDigits + 1]; // String
|
||||
int16_t mSigLen; // Length of string
|
||||
int16_t mExponent; // Exponent value.
|
||||
|
||||
DoubleValue()
|
||||
: mSigLen(0), mExponent(0)
|
||||
{ mSigStr[0] = 0; }
|
||||
|
||||
double ToDouble() const;
|
||||
};
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// FormatData
|
||||
//
|
||||
// Used by Scanf's state machine.
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct FormatData
|
||||
{
|
||||
int mnWidth; // Field width in characters.
|
||||
Modifier mModifier; // One of enum Modifier. Example is 'h' for 'short'.
|
||||
int mnType; // 'c', 'C', 'b', 'd', 'i', 'u', 'e', 'E', 'f', 'g', 'G', 'o', 's', 'S', 'x', 'X', 'p', 'n', '%', or 0 (for error).
|
||||
bool mbWidthSpecified; // True if the field width was specified by the user.
|
||||
bool mbSkipAssignment; // True if the user used * in the format, which means to eat the input field without assigning it to anything.
|
||||
CharBitmap mCharBitmap; // Allows fast character inclusion/exclusion tests.
|
||||
int mDecimalPoint; // Typically equal to '.', but could be ',' for some locales.
|
||||
|
||||
FormatData()
|
||||
: mnWidth(kNoWidthLimit),
|
||||
mModifier(kModifierNone),
|
||||
mnType(kFormatError),
|
||||
mbWidthSpecified(false),
|
||||
mbSkipAssignment(false),
|
||||
mCharBitmap(),
|
||||
mDecimalPoint('.')
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// SscanfContext8 / SscanfContext16
|
||||
//
|
||||
// Used by StringReader8 / StringReader16
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct SscanfContext8
|
||||
{
|
||||
const char* mpSource;
|
||||
int mbEndFound;
|
||||
|
||||
SscanfContext8(const char* pSource = NULL)
|
||||
: mpSource(pSource),
|
||||
mbEndFound(0)
|
||||
{}
|
||||
};
|
||||
|
||||
struct SscanfContext16
|
||||
{
|
||||
const char16_t* mpSource;
|
||||
int mbEndFound;
|
||||
|
||||
SscanfContext16(const char16_t* pSource = NULL)
|
||||
: mpSource(pSource),
|
||||
mbEndFound(0)
|
||||
{}
|
||||
};
|
||||
|
||||
struct SscanfContext32
|
||||
{
|
||||
const char32_t* mpSource;
|
||||
int mbEndFound;
|
||||
|
||||
SscanfContext32(const char32_t* pSource = NULL)
|
||||
: mpSource(pSource),
|
||||
mbEndFound(0)
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Reader functions, of type ReadFunction8
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int FILEReader8 (ReadAction readAction, int value, void* pContext);
|
||||
int FILEReader16 (ReadAction readAction, int value, void* pContext);
|
||||
int FILEReader32 (ReadAction readAction, int value, void* pContext);
|
||||
|
||||
int StringReader8 (ReadAction readAction, int value, void* pContext);
|
||||
int StringReader16(ReadAction readAction, int value, void* pContext);
|
||||
int StringReader32(ReadAction readAction, int value, void* pContext);
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// VscanfCore
|
||||
//
|
||||
int VscanfCore(ReadFunction8 pReadFunction8, void* pReadFunction8Context, const char* pFormat, va_list arguments);
|
||||
int VscanfCore(ReadFunction16 pReadFunction16, void* pReadFunction8Context, const char16_t* pFormat, va_list arguments);
|
||||
int VscanfCore(ReadFunction32 pReadFunction32, void* pReadFunction8Context, const char32_t* pFormat, va_list arguments);
|
||||
|
||||
|
||||
} // namespace ScanfLocal
|
||||
} // namespace StdC
|
||||
} // namespace EA
|
||||
|
||||
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
|
||||
#endif // Header include guard
|
||||
|
||||
|
||||
@@ -0,0 +1,305 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Electronic Arts Inc. All rights reserved.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef EASTDC_INTERNAL_SPRINTFCORE_H
|
||||
#define EASTDC_INTERNAL_SPRINTFCORE_H
|
||||
|
||||
|
||||
#include <EABase/eabase.h>
|
||||
#include <EAStdC/internal/Config.h>
|
||||
#include <EAStdC/internal/stdioEA.h>
|
||||
#include <EAStdC/EASprintf.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push, 0)
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
#include <float.h>
|
||||
#include <math.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// IsNeg
|
||||
//
|
||||
// bool IsNeg(double x);
|
||||
//
|
||||
#if defined(EA_PLATFORM_MICROSOFT)
|
||||
#define IsNeg(x) ((*((uint64_t*)&(x)) & UINT64_C(0x8000000000000000)) != 0) // This assumes that double is a 64 bit value.
|
||||
#elif defined(__APPLE__) || defined(EA_PLATFORM_ANDROID)
|
||||
inline bool IsNeg(double x) { union { double f; uint64_t i; } u = { x }; return (u.i & UINT64_C(0x8000000000000000)) != 0; }
|
||||
#elif defined(__CYGWIN__) && defined(__GNUC__)
|
||||
inline bool IsNeg(double x) { union { double f; uint64_t i; } u = { x }; return (u.i & UINT64_C(0x8000000000000000)) != 0; }
|
||||
#else
|
||||
inline bool IsNeg(double x) { union { double f; uint64_t i; } u = { x }; return (u.i & UINT64_C(0x8000000000000000)) != 0; }
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// EASPRINTF_MIN / EASPRINTF_MAX
|
||||
//
|
||||
#ifndef EASPRINTF_MIN
|
||||
#define EASPRINTF_MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
#ifndef EASPRINTF_MAX
|
||||
#define EASPRINTF_MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
namespace EA
|
||||
{
|
||||
namespace StdC
|
||||
{
|
||||
|
||||
|
||||
namespace SprintfLocal
|
||||
{
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Constants
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const int kConversionBufferSize = EASPRINTF_FIELD_MAX + 8; // This is the size for a single field's representation, and not the entire formatted string representation. Multiple references say that this value must be at least 509, but I can't find that in the C99 standard.
|
||||
const int kMaxWidth = kConversionBufferSize - 8;
|
||||
const int kMaxPrecision = kConversionBufferSize - 8;
|
||||
const int kNoPrecision = INT_MAX;
|
||||
const int kFormatError = 0;
|
||||
const char kStringNull8[] = { '(', 'n', 'u', 'l', 'l', ')', '\0' }; // Used if the user uses "%s" but passes NULL as the string pointer.
|
||||
const char16_t kStringNull16[] = { '(', 'n', 'u', 'l', 'l', ')', '\0' };
|
||||
const char32_t kStringNull32[] = { '(', 'n', 'u', 'l', 'l', ')', '\0' };
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Enumerations
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
enum Alignment // The C99 standard incorrectly uses the term "justification" when it means to use "alignment". There is no such thing as left or right justification, but thinking so is a common mistake for typography newbies.
|
||||
{ //
|
||||
kAlignmentLeft, //
|
||||
kAlignmentRight, //
|
||||
kAlignmentZeroFill //
|
||||
};
|
||||
|
||||
enum Sign
|
||||
{
|
||||
kSignNone, // Never show any sign.
|
||||
kSignMinus, // Only show sign if minus (this is default).
|
||||
kSignMinusPlus, // Show sign if plus or minus.
|
||||
kSignSpace // Show space in place of a plus sign.
|
||||
};
|
||||
|
||||
enum Modifier
|
||||
{
|
||||
kModifierNone, // No modifier, use the type as-is.
|
||||
kModifierChar, // Use char instead of int. Specified by hh in front of d, i, o, u, x, or X.
|
||||
kModifierShort, // Use short instead of int. Specified by h in front of d, i, o, u, x, or X.
|
||||
kModifierInt, // This is a placeholder, as integer is the default format for integral types.
|
||||
kModifierLong, // Use long instead of int. Specified by l in front of d, i, o, u, x, or X.
|
||||
kModifierLongLong, // Use long long instead of int. Specified by ll in front of d, i, o, u, x, or X.
|
||||
kModifierMax_t, // Use intmax_t argument. Specified by 'j' in front of d, i, o, u, x, or X.
|
||||
kModifierSize_t, // Use size_t argument. Specified by 'z' in front of d, i, o, u, x, or X.
|
||||
kModifierPtrdiff_t, // Use ptrdiff_t argument. Specified by 't' in front of d, i, o, u, x, or X.
|
||||
kModifierDouble, // This is a placeholder, as double is the default format for floating point types.
|
||||
kModifierLongDouble, // Use long double instead of double. Specified by l in front of e, E, f, g, G.
|
||||
kModifierWChar, // Use wide char instead of char. Specified by l (in front of c).
|
||||
kModifierInt8, // Use int8_t or uint8_t. Specified by I8 in front of d, i, o, u.
|
||||
kModifierInt16, // Use int16_t or uint16_t. Specified by I16 in front of d, i, o, u.
|
||||
kModifierInt32, // Use int32_t or uint32_t. Specified by I32 in front of d, i, o, u.
|
||||
kModifierInt64, // Use int64_t or uint64_t. Specified by I64 in front of d, i, o, u.
|
||||
kModifierInt128 // Use int128_t or uint128_t. Specified by I128 in front of d, i, o, u.
|
||||
};
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Structs
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
union AllTypes
|
||||
{
|
||||
char mChar;
|
||||
short mShort;
|
||||
int mInt;
|
||||
long mLong;
|
||||
long long mLongLong;
|
||||
intmax_t mMax;
|
||||
size_t mSize;
|
||||
ptrdiff_t mPtrDiff;
|
||||
double mDouble;
|
||||
long double mLongDouble;
|
||||
wchar_t mWChar;
|
||||
int8_t mInt8;
|
||||
int16_t mInt16;
|
||||
int32_t mInt32;
|
||||
int64_t mInt64;
|
||||
//int128_t mInt128; // Disabled until we support it fully. We have the EAStdC int128_t type we can use.
|
||||
};
|
||||
|
||||
struct FormatData
|
||||
{
|
||||
Alignment mAlignment; // One of enum Alignment.
|
||||
Sign mSign; // One of enum Sign.
|
||||
bool mbAlternativeForm; // See the C99 standard, section 7.19.6.1.6.
|
||||
int mnWidth; // Field width in characters.
|
||||
int mnPrecision; // A value of kNoPrecision means that precision was not specified.
|
||||
Modifier mModifier; // One of enum Modifier. Example is 'h' for 'short'.
|
||||
int mnType; // 'c', 'C', 'b', 'd', 'i', 'u', 'e', 'E', 'f', 'g', 'G', 'o', 's', 'S', 'x', 'X', 'p', 'n', '%', or 0 (for error).
|
||||
int mDecimalPoint; // Typically equal to '.', but could be ',' for some locales.
|
||||
bool mbDisplayThousands; // Non-standard extension, though found in a number of Unix compiler C library printf implementations.
|
||||
int mThousandsSeparator; // Typically equal to ',' but could be '.' or ' ' for some locales.
|
||||
|
||||
FormatData()
|
||||
: mAlignment(kAlignmentRight), // The standard states (7.19.6.1.6) that right alignment is default.
|
||||
mSign(kSignMinus), // The standard states (7.19.6.1.6) that the default mode is to display the minus sign only.
|
||||
mbAlternativeForm(false),
|
||||
mnWidth(0),
|
||||
mnPrecision(kNoPrecision),
|
||||
mModifier(kModifierNone),
|
||||
mnType(kFormatError),
|
||||
mDecimalPoint('.'), // To do: Make this configurable.
|
||||
mbDisplayThousands(false),
|
||||
mThousandsSeparator(',') // To do: Make this configurable.
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct SnprintfContext8
|
||||
{
|
||||
char* mpDestination; // Start of destination data. Ptr doesn't change once it has been initialized.
|
||||
size_t mnCount; // Count written to destination so far.
|
||||
size_t mnMaxCount; // The max count we can write to the destination.
|
||||
bool mbMaxCountReached; // True if the max count has been reached. Used because multi-byte strings (e.g. UTF8) could end with mnCount < mnMaxCount.
|
||||
|
||||
SnprintfContext8(char* pDestination = NULL, size_t nCount = 0, size_t nMaxCount = (size_t)-1)
|
||||
: mpDestination(pDestination),
|
||||
mnCount(nCount),
|
||||
mnMaxCount(nMaxCount),
|
||||
mbMaxCountReached(false)
|
||||
{}
|
||||
};
|
||||
|
||||
struct SnprintfContext16
|
||||
{
|
||||
char16_t* mpDestination;
|
||||
size_t mnCount;
|
||||
size_t mnMaxCount;
|
||||
|
||||
SnprintfContext16(char16_t* pDestination = NULL, size_t nCount = 0, size_t nMaxCount = (size_t)-1)
|
||||
: mpDestination(pDestination),
|
||||
mnCount(nCount),
|
||||
mnMaxCount(nMaxCount)
|
||||
{}
|
||||
};
|
||||
|
||||
struct SnprintfContext32
|
||||
{
|
||||
char32_t* mpDestination;
|
||||
size_t mnCount;
|
||||
size_t mnMaxCount;
|
||||
|
||||
SnprintfContext32(char32_t* pDestination = NULL, size_t nCount = 0, size_t nMaxCount = (size_t)-1)
|
||||
: mpDestination(pDestination),
|
||||
mnCount(nCount),
|
||||
mnMaxCount(nMaxCount)
|
||||
{}
|
||||
};
|
||||
|
||||
#ifdef EA_PLATFORM_ANDROID
|
||||
struct PlatformLogWriterContext8
|
||||
{
|
||||
char mBuffer[512];
|
||||
size_t mPosition;
|
||||
|
||||
PlatformLogWriterContext8() { mBuffer[0] = 0; mPosition = 0; }
|
||||
};
|
||||
#else
|
||||
// No context is required on most platforms
|
||||
struct PlatformLogWriterContext8 {};
|
||||
#endif
|
||||
|
||||
// Default output writers
|
||||
int StringWriter8 (const char* EA_RESTRICT pData, size_t nCount, void* EA_RESTRICT pContext8, WriteFunctionState wfs);
|
||||
int FILEWriter8 (const char* EA_RESTRICT pData, size_t nCount, void* EA_RESTRICT pContext8, WriteFunctionState wfs);
|
||||
int PlatformLogWriter8(const char* EA_RESTRICT pData, size_t nCount, void* EA_RESTRICT pContext8, WriteFunctionState wfs);
|
||||
|
||||
int StringWriter16 (const char16_t* EA_RESTRICT pData, size_t nCount, void* EA_RESTRICT pContext16, WriteFunctionState wfs);
|
||||
int FILEWriter16 (const char16_t* EA_RESTRICT pData, size_t nCount, void* EA_RESTRICT pContext16, WriteFunctionState wfs);
|
||||
|
||||
int StringWriter32 (const char32_t* EA_RESTRICT pData, size_t nCount, void* EA_RESTRICT pContext32, WriteFunctionState wfs);
|
||||
int FILEWriter32 (const char32_t* EA_RESTRICT pData, size_t nCount, void* EA_RESTRICT pContext32, WriteFunctionState wfs);
|
||||
|
||||
|
||||
// VaListContainer
|
||||
//
|
||||
// This exists because some compilers (e.g. some versions of GCC, Green Hills) aren't complaint with the
|
||||
// C99 Standard in allowing the taking of the address of a va_list variable. The problem is that some
|
||||
// compilers implement va_list as an array[1], which is converted to a pointer upon passing to a function,
|
||||
// and this breaks the ability of us to take its address as a va_list. We work around that by putting
|
||||
// va_list objects in a struct, which can be passed as an argument to a function without changing its type.
|
||||
// This struct isn't yet in use while we investigate possible alternative workarounds for the compiler issues.
|
||||
//
|
||||
struct VaListContainer
|
||||
{
|
||||
va_list value;
|
||||
};
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Internal init/shutdown functions.
|
||||
// The user doesn't need to call these, as these are already called when the
|
||||
// user calls the EAStdC::Init and EAStdC::Shutdown high level functions.
|
||||
//
|
||||
void EASprintfInit();
|
||||
void EASprintfShutdown();
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// ReadFormat
|
||||
//
|
||||
// Reads the current format into FormatData. Return value is pointer to first
|
||||
// char/char16_t after the format data.
|
||||
//
|
||||
// To know how printf truly needs to work, see the ISO C 1999 standard, section 7.19.6.1.
|
||||
// See http://www.cplusplus.com/ref/cstdio/printf.html or http://www.opengroup.org/onlinepubs/007908799/xsh/fprintf.html
|
||||
// for decent basic online documentation about how printf is supposed to work.
|
||||
//
|
||||
// Argument pFormat is a string pointing to a % format specification of the form:
|
||||
// %[flags][width][.precision][modifiers]type
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
template <typename CharT>
|
||||
const CharT* ReadFormat(const CharT* EA_RESTRICT pFormat, SprintfLocal::FormatData* EA_RESTRICT pFormatData, va_list* EA_RESTRICT pArguments);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// VprintfCore
|
||||
//
|
||||
int VprintfCore(WriteFunction8 pWriteFunction8, void* EA_RESTRICT pWriteFunctionContext8, const char* EA_RESTRICT pFormat, va_list arguments);
|
||||
int VprintfCore(WriteFunction16 pWriteFunction16, void* EA_RESTRICT pWriteFunctionContext16, const char16_t* EA_RESTRICT pFormat, va_list arguments);
|
||||
int VprintfCore(WriteFunction32 pWriteFunction32, void* EA_RESTRICT pWriteFunctionContext32, const char32_t* EA_RESTRICT pFormat, va_list arguments);
|
||||
|
||||
|
||||
|
||||
} // namespace SprintfLocal
|
||||
} // namespace StdC
|
||||
} // namespace EA
|
||||
|
||||
|
||||
#endif // Header include guard
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Electronic Arts Inc. All rights reserved.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef EASTDC_THREAD_H
|
||||
#define EASTDC_THREAD_H
|
||||
|
||||
|
||||
#include <EABase/eabase.h>
|
||||
#include <eathread/eathread_atomic.h>
|
||||
#include <eathread/eathread_mutex.h>
|
||||
|
||||
|
||||
namespace EA
|
||||
{
|
||||
namespace StdC
|
||||
{
|
||||
/// Safely sets a new value. Returns the old value.
|
||||
uint32_t AtomicSet(uint32_t* pValue, uint32_t newValue);
|
||||
|
||||
/// Safely increments the value. Returns the new value.
|
||||
/// This function acts the same as the C++ pre-increment operator.
|
||||
uint32_t AtomicIncrement(uint32_t* pValue);
|
||||
|
||||
/// Safely decrements the value. Returns the new value.
|
||||
/// This function acts the same as the C++ pre-decrement operator.
|
||||
uint32_t AtomicDecrement(uint32_t* pValue);
|
||||
|
||||
/// Safely sets the value to a new value if the original value is equal to
|
||||
/// a condition value. Returns true if the condition was met and the
|
||||
/// assignment occurred. The comparison and value setting are done as
|
||||
/// an atomic operation and thus another thread cannot intervene between
|
||||
/// the two as would be the case with simple C code.
|
||||
bool AtomicCompareSwap(uint32_t* pValue, uint32_t newValue, uint32_t condition);
|
||||
|
||||
|
||||
/// Mutex
|
||||
///
|
||||
/// Implements a very simple portable Mutex class.
|
||||
///
|
||||
class Mutex
|
||||
{
|
||||
public:
|
||||
void Lock() { mMutex.Lock(); }
|
||||
void Unlock() { mMutex.Unlock(); }
|
||||
|
||||
protected:
|
||||
EA::Thread::Mutex mMutex;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// inline implmentation
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
namespace EA
|
||||
{
|
||||
namespace StdC
|
||||
{
|
||||
inline uint32_t AtomicIncrement(uint32_t* pValue) { return EA::Thread::AtomicFetchIncrement(pValue) + 1; }
|
||||
|
||||
inline uint32_t AtomicDecrement(uint32_t* pValue) { return EA::Thread::AtomicFetchDecrement(pValue) - 1; }
|
||||
|
||||
inline uint32_t AtomicSet(uint32_t* pValue, uint32_t newValue)
|
||||
{
|
||||
return EA::Thread::AtomicSetValue(pValue, newValue);
|
||||
}
|
||||
|
||||
inline bool AtomicCompareSwap(uint32_t* pValue, uint32_t newValue, uint32_t condition)
|
||||
{
|
||||
return EA::Thread::AtomicSetValueConditional(pValue, newValue, condition);
|
||||
}
|
||||
|
||||
} // namespace StdC
|
||||
|
||||
} // namespace EA
|
||||
|
||||
|
||||
|
||||
|
||||
#endif // Header include guard
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Electronic Arts Inc. All rights reserved.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// stdioEA.h
|
||||
//
|
||||
// Declares elements of stdio.h that are missing from various platforms.
|
||||
// Some platform/compiler combinations don't support some or all of the
|
||||
// standard C stdio.h functionality, so we declare the functionality
|
||||
// ourselves here. This doesn't always mean that we implement the functionality.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#ifndef EASTDC_INTERNAL_STDIOEA_H
|
||||
#define EASTDC_INTERNAL_STDIOEA_H
|
||||
|
||||
|
||||
#include <EAStdC/internal/Config.h>
|
||||
|
||||
|
||||
#if !EASTDC_FILE_AVAILABLE
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
//struct FILE
|
||||
//{
|
||||
// char8_t* mpMemory;
|
||||
// int32_t mPosition;
|
||||
//};
|
||||
|
||||
size_t fread(void* ptr, size_t size, size_t count, FILE* stream);
|
||||
size_t fwrite(const void* ptr, size_t size, size_t count, FILE* stream);
|
||||
int fwide(FILE* stream, int mode);
|
||||
char* fgets(char* str, int num, FILE* stream);
|
||||
int fputs(const char* str, FILE* stream);
|
||||
int fgetc(FILE* stream);
|
||||
int ungetc(int character, FILE* stream);
|
||||
int feof(FILE* stream);
|
||||
int ferror(FILE* stream);
|
||||
|
||||
#endif // EASTDC_FILE_AVAILABLE
|
||||
|
||||
#endif // Header include guard
|
||||
@@ -0,0 +1,15 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Electronic Arts Inc. All rights reserved.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef EASTDC_VERSION_H
|
||||
#define EASTDC_VERSION_H
|
||||
|
||||
#include <EABase/eabase.h>
|
||||
#if defined(EA_PRAGMA_ONCE_SUPPORTED)
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include <EAStdC/internal/Config.h>
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user