# HG changeset patch # User John Tsiombikas # Date 1379207405 -10800 # Node ID b069a5c273885b6a5e9781dfa0104c50196b3c87 # Parent 59fc487ba58e8bc1015cb994bb5bc962fe671938 added a couple more stuff, fixed all the LibOVR line endings diff -r 59fc487ba58e -r b069a5c27388 libovr/Src/Kernel/OVR_Alg.cpp --- a/libovr/Src/Kernel/OVR_Alg.cpp Sat Sep 14 17:51:03 2013 +0300 +++ b/libovr/Src/Kernel/OVR_Alg.cpp Sun Sep 15 04:10:05 2013 +0300 @@ -1,1 +1,46 @@ -/************************************************************************************ Filename : OVR_Alg.cpp Content : Static lookup tables for Alg functions Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #include "OVR_Types.h" namespace OVR { namespace Alg { //------------------------------------------------------------------------ extern const UByte UpperBitTable[256] = { 0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 }; extern const UByte LowerBitTable[256] = { 8,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 7,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 }; }} // OVE::Alg \ No newline at end of file +/************************************************************************************ + +Filename : OVR_Alg.cpp +Content : Static lookup tables for Alg functions +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#include "OVR_Types.h" + +namespace OVR { namespace Alg { + +//------------------------------------------------------------------------ +extern const UByte UpperBitTable[256] = +{ + 0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +}; + +extern const UByte LowerBitTable[256] = +{ + 8,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, + 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, + 6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, + 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, + 7,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, + 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, + 6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, + 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 +}; + + +}} // OVE::Alg diff -r 59fc487ba58e -r b069a5c27388 libovr/Src/Kernel/OVR_Alg.h --- a/libovr/Src/Kernel/OVR_Alg.h Sat Sep 14 17:51:03 2013 +0300 +++ b/libovr/Src/Kernel/OVR_Alg.h Sun Sep 15 04:10:05 2013 +0300 @@ -1,1 +1,953 @@ -/************************************************************************************ PublicHeader: OVR.h Filename : OVR_Alg.h Content : Simple general purpose algorithms: Sort, Binary Search, etc. Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #ifndef OVR_Alg_h #define OVR_Alg_h #include "OVR_Types.h" #include namespace OVR { namespace Alg { //----------------------------------------------------------------------------------- // ***** Operator extensions template OVR_FORCE_INLINE void Swap(T &a, T &b) { T temp(a); a = b; b = temp; } // ***** min/max are not implemented in Visual Studio 6 standard STL template OVR_FORCE_INLINE const T Min(const T a, const T b) { return (a < b) ? a : b; } template OVR_FORCE_INLINE const T Max(const T a, const T b) { return (b < a) ? a : b; } template OVR_FORCE_INLINE const T Clamp(const T v, const T minVal, const T maxVal) { return Max(minVal, Min(v, maxVal)); } template OVR_FORCE_INLINE int Chop(T f) { return (int)f; } template OVR_FORCE_INLINE T Lerp(T a, T b, T f) { return (b - a) * f + a; } // These functions stand to fix a stupid VC++ warning (with /Wp64 on): // "warning C4267: 'argument' : conversion from 'size_t' to 'const unsigned', possible loss of data" // Use these functions instead of gmin/gmax if the argument has size // of the pointer to avoid the warning. Though, functionally they are // absolutelly the same as regular gmin/gmax. template OVR_FORCE_INLINE const T PMin(const T a, const T b) { OVR_COMPILER_ASSERT(sizeof(T) == sizeof(UPInt)); return (a < b) ? a : b; } template OVR_FORCE_INLINE const T PMax(const T a, const T b) { OVR_COMPILER_ASSERT(sizeof(T) == sizeof(UPInt)); return (b < a) ? a : b; } template OVR_FORCE_INLINE const T Abs(const T v) { return (v>=0) ? v : -v; } //----------------------------------------------------------------------------------- // ***** OperatorLess // template struct OperatorLess { static bool Compare(const T& a, const T& b) { return a < b; } }; //----------------------------------------------------------------------------------- // ***** QuickSortSliced // // Sort any part of any array: plain, Array, ArrayPaged, ArrayUnsafe. // The range is specified with start, end, where "end" is exclusive! // The comparison predicate must be specified. template void QuickSortSliced(Array& arr, UPInt start, UPInt end, Less less) { enum { Threshold = 9 }; if(end - start < 2) return; SPInt stack[80]; SPInt* top = stack; SPInt base = (SPInt)start; SPInt limit = (SPInt)end; for(;;) { SPInt len = limit - base; SPInt i, j, pivot; if(len > Threshold) { // we use base + len/2 as the pivot pivot = base + len / 2; Swap(arr[base], arr[pivot]); i = base + 1; j = limit - 1; // now ensure that *i <= *base <= *j if(less(arr[j], arr[i])) Swap(arr[j], arr[i]); if(less(arr[base], arr[i])) Swap(arr[base], arr[i]); if(less(arr[j], arr[base])) Swap(arr[j], arr[base]); for(;;) { do i++; while( less(arr[i], arr[base]) ); do j--; while( less(arr[base], arr[j]) ); if( i > j ) { break; } Swap(arr[i], arr[j]); } Swap(arr[base], arr[j]); // now, push the largest sub-array if(j - base > limit - i) { top[0] = base; top[1] = j; base = i; } else { top[0] = i; top[1] = limit; limit = j; } top += 2; } else { // the sub-array is small, perform insertion sort j = base; i = j + 1; for(; i < limit; j = i, i++) { for(; less(arr[j + 1], arr[j]); j--) { Swap(arr[j + 1], arr[j]); if(j == base) { break; } } } if(top > stack) { top -= 2; base = top[0]; limit = top[1]; } else { break; } } } } //----------------------------------------------------------------------------------- // ***** QuickSortSliced // // Sort any part of any array: plain, Array, ArrayPaged, ArrayUnsafe. // The range is specified with start, end, where "end" is exclusive! // The data type must have a defined "<" operator. template void QuickSortSliced(Array& arr, UPInt start, UPInt end) { typedef typename Array::ValueType ValueType; QuickSortSliced(arr, start, end, OperatorLess::Compare); } // Same as corresponding G_QuickSortSliced but with checking array limits to avoid // crash in the case of wrong comparator functor. template bool QuickSortSlicedSafe(Array& arr, UPInt start, UPInt end, Less less) { enum { Threshold = 9 }; if(end - start < 2) return true; SPInt stack[80]; SPInt* top = stack; SPInt base = (SPInt)start; SPInt limit = (SPInt)end; for(;;) { SPInt len = limit - base; SPInt i, j, pivot; if(len > Threshold) { // we use base + len/2 as the pivot pivot = base + len / 2; Swap(arr[base], arr[pivot]); i = base + 1; j = limit - 1; // now ensure that *i <= *base <= *j if(less(arr[j], arr[i])) Swap(arr[j], arr[i]); if(less(arr[base], arr[i])) Swap(arr[base], arr[i]); if(less(arr[j], arr[base])) Swap(arr[j], arr[base]); for(;;) { do { i++; if (i >= limit) return false; } while( less(arr[i], arr[base]) ); do { j--; if (j < 0) return false; } while( less(arr[base], arr[j]) ); if( i > j ) { break; } Swap(arr[i], arr[j]); } Swap(arr[base], arr[j]); // now, push the largest sub-array if(j - base > limit - i) { top[0] = base; top[1] = j; base = i; } else { top[0] = i; top[1] = limit; limit = j; } top += 2; } else { // the sub-array is small, perform insertion sort j = base; i = j + 1; for(; i < limit; j = i, i++) { for(; less(arr[j + 1], arr[j]); j--) { Swap(arr[j + 1], arr[j]); if(j == base) { break; } } } if(top > stack) { top -= 2; base = top[0]; limit = top[1]; } else { break; } } } return true; } template bool QuickSortSlicedSafe(Array& arr, UPInt start, UPInt end) { typedef typename Array::ValueType ValueType; return QuickSortSlicedSafe(arr, start, end, OperatorLess::Compare); } //----------------------------------------------------------------------------------- // ***** QuickSort // // Sort an array Array, ArrayPaged, ArrayUnsafe. // The array must have GetSize() function. // The comparison predicate must be specified. template void QuickSort(Array& arr, Less less) { QuickSortSliced(arr, 0, arr.GetSize(), less); } // checks for boundaries template bool QuickSortSafe(Array& arr, Less less) { return QuickSortSlicedSafe(arr, 0, arr.GetSize(), less); } //----------------------------------------------------------------------------------- // ***** QuickSort // // Sort an array Array, ArrayPaged, ArrayUnsafe. // The array must have GetSize() function. // The data type must have a defined "<" operator. template void QuickSort(Array& arr) { typedef typename Array::ValueType ValueType; QuickSortSliced(arr, 0, arr.GetSize(), OperatorLess::Compare); } template bool QuickSortSafe(Array& arr) { typedef typename Array::ValueType ValueType; return QuickSortSlicedSafe(arr, 0, arr.GetSize(), OperatorLess::Compare); } //----------------------------------------------------------------------------------- // ***** InsertionSortSliced // // Sort any part of any array: plain, Array, ArrayPaged, ArrayUnsafe. // The range is specified with start, end, where "end" is exclusive! // The comparison predicate must be specified. // Unlike Quick Sort, the Insertion Sort works much slower in average, // but may be much faster on almost sorted arrays. Besides, it guarantees // that the elements will not be swapped if not necessary. For example, // an array with all equal elements will remain "untouched", while // Quick Sort will considerably shuffle the elements in this case. template void InsertionSortSliced(Array& arr, UPInt start, UPInt end, Less less) { UPInt j = start; UPInt i = j + 1; UPInt limit = end; for(; i < limit; j = i, i++) { for(; less(arr[j + 1], arr[j]); j--) { Swap(arr[j + 1], arr[j]); if(j <= start) { break; } } } } //----------------------------------------------------------------------------------- // ***** InsertionSortSliced // // Sort any part of any array: plain, Array, ArrayPaged, ArrayUnsafe. // The range is specified with start, end, where "end" is exclusive! // The data type must have a defined "<" operator. template void InsertionSortSliced(Array& arr, UPInt start, UPInt end) { typedef typename Array::ValueType ValueType; InsertionSortSliced(arr, start, end, OperatorLess::Compare); } //----------------------------------------------------------------------------------- // ***** InsertionSort // // Sort an array Array, ArrayPaged, ArrayUnsafe. // The array must have GetSize() function. // The comparison predicate must be specified. template void InsertionSort(Array& arr, Less less) { InsertionSortSliced(arr, 0, arr.GetSize(), less); } //----------------------------------------------------------------------------------- // ***** InsertionSort // // Sort an array Array, ArrayPaged, ArrayUnsafe. // The array must have GetSize() function. // The data type must have a defined "<" operator. template void InsertionSort(Array& arr) { typedef typename Array::ValueType ValueType; InsertionSortSliced(arr, 0, arr.GetSize(), OperatorLess::Compare); } //----------------------------------------------------------------------------------- // ***** LowerBoundSliced // template UPInt LowerBoundSliced(const Array& arr, UPInt start, UPInt end, const Value& val, Less less) { SPInt first = (SPInt)start; SPInt len = (SPInt)(end - start); SPInt half; SPInt middle; while(len > 0) { half = len >> 1; middle = first + half; if(less(arr[middle], val)) { first = middle + 1; len = len - half - 1; } else { len = half; } } return (UPInt)first; } //----------------------------------------------------------------------------------- // ***** LowerBoundSliced // template UPInt LowerBoundSliced(const Array& arr, UPInt start, UPInt end, const Value& val) { return LowerBoundSliced(arr, start, end, val, OperatorLess::Compare); } //----------------------------------------------------------------------------------- // ***** LowerBoundSized // template UPInt LowerBoundSized(const Array& arr, UPInt size, const Value& val) { return LowerBoundSliced(arr, 0, size, val, OperatorLess::Compare); } //----------------------------------------------------------------------------------- // ***** LowerBound // template UPInt LowerBound(const Array& arr, const Value& val, Less less) { return LowerBoundSliced(arr, 0, arr.GetSize(), val, less); } //----------------------------------------------------------------------------------- // ***** LowerBound // template UPInt LowerBound(const Array& arr, const Value& val) { return LowerBoundSliced(arr, 0, arr.GetSize(), val, OperatorLess::Compare); } //----------------------------------------------------------------------------------- // ***** UpperBoundSliced // template UPInt UpperBoundSliced(const Array& arr, UPInt start, UPInt end, const Value& val, Less less) { SPInt first = (SPInt)start; SPInt len = (SPInt)(end - start); SPInt half; SPInt middle; while(len > 0) { half = len >> 1; middle = first + half; if(less(val, arr[middle])) { len = half; } else { first = middle + 1; len = len - half - 1; } } return (UPInt)first; } //----------------------------------------------------------------------------------- // ***** UpperBoundSliced // template UPInt UpperBoundSliced(const Array& arr, UPInt start, UPInt end, const Value& val) { return UpperBoundSliced(arr, start, end, val, OperatorLess::Compare); } //----------------------------------------------------------------------------------- // ***** UpperBoundSized // template UPInt UpperBoundSized(const Array& arr, UPInt size, const Value& val) { return UpperBoundSliced(arr, 0, size, val, OperatorLess::Compare); } //----------------------------------------------------------------------------------- // ***** UpperBound // template UPInt UpperBound(const Array& arr, const Value& val, Less less) { return UpperBoundSliced(arr, 0, arr.GetSize(), val, less); } //----------------------------------------------------------------------------------- // ***** UpperBound // template UPInt UpperBound(const Array& arr, const Value& val) { return UpperBoundSliced(arr, 0, arr.GetSize(), val, OperatorLess::Compare); } //----------------------------------------------------------------------------------- // ***** ReverseArray // template void ReverseArray(Array& arr) { SPInt from = 0; SPInt to = arr.GetSize() - 1; while(from < to) { Swap(arr[from], arr[to]); ++from; --to; } } // ***** AppendArray // template void AppendArray(CDst& dst, const CSrc& src) { UPInt i; for(i = 0; i < src.GetSize(); i++) dst.PushBack(src[i]); } //----------------------------------------------------------------------------------- // ***** ArrayAdaptor // // A simple adapter that provides the GetSize() method and overloads // operator []. Used to wrap plain arrays in QuickSort and such. template class ArrayAdaptor { public: typedef T ValueType; ArrayAdaptor() : Data(0), Size(0) {} ArrayAdaptor(T* ptr, UPInt size) : Data(ptr), Size(size) {} UPInt GetSize() const { return Size; } const T& operator [] (UPInt i) const { return Data[i]; } T& operator [] (UPInt i) { return Data[i]; } private: T* Data; UPInt Size; }; //----------------------------------------------------------------------------------- // ***** GConstArrayAdaptor // // A simple const adapter that provides the GetSize() method and overloads // operator []. Used to wrap plain arrays in LowerBound and such. template class ConstArrayAdaptor { public: typedef T ValueType; ConstArrayAdaptor() : Data(0), Size(0) {} ConstArrayAdaptor(const T* ptr, UPInt size) : Data(ptr), Size(size) {} UPInt GetSize() const { return Size; } const T& operator [] (UPInt i) const { return Data[i]; } private: const T* Data; UPInt Size; }; //----------------------------------------------------------------------------------- extern const UByte UpperBitTable[256]; extern const UByte LowerBitTable[256]; //----------------------------------------------------------------------------------- inline UByte UpperBit(UPInt val) { #ifndef OVR_64BIT_POINTERS if (val & 0xFFFF0000) { return (val & 0xFF000000) ? UpperBitTable[(val >> 24) ] + 24: UpperBitTable[(val >> 16) & 0xFF] + 16; } return (val & 0xFF00) ? UpperBitTable[(val >> 8) & 0xFF] + 8: UpperBitTable[(val ) & 0xFF]; #else if (val & 0xFFFFFFFF00000000) { if (val & 0xFFFF000000000000) { return (val & 0xFF00000000000000) ? UpperBitTable[(val >> 56) ] + 56: UpperBitTable[(val >> 48) & 0xFF] + 48; } return (val & 0xFF0000000000) ? UpperBitTable[(val >> 40) & 0xFF] + 40: UpperBitTable[(val >> 32) & 0xFF] + 32; } else { if (val & 0xFFFF0000) { return (val & 0xFF000000) ? UpperBitTable[(val >> 24) ] + 24: UpperBitTable[(val >> 16) & 0xFF] + 16; } return (val & 0xFF00) ? UpperBitTable[(val >> 8) & 0xFF] + 8: UpperBitTable[(val ) & 0xFF]; } #endif } //----------------------------------------------------------------------------------- inline UByte LowerBit(UPInt val) { #ifndef OVR_64BIT_POINTERS if (val & 0xFFFF) { return (val & 0xFF) ? LowerBitTable[ val & 0xFF]: LowerBitTable[(val >> 8) & 0xFF] + 8; } return (val & 0xFF0000) ? LowerBitTable[(val >> 16) & 0xFF] + 16: LowerBitTable[(val >> 24) & 0xFF] + 24; #else if (val & 0xFFFFFFFF) { if (val & 0xFFFF) { return (val & 0xFF) ? LowerBitTable[ val & 0xFF]: LowerBitTable[(val >> 8) & 0xFF] + 8; } return (val & 0xFF0000) ? LowerBitTable[(val >> 16) & 0xFF] + 16: LowerBitTable[(val >> 24) & 0xFF] + 24; } else { if (val & 0xFFFF00000000) { return (val & 0xFF00000000) ? LowerBitTable[(val >> 32) & 0xFF] + 32: LowerBitTable[(val >> 40) & 0xFF] + 40; } return (val & 0xFF000000000000) ? LowerBitTable[(val >> 48) & 0xFF] + 48: LowerBitTable[(val >> 56) & 0xFF] + 56; } #endif } // ******* Special (optimized) memory routines // Note: null (bad) pointer is not tested class MemUtil { public: // Memory compare static int Cmp (const void* p1, const void* p2, UPInt byteCount) { return memcmp(p1, p2, byteCount); } static int Cmp16(const void* p1, const void* p2, UPInt int16Count); static int Cmp32(const void* p1, const void* p2, UPInt int32Count); static int Cmp64(const void* p1, const void* p2, UPInt int64Count); }; // ** Inline Implementation inline int MemUtil::Cmp16(const void* p1, const void* p2, UPInt int16Count) { SInt16* pa = (SInt16*)p1; SInt16* pb = (SInt16*)p2; unsigned ic = 0; if (int16Count == 0) return 0; while (pa[ic] == pb[ic]) if (++ic==int16Count) return 0; return pa[ic] > pb[ic] ? 1 : -1; } inline int MemUtil::Cmp32(const void* p1, const void* p2, UPInt int32Count) { SInt32* pa = (SInt32*)p1; SInt32* pb = (SInt32*)p2; unsigned ic = 0; if (int32Count == 0) return 0; while (pa[ic] == pb[ic]) if (++ic==int32Count) return 0; return pa[ic] > pb[ic] ? 1 : -1; } inline int MemUtil::Cmp64(const void* p1, const void* p2, UPInt int64Count) { SInt64* pa = (SInt64*)p1; SInt64* pb = (SInt64*)p2; unsigned ic = 0; if (int64Count == 0) return 0; while (pa[ic] == pb[ic]) if (++ic==int64Count) return 0; return pa[ic] > pb[ic] ? 1 : -1; } // ** End Inline Implementation //----------------------------------------------------------------------------------- // ******* Byte Order Conversions namespace ByteUtil { // *** Swap Byte Order // Swap the byte order of a byte array inline void SwapOrder(void* pv, int size) { UByte* pb = (UByte*)pv; UByte temp; for (int i = 0; i < size>>1; i++) { temp = pb[size-1-i]; pb[size-1-i] = pb[i]; pb[i] = temp; } } // Swap the byte order of primitive types inline UByte SwapOrder(UByte v) { return v; } inline SByte SwapOrder(SByte v) { return v; } inline UInt16 SwapOrder(UInt16 v) { return UInt16(v>>8)|UInt16(v<<8); } inline SInt16 SwapOrder(SInt16 v) { return SInt16((UInt16(v)>>8)|(v<<8)); } inline UInt32 SwapOrder(UInt32 v) { return (v>>24)|((v&0x00FF0000)>>8)|((v&0x0000FF00)<<8)|(v<<24); } inline SInt32 SwapOrder(SInt32 p) { return (SInt32)SwapOrder(UInt32(p)); } inline UInt64 SwapOrder(UInt64 v) { return (v>>56) | ((v&UInt64(0x00FF000000000000))>>40) | ((v&UInt64(0x0000FF0000000000))>>24) | ((v&UInt64(0x000000FF00000000))>>8) | ((v&UInt64(0x00000000FF000000))<<8) | ((v&UInt64(0x0000000000FF0000))<<24) | ((v&UInt64(0x000000000000FF00))<<40) | (v<<56); } inline SInt64 SwapOrder(SInt64 v) { return (SInt64)SwapOrder(UInt64(v)); } inline float SwapOrder(float p) { union { float p; UInt32 v; } u; u.p = p; u.v = SwapOrder(u.v); return u.p; } inline double SwapOrder(double p) { union { double p; UInt64 v; } u; u.p = p; u.v = SwapOrder(u.v); return u.p; } // *** Byte-order conversion #if (OVR_BYTE_ORDER == OVR_LITTLE_ENDIAN) // Little Endian to System (LE) inline UByte LEToSystem(UByte v) { return v; } inline SByte LEToSystem(SByte v) { return v; } inline UInt16 LEToSystem(UInt16 v) { return v; } inline SInt16 LEToSystem(SInt16 v) { return v; } inline UInt32 LEToSystem(UInt32 v) { return v; } inline SInt32 LEToSystem(SInt32 v) { return v; } inline UInt64 LEToSystem(UInt64 v) { return v; } inline SInt64 LEToSystem(SInt64 v) { return v; } inline float LEToSystem(float v) { return v; } inline double LEToSystem(double v) { return v; } // Big Endian to System (LE) inline UByte BEToSystem(UByte v) { return SwapOrder(v); } inline SByte BEToSystem(SByte v) { return SwapOrder(v); } inline UInt16 BEToSystem(UInt16 v) { return SwapOrder(v); } inline SInt16 BEToSystem(SInt16 v) { return SwapOrder(v); } inline UInt32 BEToSystem(UInt32 v) { return SwapOrder(v); } inline SInt32 BEToSystem(SInt32 v) { return SwapOrder(v); } inline UInt64 BEToSystem(UInt64 v) { return SwapOrder(v); } inline SInt64 BEToSystem(SInt64 v) { return SwapOrder(v); } inline float BEToSystem(float v) { return SwapOrder(v); } inline double BEToSystem(double v) { return SwapOrder(v); } // System (LE) to Little Endian inline UByte SystemToLE(UByte v) { return v; } inline SByte SystemToLE(SByte v) { return v; } inline UInt16 SystemToLE(UInt16 v) { return v; } inline SInt16 SystemToLE(SInt16 v) { return v; } inline UInt32 SystemToLE(UInt32 v) { return v; } inline SInt32 SystemToLE(SInt32 v) { return v; } inline UInt64 SystemToLE(UInt64 v) { return v; } inline SInt64 SystemToLE(SInt64 v) { return v; } inline float SystemToLE(float v) { return v; } inline double SystemToLE(double v) { return v; } // System (LE) to Big Endian inline UByte SystemToBE(UByte v) { return SwapOrder(v); } inline SByte SystemToBE(SByte v) { return SwapOrder(v); } inline UInt16 SystemToBE(UInt16 v) { return SwapOrder(v); } inline SInt16 SystemToBE(SInt16 v) { return SwapOrder(v); } inline UInt32 SystemToBE(UInt32 v) { return SwapOrder(v); } inline SInt32 SystemToBE(SInt32 v) { return SwapOrder(v); } inline UInt64 SystemToBE(UInt64 v) { return SwapOrder(v); } inline SInt64 SystemToBE(SInt64 v) { return SwapOrder(v); } inline float SystemToBE(float v) { return SwapOrder(v); } inline double SystemToBE(double v) { return SwapOrder(v); } #elif (OVR_BYTE_ORDER == OVR_BIG_ENDIAN) // Little Endian to System (BE) inline UByte LEToSystem(UByte v) { return SwapOrder(v); } inline SByte LEToSystem(SByte v) { return SwapOrder(v); } inline UInt16 LEToSystem(UInt16 v) { return SwapOrder(v); } inline SInt16 LEToSystem(SInt16 v) { return SwapOrder(v); } inline UInt32 LEToSystem(UInt32 v) { return SwapOrder(v); } inline SInt32 LEToSystem(SInt32 v) { return SwapOrder(v); } inline UInt64 LEToSystem(UInt64 v) { return SwapOrder(v); } inline SInt64 LEToSystem(SInt64 v) { return SwapOrder(v); } inline float LEToSystem(float v) { return SwapOrder(v); } inline double LEToSystem(double v) { return SwapOrder(v); } // Big Endian to System (BE) inline UByte BEToSystem(UByte v) { return v; } inline SByte BEToSystem(SByte v) { return v; } inline UInt16 BEToSystem(UInt16 v) { return v; } inline SInt16 BEToSystem(SInt16 v) { return v; } inline UInt32 BEToSystem(UInt32 v) { return v; } inline SInt32 BEToSystem(SInt32 v) { return v; } inline UInt64 BEToSystem(UInt64 v) { return v; } inline SInt64 BEToSystem(SInt64 v) { return v; } inline float BEToSystem(float v) { return v; } inline double BEToSystem(double v) { return v; } // System (BE) to Little Endian inline UByte SystemToLE(UByte v) { return SwapOrder(v); } inline SByte SystemToLE(SByte v) { return SwapOrder(v); } inline UInt16 SystemToLE(UInt16 v) { return SwapOrder(v); } inline SInt16 SystemToLE(SInt16 v) { return SwapOrder(v); } inline UInt32 SystemToLE(UInt32 v) { return SwapOrder(v); } inline SInt32 SystemToLE(SInt32 v) { return SwapOrder(v); } inline UInt64 SystemToLE(UInt64 v) { return SwapOrder(v); } inline SInt64 SystemToLE(SInt64 v) { return SwapOrder(v); } inline float SystemToLE(float v) { return SwapOrder(v); } inline double SystemToLE(double v) { return SwapOrder(v); } // System (BE) to Big Endian inline UByte SystemToBE(UByte v) { return v; } inline SByte SystemToBE(SByte v) { return v; } inline UInt16 SystemToBE(UInt16 v) { return v; } inline SInt16 SystemToBE(SInt16 v) { return v; } inline UInt32 SystemToBE(UInt32 v) { return v; } inline SInt32 SystemToBE(SInt32 v) { return v; } inline UInt64 SystemToBE(UInt64 v) { return v; } inline SInt64 SystemToBE(SInt64 v) { return v; } inline float SystemToBE(float v) { return v; } inline double SystemToBE(double v) { return v; } #else #error "OVR_BYTE_ORDER must be defined to OVR_LITTLE_ENDIAN or OVR_BIG_ENDIAN" #endif } // namespace ByteUtil }} // OVR::Alg #endif \ No newline at end of file +/************************************************************************************ + +PublicHeader: OVR.h +Filename : OVR_Alg.h +Content : Simple general purpose algorithms: Sort, Binary Search, etc. +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#ifndef OVR_Alg_h +#define OVR_Alg_h + +#include "OVR_Types.h" +#include + +namespace OVR { namespace Alg { + + +//----------------------------------------------------------------------------------- +// ***** Operator extensions + +template OVR_FORCE_INLINE void Swap(T &a, T &b) +{ T temp(a); a = b; b = temp; } + + +// ***** min/max are not implemented in Visual Studio 6 standard STL + +template OVR_FORCE_INLINE const T Min(const T a, const T b) +{ return (a < b) ? a : b; } + +template OVR_FORCE_INLINE const T Max(const T a, const T b) +{ return (b < a) ? a : b; } + +template OVR_FORCE_INLINE const T Clamp(const T v, const T minVal, const T maxVal) +{ return Max(minVal, Min(v, maxVal)); } + +template OVR_FORCE_INLINE int Chop(T f) +{ return (int)f; } + +template OVR_FORCE_INLINE T Lerp(T a, T b, T f) +{ return (b - a) * f + a; } + + +// These functions stand to fix a stupid VC++ warning (with /Wp64 on): +// "warning C4267: 'argument' : conversion from 'size_t' to 'const unsigned', possible loss of data" +// Use these functions instead of gmin/gmax if the argument has size +// of the pointer to avoid the warning. Though, functionally they are +// absolutelly the same as regular gmin/gmax. +template OVR_FORCE_INLINE const T PMin(const T a, const T b) +{ + OVR_COMPILER_ASSERT(sizeof(T) == sizeof(UPInt)); + return (a < b) ? a : b; +} +template OVR_FORCE_INLINE const T PMax(const T a, const T b) +{ + OVR_COMPILER_ASSERT(sizeof(T) == sizeof(UPInt)); + return (b < a) ? a : b; +} + + +template OVR_FORCE_INLINE const T Abs(const T v) +{ return (v>=0) ? v : -v; } + + +//----------------------------------------------------------------------------------- +// ***** OperatorLess +// +template struct OperatorLess +{ + static bool Compare(const T& a, const T& b) + { + return a < b; + } +}; + + +//----------------------------------------------------------------------------------- +// ***** QuickSortSliced +// +// Sort any part of any array: plain, Array, ArrayPaged, ArrayUnsafe. +// The range is specified with start, end, where "end" is exclusive! +// The comparison predicate must be specified. +template +void QuickSortSliced(Array& arr, UPInt start, UPInt end, Less less) +{ + enum + { + Threshold = 9 + }; + + if(end - start < 2) return; + + SPInt stack[80]; + SPInt* top = stack; + SPInt base = (SPInt)start; + SPInt limit = (SPInt)end; + + for(;;) + { + SPInt len = limit - base; + SPInt i, j, pivot; + + if(len > Threshold) + { + // we use base + len/2 as the pivot + pivot = base + len / 2; + Swap(arr[base], arr[pivot]); + + i = base + 1; + j = limit - 1; + + // now ensure that *i <= *base <= *j + if(less(arr[j], arr[i])) Swap(arr[j], arr[i]); + if(less(arr[base], arr[i])) Swap(arr[base], arr[i]); + if(less(arr[j], arr[base])) Swap(arr[j], arr[base]); + + for(;;) + { + do i++; while( less(arr[i], arr[base]) ); + do j--; while( less(arr[base], arr[j]) ); + + if( i > j ) + { + break; + } + + Swap(arr[i], arr[j]); + } + + Swap(arr[base], arr[j]); + + // now, push the largest sub-array + if(j - base > limit - i) + { + top[0] = base; + top[1] = j; + base = i; + } + else + { + top[0] = i; + top[1] = limit; + limit = j; + } + top += 2; + } + else + { + // the sub-array is small, perform insertion sort + j = base; + i = j + 1; + + for(; i < limit; j = i, i++) + { + for(; less(arr[j + 1], arr[j]); j--) + { + Swap(arr[j + 1], arr[j]); + if(j == base) + { + break; + } + } + } + if(top > stack) + { + top -= 2; + base = top[0]; + limit = top[1]; + } + else + { + break; + } + } + } +} + + +//----------------------------------------------------------------------------------- +// ***** QuickSortSliced +// +// Sort any part of any array: plain, Array, ArrayPaged, ArrayUnsafe. +// The range is specified with start, end, where "end" is exclusive! +// The data type must have a defined "<" operator. +template +void QuickSortSliced(Array& arr, UPInt start, UPInt end) +{ + typedef typename Array::ValueType ValueType; + QuickSortSliced(arr, start, end, OperatorLess::Compare); +} + +// Same as corresponding G_QuickSortSliced but with checking array limits to avoid +// crash in the case of wrong comparator functor. +template +bool QuickSortSlicedSafe(Array& arr, UPInt start, UPInt end, Less less) +{ + enum + { + Threshold = 9 + }; + + if(end - start < 2) return true; + + SPInt stack[80]; + SPInt* top = stack; + SPInt base = (SPInt)start; + SPInt limit = (SPInt)end; + + for(;;) + { + SPInt len = limit - base; + SPInt i, j, pivot; + + if(len > Threshold) + { + // we use base + len/2 as the pivot + pivot = base + len / 2; + Swap(arr[base], arr[pivot]); + + i = base + 1; + j = limit - 1; + + // now ensure that *i <= *base <= *j + if(less(arr[j], arr[i])) Swap(arr[j], arr[i]); + if(less(arr[base], arr[i])) Swap(arr[base], arr[i]); + if(less(arr[j], arr[base])) Swap(arr[j], arr[base]); + + for(;;) + { + do + { + i++; + if (i >= limit) + return false; + } while( less(arr[i], arr[base]) ); + do + { + j--; + if (j < 0) + return false; + } while( less(arr[base], arr[j]) ); + + if( i > j ) + { + break; + } + + Swap(arr[i], arr[j]); + } + + Swap(arr[base], arr[j]); + + // now, push the largest sub-array + if(j - base > limit - i) + { + top[0] = base; + top[1] = j; + base = i; + } + else + { + top[0] = i; + top[1] = limit; + limit = j; + } + top += 2; + } + else + { + // the sub-array is small, perform insertion sort + j = base; + i = j + 1; + + for(; i < limit; j = i, i++) + { + for(; less(arr[j + 1], arr[j]); j--) + { + Swap(arr[j + 1], arr[j]); + if(j == base) + { + break; + } + } + } + if(top > stack) + { + top -= 2; + base = top[0]; + limit = top[1]; + } + else + { + break; + } + } + } + return true; +} + +template +bool QuickSortSlicedSafe(Array& arr, UPInt start, UPInt end) +{ + typedef typename Array::ValueType ValueType; + return QuickSortSlicedSafe(arr, start, end, OperatorLess::Compare); +} + +//----------------------------------------------------------------------------------- +// ***** QuickSort +// +// Sort an array Array, ArrayPaged, ArrayUnsafe. +// The array must have GetSize() function. +// The comparison predicate must be specified. +template +void QuickSort(Array& arr, Less less) +{ + QuickSortSliced(arr, 0, arr.GetSize(), less); +} + +// checks for boundaries +template +bool QuickSortSafe(Array& arr, Less less) +{ + return QuickSortSlicedSafe(arr, 0, arr.GetSize(), less); +} + + +//----------------------------------------------------------------------------------- +// ***** QuickSort +// +// Sort an array Array, ArrayPaged, ArrayUnsafe. +// The array must have GetSize() function. +// The data type must have a defined "<" operator. +template +void QuickSort(Array& arr) +{ + typedef typename Array::ValueType ValueType; + QuickSortSliced(arr, 0, arr.GetSize(), OperatorLess::Compare); +} + +template +bool QuickSortSafe(Array& arr) +{ + typedef typename Array::ValueType ValueType; + return QuickSortSlicedSafe(arr, 0, arr.GetSize(), OperatorLess::Compare); +} + +//----------------------------------------------------------------------------------- +// ***** InsertionSortSliced +// +// Sort any part of any array: plain, Array, ArrayPaged, ArrayUnsafe. +// The range is specified with start, end, where "end" is exclusive! +// The comparison predicate must be specified. +// Unlike Quick Sort, the Insertion Sort works much slower in average, +// but may be much faster on almost sorted arrays. Besides, it guarantees +// that the elements will not be swapped if not necessary. For example, +// an array with all equal elements will remain "untouched", while +// Quick Sort will considerably shuffle the elements in this case. +template +void InsertionSortSliced(Array& arr, UPInt start, UPInt end, Less less) +{ + UPInt j = start; + UPInt i = j + 1; + UPInt limit = end; + + for(; i < limit; j = i, i++) + { + for(; less(arr[j + 1], arr[j]); j--) + { + Swap(arr[j + 1], arr[j]); + if(j <= start) + { + break; + } + } + } +} + + +//----------------------------------------------------------------------------------- +// ***** InsertionSortSliced +// +// Sort any part of any array: plain, Array, ArrayPaged, ArrayUnsafe. +// The range is specified with start, end, where "end" is exclusive! +// The data type must have a defined "<" operator. +template +void InsertionSortSliced(Array& arr, UPInt start, UPInt end) +{ + typedef typename Array::ValueType ValueType; + InsertionSortSliced(arr, start, end, OperatorLess::Compare); +} + +//----------------------------------------------------------------------------------- +// ***** InsertionSort +// +// Sort an array Array, ArrayPaged, ArrayUnsafe. +// The array must have GetSize() function. +// The comparison predicate must be specified. + +template +void InsertionSort(Array& arr, Less less) +{ + InsertionSortSliced(arr, 0, arr.GetSize(), less); +} + +//----------------------------------------------------------------------------------- +// ***** InsertionSort +// +// Sort an array Array, ArrayPaged, ArrayUnsafe. +// The array must have GetSize() function. +// The data type must have a defined "<" operator. +template +void InsertionSort(Array& arr) +{ + typedef typename Array::ValueType ValueType; + InsertionSortSliced(arr, 0, arr.GetSize(), OperatorLess::Compare); +} + + + +//----------------------------------------------------------------------------------- +// ***** LowerBoundSliced +// +template +UPInt LowerBoundSliced(const Array& arr, UPInt start, UPInt end, const Value& val, Less less) +{ + SPInt first = (SPInt)start; + SPInt len = (SPInt)(end - start); + SPInt half; + SPInt middle; + + while(len > 0) + { + half = len >> 1; + middle = first + half; + if(less(arr[middle], val)) + { + first = middle + 1; + len = len - half - 1; + } + else + { + len = half; + } + } + return (UPInt)first; +} + + +//----------------------------------------------------------------------------------- +// ***** LowerBoundSliced +// +template +UPInt LowerBoundSliced(const Array& arr, UPInt start, UPInt end, const Value& val) +{ + return LowerBoundSliced(arr, start, end, val, OperatorLess::Compare); +} + +//----------------------------------------------------------------------------------- +// ***** LowerBoundSized +// +template +UPInt LowerBoundSized(const Array& arr, UPInt size, const Value& val) +{ + return LowerBoundSliced(arr, 0, size, val, OperatorLess::Compare); +} + +//----------------------------------------------------------------------------------- +// ***** LowerBound +// +template +UPInt LowerBound(const Array& arr, const Value& val, Less less) +{ + return LowerBoundSliced(arr, 0, arr.GetSize(), val, less); +} + + +//----------------------------------------------------------------------------------- +// ***** LowerBound +// +template +UPInt LowerBound(const Array& arr, const Value& val) +{ + return LowerBoundSliced(arr, 0, arr.GetSize(), val, OperatorLess::Compare); +} + + + +//----------------------------------------------------------------------------------- +// ***** UpperBoundSliced +// +template +UPInt UpperBoundSliced(const Array& arr, UPInt start, UPInt end, const Value& val, Less less) +{ + SPInt first = (SPInt)start; + SPInt len = (SPInt)(end - start); + SPInt half; + SPInt middle; + + while(len > 0) + { + half = len >> 1; + middle = first + half; + if(less(val, arr[middle])) + { + len = half; + } + else + { + first = middle + 1; + len = len - half - 1; + } + } + return (UPInt)first; +} + + +//----------------------------------------------------------------------------------- +// ***** UpperBoundSliced +// +template +UPInt UpperBoundSliced(const Array& arr, UPInt start, UPInt end, const Value& val) +{ + return UpperBoundSliced(arr, start, end, val, OperatorLess::Compare); +} + + +//----------------------------------------------------------------------------------- +// ***** UpperBoundSized +// +template +UPInt UpperBoundSized(const Array& arr, UPInt size, const Value& val) +{ + return UpperBoundSliced(arr, 0, size, val, OperatorLess::Compare); +} + + +//----------------------------------------------------------------------------------- +// ***** UpperBound +// +template +UPInt UpperBound(const Array& arr, const Value& val, Less less) +{ + return UpperBoundSliced(arr, 0, arr.GetSize(), val, less); +} + + +//----------------------------------------------------------------------------------- +// ***** UpperBound +// +template +UPInt UpperBound(const Array& arr, const Value& val) +{ + return UpperBoundSliced(arr, 0, arr.GetSize(), val, OperatorLess::Compare); +} + + +//----------------------------------------------------------------------------------- +// ***** ReverseArray +// +template void ReverseArray(Array& arr) +{ + SPInt from = 0; + SPInt to = arr.GetSize() - 1; + while(from < to) + { + Swap(arr[from], arr[to]); + ++from; + --to; + } +} + + +// ***** AppendArray +// +template +void AppendArray(CDst& dst, const CSrc& src) +{ + UPInt i; + for(i = 0; i < src.GetSize(); i++) + dst.PushBack(src[i]); +} + +//----------------------------------------------------------------------------------- +// ***** ArrayAdaptor +// +// A simple adapter that provides the GetSize() method and overloads +// operator []. Used to wrap plain arrays in QuickSort and such. +template class ArrayAdaptor +{ +public: + typedef T ValueType; + ArrayAdaptor() : Data(0), Size(0) {} + ArrayAdaptor(T* ptr, UPInt size) : Data(ptr), Size(size) {} + UPInt GetSize() const { return Size; } + const T& operator [] (UPInt i) const { return Data[i]; } + T& operator [] (UPInt i) { return Data[i]; } +private: + T* Data; + UPInt Size; +}; + + +//----------------------------------------------------------------------------------- +// ***** GConstArrayAdaptor +// +// A simple const adapter that provides the GetSize() method and overloads +// operator []. Used to wrap plain arrays in LowerBound and such. +template class ConstArrayAdaptor +{ +public: + typedef T ValueType; + ConstArrayAdaptor() : Data(0), Size(0) {} + ConstArrayAdaptor(const T* ptr, UPInt size) : Data(ptr), Size(size) {} + UPInt GetSize() const { return Size; } + const T& operator [] (UPInt i) const { return Data[i]; } +private: + const T* Data; + UPInt Size; +}; + + + +//----------------------------------------------------------------------------------- +extern const UByte UpperBitTable[256]; +extern const UByte LowerBitTable[256]; + + + +//----------------------------------------------------------------------------------- +inline UByte UpperBit(UPInt val) +{ +#ifndef OVR_64BIT_POINTERS + + if (val & 0xFFFF0000) + { + return (val & 0xFF000000) ? + UpperBitTable[(val >> 24) ] + 24: + UpperBitTable[(val >> 16) & 0xFF] + 16; + } + return (val & 0xFF00) ? + UpperBitTable[(val >> 8) & 0xFF] + 8: + UpperBitTable[(val ) & 0xFF]; + +#else + + if (val & 0xFFFFFFFF00000000) + { + if (val & 0xFFFF000000000000) + { + return (val & 0xFF00000000000000) ? + UpperBitTable[(val >> 56) ] + 56: + UpperBitTable[(val >> 48) & 0xFF] + 48; + } + return (val & 0xFF0000000000) ? + UpperBitTable[(val >> 40) & 0xFF] + 40: + UpperBitTable[(val >> 32) & 0xFF] + 32; + } + else + { + if (val & 0xFFFF0000) + { + return (val & 0xFF000000) ? + UpperBitTable[(val >> 24) ] + 24: + UpperBitTable[(val >> 16) & 0xFF] + 16; + } + return (val & 0xFF00) ? + UpperBitTable[(val >> 8) & 0xFF] + 8: + UpperBitTable[(val ) & 0xFF]; + } + +#endif +} + +//----------------------------------------------------------------------------------- +inline UByte LowerBit(UPInt val) +{ +#ifndef OVR_64BIT_POINTERS + + if (val & 0xFFFF) + { + return (val & 0xFF) ? + LowerBitTable[ val & 0xFF]: + LowerBitTable[(val >> 8) & 0xFF] + 8; + } + return (val & 0xFF0000) ? + LowerBitTable[(val >> 16) & 0xFF] + 16: + LowerBitTable[(val >> 24) & 0xFF] + 24; + +#else + + if (val & 0xFFFFFFFF) + { + if (val & 0xFFFF) + { + return (val & 0xFF) ? + LowerBitTable[ val & 0xFF]: + LowerBitTable[(val >> 8) & 0xFF] + 8; + } + return (val & 0xFF0000) ? + LowerBitTable[(val >> 16) & 0xFF] + 16: + LowerBitTable[(val >> 24) & 0xFF] + 24; + } + else + { + if (val & 0xFFFF00000000) + { + return (val & 0xFF00000000) ? + LowerBitTable[(val >> 32) & 0xFF] + 32: + LowerBitTable[(val >> 40) & 0xFF] + 40; + } + return (val & 0xFF000000000000) ? + LowerBitTable[(val >> 48) & 0xFF] + 48: + LowerBitTable[(val >> 56) & 0xFF] + 56; + } + +#endif +} + + + +// ******* Special (optimized) memory routines +// Note: null (bad) pointer is not tested +class MemUtil +{ +public: + + // Memory compare + static int Cmp (const void* p1, const void* p2, UPInt byteCount) { return memcmp(p1, p2, byteCount); } + static int Cmp16(const void* p1, const void* p2, UPInt int16Count); + static int Cmp32(const void* p1, const void* p2, UPInt int32Count); + static int Cmp64(const void* p1, const void* p2, UPInt int64Count); +}; + +// ** Inline Implementation + +inline int MemUtil::Cmp16(const void* p1, const void* p2, UPInt int16Count) +{ + SInt16* pa = (SInt16*)p1; + SInt16* pb = (SInt16*)p2; + unsigned ic = 0; + if (int16Count == 0) + return 0; + while (pa[ic] == pb[ic]) + if (++ic==int16Count) + return 0; + return pa[ic] > pb[ic] ? 1 : -1; +} +inline int MemUtil::Cmp32(const void* p1, const void* p2, UPInt int32Count) +{ + SInt32* pa = (SInt32*)p1; + SInt32* pb = (SInt32*)p2; + unsigned ic = 0; + if (int32Count == 0) + return 0; + while (pa[ic] == pb[ic]) + if (++ic==int32Count) + return 0; + return pa[ic] > pb[ic] ? 1 : -1; +} +inline int MemUtil::Cmp64(const void* p1, const void* p2, UPInt int64Count) +{ + SInt64* pa = (SInt64*)p1; + SInt64* pb = (SInt64*)p2; + unsigned ic = 0; + if (int64Count == 0) + return 0; + while (pa[ic] == pb[ic]) + if (++ic==int64Count) + return 0; + return pa[ic] > pb[ic] ? 1 : -1; +} + +// ** End Inline Implementation + + +//----------------------------------------------------------------------------------- +// ******* Byte Order Conversions +namespace ByteUtil { + + // *** Swap Byte Order + + // Swap the byte order of a byte array + inline void SwapOrder(void* pv, int size) + { + UByte* pb = (UByte*)pv; + UByte temp; + for (int i = 0; i < size>>1; i++) + { + temp = pb[size-1-i]; + pb[size-1-i] = pb[i]; + pb[i] = temp; + } + } + + // Swap the byte order of primitive types + inline UByte SwapOrder(UByte v) { return v; } + inline SByte SwapOrder(SByte v) { return v; } + inline UInt16 SwapOrder(UInt16 v) { return UInt16(v>>8)|UInt16(v<<8); } + inline SInt16 SwapOrder(SInt16 v) { return SInt16((UInt16(v)>>8)|(v<<8)); } + inline UInt32 SwapOrder(UInt32 v) { return (v>>24)|((v&0x00FF0000)>>8)|((v&0x0000FF00)<<8)|(v<<24); } + inline SInt32 SwapOrder(SInt32 p) { return (SInt32)SwapOrder(UInt32(p)); } + inline UInt64 SwapOrder(UInt64 v) + { + return (v>>56) | + ((v&UInt64(0x00FF000000000000))>>40) | + ((v&UInt64(0x0000FF0000000000))>>24) | + ((v&UInt64(0x000000FF00000000))>>8) | + ((v&UInt64(0x00000000FF000000))<<8) | + ((v&UInt64(0x0000000000FF0000))<<24) | + ((v&UInt64(0x000000000000FF00))<<40) | + (v<<56); + } + inline SInt64 SwapOrder(SInt64 v) { return (SInt64)SwapOrder(UInt64(v)); } + inline float SwapOrder(float p) + { + union { + float p; + UInt32 v; + } u; + u.p = p; + u.v = SwapOrder(u.v); + return u.p; + } + + inline double SwapOrder(double p) + { + union { + double p; + UInt64 v; + } u; + u.p = p; + u.v = SwapOrder(u.v); + return u.p; + } + + // *** Byte-order conversion + +#if (OVR_BYTE_ORDER == OVR_LITTLE_ENDIAN) + // Little Endian to System (LE) + inline UByte LEToSystem(UByte v) { return v; } + inline SByte LEToSystem(SByte v) { return v; } + inline UInt16 LEToSystem(UInt16 v) { return v; } + inline SInt16 LEToSystem(SInt16 v) { return v; } + inline UInt32 LEToSystem(UInt32 v) { return v; } + inline SInt32 LEToSystem(SInt32 v) { return v; } + inline UInt64 LEToSystem(UInt64 v) { return v; } + inline SInt64 LEToSystem(SInt64 v) { return v; } + inline float LEToSystem(float v) { return v; } + inline double LEToSystem(double v) { return v; } + + // Big Endian to System (LE) + inline UByte BEToSystem(UByte v) { return SwapOrder(v); } + inline SByte BEToSystem(SByte v) { return SwapOrder(v); } + inline UInt16 BEToSystem(UInt16 v) { return SwapOrder(v); } + inline SInt16 BEToSystem(SInt16 v) { return SwapOrder(v); } + inline UInt32 BEToSystem(UInt32 v) { return SwapOrder(v); } + inline SInt32 BEToSystem(SInt32 v) { return SwapOrder(v); } + inline UInt64 BEToSystem(UInt64 v) { return SwapOrder(v); } + inline SInt64 BEToSystem(SInt64 v) { return SwapOrder(v); } + inline float BEToSystem(float v) { return SwapOrder(v); } + inline double BEToSystem(double v) { return SwapOrder(v); } + + // System (LE) to Little Endian + inline UByte SystemToLE(UByte v) { return v; } + inline SByte SystemToLE(SByte v) { return v; } + inline UInt16 SystemToLE(UInt16 v) { return v; } + inline SInt16 SystemToLE(SInt16 v) { return v; } + inline UInt32 SystemToLE(UInt32 v) { return v; } + inline SInt32 SystemToLE(SInt32 v) { return v; } + inline UInt64 SystemToLE(UInt64 v) { return v; } + inline SInt64 SystemToLE(SInt64 v) { return v; } + inline float SystemToLE(float v) { return v; } + inline double SystemToLE(double v) { return v; } + + // System (LE) to Big Endian + inline UByte SystemToBE(UByte v) { return SwapOrder(v); } + inline SByte SystemToBE(SByte v) { return SwapOrder(v); } + inline UInt16 SystemToBE(UInt16 v) { return SwapOrder(v); } + inline SInt16 SystemToBE(SInt16 v) { return SwapOrder(v); } + inline UInt32 SystemToBE(UInt32 v) { return SwapOrder(v); } + inline SInt32 SystemToBE(SInt32 v) { return SwapOrder(v); } + inline UInt64 SystemToBE(UInt64 v) { return SwapOrder(v); } + inline SInt64 SystemToBE(SInt64 v) { return SwapOrder(v); } + inline float SystemToBE(float v) { return SwapOrder(v); } + inline double SystemToBE(double v) { return SwapOrder(v); } + +#elif (OVR_BYTE_ORDER == OVR_BIG_ENDIAN) + // Little Endian to System (BE) + inline UByte LEToSystem(UByte v) { return SwapOrder(v); } + inline SByte LEToSystem(SByte v) { return SwapOrder(v); } + inline UInt16 LEToSystem(UInt16 v) { return SwapOrder(v); } + inline SInt16 LEToSystem(SInt16 v) { return SwapOrder(v); } + inline UInt32 LEToSystem(UInt32 v) { return SwapOrder(v); } + inline SInt32 LEToSystem(SInt32 v) { return SwapOrder(v); } + inline UInt64 LEToSystem(UInt64 v) { return SwapOrder(v); } + inline SInt64 LEToSystem(SInt64 v) { return SwapOrder(v); } + inline float LEToSystem(float v) { return SwapOrder(v); } + inline double LEToSystem(double v) { return SwapOrder(v); } + + // Big Endian to System (BE) + inline UByte BEToSystem(UByte v) { return v; } + inline SByte BEToSystem(SByte v) { return v; } + inline UInt16 BEToSystem(UInt16 v) { return v; } + inline SInt16 BEToSystem(SInt16 v) { return v; } + inline UInt32 BEToSystem(UInt32 v) { return v; } + inline SInt32 BEToSystem(SInt32 v) { return v; } + inline UInt64 BEToSystem(UInt64 v) { return v; } + inline SInt64 BEToSystem(SInt64 v) { return v; } + inline float BEToSystem(float v) { return v; } + inline double BEToSystem(double v) { return v; } + + // System (BE) to Little Endian + inline UByte SystemToLE(UByte v) { return SwapOrder(v); } + inline SByte SystemToLE(SByte v) { return SwapOrder(v); } + inline UInt16 SystemToLE(UInt16 v) { return SwapOrder(v); } + inline SInt16 SystemToLE(SInt16 v) { return SwapOrder(v); } + inline UInt32 SystemToLE(UInt32 v) { return SwapOrder(v); } + inline SInt32 SystemToLE(SInt32 v) { return SwapOrder(v); } + inline UInt64 SystemToLE(UInt64 v) { return SwapOrder(v); } + inline SInt64 SystemToLE(SInt64 v) { return SwapOrder(v); } + inline float SystemToLE(float v) { return SwapOrder(v); } + inline double SystemToLE(double v) { return SwapOrder(v); } + + // System (BE) to Big Endian + inline UByte SystemToBE(UByte v) { return v; } + inline SByte SystemToBE(SByte v) { return v; } + inline UInt16 SystemToBE(UInt16 v) { return v; } + inline SInt16 SystemToBE(SInt16 v) { return v; } + inline UInt32 SystemToBE(UInt32 v) { return v; } + inline SInt32 SystemToBE(SInt32 v) { return v; } + inline UInt64 SystemToBE(UInt64 v) { return v; } + inline SInt64 SystemToBE(SInt64 v) { return v; } + inline float SystemToBE(float v) { return v; } + inline double SystemToBE(double v) { return v; } + +#else + #error "OVR_BYTE_ORDER must be defined to OVR_LITTLE_ENDIAN or OVR_BIG_ENDIAN" +#endif + +} // namespace ByteUtil + + + +}} // OVR::Alg + +#endif diff -r 59fc487ba58e -r b069a5c27388 libovr/Src/Kernel/OVR_Allocator.cpp --- a/libovr/Src/Kernel/OVR_Allocator.cpp Sat Sep 14 17:51:03 2013 +0300 +++ b/libovr/Src/Kernel/OVR_Allocator.cpp Sun Sep 15 04:10:05 2013 +0300 @@ -1,1 +1,84 @@ -/************************************************************************************ Filename : OVR_Allocator.cpp Content : Installable memory allocator implementation Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #include "OVR_Allocator.h" #ifdef OVR_OS_MAC #include #else #include #endif namespace OVR { //----------------------------------------------------------------------------------- // ***** Allocator Allocator* Allocator::pInstance = 0; // Default AlignedAlloc implementation will delegate to Alloc/Free after doing rounding. void* Allocator::AllocAligned(UPInt size, UPInt align) { OVR_ASSERT((align & (align-1)) == 0); align = (align > sizeof(UPInt)) ? align : sizeof(UPInt); UPInt p = (UPInt)Alloc(size+align); UPInt aligned = 0; if (p) { aligned = (UPInt(p) + align-1) & ~(align-1); if (aligned == p) aligned += align; *(((UPInt*)aligned)-1) = aligned-p; } return (void*)aligned; } void Allocator::FreeAligned(void* p) { UPInt src = UPInt(p) - *(((UPInt*)p)-1); Free((void*)src); } //------------------------------------------------------------------------ // ***** Default Allocator // This allocator is created and used if no other allocator is installed. // Default allocator delegates to system malloc. void* DefaultAllocator::Alloc(UPInt size) { return malloc(size); } void* DefaultAllocator::AllocDebug(UPInt size, const char* file, unsigned line) { #if defined(OVR_CC_MSVC) && defined(_CRTDBG_MAP_ALLOC) return _malloc_dbg(size, _NORMAL_BLOCK, file, line); #else OVR_UNUSED2(file, line); return malloc(size); #endif } void* DefaultAllocator::Realloc(void* p, UPInt newSize) { return realloc(p, newSize); } void DefaultAllocator::Free(void *p) { return free(p); } } // OVR \ No newline at end of file +/************************************************************************************ + +Filename : OVR_Allocator.cpp +Content : Installable memory allocator implementation +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#include "OVR_Allocator.h" +#ifdef OVR_OS_MAC + #include +#else + #include +#endif + +namespace OVR { + +//----------------------------------------------------------------------------------- +// ***** Allocator + +Allocator* Allocator::pInstance = 0; + +// Default AlignedAlloc implementation will delegate to Alloc/Free after doing rounding. +void* Allocator::AllocAligned(UPInt size, UPInt align) +{ + OVR_ASSERT((align & (align-1)) == 0); + align = (align > sizeof(UPInt)) ? align : sizeof(UPInt); + UPInt p = (UPInt)Alloc(size+align); + UPInt aligned = 0; + if (p) + { + aligned = (UPInt(p) + align-1) & ~(align-1); + if (aligned == p) + aligned += align; + *(((UPInt*)aligned)-1) = aligned-p; + } + return (void*)aligned; +} + +void Allocator::FreeAligned(void* p) +{ + UPInt src = UPInt(p) - *(((UPInt*)p)-1); + Free((void*)src); +} + + +//------------------------------------------------------------------------ +// ***** Default Allocator + +// This allocator is created and used if no other allocator is installed. +// Default allocator delegates to system malloc. + +void* DefaultAllocator::Alloc(UPInt size) +{ + return malloc(size); +} +void* DefaultAllocator::AllocDebug(UPInt size, const char* file, unsigned line) +{ +#if defined(OVR_CC_MSVC) && defined(_CRTDBG_MAP_ALLOC) + return _malloc_dbg(size, _NORMAL_BLOCK, file, line); +#else + OVR_UNUSED2(file, line); + return malloc(size); +#endif +} + +void* DefaultAllocator::Realloc(void* p, UPInt newSize) +{ + return realloc(p, newSize); +} +void DefaultAllocator::Free(void *p) +{ + return free(p); +} + + +} // OVR diff -r 59fc487ba58e -r b069a5c27388 libovr/Src/Kernel/OVR_Allocator.h --- a/libovr/Src/Kernel/OVR_Allocator.h Sat Sep 14 17:51:03 2013 +0300 +++ b/libovr/Src/Kernel/OVR_Allocator.h Sun Sep 15 04:10:05 2013 +0300 @@ -1,1 +1,336 @@ -/************************************************************************************ PublicHeader: OVR.h Filename : OVR_Allocator.h Content : Installable memory allocator Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #ifndef OVR_Allocator_h #define OVR_Allocator_h #include "OVR_Types.h" //----------------------------------------------------------------------------------- // ***** Disable template-unfriendly MS VC++ warnings #if defined(OVR_CC_MSVC) // Pragma to prevent long name warnings in in VC++ #pragma warning(disable : 4503) #pragma warning(disable : 4786) // In MSVC 7.1, warning about placement new POD default initializer #pragma warning(disable : 4345) #endif // Un-define new so that placement constructors work #undef new //----------------------------------------------------------------------------------- // ***** Placement new overrides // Calls constructor on own memory created with "new(ptr) type" #ifndef __PLACEMENT_NEW_INLINE #define __PLACEMENT_NEW_INLINE # if defined(OVR_CC_MWERKS) || defined(OVR_CC_BORLAND) || defined(OVR_CC_GNU) # include # else // Useful on MSVC OVR_FORCE_INLINE void* operator new (OVR::UPInt n, void *ptr) { OVR_UNUSED(n); return ptr; } OVR_FORCE_INLINE void operator delete (void *, void *) { } # endif #endif // __PLACEMENT_NEW_INLINE //------------------------------------------------------------------------ // ***** Macros to redefine class new/delete operators // Types specifically declared to allow disambiguation of address in // class member operator new. #define OVR_MEMORY_REDEFINE_NEW_IMPL(class_name, check_delete) \ void* operator new(UPInt sz) \ { void *p = OVR_ALLOC_DEBUG(sz, __FILE__, __LINE__); return p; } \ void* operator new(UPInt sz, const char* file, int line) \ { void* p = OVR_ALLOC_DEBUG(sz, file, line); OVR_UNUSED2(file, line); return p; } \ void operator delete(void *p) \ { check_delete(class_name, p); OVR_FREE(p); } \ void operator delete(void *p, const char*, int) \ { check_delete(class_name, p); OVR_FREE(p); } #define OVR_MEMORY_DEFINE_PLACEMENT_NEW \ void* operator new (UPInt n, void *ptr) { OVR_UNUSED(n); return ptr; } \ void operator delete (void *ptr, void *ptr2) { OVR_UNUSED2(ptr,ptr2); } #define OVR_MEMORY_CHECK_DELETE_NONE(class_name, p) // Redefined all delete/new operators in a class without custom memory initialization #define OVR_MEMORY_REDEFINE_NEW(class_name) \ OVR_MEMORY_REDEFINE_NEW_IMPL(class_name, OVR_MEMORY_CHECK_DELETE_NONE) namespace OVR { //----------------------------------------------------------------------------------- // ***** Construct / Destruct // Construct/Destruct functions are useful when new is redefined, as they can // be called instead of placement new constructors. template OVR_FORCE_INLINE T* Construct(void *p) { return ::new(p) T; } template OVR_FORCE_INLINE T* Construct(void *p, const T& source) { return ::new(p) T(source); } // Same as above, but allows for a different type of constructor. template OVR_FORCE_INLINE T* ConstructAlt(void *p, const S& source) { return ::new(p) T(source); } template OVR_FORCE_INLINE T* ConstructAlt(void *p, const S1& src1, const S2& src2) { return ::new(p) T(src1, src2); } template OVR_FORCE_INLINE void ConstructArray(void *p, UPInt count) { UByte *pdata = (UByte*)p; for (UPInt i=0; i< count; ++i, pdata += sizeof(T)) { Construct(pdata); } } template OVR_FORCE_INLINE void ConstructArray(void *p, UPInt count, const T& source) { UByte *pdata = (UByte*)p; for (UPInt i=0; i< count; ++i, pdata += sizeof(T)) { Construct(pdata, source); } } template OVR_FORCE_INLINE void Destruct(T *pobj) { pobj->~T(); OVR_UNUSED1(pobj); // Fix incorrect 'unused variable' MSVC warning. } template OVR_FORCE_INLINE void DestructArray(T *pobj, UPInt count) { for (UPInt i=0; i~T(); } //----------------------------------------------------------------------------------- // ***** Allocator // Allocator defines a memory allocation interface that developers can override // to to provide memory for OVR; an instance of this class is typically created on // application startup and passed into System or OVR::System constructor. // // // Users implementing this interface must provide three functions: Alloc, Free, // and Realloc. Implementations of these functions must honor the requested alignment. // Although arbitrary alignment requests are possible, requested alignment will // typically be small, such as 16 bytes or less. class Allocator { friend class System; public: // *** Standard Alignment Alloc/Free // Allocate memory of specified size with default alignment. // Alloc of size==0 will allocate a tiny block & return a valid pointer; // this makes it suitable for new operator. virtual void* Alloc(UPInt size) = 0; // Same as Alloc, but provides an option of passing debug data. virtual void* AllocDebug(UPInt size, const char* file, unsigned line) { OVR_UNUSED2(file, line); return Alloc(size); } // Reallocate memory block to a new size, copying data if necessary. Returns the pointer to // new memory block, which may be the same as original pointer. Will return 0 if reallocation // failed, in which case previous memory is still valid. // Realloc to decrease size will never fail. // Realloc of pointer == 0 is equivalent to Alloc // Realloc to size == 0, shrinks to the minimal size, pointer remains valid and requires Free(). virtual void* Realloc(void* p, UPInt newSize) = 0; // Frees memory allocated by Alloc/Realloc. // Free of null pointer is valid and will do nothing. virtual void Free(void *p) = 0; // *** Standard Alignment Alloc/Free // Allocate memory of specified alignment. // Memory allocated with AllocAligned MUST be freed with FreeAligned. // Default implementation will delegate to Alloc/Free after doing rounding. virtual void* AllocAligned(UPInt size, UPInt align); // Frees memory allocated with AllocAligned. virtual void FreeAligned(void* p); // Returns the pointer to the current globally installed Allocator instance. // This pointer is used for most of the memory allocations. static Allocator* GetInstance() { return pInstance; } protected: // onSystemShutdown is called on the allocator during System::Shutdown. // At this point, all allocations should've been freed. virtual void onSystemShutdown() { } public: static void setInstance(Allocator* palloc) { OVR_ASSERT((pInstance == 0) || (palloc == 0)); pInstance = palloc; } private: static Allocator* pInstance; }; //------------------------------------------------------------------------ // ***** Allocator_SingletonSupport // Allocator_SingletonSupport is a Allocator wrapper class that implements // the InitSystemSingleton static function, used to create a global singleton // used for the OVR::System default argument initialization. // // End users implementing custom Allocator interface don't need to make use of this base // class; they can just create an instance of their own class on stack and pass it to System. template class Allocator_SingletonSupport : public Allocator { struct AllocContainer { UPInt Data[(sizeof(D) + sizeof(UPInt)-1) / sizeof(UPInt)]; bool Initialized; AllocContainer() : Initialized(0) { } }; AllocContainer* pContainer; public: Allocator_SingletonSupport() : pContainer(0) { } // Creates a singleton instance of this Allocator class used // on OVR_DEFAULT_ALLOCATOR during System initialization. static D* InitSystemSingleton() { static AllocContainer Container; OVR_ASSERT(Container.Initialized == false); Allocator_SingletonSupport *presult = Construct((void*)Container.Data); presult->pContainer = &Container; Container.Initialized = true; return (D*)presult; } protected: virtual void onSystemShutdown() { Allocator::onSystemShutdown(); if (pContainer) { pContainer->Initialized = false; Destruct((D*)this); pContainer = 0; } } }; //------------------------------------------------------------------------ // ***** Default Allocator // This allocator is created and used if no other allocator is installed. // Default allocator delegates to system malloc. class DefaultAllocator : public Allocator_SingletonSupport { public: virtual void* Alloc(UPInt size); virtual void* AllocDebug(UPInt size, const char* file, unsigned line); virtual void* Realloc(void* p, UPInt newSize); virtual void Free(void *p); }; //------------------------------------------------------------------------ // ***** Memory Allocation Macros // These macros should be used for global allocation. In the future, these // macros will allows allocation to be extended with debug file/line information // if necessary. #define OVR_REALLOC(p,s) OVR::Allocator::GetInstance()->Realloc((p),(s)) #define OVR_FREE(p) OVR::Allocator::GetInstance()->Free((p)) #define OVR_ALLOC_ALIGNED(s,a) OVR::Allocator::GetInstance()->AllocAligned((s),(a)) #define OVR_FREE_ALIGNED(p) OVR::Allocator::GetInstance()->FreeAligned((p)) #ifdef OVR_BUILD_DEBUG #define OVR_ALLOC(s) OVR::Allocator::GetInstance()->AllocDebug((s), __FILE__, __LINE__) #define OVR_ALLOC_DEBUG(s,f,l) OVR::Allocator::GetInstance()->AllocDebug((s), f, l) #else #define OVR_ALLOC(s) OVR::Allocator::GetInstance()->Alloc((s)) #define OVR_ALLOC_DEBUG(s,f,l) OVR::Allocator::GetInstance()->Alloc((s)) #endif //------------------------------------------------------------------------ // Base class that overrides the new and delete operators. // Deriving from this class, even as a multiple base, incurs no space overhead. class NewOverrideBase { public: // Redefine all new & delete operators. OVR_MEMORY_REDEFINE_NEW(NewOverrideBase) }; } // OVR // Redefine operator 'new' if necessary. #if defined(OVR_DEFINE_NEW) #define new OVR_DEFINE_NEW #endif #endif // OVR_Memory \ No newline at end of file +/************************************************************************************ + +PublicHeader: OVR.h +Filename : OVR_Allocator.h +Content : Installable memory allocator +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#ifndef OVR_Allocator_h +#define OVR_Allocator_h + +#include "OVR_Types.h" + +//----------------------------------------------------------------------------------- + +// ***** Disable template-unfriendly MS VC++ warnings +#if defined(OVR_CC_MSVC) +// Pragma to prevent long name warnings in in VC++ +#pragma warning(disable : 4503) +#pragma warning(disable : 4786) +// In MSVC 7.1, warning about placement new POD default initializer +#pragma warning(disable : 4345) +#endif + +// Un-define new so that placement constructors work +#undef new + + +//----------------------------------------------------------------------------------- +// ***** Placement new overrides + +// Calls constructor on own memory created with "new(ptr) type" +#ifndef __PLACEMENT_NEW_INLINE +#define __PLACEMENT_NEW_INLINE + +# if defined(OVR_CC_MWERKS) || defined(OVR_CC_BORLAND) || defined(OVR_CC_GNU) +# include +# else + // Useful on MSVC + OVR_FORCE_INLINE void* operator new (OVR::UPInt n, void *ptr) { OVR_UNUSED(n); return ptr; } + OVR_FORCE_INLINE void operator delete (void *, void *) { } +# endif + +#endif // __PLACEMENT_NEW_INLINE + + + +//------------------------------------------------------------------------ +// ***** Macros to redefine class new/delete operators + +// Types specifically declared to allow disambiguation of address in +// class member operator new. + +#define OVR_MEMORY_REDEFINE_NEW_IMPL(class_name, check_delete) \ + void* operator new(UPInt sz) \ + { void *p = OVR_ALLOC_DEBUG(sz, __FILE__, __LINE__); return p; } \ + void* operator new(UPInt sz, const char* file, int line) \ + { void* p = OVR_ALLOC_DEBUG(sz, file, line); OVR_UNUSED2(file, line); return p; } \ + void operator delete(void *p) \ + { check_delete(class_name, p); OVR_FREE(p); } \ + void operator delete(void *p, const char*, int) \ + { check_delete(class_name, p); OVR_FREE(p); } + +#define OVR_MEMORY_DEFINE_PLACEMENT_NEW \ + void* operator new (UPInt n, void *ptr) { OVR_UNUSED(n); return ptr; } \ + void operator delete (void *ptr, void *ptr2) { OVR_UNUSED2(ptr,ptr2); } + + +#define OVR_MEMORY_CHECK_DELETE_NONE(class_name, p) + +// Redefined all delete/new operators in a class without custom memory initialization +#define OVR_MEMORY_REDEFINE_NEW(class_name) \ + OVR_MEMORY_REDEFINE_NEW_IMPL(class_name, OVR_MEMORY_CHECK_DELETE_NONE) + + +namespace OVR { + +//----------------------------------------------------------------------------------- +// ***** Construct / Destruct + +// Construct/Destruct functions are useful when new is redefined, as they can +// be called instead of placement new constructors. + + +template +OVR_FORCE_INLINE T* Construct(void *p) +{ + return ::new(p) T; +} + +template +OVR_FORCE_INLINE T* Construct(void *p, const T& source) +{ + return ::new(p) T(source); +} + +// Same as above, but allows for a different type of constructor. +template +OVR_FORCE_INLINE T* ConstructAlt(void *p, const S& source) +{ + return ::new(p) T(source); +} + +template +OVR_FORCE_INLINE T* ConstructAlt(void *p, const S1& src1, const S2& src2) +{ + return ::new(p) T(src1, src2); +} + +template +OVR_FORCE_INLINE void ConstructArray(void *p, UPInt count) +{ + UByte *pdata = (UByte*)p; + for (UPInt i=0; i< count; ++i, pdata += sizeof(T)) + { + Construct(pdata); + } +} + +template +OVR_FORCE_INLINE void ConstructArray(void *p, UPInt count, const T& source) +{ + UByte *pdata = (UByte*)p; + for (UPInt i=0; i< count; ++i, pdata += sizeof(T)) + { + Construct(pdata, source); + } +} + +template +OVR_FORCE_INLINE void Destruct(T *pobj) +{ + pobj->~T(); + OVR_UNUSED1(pobj); // Fix incorrect 'unused variable' MSVC warning. +} + +template +OVR_FORCE_INLINE void DestructArray(T *pobj, UPInt count) +{ + for (UPInt i=0; i~T(); +} + + +//----------------------------------------------------------------------------------- +// ***** Allocator + +// Allocator defines a memory allocation interface that developers can override +// to to provide memory for OVR; an instance of this class is typically created on +// application startup and passed into System or OVR::System constructor. +// +// +// Users implementing this interface must provide three functions: Alloc, Free, +// and Realloc. Implementations of these functions must honor the requested alignment. +// Although arbitrary alignment requests are possible, requested alignment will +// typically be small, such as 16 bytes or less. + +class Allocator +{ + friend class System; +public: + + // *** Standard Alignment Alloc/Free + + // Allocate memory of specified size with default alignment. + // Alloc of size==0 will allocate a tiny block & return a valid pointer; + // this makes it suitable for new operator. + virtual void* Alloc(UPInt size) = 0; + // Same as Alloc, but provides an option of passing debug data. + virtual void* AllocDebug(UPInt size, const char* file, unsigned line) + { OVR_UNUSED2(file, line); return Alloc(size); } + + // Reallocate memory block to a new size, copying data if necessary. Returns the pointer to + // new memory block, which may be the same as original pointer. Will return 0 if reallocation + // failed, in which case previous memory is still valid. + // Realloc to decrease size will never fail. + // Realloc of pointer == 0 is equivalent to Alloc + // Realloc to size == 0, shrinks to the minimal size, pointer remains valid and requires Free(). + virtual void* Realloc(void* p, UPInt newSize) = 0; + + // Frees memory allocated by Alloc/Realloc. + // Free of null pointer is valid and will do nothing. + virtual void Free(void *p) = 0; + + + // *** Standard Alignment Alloc/Free + + // Allocate memory of specified alignment. + // Memory allocated with AllocAligned MUST be freed with FreeAligned. + // Default implementation will delegate to Alloc/Free after doing rounding. + virtual void* AllocAligned(UPInt size, UPInt align); + // Frees memory allocated with AllocAligned. + virtual void FreeAligned(void* p); + + // Returns the pointer to the current globally installed Allocator instance. + // This pointer is used for most of the memory allocations. + static Allocator* GetInstance() { return pInstance; } + + +protected: + // onSystemShutdown is called on the allocator during System::Shutdown. + // At this point, all allocations should've been freed. + virtual void onSystemShutdown() { } + +public: + static void setInstance(Allocator* palloc) + { + OVR_ASSERT((pInstance == 0) || (palloc == 0)); + pInstance = palloc; + } + +private: + + static Allocator* pInstance; +}; + + + +//------------------------------------------------------------------------ +// ***** Allocator_SingletonSupport + +// Allocator_SingletonSupport is a Allocator wrapper class that implements +// the InitSystemSingleton static function, used to create a global singleton +// used for the OVR::System default argument initialization. +// +// End users implementing custom Allocator interface don't need to make use of this base +// class; they can just create an instance of their own class on stack and pass it to System. + +template +class Allocator_SingletonSupport : public Allocator +{ + struct AllocContainer + { + UPInt Data[(sizeof(D) + sizeof(UPInt)-1) / sizeof(UPInt)]; + bool Initialized; + AllocContainer() : Initialized(0) { } + }; + + AllocContainer* pContainer; + +public: + Allocator_SingletonSupport() : pContainer(0) { } + + // Creates a singleton instance of this Allocator class used + // on OVR_DEFAULT_ALLOCATOR during System initialization. + static D* InitSystemSingleton() + { + static AllocContainer Container; + OVR_ASSERT(Container.Initialized == false); + + Allocator_SingletonSupport *presult = Construct((void*)Container.Data); + presult->pContainer = &Container; + Container.Initialized = true; + return (D*)presult; + } + +protected: + virtual void onSystemShutdown() + { + Allocator::onSystemShutdown(); + if (pContainer) + { + pContainer->Initialized = false; + Destruct((D*)this); + pContainer = 0; + } + } +}; + +//------------------------------------------------------------------------ +// ***** Default Allocator + +// This allocator is created and used if no other allocator is installed. +// Default allocator delegates to system malloc. + +class DefaultAllocator : public Allocator_SingletonSupport +{ +public: + virtual void* Alloc(UPInt size); + virtual void* AllocDebug(UPInt size, const char* file, unsigned line); + virtual void* Realloc(void* p, UPInt newSize); + virtual void Free(void *p); +}; + + +//------------------------------------------------------------------------ +// ***** Memory Allocation Macros + +// These macros should be used for global allocation. In the future, these +// macros will allows allocation to be extended with debug file/line information +// if necessary. + +#define OVR_REALLOC(p,s) OVR::Allocator::GetInstance()->Realloc((p),(s)) +#define OVR_FREE(p) OVR::Allocator::GetInstance()->Free((p)) +#define OVR_ALLOC_ALIGNED(s,a) OVR::Allocator::GetInstance()->AllocAligned((s),(a)) +#define OVR_FREE_ALIGNED(p) OVR::Allocator::GetInstance()->FreeAligned((p)) + +#ifdef OVR_BUILD_DEBUG +#define OVR_ALLOC(s) OVR::Allocator::GetInstance()->AllocDebug((s), __FILE__, __LINE__) +#define OVR_ALLOC_DEBUG(s,f,l) OVR::Allocator::GetInstance()->AllocDebug((s), f, l) +#else +#define OVR_ALLOC(s) OVR::Allocator::GetInstance()->Alloc((s)) +#define OVR_ALLOC_DEBUG(s,f,l) OVR::Allocator::GetInstance()->Alloc((s)) +#endif + +//------------------------------------------------------------------------ + +// Base class that overrides the new and delete operators. +// Deriving from this class, even as a multiple base, incurs no space overhead. +class NewOverrideBase +{ +public: + + // Redefine all new & delete operators. + OVR_MEMORY_REDEFINE_NEW(NewOverrideBase) +}; + + +} // OVR + + +// Redefine operator 'new' if necessary. +#if defined(OVR_DEFINE_NEW) +#define new OVR_DEFINE_NEW +#endif + + +#endif // OVR_Memory diff -r 59fc487ba58e -r b069a5c27388 libovr/Src/Kernel/OVR_Array.h --- a/libovr/Src/Kernel/OVR_Array.h Sat Sep 14 17:51:03 2013 +0300 +++ b/libovr/Src/Kernel/OVR_Array.h Sun Sep 15 04:10:05 2013 +0300 @@ -1,1 +1,793 @@ -/************************************************************************************ PublicHeader: OVR.h Filename : OVR_Array.h Content : Template implementation for Array Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #ifndef OVR_Array_h #define OVR_Array_h #include "OVR_ContainerAllocator.h" namespace OVR { //----------------------------------------------------------------------------------- // ***** ArrayDefaultPolicy // // Default resize behavior. No minimal capacity, Granularity=4, // Shrinking as needed. ArrayConstPolicy actually is the same as // ArrayDefaultPolicy, but parametrized with constants. // This struct is used only in order to reduce the template "matroska". struct ArrayDefaultPolicy { ArrayDefaultPolicy() : Capacity(0) {} ArrayDefaultPolicy(const ArrayDefaultPolicy&) : Capacity(0) {} UPInt GetMinCapacity() const { return 0; } UPInt GetGranularity() const { return 4; } bool NeverShrinking() const { return 0; } UPInt GetCapacity() const { return Capacity; } void SetCapacity(UPInt capacity) { Capacity = capacity; } private: UPInt Capacity; }; //----------------------------------------------------------------------------------- // ***** ArrayConstPolicy // // Statically parametrized resizing behavior: // MinCapacity, Granularity, and Shrinking flag. template struct ArrayConstPolicy { typedef ArrayConstPolicy SelfType; ArrayConstPolicy() : Capacity(0) {} ArrayConstPolicy(const SelfType&) : Capacity(0) {} UPInt GetMinCapacity() const { return MinCapacity; } UPInt GetGranularity() const { return Granularity; } bool NeverShrinking() const { return NeverShrink; } UPInt GetCapacity() const { return Capacity; } void SetCapacity(UPInt capacity) { Capacity = capacity; } private: UPInt Capacity; }; //----------------------------------------------------------------------------------- // ***** ArrayDataBase // // Basic operations with array data: Reserve, Resize, Free, ArrayPolicy. // For internal use only: ArrayData,ArrayDataCC and others. template struct ArrayDataBase { typedef T ValueType; typedef Allocator AllocatorType; typedef SizePolicy SizePolicyType; typedef ArrayDataBase SelfType; ArrayDataBase() : Data(0), Size(0), Policy() {} ArrayDataBase(const SizePolicy& p) : Data(0), Size(0), Policy(p) {} ~ArrayDataBase() { Allocator::DestructArray(Data, Size); Allocator::Free(Data); } UPInt GetCapacity() const { return Policy.GetCapacity(); } void ClearAndRelease() { Allocator::DestructArray(Data, Size); Allocator::Free(Data); Data = 0; Size = 0; Policy.SetCapacity(0); } void Reserve(UPInt newCapacity) { if (Policy.NeverShrinking() && newCapacity < GetCapacity()) return; if (newCapacity < Policy.GetMinCapacity()) newCapacity = Policy.GetMinCapacity(); // Resize the buffer. if (newCapacity == 0) { if (Data) { Allocator::Free(Data); Data = 0; } Policy.SetCapacity(0); } else { UPInt gran = Policy.GetGranularity(); newCapacity = (newCapacity + gran - 1) / gran * gran; if (Data) { if (Allocator::IsMovable()) { Data = (T*)Allocator::Realloc(Data, sizeof(T) * newCapacity); } else { T* newData = (T*)Allocator::Alloc(sizeof(T) * newCapacity); UPInt i, s; s = (Size < newCapacity) ? Size : newCapacity; for (i = 0; i < s; ++i) { Allocator::Construct(&newData[i], Data[i]); Allocator::Destruct(&Data[i]); } for (i = s; i < Size; ++i) { Allocator::Destruct(&Data[i]); } Allocator::Free(Data); Data = newData; } } else { Data = (T*)Allocator::Alloc(sizeof(T) * newCapacity); //memset(Buffer, 0, (sizeof(ValueType) * newSize)); // Do we need this? } Policy.SetCapacity(newCapacity); // OVR_ASSERT(Data); // need to throw (or something) on alloc failure! } } // This version of Resize DOES NOT construct the elements. // It's done to optimize PushBack, which uses a copy constructor // instead of the default constructor and assignment void ResizeNoConstruct(UPInt newSize) { UPInt oldSize = Size; if (newSize < oldSize) { Allocator::DestructArray(Data + newSize, oldSize - newSize); if (newSize < (Policy.GetCapacity() >> 1)) { Reserve(newSize); } } else if(newSize >= Policy.GetCapacity()) { Reserve(newSize + (newSize >> 2)); } //! IMPORTANT to modify Size only after Reserve completes, because garbage collectable // array may use this array and may traverse it during Reserve (in the case, if // collection occurs because of heap limit exceeded). Size = newSize; } ValueType* Data; UPInt Size; SizePolicy Policy; }; //----------------------------------------------------------------------------------- // ***** ArrayData // // General purpose array data. // For internal use only in Array, ArrayLH, ArrayPOD and so on. template struct ArrayData : ArrayDataBase { typedef T ValueType; typedef Allocator AllocatorType; typedef SizePolicy SizePolicyType; typedef ArrayDataBase BaseType; typedef ArrayData SelfType; ArrayData() : BaseType() { } ArrayData(int size) : BaseType() { Resize(size); } ArrayData(const SelfType& a) : BaseType(a.Policy) { Append(a.Data, a.Size); } void Resize(UPInt newSize) { UPInt oldSize = this->Size; BaseType::ResizeNoConstruct(newSize); if(newSize > oldSize) Allocator::ConstructArray(this->Data + oldSize, newSize - oldSize); } void PushBack(const ValueType& val) { BaseType::ResizeNoConstruct(this->Size + 1); Allocator::Construct(this->Data + this->Size - 1, val); } template void PushBackAlt(const S& val) { BaseType::ResizeNoConstruct(this->Size + 1); Allocator::ConstructAlt(this->Data + this->Size - 1, val); } // Append the given data to the array. void Append(const ValueType other[], UPInt count) { if (count) { UPInt oldSize = this->Size; BaseType::ResizeNoConstruct(this->Size + count); Allocator::ConstructArray(this->Data + oldSize, count, other); } } }; //----------------------------------------------------------------------------------- // ***** ArrayDataCC // // A modification of ArrayData that always copy-constructs new elements // using a specified DefaultValue. For internal use only in ArrayCC. template struct ArrayDataCC : ArrayDataBase { typedef T ValueType; typedef Allocator AllocatorType; typedef SizePolicy SizePolicyType; typedef ArrayDataBase BaseType; typedef ArrayDataCC SelfType; ArrayDataCC(const ValueType& defval) : BaseType(), DefaultValue(defval) { } ArrayDataCC(const ValueType& defval, int size) : BaseType(), DefaultValue(defval) { Resize(size); } ArrayDataCC(const SelfType& a) : BaseType(a.Policy), DefaultValue(a.DefaultValue) { Append(a.Data, a.Size); } void Resize(UPInt newSize) { UPInt oldSize = this->Size; BaseType::ResizeNoConstruct(newSize); if(newSize > oldSize) Allocator::ConstructArray(this->Data + oldSize, newSize - oldSize, DefaultValue); } void PushBack(const ValueType& val) { BaseType::ResizeNoConstruct(this->Size + 1); Allocator::Construct(this->Data + this->Size - 1, val); } template void PushBackAlt(const S& val) { BaseType::ResizeNoConstruct(this->Size + 1); Allocator::ConstructAlt(this->Data + this->Size - 1, val); } // Append the given data to the array. void Append(const ValueType other[], UPInt count) { if (count) { UPInt oldSize = this->Size; BaseType::ResizeNoConstruct(this->Size + count); Allocator::ConstructArray(this->Data + oldSize, count, other); } } ValueType DefaultValue; }; //----------------------------------------------------------------------------------- // ***** ArrayBase // // Resizable array. The behavior can be POD (suffix _POD) and // Movable (no suffix) depending on the allocator policy. // In case of _POD the constructors and destructors are not called. // // Arrays can't handle non-movable objects! Don't put anything in here // that can't be moved around by bitwise copy. // // The addresses of elements are not persistent! Don't keep the address // of an element; the array contents will move around as it gets resized. template class ArrayBase { public: typedef typename ArrayData::ValueType ValueType; typedef typename ArrayData::AllocatorType AllocatorType; typedef typename ArrayData::SizePolicyType SizePolicyType; typedef ArrayBase SelfType; #undef new OVR_MEMORY_REDEFINE_NEW(ArrayBase) // Redefine operator 'new' if necessary. #if defined(OVR_DEFINE_NEW) #define new OVR_DEFINE_NEW #endif ArrayBase() : Data() {} ArrayBase(int size) : Data(size) {} ArrayBase(const SelfType& a) : Data(a.Data) {} ArrayBase(const ValueType& defval) : Data(defval) {} ArrayBase(const ValueType& defval, int size) : Data(defval, size) {} SizePolicyType* GetSizePolicy() const { return Data.Policy; } void SetSizePolicy(const SizePolicyType& p) { Data.Policy = p; } bool NeverShrinking()const { return Data.Policy.NeverShrinking(); } UPInt GetSize() const { return Data.Size; } bool IsEmpty() const { return Data.Size == 0; } UPInt GetCapacity() const { return Data.GetCapacity(); } UPInt GetNumBytes() const { return Data.GetCapacity() * sizeof(ValueType); } void ClearAndRelease() { Data.ClearAndRelease(); } void Clear() { Data.Resize(0); } void Resize(UPInt newSize) { Data.Resize(newSize); } // Reserve can only increase the capacity void Reserve(UPInt newCapacity) { if (newCapacity > Data.GetCapacity()) Data.Reserve(newCapacity); } // Basic access. ValueType& At(UPInt index) { OVR_ASSERT(index < Data.Size); return Data.Data[index]; } const ValueType& At(UPInt index) const { OVR_ASSERT(index < Data.Size); return Data.Data[index]; } ValueType ValueAt(UPInt index) const { OVR_ASSERT(index < Data.Size); return Data.Data[index]; } // Basic access. ValueType& operator [] (UPInt index) { OVR_ASSERT(index < Data.Size); return Data.Data[index]; } const ValueType& operator [] (UPInt index) const { OVR_ASSERT(index < Data.Size); return Data.Data[index]; } // Raw pointer to the data. Use with caution! const ValueType* GetDataPtr() const { return Data.Data; } ValueType* GetDataPtr() { return Data.Data; } // Insert the given element at the end of the array. void PushBack(const ValueType& val) { // DO NOT pass elements of your own vector into // push_back()! Since we're using references, // resize() may munge the element storage! // OVR_ASSERT(&val < &Buffer[0] || &val > &Buffer[BufferSize]); Data.PushBack(val); } template void PushBackAlt(const S& val) { Data.PushBackAlt(val); } // Remove the last element. void PopBack(UPInt count = 1) { OVR_ASSERT(Data.Size >= count); Data.Resize(Data.Size - count); } ValueType& PushDefault() { Data.PushBack(ValueType()); return Back(); } ValueType Pop() { ValueType t = Back(); PopBack(); return t; } // Access the first element. ValueType& Front() { return At(0); } const ValueType& Front() const { return At(0); } // Access the last element. ValueType& Back() { return At(Data.Size - 1); } const ValueType& Back() const { return At(Data.Size - 1); } // Array copy. Copies the contents of a into this array. const SelfType& operator = (const SelfType& a) { Resize(a.GetSize()); for (UPInt i = 0; i < Data.Size; i++) { *(Data.Data + i) = a[i]; } return *this; } // Removing multiple elements from the array. void RemoveMultipleAt(UPInt index, UPInt num) { OVR_ASSERT(index + num <= Data.Size); if (Data.Size == num) { Clear(); } else { AllocatorType::DestructArray(Data.Data + index, num); AllocatorType::CopyArrayForward( Data.Data + index, Data.Data + index + num, Data.Size - num - index); Data.Size -= num; } } // Removing an element from the array is an expensive operation! // It compacts only after removing the last element. void RemoveAt(UPInt index) { OVR_ASSERT(index < Data.Size); if (Data.Size == 1) { Clear(); } else { AllocatorType::Destruct(Data.Data + index); AllocatorType::CopyArrayForward( Data.Data + index, Data.Data + index + 1, Data.Size - 1 - index); --Data.Size; } } // Insert the given object at the given index shifting all the elements up. void InsertAt(UPInt index, const ValueType& val = ValueType()) { OVR_ASSERT(index <= Data.Size); Data.Resize(Data.Size + 1); if (index < Data.Size - 1) { AllocatorType::CopyArrayBackward( Data.Data + index + 1, Data.Data + index, Data.Size - 1 - index); } AllocatorType::Construct(Data.Data + index, val); } // Insert the given object at the given index shifting all the elements up. void InsertMultipleAt(UPInt index, UPInt num, const ValueType& val = ValueType()) { OVR_ASSERT(index <= Data.Size); Data.Resize(Data.Size + num); if (index < Data.Size - num) { AllocatorType::CopyArrayBackward( Data.Data + index + num, Data.Data + index, Data.Size - num - index); } for (UPInt i = 0; i < num; ++i) AllocatorType::Construct(Data.Data + index + i, val); } // Append the given data to the array. void Append(const SelfType& other) { Append(other.Data.Data, other.GetSize()); } // Append the given data to the array. void Append(const ValueType other[], UPInt count) { Data.Append(other, count); } class Iterator { SelfType* pArray; SPInt CurIndex; public: Iterator() : pArray(0), CurIndex(-1) {} Iterator(SelfType* parr, SPInt idx = 0) : pArray(parr), CurIndex(idx) {} bool operator==(const Iterator& it) const { return pArray == it.pArray && CurIndex == it.CurIndex; } bool operator!=(const Iterator& it) const { return pArray != it.pArray || CurIndex != it.CurIndex; } Iterator& operator++() { if (pArray) { if (CurIndex < (SPInt)pArray->GetSize()) ++CurIndex; } return *this; } Iterator operator++(int) { Iterator it(*this); operator++(); return it; } Iterator& operator--() { if (pArray) { if (CurIndex >= 0) --CurIndex; } return *this; } Iterator operator--(int) { Iterator it(*this); operator--(); return it; } Iterator operator+(int delta) const { return Iterator(pArray, CurIndex + delta); } Iterator operator-(int delta) const { return Iterator(pArray, CurIndex - delta); } SPInt operator-(const Iterator& right) const { OVR_ASSERT(pArray == right.pArray); return CurIndex - right.CurIndex; } ValueType& operator*() const { OVR_ASSERT(pArray); return (*pArray)[CurIndex]; } ValueType* operator->() const { OVR_ASSERT(pArray); return &(*pArray)[CurIndex]; } ValueType* GetPtr() const { OVR_ASSERT(pArray); return &(*pArray)[CurIndex]; } bool IsFinished() const { return !pArray || CurIndex < 0 || CurIndex >= (int)pArray->GetSize(); } void Remove() { if (!IsFinished()) pArray->RemoveAt(CurIndex); } SPInt GetIndex() const { return CurIndex; } }; Iterator Begin() { return Iterator(this); } Iterator End() { return Iterator(this, (SPInt)GetSize()); } Iterator Last() { return Iterator(this, (SPInt)GetSize() - 1); } class ConstIterator { const SelfType* pArray; SPInt CurIndex; public: ConstIterator() : pArray(0), CurIndex(-1) {} ConstIterator(const SelfType* parr, SPInt idx = 0) : pArray(parr), CurIndex(idx) {} bool operator==(const ConstIterator& it) const { return pArray == it.pArray && CurIndex == it.CurIndex; } bool operator!=(const ConstIterator& it) const { return pArray != it.pArray || CurIndex != it.CurIndex; } ConstIterator& operator++() { if (pArray) { if (CurIndex < (int)pArray->GetSize()) ++CurIndex; } return *this; } ConstIterator operator++(int) { ConstIterator it(*this); operator++(); return it; } ConstIterator& operator--() { if (pArray) { if (CurIndex >= 0) --CurIndex; } return *this; } ConstIterator operator--(int) { ConstIterator it(*this); operator--(); return it; } ConstIterator operator+(int delta) const { return ConstIterator(pArray, CurIndex + delta); } ConstIterator operator-(int delta) const { return ConstIterator(pArray, CurIndex - delta); } SPInt operator-(const ConstIterator& right) const { OVR_ASSERT(pArray == right.pArray); return CurIndex - right.CurIndex; } const ValueType& operator*() const { OVR_ASSERT(pArray); return (*pArray)[CurIndex]; } const ValueType* operator->() const { OVR_ASSERT(pArray); return &(*pArray)[CurIndex]; } const ValueType* GetPtr() const { OVR_ASSERT(pArray); return &(*pArray)[CurIndex]; } bool IsFinished() const { return !pArray || CurIndex < 0 || CurIndex >= (int)pArray->GetSize(); } SPInt GetIndex() const { return CurIndex; } }; ConstIterator Begin() const { return ConstIterator(this); } ConstIterator End() const { return ConstIterator(this, (SPInt)GetSize()); } ConstIterator Last() const { return ConstIterator(this, (SPInt)GetSize() - 1); } protected: ArrayData Data; }; //----------------------------------------------------------------------------------- // ***** Array // // General purpose array for movable objects that require explicit // construction/destruction. template class Array : public ArrayBase, SizePolicy> > { public: typedef T ValueType; typedef ContainerAllocator AllocatorType; typedef SizePolicy SizePolicyType; typedef Array SelfType; typedef ArrayBase, SizePolicy> > BaseType; Array() : BaseType() {} Array(int size) : BaseType(size) {} Array(const SizePolicyType& p) : BaseType() { SetSizePolicy(p); } Array(const SelfType& a) : BaseType(a) {} const SelfType& operator=(const SelfType& a) { BaseType::operator=(a); return *this; } }; // ***** ArrayPOD // // General purpose array for movable objects that DOES NOT require // construction/destruction. Constructors and destructors are not called! // Global heap is in use. template class ArrayPOD : public ArrayBase, SizePolicy> > { public: typedef T ValueType; typedef ContainerAllocator_POD AllocatorType; typedef SizePolicy SizePolicyType; typedef ArrayPOD SelfType; typedef ArrayBase, SizePolicy> > BaseType; ArrayPOD() : BaseType() {} ArrayPOD(int size) : BaseType(size) {} ArrayPOD(const SizePolicyType& p) : BaseType() { SetSizePolicy(p); } ArrayPOD(const SelfType& a) : BaseType(a) {} const SelfType& operator=(const SelfType& a) { BaseType::operator=(a); return *this; } }; // ***** ArrayCPP // // General purpose, fully C++ compliant array. Can be used with non-movable data. // Global heap is in use. template class ArrayCPP : public ArrayBase, SizePolicy> > { public: typedef T ValueType; typedef ContainerAllocator_CPP AllocatorType; typedef SizePolicy SizePolicyType; typedef ArrayCPP SelfType; typedef ArrayBase, SizePolicy> > BaseType; ArrayCPP() : BaseType() {} ArrayCPP(int size) : BaseType(size) {} ArrayCPP(const SizePolicyType& p) : BaseType() { SetSizePolicy(p); } ArrayCPP(const SelfType& a) : BaseType(a) {} const SelfType& operator=(const SelfType& a) { BaseType::operator=(a); return *this; } }; // ***** ArrayCC // // A modification of the array that uses the given default value to // construct the elements. The constructors and destructors are // properly called, the objects must be movable. template class ArrayCC : public ArrayBase, SizePolicy> > { public: typedef T ValueType; typedef ContainerAllocator AllocatorType; typedef SizePolicy SizePolicyType; typedef ArrayCC SelfType; typedef ArrayBase, SizePolicy> > BaseType; ArrayCC(const ValueType& defval) : BaseType(defval) {} ArrayCC(const ValueType& defval, int size) : BaseType(defval, size) {} ArrayCC(const ValueType& defval, const SizePolicyType& p) : BaseType(defval) { SetSizePolicy(p); } ArrayCC(const SelfType& a) : BaseType(a) {} const SelfType& operator=(const SelfType& a) { BaseType::operator=(a); return *this; } }; } // OVR #endif \ No newline at end of file +/************************************************************************************ + +PublicHeader: OVR.h +Filename : OVR_Array.h +Content : Template implementation for Array +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#ifndef OVR_Array_h +#define OVR_Array_h + +#include "OVR_ContainerAllocator.h" + +namespace OVR { + +//----------------------------------------------------------------------------------- +// ***** ArrayDefaultPolicy +// +// Default resize behavior. No minimal capacity, Granularity=4, +// Shrinking as needed. ArrayConstPolicy actually is the same as +// ArrayDefaultPolicy, but parametrized with constants. +// This struct is used only in order to reduce the template "matroska". +struct ArrayDefaultPolicy +{ + ArrayDefaultPolicy() : Capacity(0) {} + ArrayDefaultPolicy(const ArrayDefaultPolicy&) : Capacity(0) {} + + UPInt GetMinCapacity() const { return 0; } + UPInt GetGranularity() const { return 4; } + bool NeverShrinking() const { return 0; } + + UPInt GetCapacity() const { return Capacity; } + void SetCapacity(UPInt capacity) { Capacity = capacity; } +private: + UPInt Capacity; +}; + + +//----------------------------------------------------------------------------------- +// ***** ArrayConstPolicy +// +// Statically parametrized resizing behavior: +// MinCapacity, Granularity, and Shrinking flag. +template +struct ArrayConstPolicy +{ + typedef ArrayConstPolicy SelfType; + + ArrayConstPolicy() : Capacity(0) {} + ArrayConstPolicy(const SelfType&) : Capacity(0) {} + + UPInt GetMinCapacity() const { return MinCapacity; } + UPInt GetGranularity() const { return Granularity; } + bool NeverShrinking() const { return NeverShrink; } + + UPInt GetCapacity() const { return Capacity; } + void SetCapacity(UPInt capacity) { Capacity = capacity; } +private: + UPInt Capacity; +}; + +//----------------------------------------------------------------------------------- +// ***** ArrayDataBase +// +// Basic operations with array data: Reserve, Resize, Free, ArrayPolicy. +// For internal use only: ArrayData,ArrayDataCC and others. +template +struct ArrayDataBase +{ + typedef T ValueType; + typedef Allocator AllocatorType; + typedef SizePolicy SizePolicyType; + typedef ArrayDataBase SelfType; + + ArrayDataBase() + : Data(0), Size(0), Policy() {} + + ArrayDataBase(const SizePolicy& p) + : Data(0), Size(0), Policy(p) {} + + ~ArrayDataBase() + { + Allocator::DestructArray(Data, Size); + Allocator::Free(Data); + } + + UPInt GetCapacity() const + { + return Policy.GetCapacity(); + } + + void ClearAndRelease() + { + Allocator::DestructArray(Data, Size); + Allocator::Free(Data); + Data = 0; + Size = 0; + Policy.SetCapacity(0); + } + + void Reserve(UPInt newCapacity) + { + if (Policy.NeverShrinking() && newCapacity < GetCapacity()) + return; + + if (newCapacity < Policy.GetMinCapacity()) + newCapacity = Policy.GetMinCapacity(); + + // Resize the buffer. + if (newCapacity == 0) + { + if (Data) + { + Allocator::Free(Data); + Data = 0; + } + Policy.SetCapacity(0); + } + else + { + UPInt gran = Policy.GetGranularity(); + newCapacity = (newCapacity + gran - 1) / gran * gran; + if (Data) + { + if (Allocator::IsMovable()) + { + Data = (T*)Allocator::Realloc(Data, sizeof(T) * newCapacity); + } + else + { + T* newData = (T*)Allocator::Alloc(sizeof(T) * newCapacity); + UPInt i, s; + s = (Size < newCapacity) ? Size : newCapacity; + for (i = 0; i < s; ++i) + { + Allocator::Construct(&newData[i], Data[i]); + Allocator::Destruct(&Data[i]); + } + for (i = s; i < Size; ++i) + { + Allocator::Destruct(&Data[i]); + } + Allocator::Free(Data); + Data = newData; + } + } + else + { + Data = (T*)Allocator::Alloc(sizeof(T) * newCapacity); + //memset(Buffer, 0, (sizeof(ValueType) * newSize)); // Do we need this? + } + Policy.SetCapacity(newCapacity); + // OVR_ASSERT(Data); // need to throw (or something) on alloc failure! + } + } + + // This version of Resize DOES NOT construct the elements. + // It's done to optimize PushBack, which uses a copy constructor + // instead of the default constructor and assignment + void ResizeNoConstruct(UPInt newSize) + { + UPInt oldSize = Size; + + if (newSize < oldSize) + { + Allocator::DestructArray(Data + newSize, oldSize - newSize); + if (newSize < (Policy.GetCapacity() >> 1)) + { + Reserve(newSize); + } + } + else if(newSize >= Policy.GetCapacity()) + { + Reserve(newSize + (newSize >> 2)); + } + //! IMPORTANT to modify Size only after Reserve completes, because garbage collectable + // array may use this array and may traverse it during Reserve (in the case, if + // collection occurs because of heap limit exceeded). + Size = newSize; + } + + ValueType* Data; + UPInt Size; + SizePolicy Policy; +}; + + + +//----------------------------------------------------------------------------------- +// ***** ArrayData +// +// General purpose array data. +// For internal use only in Array, ArrayLH, ArrayPOD and so on. +template +struct ArrayData : ArrayDataBase +{ + typedef T ValueType; + typedef Allocator AllocatorType; + typedef SizePolicy SizePolicyType; + typedef ArrayDataBase BaseType; + typedef ArrayData SelfType; + + ArrayData() + : BaseType() { } + + ArrayData(int size) + : BaseType() { Resize(size); } + + ArrayData(const SelfType& a) + : BaseType(a.Policy) { Append(a.Data, a.Size); } + + + void Resize(UPInt newSize) + { + UPInt oldSize = this->Size; + BaseType::ResizeNoConstruct(newSize); + if(newSize > oldSize) + Allocator::ConstructArray(this->Data + oldSize, newSize - oldSize); + } + + void PushBack(const ValueType& val) + { + BaseType::ResizeNoConstruct(this->Size + 1); + Allocator::Construct(this->Data + this->Size - 1, val); + } + + template + void PushBackAlt(const S& val) + { + BaseType::ResizeNoConstruct(this->Size + 1); + Allocator::ConstructAlt(this->Data + this->Size - 1, val); + } + + // Append the given data to the array. + void Append(const ValueType other[], UPInt count) + { + if (count) + { + UPInt oldSize = this->Size; + BaseType::ResizeNoConstruct(this->Size + count); + Allocator::ConstructArray(this->Data + oldSize, count, other); + } + } +}; + + + +//----------------------------------------------------------------------------------- +// ***** ArrayDataCC +// +// A modification of ArrayData that always copy-constructs new elements +// using a specified DefaultValue. For internal use only in ArrayCC. +template +struct ArrayDataCC : ArrayDataBase +{ + typedef T ValueType; + typedef Allocator AllocatorType; + typedef SizePolicy SizePolicyType; + typedef ArrayDataBase BaseType; + typedef ArrayDataCC SelfType; + + ArrayDataCC(const ValueType& defval) + : BaseType(), DefaultValue(defval) { } + + ArrayDataCC(const ValueType& defval, int size) + : BaseType(), DefaultValue(defval) { Resize(size); } + + ArrayDataCC(const SelfType& a) + : BaseType(a.Policy), DefaultValue(a.DefaultValue) { Append(a.Data, a.Size); } + + + void Resize(UPInt newSize) + { + UPInt oldSize = this->Size; + BaseType::ResizeNoConstruct(newSize); + if(newSize > oldSize) + Allocator::ConstructArray(this->Data + oldSize, newSize - oldSize, DefaultValue); + } + + void PushBack(const ValueType& val) + { + BaseType::ResizeNoConstruct(this->Size + 1); + Allocator::Construct(this->Data + this->Size - 1, val); + } + + template + void PushBackAlt(const S& val) + { + BaseType::ResizeNoConstruct(this->Size + 1); + Allocator::ConstructAlt(this->Data + this->Size - 1, val); + } + + // Append the given data to the array. + void Append(const ValueType other[], UPInt count) + { + if (count) + { + UPInt oldSize = this->Size; + BaseType::ResizeNoConstruct(this->Size + count); + Allocator::ConstructArray(this->Data + oldSize, count, other); + } + } + + ValueType DefaultValue; +}; + + + + + +//----------------------------------------------------------------------------------- +// ***** ArrayBase +// +// Resizable array. The behavior can be POD (suffix _POD) and +// Movable (no suffix) depending on the allocator policy. +// In case of _POD the constructors and destructors are not called. +// +// Arrays can't handle non-movable objects! Don't put anything in here +// that can't be moved around by bitwise copy. +// +// The addresses of elements are not persistent! Don't keep the address +// of an element; the array contents will move around as it gets resized. +template +class ArrayBase +{ +public: + typedef typename ArrayData::ValueType ValueType; + typedef typename ArrayData::AllocatorType AllocatorType; + typedef typename ArrayData::SizePolicyType SizePolicyType; + typedef ArrayBase SelfType; + + +#undef new + OVR_MEMORY_REDEFINE_NEW(ArrayBase) +// Redefine operator 'new' if necessary. +#if defined(OVR_DEFINE_NEW) +#define new OVR_DEFINE_NEW +#endif + + + ArrayBase() + : Data() {} + ArrayBase(int size) + : Data(size) {} + ArrayBase(const SelfType& a) + : Data(a.Data) {} + + ArrayBase(const ValueType& defval) + : Data(defval) {} + ArrayBase(const ValueType& defval, int size) + : Data(defval, size) {} + + SizePolicyType* GetSizePolicy() const { return Data.Policy; } + void SetSizePolicy(const SizePolicyType& p) { Data.Policy = p; } + + bool NeverShrinking()const { return Data.Policy.NeverShrinking(); } + UPInt GetSize() const { return Data.Size; } + bool IsEmpty() const { return Data.Size == 0; } + UPInt GetCapacity() const { return Data.GetCapacity(); } + UPInt GetNumBytes() const { return Data.GetCapacity() * sizeof(ValueType); } + + void ClearAndRelease() { Data.ClearAndRelease(); } + void Clear() { Data.Resize(0); } + void Resize(UPInt newSize) { Data.Resize(newSize); } + + // Reserve can only increase the capacity + void Reserve(UPInt newCapacity) + { + if (newCapacity > Data.GetCapacity()) + Data.Reserve(newCapacity); + } + + // Basic access. + ValueType& At(UPInt index) + { + OVR_ASSERT(index < Data.Size); + return Data.Data[index]; + } + const ValueType& At(UPInt index) const + { + OVR_ASSERT(index < Data.Size); + return Data.Data[index]; + } + + ValueType ValueAt(UPInt index) const + { + OVR_ASSERT(index < Data.Size); + return Data.Data[index]; + } + + // Basic access. + ValueType& operator [] (UPInt index) + { + OVR_ASSERT(index < Data.Size); + return Data.Data[index]; + } + const ValueType& operator [] (UPInt index) const + { + OVR_ASSERT(index < Data.Size); + return Data.Data[index]; + } + + // Raw pointer to the data. Use with caution! + const ValueType* GetDataPtr() const { return Data.Data; } + ValueType* GetDataPtr() { return Data.Data; } + + // Insert the given element at the end of the array. + void PushBack(const ValueType& val) + { + // DO NOT pass elements of your own vector into + // push_back()! Since we're using references, + // resize() may munge the element storage! + // OVR_ASSERT(&val < &Buffer[0] || &val > &Buffer[BufferSize]); + Data.PushBack(val); + } + + template + void PushBackAlt(const S& val) + { + Data.PushBackAlt(val); + } + + // Remove the last element. + void PopBack(UPInt count = 1) + { + OVR_ASSERT(Data.Size >= count); + Data.Resize(Data.Size - count); + } + + ValueType& PushDefault() + { + Data.PushBack(ValueType()); + return Back(); + } + + ValueType Pop() + { + ValueType t = Back(); + PopBack(); + return t; + } + + + // Access the first element. + ValueType& Front() { return At(0); } + const ValueType& Front() const { return At(0); } + + // Access the last element. + ValueType& Back() { return At(Data.Size - 1); } + const ValueType& Back() const { return At(Data.Size - 1); } + + // Array copy. Copies the contents of a into this array. + const SelfType& operator = (const SelfType& a) + { + Resize(a.GetSize()); + for (UPInt i = 0; i < Data.Size; i++) { + *(Data.Data + i) = a[i]; + } + return *this; + } + + // Removing multiple elements from the array. + void RemoveMultipleAt(UPInt index, UPInt num) + { + OVR_ASSERT(index + num <= Data.Size); + if (Data.Size == num) + { + Clear(); + } + else + { + AllocatorType::DestructArray(Data.Data + index, num); + AllocatorType::CopyArrayForward( + Data.Data + index, + Data.Data + index + num, + Data.Size - num - index); + Data.Size -= num; + } + } + + // Removing an element from the array is an expensive operation! + // It compacts only after removing the last element. + void RemoveAt(UPInt index) + { + OVR_ASSERT(index < Data.Size); + if (Data.Size == 1) + { + Clear(); + } + else + { + AllocatorType::Destruct(Data.Data + index); + AllocatorType::CopyArrayForward( + Data.Data + index, + Data.Data + index + 1, + Data.Size - 1 - index); + --Data.Size; + } + } + + // Insert the given object at the given index shifting all the elements up. + void InsertAt(UPInt index, const ValueType& val = ValueType()) + { + OVR_ASSERT(index <= Data.Size); + + Data.Resize(Data.Size + 1); + if (index < Data.Size - 1) + { + AllocatorType::CopyArrayBackward( + Data.Data + index + 1, + Data.Data + index, + Data.Size - 1 - index); + } + AllocatorType::Construct(Data.Data + index, val); + } + + // Insert the given object at the given index shifting all the elements up. + void InsertMultipleAt(UPInt index, UPInt num, const ValueType& val = ValueType()) + { + OVR_ASSERT(index <= Data.Size); + + Data.Resize(Data.Size + num); + if (index < Data.Size - num) + { + AllocatorType::CopyArrayBackward( + Data.Data + index + num, + Data.Data + index, + Data.Size - num - index); + } + for (UPInt i = 0; i < num; ++i) + AllocatorType::Construct(Data.Data + index + i, val); + } + + // Append the given data to the array. + void Append(const SelfType& other) + { + Append(other.Data.Data, other.GetSize()); + } + + // Append the given data to the array. + void Append(const ValueType other[], UPInt count) + { + Data.Append(other, count); + } + + class Iterator + { + SelfType* pArray; + SPInt CurIndex; + + public: + Iterator() : pArray(0), CurIndex(-1) {} + Iterator(SelfType* parr, SPInt idx = 0) : pArray(parr), CurIndex(idx) {} + + bool operator==(const Iterator& it) const { return pArray == it.pArray && CurIndex == it.CurIndex; } + bool operator!=(const Iterator& it) const { return pArray != it.pArray || CurIndex != it.CurIndex; } + + Iterator& operator++() + { + if (pArray) + { + if (CurIndex < (SPInt)pArray->GetSize()) + ++CurIndex; + } + return *this; + } + Iterator operator++(int) + { + Iterator it(*this); + operator++(); + return it; + } + Iterator& operator--() + { + if (pArray) + { + if (CurIndex >= 0) + --CurIndex; + } + return *this; + } + Iterator operator--(int) + { + Iterator it(*this); + operator--(); + return it; + } + Iterator operator+(int delta) const + { + return Iterator(pArray, CurIndex + delta); + } + Iterator operator-(int delta) const + { + return Iterator(pArray, CurIndex - delta); + } + SPInt operator-(const Iterator& right) const + { + OVR_ASSERT(pArray == right.pArray); + return CurIndex - right.CurIndex; + } + ValueType& operator*() const { OVR_ASSERT(pArray); return (*pArray)[CurIndex]; } + ValueType* operator->() const { OVR_ASSERT(pArray); return &(*pArray)[CurIndex]; } + ValueType* GetPtr() const { OVR_ASSERT(pArray); return &(*pArray)[CurIndex]; } + + bool IsFinished() const { return !pArray || CurIndex < 0 || CurIndex >= (int)pArray->GetSize(); } + + void Remove() + { + if (!IsFinished()) + pArray->RemoveAt(CurIndex); + } + + SPInt GetIndex() const { return CurIndex; } + }; + + Iterator Begin() { return Iterator(this); } + Iterator End() { return Iterator(this, (SPInt)GetSize()); } + Iterator Last() { return Iterator(this, (SPInt)GetSize() - 1); } + + class ConstIterator + { + const SelfType* pArray; + SPInt CurIndex; + + public: + ConstIterator() : pArray(0), CurIndex(-1) {} + ConstIterator(const SelfType* parr, SPInt idx = 0) : pArray(parr), CurIndex(idx) {} + + bool operator==(const ConstIterator& it) const { return pArray == it.pArray && CurIndex == it.CurIndex; } + bool operator!=(const ConstIterator& it) const { return pArray != it.pArray || CurIndex != it.CurIndex; } + + ConstIterator& operator++() + { + if (pArray) + { + if (CurIndex < (int)pArray->GetSize()) + ++CurIndex; + } + return *this; + } + ConstIterator operator++(int) + { + ConstIterator it(*this); + operator++(); + return it; + } + ConstIterator& operator--() + { + if (pArray) + { + if (CurIndex >= 0) + --CurIndex; + } + return *this; + } + ConstIterator operator--(int) + { + ConstIterator it(*this); + operator--(); + return it; + } + ConstIterator operator+(int delta) const + { + return ConstIterator(pArray, CurIndex + delta); + } + ConstIterator operator-(int delta) const + { + return ConstIterator(pArray, CurIndex - delta); + } + SPInt operator-(const ConstIterator& right) const + { + OVR_ASSERT(pArray == right.pArray); + return CurIndex - right.CurIndex; + } + const ValueType& operator*() const { OVR_ASSERT(pArray); return (*pArray)[CurIndex]; } + const ValueType* operator->() const { OVR_ASSERT(pArray); return &(*pArray)[CurIndex]; } + const ValueType* GetPtr() const { OVR_ASSERT(pArray); return &(*pArray)[CurIndex]; } + + bool IsFinished() const { return !pArray || CurIndex < 0 || CurIndex >= (int)pArray->GetSize(); } + + SPInt GetIndex() const { return CurIndex; } + }; + ConstIterator Begin() const { return ConstIterator(this); } + ConstIterator End() const { return ConstIterator(this, (SPInt)GetSize()); } + ConstIterator Last() const { return ConstIterator(this, (SPInt)GetSize() - 1); } + +protected: + ArrayData Data; +}; + + + +//----------------------------------------------------------------------------------- +// ***** Array +// +// General purpose array for movable objects that require explicit +// construction/destruction. +template +class Array : public ArrayBase, SizePolicy> > +{ +public: + typedef T ValueType; + typedef ContainerAllocator AllocatorType; + typedef SizePolicy SizePolicyType; + typedef Array SelfType; + typedef ArrayBase, SizePolicy> > BaseType; + + Array() : BaseType() {} + Array(int size) : BaseType(size) {} + Array(const SizePolicyType& p) : BaseType() { SetSizePolicy(p); } + Array(const SelfType& a) : BaseType(a) {} + const SelfType& operator=(const SelfType& a) { BaseType::operator=(a); return *this; } +}; + +// ***** ArrayPOD +// +// General purpose array for movable objects that DOES NOT require +// construction/destruction. Constructors and destructors are not called! +// Global heap is in use. +template +class ArrayPOD : public ArrayBase, SizePolicy> > +{ +public: + typedef T ValueType; + typedef ContainerAllocator_POD AllocatorType; + typedef SizePolicy SizePolicyType; + typedef ArrayPOD SelfType; + typedef ArrayBase, SizePolicy> > BaseType; + + ArrayPOD() : BaseType() {} + ArrayPOD(int size) : BaseType(size) {} + ArrayPOD(const SizePolicyType& p) : BaseType() { SetSizePolicy(p); } + ArrayPOD(const SelfType& a) : BaseType(a) {} + const SelfType& operator=(const SelfType& a) { BaseType::operator=(a); return *this; } +}; + + +// ***** ArrayCPP +// +// General purpose, fully C++ compliant array. Can be used with non-movable data. +// Global heap is in use. +template +class ArrayCPP : public ArrayBase, SizePolicy> > +{ +public: + typedef T ValueType; + typedef ContainerAllocator_CPP AllocatorType; + typedef SizePolicy SizePolicyType; + typedef ArrayCPP SelfType; + typedef ArrayBase, SizePolicy> > BaseType; + + ArrayCPP() : BaseType() {} + ArrayCPP(int size) : BaseType(size) {} + ArrayCPP(const SizePolicyType& p) : BaseType() { SetSizePolicy(p); } + ArrayCPP(const SelfType& a) : BaseType(a) {} + const SelfType& operator=(const SelfType& a) { BaseType::operator=(a); return *this; } +}; + + +// ***** ArrayCC +// +// A modification of the array that uses the given default value to +// construct the elements. The constructors and destructors are +// properly called, the objects must be movable. + +template +class ArrayCC : public ArrayBase, SizePolicy> > +{ +public: + typedef T ValueType; + typedef ContainerAllocator AllocatorType; + typedef SizePolicy SizePolicyType; + typedef ArrayCC SelfType; + typedef ArrayBase, SizePolicy> > BaseType; + + ArrayCC(const ValueType& defval) : BaseType(defval) {} + ArrayCC(const ValueType& defval, int size) : BaseType(defval, size) {} + ArrayCC(const ValueType& defval, const SizePolicyType& p) : BaseType(defval) { SetSizePolicy(p); } + ArrayCC(const SelfType& a) : BaseType(a) {} + const SelfType& operator=(const SelfType& a) { BaseType::operator=(a); return *this; } +}; + +} // OVR + +#endif diff -r 59fc487ba58e -r b069a5c27388 libovr/Src/Kernel/OVR_Atomic.cpp --- a/libovr/Src/Kernel/OVR_Atomic.cpp Sat Sep 14 17:51:03 2013 +0300 +++ b/libovr/Src/Kernel/OVR_Atomic.cpp Sun Sep 15 04:10:05 2013 +0300 @@ -1,1 +1,82 @@ -/************************************************************************************ Filename : OVR_Atomic.cpp Content : Contains atomic operations and inline fastest locking functionality. Will contain #ifdefs for OS efficiency. Have non-thread-safe implementation if not available. Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #include "OVR_Atomic.h" #ifdef OVR_ENABLE_THREADS // Include Windows 8-Metro compatible Synchronization API #if defined(OVR_OS_WIN32) && defined(NTDDI_WIN8) && (NTDDI_VERSION >= NTDDI_WIN8) #include #endif namespace OVR { // ***** Windows Lock implementation #if defined(OVR_OS_WIN32) // ***** Standard Win32 Lock implementation // Constructors Lock::Lock(unsigned spinCount) { #if defined(NTDDI_WIN8) && (NTDDI_VERSION >= NTDDI_WIN8) // On Windows 8 we use InitializeCriticalSectionEx due to Metro-Compatibility InitializeCriticalSectionEx(&cs, spinCount, OVR_DEBUG_SELECT(NULL, CRITICAL_SECTION_NO_DEBUG_INFO)); #else // Spin count init critical section function prototype for Window NT typedef BOOL (WINAPI *Function_InitializeCriticalSectionAndSpinCount) (LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount); // Try to load function dynamically so that we don't require NT // On Windows NT we will use InitializeCriticalSectionAndSpinCount static bool initTried = 0; static Function_InitializeCriticalSectionAndSpinCount pInitFn = 0; if (!initTried) { HMODULE hmodule = ::LoadLibrary(OVR_STR("kernel32.dll")); pInitFn = (Function_InitializeCriticalSectionAndSpinCount) ::GetProcAddress(hmodule, "InitializeCriticalSectionAndSpinCount"); initTried = true; } // Initialize the critical section if (pInitFn) pInitFn(&cs, spinCount); else ::InitializeCriticalSection(&cs); #endif } Lock::~Lock() { DeleteCriticalSection(&cs); } #endif } // OVR #endif // OVR_ENABLE_THREADS \ No newline at end of file +/************************************************************************************ + +Filename : OVR_Atomic.cpp +Content : Contains atomic operations and inline fastest locking + functionality. Will contain #ifdefs for OS efficiency. + Have non-thread-safe implementation if not available. +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#include "OVR_Atomic.h" + +#ifdef OVR_ENABLE_THREADS + +// Include Windows 8-Metro compatible Synchronization API +#if defined(OVR_OS_WIN32) && defined(NTDDI_WIN8) && (NTDDI_VERSION >= NTDDI_WIN8) +#include +#endif + + +namespace OVR { + +// ***** Windows Lock implementation + +#if defined(OVR_OS_WIN32) + +// ***** Standard Win32 Lock implementation + +// Constructors +Lock::Lock(unsigned spinCount) +{ +#if defined(NTDDI_WIN8) && (NTDDI_VERSION >= NTDDI_WIN8) + // On Windows 8 we use InitializeCriticalSectionEx due to Metro-Compatibility + InitializeCriticalSectionEx(&cs, spinCount, + OVR_DEBUG_SELECT(NULL, CRITICAL_SECTION_NO_DEBUG_INFO)); +#else + // Spin count init critical section function prototype for Window NT + typedef BOOL (WINAPI *Function_InitializeCriticalSectionAndSpinCount) + (LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount); + + + // Try to load function dynamically so that we don't require NT + // On Windows NT we will use InitializeCriticalSectionAndSpinCount + static bool initTried = 0; + static Function_InitializeCriticalSectionAndSpinCount pInitFn = 0; + + if (!initTried) + { + HMODULE hmodule = ::LoadLibrary(OVR_STR("kernel32.dll")); + pInitFn = (Function_InitializeCriticalSectionAndSpinCount) + ::GetProcAddress(hmodule, "InitializeCriticalSectionAndSpinCount"); + initTried = true; + } + + // Initialize the critical section + if (pInitFn) + pInitFn(&cs, spinCount); + else + ::InitializeCriticalSection(&cs); +#endif + +} + + +Lock::~Lock() +{ + DeleteCriticalSection(&cs); +} + + +#endif + +} // OVR + +#endif // OVR_ENABLE_THREADS diff -r 59fc487ba58e -r b069a5c27388 libovr/Src/Kernel/OVR_Atomic.h --- a/libovr/Src/Kernel/OVR_Atomic.h Sat Sep 14 17:51:03 2013 +0300 +++ b/libovr/Src/Kernel/OVR_Atomic.h Sun Sep 15 04:10:05 2013 +0300 @@ -1,1 +1,859 @@ -/************************************************************************************ PublicHeader: OVR.h Filename : OVR_Atomic.h Content : Contains atomic operations and inline fastest locking functionality. Will contain #ifdefs for OS efficiency. Have non-thread-safe implementaion if not available. Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #ifndef OVR_Atomic_h #define OVR_Atomic_h #include "OVR_Types.h" // Include System thread functionality. #if defined(OVR_OS_WIN32) #include #else #include #endif namespace OVR { // ****** Declared classes // If there is NO thread support we implement AtomicOps and // Lock objects as no-ops. The other classes are not defined. template class AtomicOps; template class AtomicInt; template class AtomicPtr; class Lock; //----------------------------------------------------------------------------------- // ***** AtomicOps // Atomic operations are provided by the AtomicOps templates class, // implemented through system-specific AtomicOpsRaw specializations. // It provides several fundamental operations such as Exchange, ExchangeAdd // CompareAndSet, and Store_Release. Each function includes several memory // synchronization versions, important for multiprocessing CPUs with weak // memory consistency. The following memory fencing strategies are supported: // // - NoSync. No memory synchronization is done for atomic op. // - Release. All other memory writes are completed before atomic op // writes its results. // - Acquire. Further memory reads are forced to wait until atomic op // executes, guaranteeing that the right values will be seen. // - Sync. A combination of Release and Acquire. // *** AtomicOpsRaw // AtomicOpsRaw is a specialized template that provides atomic operations // used by AtomicOps. This class has two fundamental qualities: (1) it // defines a type T of correct size, and (2) provides operations that work // atomically, such as Exchange_Sync and CompareAndSet_Release. // AtomicOpsRawBase class contains shared constants/classes for AtomicOpsRaw. // The primary thing is does is define sync class objects, whose destructor and // constructor provide places to insert appropriate synchronization calls, on // systems where such calls are necessary. So far, the breakdown is as follows: // // - X86 systems don't need custom syncs, since their exchange/atomic // instructions are implicitly synchronized. // - PowerPC requires lwsync/isync instructions that can use this mechanism. // - If some other systems require a mechanism where syncing type is associated // with a particular instruction, the default implementation (which implements // all Sync, Acquire, and Release modes in terms of NoSync and fence) may not // work. Ii that case it will need to be #ifdef-ed conditionally. struct AtomicOpsRawBase { #if !defined(OVR_ENABLE_THREADS) || defined(OVR_CPU_X86) || defined(OVR_OS_WIN32) || defined(OVR_OS_IPHONE) // Need to have empty constructor to avoid class 'unused' variable warning. struct FullSync { inline FullSync() { } }; struct AcquireSync { inline AcquireSync() { } }; struct ReleaseSync { inline ReleaseSync() { } }; #elif defined(OVR_CPU_PPC64) || defined(OVR_CPU_PPC) struct FullSync { inline FullSync() { asm volatile("sync\n"); } ~FullSync() { asm volatile("isync\n"); } }; struct AcquireSync { inline AcquireSync() { } ~AcquireSync() { asm volatile("isync\n"); } }; struct ReleaseSync { inline ReleaseSync() { asm volatile("sync\n"); } }; #elif defined(OVR_CPU_MIPS) struct FullSync { inline FullSync() { asm volatile("sync\n"); } ~FullSync() { asm volatile("sync\n"); } }; struct AcquireSync { inline AcquireSync() { } ~AcquireSync() { asm volatile("sync\n"); } }; struct ReleaseSync { inline ReleaseSync() { asm volatile("sync\n"); } }; #elif defined(OVR_CPU_ARM) struct FullSync { inline FullSync() { asm volatile("dmb\n"); } ~FullSync() { asm volatile("dmb\n"); } }; struct AcquireSync { inline AcquireSync() { } ~AcquireSync() { asm volatile("dmb\n"); } }; struct ReleaseSync { inline ReleaseSync() { asm volatile("dmb\n"); } }; #elif defined(OVR_CC_GNU) && (__GNUC__ >= 4) // __sync functions are already full sync struct FullSync { inline FullSync() { } }; struct AcquireSync { inline AcquireSync() { } }; struct ReleaseSync { inline ReleaseSync() { } }; #endif }; // 4-Byte raw data atomic op implementation class. struct AtomicOpsRaw_4ByteImpl : public AtomicOpsRawBase { #if !defined(OVR_ENABLE_THREADS) // Provide a type for no-thread-support cases. Used by AtomicOpsRaw_DefImpl. typedef UInt32 T; // *** Thread - Safe Atomic Versions. #elif defined(OVR_OS_WIN32) // Use special defined for VC6, where volatile is not used and // InterlockedCompareExchange is declared incorrectly. typedef LONG T; #if defined(OVR_CC_MSVC) && (OVR_CC_MSVC < 1300) typedef T* InterlockTPtr; typedef LPVOID ET; typedef ET* InterlockETPtr; #else typedef volatile T* InterlockTPtr; typedef T ET; typedef InterlockTPtr InterlockETPtr; #endif inline static T Exchange_NoSync(volatile T* p, T val) { return InterlockedExchange((InterlockTPtr)p, val); } inline static T ExchangeAdd_NoSync(volatile T* p, T val) { return InterlockedExchangeAdd((InterlockTPtr)p, val); } inline static bool CompareAndSet_NoSync(volatile T* p, T c, T val) { return InterlockedCompareExchange((InterlockETPtr)p, (ET)val, (ET)c) == (ET)c; } #elif defined(OVR_CPU_PPC64) || defined(OVR_CPU_PPC) typedef UInt32 T; static inline UInt32 Exchange_NoSync(volatile UInt32 *i, UInt32 j) { UInt32 ret; asm volatile("1:\n\t" "lwarx %[r],0,%[i]\n\t" "stwcx. %[j],0,%[i]\n\t" "bne- 1b\n" : "+m" (*i), [r] "=&b" (ret) : [i] "b" (i), [j] "b" (j) : "cc", "memory"); return ret; } static inline UInt32 ExchangeAdd_NoSync(volatile UInt32 *i, UInt32 j) { UInt32 dummy, ret; asm volatile("1:\n\t" "lwarx %[r],0,%[i]\n\t" "add %[o],%[r],%[j]\n\t" "stwcx. %[o],0,%[i]\n\t" "bne- 1b\n" : "+m" (*i), [r] "=&b" (ret), [o] "=&r" (dummy) : [i] "b" (i), [j] "b" (j) : "cc", "memory"); return ret; } static inline bool CompareAndSet_NoSync(volatile UInt32 *i, UInt32 c, UInt32 value) { UInt32 ret; asm volatile("1:\n\t" "lwarx %[r],0,%[i]\n\t" "cmpw 0,%[r],%[cmp]\n\t" "mfcr %[r]\n\t" "bne- 2f\n\t" "stwcx. %[val],0,%[i]\n\t" "bne- 1b\n\t" "2:\n" : "+m" (*i), [r] "=&b" (ret) : [i] "b" (i), [cmp] "b" (c), [val] "b" (value) : "cc", "memory"); return (ret & 0x20000000) ? 1 : 0; } #elif defined(OVR_CPU_MIPS) typedef UInt32 T; static inline UInt32 Exchange_NoSync(volatile UInt32 *i, UInt32 j) { UInt32 ret; asm volatile("1:\n\t" "ll %[r],0(%[i])\n\t" "sc %[j],0(%[i])\n\t" "beq %[j],$0,1b\n\t" "nop \n" : "+m" (*i), [r] "=&d" (ret) : [i] "d" (i), [j] "d" (j) : "cc", "memory"); return ret; } static inline UInt32 ExchangeAdd_NoSync(volatile UInt32 *i, UInt32 j) { UInt32 ret; asm volatile("1:\n\t" "ll %[r],0(%[i])\n\t" "addu %[j],%[r],%[j]\n\t" "sc %[j],0(%[i])\n\t" "beq %[j],$0,1b\n\t" "nop \n" : "+m" (*i), [r] "=&d" (ret) : [i] "d" (i), [j] "d" (j) : "cc", "memory"); return ret; } static inline bool CompareAndSet_NoSync(volatile UInt32 *i, UInt32 c, UInt32 value) { UInt32 ret, dummy; asm volatile("1:\n\t" "move %[r],$0\n\t" "ll %[o],0(%[i])\n\t" "bne %[o],%[c],2f\n\t" "move %[r],%[v]\n\t" "sc %[r],0(%[i])\n\t" "beq %[r],$0,1b\n\t" "nop \n\t" "2:\n" : "+m" (*i),[r] "=&d" (ret), [o] "=&d" (dummy) : [i] "d" (i), [c] "d" (c), [v] "d" (value) : "cc", "memory"); return ret; } #elif defined(OVR_CPU_ARM) && defined(OVR_CC_ARM) typedef UInt32 T; static inline UInt32 Exchange_NoSync(volatile UInt32 *i, UInt32 j) { for(;;) { T r = __ldrex(i); if (__strex(j, i) == 0) return r; } } static inline UInt32 ExchangeAdd_NoSync(volatile UInt32 *i, UInt32 j) { for(;;) { T r = __ldrex(i); if (__strex(r + j, i) == 0) return r; } } static inline bool CompareAndSet_NoSync(volatile UInt32 *i, UInt32 c, UInt32 value) { for(;;) { T r = __ldrex(i); if (r != c) return 0; if (__strex(value, i) == 0) return 1; } } #elif defined(OVR_CPU_ARM) typedef UInt32 T; static inline UInt32 Exchange_NoSync(volatile UInt32 *i, UInt32 j) { UInt32 ret, dummy; asm volatile("1:\n\t" "ldrex %[r],[%[i]]\n\t" "strex %[t],%[j],[%[i]]\n\t" "cmp %[t],#0\n\t" "bne 1b\n\t" : "+m" (*i), [r] "=&r" (ret), [t] "=&r" (dummy) : [i] "r" (i), [j] "r" (j) : "cc", "memory"); return ret; } static inline UInt32 ExchangeAdd_NoSync(volatile UInt32 *i, UInt32 j) { UInt32 ret, dummy, test; asm volatile("1:\n\t" "ldrex %[r],[%[i]]\n\t" "add %[o],%[r],%[j]\n\t" "strex %[t],%[o],[%[i]]\n\t" "cmp %[t],#0\n\t" "bne 1b\n\t" : "+m" (*i), [r] "=&r" (ret), [o] "=&r" (dummy), [t] "=&r" (test) : [i] "r" (i), [j] "r" (j) : "cc", "memory"); return ret; } static inline bool CompareAndSet_NoSync(volatile UInt32 *i, UInt32 c, UInt32 value) { UInt32 ret = 1, dummy, test; asm volatile("1:\n\t" "ldrex %[o],[%[i]]\n\t" "cmp %[o],%[c]\n\t" "bne 2f\n\t" "strex %[r],%[v],[%[i]]\n\t" "cmp %[r],#0\n\t" "bne 1b\n\t" "2:\n" : "+m" (*i),[r] "=&r" (ret), [o] "=&r" (dummy), [t] "=&r" (test) : [i] "r" (i), [c] "r" (c), [v] "r" (value) : "cc", "memory"); return !ret; } #elif defined(OVR_CPU_X86) typedef UInt32 T; static inline UInt32 Exchange_NoSync(volatile UInt32 *i, UInt32 j) { asm volatile("xchgl %1,%[i]\n" : "+m" (*i), "=q" (j) : [i] "m" (*i), "1" (j) : "cc", "memory"); return j; } static inline UInt32 ExchangeAdd_NoSync(volatile UInt32 *i, UInt32 j) { asm volatile("lock; xaddl %1,%[i]\n" : "+m" (*i), "+q" (j) : [i] "m" (*i) : "cc", "memory"); return j; } static inline bool CompareAndSet_NoSync(volatile UInt32 *i, UInt32 c, UInt32 value) { UInt32 ret; asm volatile("lock; cmpxchgl %[v],%[i]\n" : "+m" (*i), "=a" (ret) : [i] "m" (*i), "1" (c), [v] "q" (value) : "cc", "memory"); return (ret == c); } #elif defined(OVR_CC_GNU) && (__GNUC__ >= 4 && __GNUC_MINOR__ >= 1) typedef UInt32 T; static inline T Exchange_NoSync(volatile T *i, T j) { T v; do { v = *i; } while (!__sync_bool_compare_and_swap(i, v, j)); return v; } static inline T ExchangeAdd_NoSync(volatile T *i, T j) { return __sync_fetch_and_add(i, j); } static inline bool CompareAndSet_NoSync(volatile T *i, T c, T value) { return __sync_bool_compare_and_swap(i, c, value); } #endif // OS }; // 8-Byte raw data data atomic op implementation class. // Currently implementation is provided only on systems with 64-bit pointers. struct AtomicOpsRaw_8ByteImpl : public AtomicOpsRawBase { #if !defined(OVR_64BIT_POINTERS) || !defined(OVR_ENABLE_THREADS) // Provide a type for no-thread-support cases. Used by AtomicOpsRaw_DefImpl. typedef UInt64 T; // *** Thread - Safe OS specific versions. #elif defined(OVR_OS_WIN32) // This is only for 64-bit systems. typedef LONG64 T; typedef volatile T* InterlockTPtr; inline static T Exchange_NoSync(volatile T* p, T val) { return InterlockedExchange64((InterlockTPtr)p, val); } inline static T ExchangeAdd_NoSync(volatile T* p, T val) { return InterlockedExchangeAdd64((InterlockTPtr)p, val); } inline static bool CompareAndSet_NoSync(volatile T* p, T c, T val) { return InterlockedCompareExchange64((InterlockTPtr)p, val, c) == c; } #elif defined(OVR_CPU_PPC64) typedef UInt64 T; static inline UInt64 Exchange_NoSync(volatile UInt64 *i, UInt64 j) { UInt64 dummy, ret; asm volatile("1:\n\t" "ldarx %[r],0,%[i]\n\t" "mr %[o],%[j]\n\t" "stdcx. %[o],0,%[i]\n\t" "bne- 1b\n" : "+m" (*i), [r] "=&b" (ret), [o] "=&r" (dummy) : [i] "b" (i), [j] "b" (j) : "cc"); return ret; } static inline UInt64 ExchangeAdd_NoSync(volatile UInt64 *i, UInt64 j) { UInt64 dummy, ret; asm volatile("1:\n\t" "ldarx %[r],0,%[i]\n\t" "add %[o],%[r],%[j]\n\t" "stdcx. %[o],0,%[i]\n\t" "bne- 1b\n" : "+m" (*i), [r] "=&b" (ret), [o] "=&r" (dummy) : [i] "b" (i), [j] "b" (j) : "cc"); return ret; } static inline bool CompareAndSet_NoSync(volatile UInt64 *i, UInt64 c, UInt64 value) { UInt64 ret, dummy; asm volatile("1:\n\t" "ldarx %[r],0,%[i]\n\t" "cmpw 0,%[r],%[cmp]\n\t" "mfcr %[r]\n\t" "bne- 2f\n\t" "stdcx. %[val],0,%[i]\n\t" "bne- 1b\n\t" "2:\n" : "+m" (*i), [r] "=&b" (ret), [o] "=&r" (dummy) : [i] "b" (i), [cmp] "b" (c), [val] "b" (value) : "cc"); return (ret & 0x20000000) ? 1 : 0; } #elif defined(OVR_CC_GNU) && (__GNUC__ >= 4 && __GNUC_MINOR__ >= 1) typedef UInt64 T; static inline T Exchange_NoSync(volatile T *i, T j) { T v; do { v = *i; } while (!__sync_bool_compare_and_swap(i, v, j)); return v; } static inline T ExchangeAdd_NoSync(volatile T *i, T j) { return __sync_fetch_and_add(i, j); } static inline bool CompareAndSet_NoSync(volatile T *i, T c, T value) { return __sync_bool_compare_and_swap(i, c, value); } #endif // OS }; // Default implementation for AtomicOpsRaw; provides implementation of mem-fenced // atomic operations where fencing is done with a sync object wrapped around a NoSync // operation implemented in the base class. If such implementation is not possible // on a given platform, #ifdefs can be used to disable it and then op functions can be // implemented individually in the appropriate AtomicOpsRaw class. template struct AtomicOpsRaw_DefImpl : public O { typedef typename O::T O_T; typedef typename O::FullSync O_FullSync; typedef typename O::AcquireSync O_AcquireSync; typedef typename O::ReleaseSync O_ReleaseSync; // If there is no thread support, provide the default implementation. In this case, // the base class (0) must still provide the T declaration. #ifndef OVR_ENABLE_THREADS // Atomic exchange of val with argument. Returns old val. inline static O_T Exchange_NoSync(volatile O_T* p, O_T val) { O_T old = *p; *p = val; return old; } // Adds a new val to argument; returns its old val. inline static O_T ExchangeAdd_NoSync(volatile O_T* p, O_T val) { O_T old = *p; *p += val; return old; } // Compares the argument data with 'c' val. // If succeeded, stores val int '*p' and returns true; otherwise returns false. inline static bool CompareAndSet_NoSync(volatile O_T* p, O_T c, O_T val) { if (*p==c) { *p = val; return 1; } return 0; } #endif // If NoSync wrapped implementation may not be possible, it this block should be // replaced with per-function implementation in O. // "AtomicOpsRaw_DefImpl::" prefix in calls below. inline static O_T Exchange_Sync(volatile O_T* p, O_T val) { O_FullSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl::Exchange_NoSync(p, val); } inline static O_T Exchange_Release(volatile O_T* p, O_T val) { O_ReleaseSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl::Exchange_NoSync(p, val); } inline static O_T Exchange_Acquire(volatile O_T* p, O_T val) { O_AcquireSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl::Exchange_NoSync(p, val); } inline static O_T ExchangeAdd_Sync(volatile O_T* p, O_T val) { O_FullSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl::ExchangeAdd_NoSync(p, val); } inline static O_T ExchangeAdd_Release(volatile O_T* p, O_T val) { O_ReleaseSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl::ExchangeAdd_NoSync(p, val); } inline static O_T ExchangeAdd_Acquire(volatile O_T* p, O_T val) { O_AcquireSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl::ExchangeAdd_NoSync(p, val); } inline static bool CompareAndSet_Sync(volatile O_T* p, O_T c, O_T val) { O_FullSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl::CompareAndSet_NoSync(p,c,val); } inline static bool CompareAndSet_Release(volatile O_T* p, O_T c, O_T val) { O_ReleaseSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl::CompareAndSet_NoSync(p,c,val); } inline static bool CompareAndSet_Acquire(volatile O_T* p, O_T c, O_T val) { O_AcquireSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl::CompareAndSet_NoSync(p,c,val); } // Loads and stores with memory fence. These have only the relevant versions. #ifdef OVR_CPU_X86 // On X86, Store_Release is implemented as exchange. Note that we can also // consider 'sfence' in the future, although it is not as compatible with older CPUs. inline static void Store_Release(volatile O_T* p, O_T val) { Exchange_Release(p, val); } #else inline static void Store_Release(volatile O_T* p, O_T val) { O_ReleaseSync sync; OVR_UNUSED(sync); *p = val; } #endif inline static O_T Load_Acquire(const volatile O_T* p) { O_AcquireSync sync; OVR_UNUSED(sync); return *p; } }; template struct AtomicOpsRaw : public AtomicOpsRawBase { }; template<> struct AtomicOpsRaw<4> : public AtomicOpsRaw_DefImpl { // Ensure that assigned type size is correct. AtomicOpsRaw() { OVR_COMPILER_ASSERT(sizeof(AtomicOpsRaw_DefImpl::T) == 4); } }; template<> struct AtomicOpsRaw<8> : public AtomicOpsRaw_DefImpl { AtomicOpsRaw() { OVR_COMPILER_ASSERT(sizeof(AtomicOpsRaw_DefImpl::T) == 8); } }; // *** AtomicOps - implementation of atomic Ops for specified class // Implements atomic ops on a class, provided that the object is either // 4 or 8 bytes in size (depending on the AtomicOpsRaw specializations // available). Relies on AtomicOpsRaw for much of implementation. template class AtomicOps { typedef AtomicOpsRaw Ops; typedef typename Ops::T T; typedef volatile typename Ops::T* PT; // We cast through unions to (1) avoid pointer size compiler warnings // and (2) ensure that there are no problems with strict pointer aliasing. union C2T_union { C c; T t; }; public: // General purpose implementation for standard syncs. inline static C Exchange_Sync(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::Exchange_Sync((PT)p, u.t); return u.c; } inline static C Exchange_Release(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::Exchange_Release((PT)p, u.t); return u.c; } inline static C Exchange_Acquire(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::Exchange_Acquire((PT)p, u.t); return u.c; } inline static C Exchange_NoSync(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::Exchange_NoSync((PT)p, u.t); return u.c; } inline static C ExchangeAdd_Sync(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::ExchangeAdd_Sync((PT)p, u.t); return u.c; } inline static C ExchangeAdd_Release(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::ExchangeAdd_Release((PT)p, u.t); return u.c; } inline static C ExchangeAdd_Acquire(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::ExchangeAdd_Acquire((PT)p, u.t); return u.c; } inline static C ExchangeAdd_NoSync(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::ExchangeAdd_NoSync((PT)p, u.t); return u.c; } inline static bool CompareAndSet_Sync(volatile C* p, C c, C val) { C2T_union u,cu; u.c = val; cu.c = c; return Ops::CompareAndSet_Sync((PT)p, cu.t, u.t); } inline static bool CompareAndSet_Release(volatile C* p, C c, C val){ C2T_union u,cu; u.c = val; cu.c = c; return Ops::CompareAndSet_Release((PT)p, cu.t, u.t); } inline static bool CompareAndSet_Relse(volatile C* p, C c, C val){ C2T_union u,cu; u.c = val; cu.c = c; return Ops::CompareAndSet_Acquire((PT)p, cu.t, u.t); } inline static bool CompareAndSet_NoSync(volatile C* p, C c, C val) { C2T_union u,cu; u.c = val; cu.c = c; return Ops::CompareAndSet_NoSync((PT)p, cu.t, u.t); } // Loads and stores with memory fence. These have only the relevant versions. inline static void Store_Release(volatile C* p, C val) { C2T_union u; u.c = val; Ops::Store_Release((PT)p, u.t); } inline static C Load_Acquire(const volatile C* p) { C2T_union u; u.t = Ops::Load_Acquire((PT)p); return u.c; } }; // Atomic value base class - implements operations shared for integers and pointers. template class AtomicValueBase { protected: typedef AtomicOps Ops; public: volatile T Value; inline AtomicValueBase() { } explicit inline AtomicValueBase(T val) { Ops::Store_Release(&Value, val); } // Most libraries (TBB and Joshua Scholar's) library do not do Load_Acquire // here, since most algorithms do not require atomic loads. Needs some research. inline operator T() const { return Value; } // *** Standard Atomic inlines inline T Exchange_Sync(T val) { return Ops::Exchange_Sync(&Value, val); } inline T Exchange_Release(T val) { return Ops::Exchange_Release(&Value, val); } inline T Exchange_Acquire(T val) { return Ops::Exchange_Acquire(&Value, val); } inline T Exchange_NoSync(T val) { return Ops::Exchange_NoSync(&Value, val); } inline bool CompareAndSet_Sync(T c, T val) { return Ops::CompareAndSet_Sync(&Value, c, val); } inline bool CompareAndSet_Release(T c, T val) { return Ops::CompareAndSet_Release(&Value, c, val); } inline bool CompareAndSet_Acquire(T c, T val) { return Ops::CompareAndSet_Relse(&Value, c, val); } inline bool CompareAndSet_NoSync(T c, T val) { return Ops::CompareAndSet_NoSync(&Value, c, val); } // Load & Store. inline void Store_Release(T val) { Ops::Store_Release(&Value, val); } inline T Load_Acquire() const { return Ops::Load_Acquire(&Value); } }; // ***** AtomicPtr - Atomic pointer template // This pointer class supports atomic assignments with release, // increment / decrement operations, and conditional compare + set. template class AtomicPtr : public AtomicValueBase { typedef typename AtomicValueBase::Ops Ops; public: // Initialize pointer value to 0 by default; use Store_Release only with explicit constructor. inline AtomicPtr() : AtomicValueBase() { this->Value = 0; } explicit inline AtomicPtr(T* val) : AtomicValueBase(val) { } // Pointer access. inline T* operator -> () const { return this->Load_Acquire(); } // It looks like it is convenient to have Load_Acquire characteristics // for this, since that is convenient for algorithms such as linked // list traversals that can be added to bu another thread. inline operator T* () const { return this->Load_Acquire(); } // *** Standard Atomic inlines (applicable to pointers) // ExhangeAdd considers pointer size for pointers. template inline T* ExchangeAdd_Sync(I incr) { return Ops::ExchangeAdd_Sync(&this->Value, ((T*)0) + incr); } template inline T* ExchangeAdd_Release(I incr) { return Ops::ExchangeAdd_Release(&this->Value, ((T*)0) + incr); } template inline T* ExchangeAdd_Acquire(I incr) { return Ops::ExchangeAdd_Acquire(&this->Value, ((T*)0) + incr); } template inline T* ExchangeAdd_NoSync(I incr) { return Ops::ExchangeAdd_NoSync(&this->Value, ((T*)0) + incr); } // *** Atomic Operators inline T* operator = (T* val) { this->Store_Release(val); return val; } template inline T* operator += (I val) { return ExchangeAdd_Sync(val) + val; } template inline T* operator -= (I val) { return operator += (-val); } inline T* operator ++ () { return ExchangeAdd_Sync(1) + 1; } inline T* operator -- () { return ExchangeAdd_Sync(-1) - 1; } inline T* operator ++ (int) { return ExchangeAdd_Sync(1); } inline T* operator -- (int) { return ExchangeAdd_Sync(-1); } }; // ***** AtomicInt - Atomic integer template // Implements an atomic integer type; the exact type to use is provided // as an argument. Supports atomic Acquire / Release semantics, atomic // arithmetic operations, and atomic conditional compare + set. template class AtomicInt : public AtomicValueBase { typedef typename AtomicValueBase::Ops Ops; public: inline AtomicInt() : AtomicValueBase() { } explicit inline AtomicInt(T val) : AtomicValueBase(val) { } // *** Standard Atomic inlines (applicable to int) inline T ExchangeAdd_Sync(T val) { return Ops::ExchangeAdd_Sync(&this->Value, val); } inline T ExchangeAdd_Release(T val) { return Ops::ExchangeAdd_Release(&this->Value, val); } inline T ExchangeAdd_Acquire(T val) { return Ops::ExchangeAdd_Acquire(&this->Value, val); } inline T ExchangeAdd_NoSync(T val) { return Ops::ExchangeAdd_NoSync(&this->Value, val); } // These increments could be more efficient because they don't return a value. inline void Increment_Sync() { ExchangeAdd_Sync((T)1); } inline void Increment_Release() { ExchangeAdd_Release((T)1); } inline void Increment_Acquire() { ExchangeAdd_Acquire((T)1); } inline void Increment_NoSync() { ExchangeAdd_NoSync((T)1); } // *** Atomic Operators inline T operator = (T val) { this->Store_Release(val); return val; } inline T operator += (T val) { return ExchangeAdd_Sync(val) + val; } inline T operator -= (T val) { return ExchangeAdd_Sync(0 - val) - val; } inline T operator ++ () { return ExchangeAdd_Sync((T)1) + 1; } inline T operator -- () { return ExchangeAdd_Sync(((T)0)-1) - 1; } inline T operator ++ (int) { return ExchangeAdd_Sync((T)1); } inline T operator -- (int) { return ExchangeAdd_Sync(((T)0)-1); } // More complex atomic operations. Leave it to compiler whether to optimize them or not. T operator &= (T arg) { T comp, newVal; do { comp = this->Value; newVal = comp & arg; } while(!this->CompareAndSet_Sync(comp, newVal)); return newVal; } T operator |= (T arg) { T comp, newVal; do { comp = this->Value; newVal = comp | arg; } while(!this->CompareAndSet_Sync(comp, newVal)); return newVal; } T operator ^= (T arg) { T comp, newVal; do { comp = this->Value; newVal = comp ^ arg; } while(!this->CompareAndSet_Sync(comp, newVal)); return newVal; } T operator *= (T arg) { T comp, newVal; do { comp = this->Value; newVal = comp * arg; } while(!this->CompareAndSet_Sync(comp, newVal)); return newVal; } T operator /= (T arg) { T comp, newVal; do { comp = this->Value; newVal = comp / arg; } while(!CompareAndSet_Sync(comp, newVal)); return newVal; } T operator >>= (unsigned bits) { T comp, newVal; do { comp = this->Value; newVal = comp >> bits; } while(!CompareAndSet_Sync(comp, newVal)); return newVal; } T operator <<= (unsigned bits) { T comp, newVal; do { comp = this->Value; newVal = comp << bits; } while(!this->CompareAndSet_Sync(comp, newVal)); return newVal; } }; //----------------------------------------------------------------------------------- // ***** Lock // Lock is a simplest and most efficient mutual-exclusion lock class. // Unlike Mutex, it cannot be waited on. class Lock { // NOTE: Locks are not allocatable and they themselves should not allocate // memory by standard means. This is the case because StandardAllocator // relies on this class. // Make 'delete' private. Don't do this for 'new' since it can be redefined. void operator delete(void*) {} // *** Lock implementation for various platforms. #if !defined(OVR_ENABLE_THREADS) public: // With no thread support, lock does nothing. inline Lock() { } inline Lock(unsigned) { } inline ~Lock() { } inline void DoLock() { } inline void Unlock() { } // Windows. #elif defined(OVR_OS_WIN32) CRITICAL_SECTION cs; public: Lock(unsigned spinCount = 0); ~Lock(); // Locking functions. inline void DoLock() { ::EnterCriticalSection(&cs); } inline void Unlock() { ::LeaveCriticalSection(&cs); } #else pthread_mutex_t mutex; public: static pthread_mutexattr_t RecursiveAttr; static bool RecursiveAttrInit; Lock (unsigned dummy = 0) { if (!RecursiveAttrInit) { pthread_mutexattr_init(&RecursiveAttr); pthread_mutexattr_settype(&RecursiveAttr, PTHREAD_MUTEX_RECURSIVE); RecursiveAttrInit = 1; } pthread_mutex_init(&mutex,&RecursiveAttr); } ~Lock () { pthread_mutex_destroy(&mutex); } inline void DoLock() { pthread_mutex_lock(&mutex); } inline void Unlock() { pthread_mutex_unlock(&mutex); } #endif // OVR_ENABLE_THREDS public: // Locker class, used for automatic locking class Locker { public: Lock *pLock; inline Locker(Lock *plock) { pLock = plock; pLock->DoLock(); } inline ~Locker() { pLock->Unlock(); } }; }; } // OVR #endif \ No newline at end of file +/************************************************************************************ + +PublicHeader: OVR.h +Filename : OVR_Atomic.h +Content : Contains atomic operations and inline fastest locking + functionality. Will contain #ifdefs for OS efficiency. + Have non-thread-safe implementaion if not available. +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ +#ifndef OVR_Atomic_h +#define OVR_Atomic_h + +#include "OVR_Types.h" + +// Include System thread functionality. +#if defined(OVR_OS_WIN32) +#include +#else +#include +#endif + + +namespace OVR { + + +// ****** Declared classes + +// If there is NO thread support we implement AtomicOps and +// Lock objects as no-ops. The other classes are not defined. +template class AtomicOps; +template class AtomicInt; +template class AtomicPtr; + +class Lock; + + +//----------------------------------------------------------------------------------- +// ***** AtomicOps + +// Atomic operations are provided by the AtomicOps templates class, +// implemented through system-specific AtomicOpsRaw specializations. +// It provides several fundamental operations such as Exchange, ExchangeAdd +// CompareAndSet, and Store_Release. Each function includes several memory +// synchronization versions, important for multiprocessing CPUs with weak +// memory consistency. The following memory fencing strategies are supported: +// +// - NoSync. No memory synchronization is done for atomic op. +// - Release. All other memory writes are completed before atomic op +// writes its results. +// - Acquire. Further memory reads are forced to wait until atomic op +// executes, guaranteeing that the right values will be seen. +// - Sync. A combination of Release and Acquire. + + +// *** AtomicOpsRaw + +// AtomicOpsRaw is a specialized template that provides atomic operations +// used by AtomicOps. This class has two fundamental qualities: (1) it +// defines a type T of correct size, and (2) provides operations that work +// atomically, such as Exchange_Sync and CompareAndSet_Release. + +// AtomicOpsRawBase class contains shared constants/classes for AtomicOpsRaw. +// The primary thing is does is define sync class objects, whose destructor and +// constructor provide places to insert appropriate synchronization calls, on +// systems where such calls are necessary. So far, the breakdown is as follows: +// +// - X86 systems don't need custom syncs, since their exchange/atomic +// instructions are implicitly synchronized. +// - PowerPC requires lwsync/isync instructions that can use this mechanism. +// - If some other systems require a mechanism where syncing type is associated +// with a particular instruction, the default implementation (which implements +// all Sync, Acquire, and Release modes in terms of NoSync and fence) may not +// work. Ii that case it will need to be #ifdef-ed conditionally. + +struct AtomicOpsRawBase +{ +#if !defined(OVR_ENABLE_THREADS) || defined(OVR_CPU_X86) || defined(OVR_OS_WIN32) || defined(OVR_OS_IPHONE) + // Need to have empty constructor to avoid class 'unused' variable warning. + struct FullSync { inline FullSync() { } }; + struct AcquireSync { inline AcquireSync() { } }; + struct ReleaseSync { inline ReleaseSync() { } }; + +#elif defined(OVR_CPU_PPC64) || defined(OVR_CPU_PPC) + struct FullSync { inline FullSync() { asm volatile("sync\n"); } ~FullSync() { asm volatile("isync\n"); } }; + struct AcquireSync { inline AcquireSync() { } ~AcquireSync() { asm volatile("isync\n"); } }; + struct ReleaseSync { inline ReleaseSync() { asm volatile("sync\n"); } }; + +#elif defined(OVR_CPU_MIPS) + struct FullSync { inline FullSync() { asm volatile("sync\n"); } ~FullSync() { asm volatile("sync\n"); } }; + struct AcquireSync { inline AcquireSync() { } ~AcquireSync() { asm volatile("sync\n"); } }; + struct ReleaseSync { inline ReleaseSync() { asm volatile("sync\n"); } }; + +#elif defined(OVR_CPU_ARM) + struct FullSync { inline FullSync() { asm volatile("dmb\n"); } ~FullSync() { asm volatile("dmb\n"); } }; + struct AcquireSync { inline AcquireSync() { } ~AcquireSync() { asm volatile("dmb\n"); } }; + struct ReleaseSync { inline ReleaseSync() { asm volatile("dmb\n"); } }; + + +#elif defined(OVR_CC_GNU) && (__GNUC__ >= 4) + // __sync functions are already full sync + struct FullSync { inline FullSync() { } }; + struct AcquireSync { inline AcquireSync() { } }; + struct ReleaseSync { inline ReleaseSync() { } }; +#endif +}; + + +// 4-Byte raw data atomic op implementation class. +struct AtomicOpsRaw_4ByteImpl : public AtomicOpsRawBase +{ +#if !defined(OVR_ENABLE_THREADS) + + // Provide a type for no-thread-support cases. Used by AtomicOpsRaw_DefImpl. + typedef UInt32 T; + + // *** Thread - Safe Atomic Versions. + +#elif defined(OVR_OS_WIN32) + + // Use special defined for VC6, where volatile is not used and + // InterlockedCompareExchange is declared incorrectly. + typedef LONG T; +#if defined(OVR_CC_MSVC) && (OVR_CC_MSVC < 1300) + typedef T* InterlockTPtr; + typedef LPVOID ET; + typedef ET* InterlockETPtr; +#else + typedef volatile T* InterlockTPtr; + typedef T ET; + typedef InterlockTPtr InterlockETPtr; +#endif + inline static T Exchange_NoSync(volatile T* p, T val) { return InterlockedExchange((InterlockTPtr)p, val); } + inline static T ExchangeAdd_NoSync(volatile T* p, T val) { return InterlockedExchangeAdd((InterlockTPtr)p, val); } + inline static bool CompareAndSet_NoSync(volatile T* p, T c, T val) { return InterlockedCompareExchange((InterlockETPtr)p, (ET)val, (ET)c) == (ET)c; } + +#elif defined(OVR_CPU_PPC64) || defined(OVR_CPU_PPC) + typedef UInt32 T; + static inline UInt32 Exchange_NoSync(volatile UInt32 *i, UInt32 j) + { + UInt32 ret; + + asm volatile("1:\n\t" + "lwarx %[r],0,%[i]\n\t" + "stwcx. %[j],0,%[i]\n\t" + "bne- 1b\n" + : "+m" (*i), [r] "=&b" (ret) : [i] "b" (i), [j] "b" (j) : "cc", "memory"); + + return ret; + } + + static inline UInt32 ExchangeAdd_NoSync(volatile UInt32 *i, UInt32 j) + { + UInt32 dummy, ret; + + asm volatile("1:\n\t" + "lwarx %[r],0,%[i]\n\t" + "add %[o],%[r],%[j]\n\t" + "stwcx. %[o],0,%[i]\n\t" + "bne- 1b\n" + : "+m" (*i), [r] "=&b" (ret), [o] "=&r" (dummy) : [i] "b" (i), [j] "b" (j) : "cc", "memory"); + + return ret; + } + + static inline bool CompareAndSet_NoSync(volatile UInt32 *i, UInt32 c, UInt32 value) + { + UInt32 ret; + + asm volatile("1:\n\t" + "lwarx %[r],0,%[i]\n\t" + "cmpw 0,%[r],%[cmp]\n\t" + "mfcr %[r]\n\t" + "bne- 2f\n\t" + "stwcx. %[val],0,%[i]\n\t" + "bne- 1b\n\t" + "2:\n" + : "+m" (*i), [r] "=&b" (ret) : [i] "b" (i), [cmp] "b" (c), [val] "b" (value) : "cc", "memory"); + + return (ret & 0x20000000) ? 1 : 0; + } + +#elif defined(OVR_CPU_MIPS) + typedef UInt32 T; + + static inline UInt32 Exchange_NoSync(volatile UInt32 *i, UInt32 j) + { + UInt32 ret; + + asm volatile("1:\n\t" + "ll %[r],0(%[i])\n\t" + "sc %[j],0(%[i])\n\t" + "beq %[j],$0,1b\n\t" + "nop \n" + : "+m" (*i), [r] "=&d" (ret) : [i] "d" (i), [j] "d" (j) : "cc", "memory"); + + return ret; + } + + static inline UInt32 ExchangeAdd_NoSync(volatile UInt32 *i, UInt32 j) + { + UInt32 ret; + + asm volatile("1:\n\t" + "ll %[r],0(%[i])\n\t" + "addu %[j],%[r],%[j]\n\t" + "sc %[j],0(%[i])\n\t" + "beq %[j],$0,1b\n\t" + "nop \n" + : "+m" (*i), [r] "=&d" (ret) : [i] "d" (i), [j] "d" (j) : "cc", "memory"); + + return ret; + } + + static inline bool CompareAndSet_NoSync(volatile UInt32 *i, UInt32 c, UInt32 value) + { + UInt32 ret, dummy; + + asm volatile("1:\n\t" + "move %[r],$0\n\t" + "ll %[o],0(%[i])\n\t" + "bne %[o],%[c],2f\n\t" + "move %[r],%[v]\n\t" + "sc %[r],0(%[i])\n\t" + "beq %[r],$0,1b\n\t" + "nop \n\t" + "2:\n" + : "+m" (*i),[r] "=&d" (ret), [o] "=&d" (dummy) : [i] "d" (i), [c] "d" (c), [v] "d" (value) + : "cc", "memory"); + + return ret; + } + +#elif defined(OVR_CPU_ARM) && defined(OVR_CC_ARM) + typedef UInt32 T; + + static inline UInt32 Exchange_NoSync(volatile UInt32 *i, UInt32 j) + { + for(;;) + { + T r = __ldrex(i); + if (__strex(j, i) == 0) + return r; + } + } + static inline UInt32 ExchangeAdd_NoSync(volatile UInt32 *i, UInt32 j) + { + for(;;) + { + T r = __ldrex(i); + if (__strex(r + j, i) == 0) + return r; + } + } + + static inline bool CompareAndSet_NoSync(volatile UInt32 *i, UInt32 c, UInt32 value) + { + for(;;) + { + T r = __ldrex(i); + if (r != c) + return 0; + if (__strex(value, i) == 0) + return 1; + } + } + +#elif defined(OVR_CPU_ARM) + typedef UInt32 T; + + static inline UInt32 Exchange_NoSync(volatile UInt32 *i, UInt32 j) + { + UInt32 ret, dummy; + + asm volatile("1:\n\t" + "ldrex %[r],[%[i]]\n\t" + "strex %[t],%[j],[%[i]]\n\t" + "cmp %[t],#0\n\t" + "bne 1b\n\t" + : "+m" (*i), [r] "=&r" (ret), [t] "=&r" (dummy) : [i] "r" (i), [j] "r" (j) : "cc", "memory"); + + return ret; + } + + static inline UInt32 ExchangeAdd_NoSync(volatile UInt32 *i, UInt32 j) + { + UInt32 ret, dummy, test; + + asm volatile("1:\n\t" + "ldrex %[r],[%[i]]\n\t" + "add %[o],%[r],%[j]\n\t" + "strex %[t],%[o],[%[i]]\n\t" + "cmp %[t],#0\n\t" + "bne 1b\n\t" + : "+m" (*i), [r] "=&r" (ret), [o] "=&r" (dummy), [t] "=&r" (test) : [i] "r" (i), [j] "r" (j) : "cc", "memory"); + + return ret; + } + + static inline bool CompareAndSet_NoSync(volatile UInt32 *i, UInt32 c, UInt32 value) + { + UInt32 ret = 1, dummy, test; + + asm volatile("1:\n\t" + "ldrex %[o],[%[i]]\n\t" + "cmp %[o],%[c]\n\t" + "bne 2f\n\t" + "strex %[r],%[v],[%[i]]\n\t" + "cmp %[r],#0\n\t" + "bne 1b\n\t" + "2:\n" + : "+m" (*i),[r] "=&r" (ret), [o] "=&r" (dummy), [t] "=&r" (test) : [i] "r" (i), [c] "r" (c), [v] "r" (value) + : "cc", "memory"); + + return !ret; + } + +#elif defined(OVR_CPU_X86) + typedef UInt32 T; + + static inline UInt32 Exchange_NoSync(volatile UInt32 *i, UInt32 j) + { + asm volatile("xchgl %1,%[i]\n" + : "+m" (*i), "=q" (j) : [i] "m" (*i), "1" (j) : "cc", "memory"); + + return j; + } + + static inline UInt32 ExchangeAdd_NoSync(volatile UInt32 *i, UInt32 j) + { + asm volatile("lock; xaddl %1,%[i]\n" + : "+m" (*i), "+q" (j) : [i] "m" (*i) : "cc", "memory"); + + return j; + } + + static inline bool CompareAndSet_NoSync(volatile UInt32 *i, UInt32 c, UInt32 value) + { + UInt32 ret; + + asm volatile("lock; cmpxchgl %[v],%[i]\n" + : "+m" (*i), "=a" (ret) : [i] "m" (*i), "1" (c), [v] "q" (value) : "cc", "memory"); + + return (ret == c); + } + +#elif defined(OVR_CC_GNU) && (__GNUC__ >= 4 && __GNUC_MINOR__ >= 1) + + typedef UInt32 T; + + static inline T Exchange_NoSync(volatile T *i, T j) + { + T v; + do { + v = *i; + } while (!__sync_bool_compare_and_swap(i, v, j)); + return v; + } + + static inline T ExchangeAdd_NoSync(volatile T *i, T j) + { + return __sync_fetch_and_add(i, j); + } + + static inline bool CompareAndSet_NoSync(volatile T *i, T c, T value) + { + return __sync_bool_compare_and_swap(i, c, value); + } + +#endif // OS +}; + + +// 8-Byte raw data data atomic op implementation class. +// Currently implementation is provided only on systems with 64-bit pointers. +struct AtomicOpsRaw_8ByteImpl : public AtomicOpsRawBase +{ +#if !defined(OVR_64BIT_POINTERS) || !defined(OVR_ENABLE_THREADS) + + // Provide a type for no-thread-support cases. Used by AtomicOpsRaw_DefImpl. + typedef UInt64 T; + + // *** Thread - Safe OS specific versions. +#elif defined(OVR_OS_WIN32) + + // This is only for 64-bit systems. + typedef LONG64 T; + typedef volatile T* InterlockTPtr; + inline static T Exchange_NoSync(volatile T* p, T val) { return InterlockedExchange64((InterlockTPtr)p, val); } + inline static T ExchangeAdd_NoSync(volatile T* p, T val) { return InterlockedExchangeAdd64((InterlockTPtr)p, val); } + inline static bool CompareAndSet_NoSync(volatile T* p, T c, T val) { return InterlockedCompareExchange64((InterlockTPtr)p, val, c) == c; } + +#elif defined(OVR_CPU_PPC64) + + typedef UInt64 T; + + static inline UInt64 Exchange_NoSync(volatile UInt64 *i, UInt64 j) + { + UInt64 dummy, ret; + + asm volatile("1:\n\t" + "ldarx %[r],0,%[i]\n\t" + "mr %[o],%[j]\n\t" + "stdcx. %[o],0,%[i]\n\t" + "bne- 1b\n" + : "+m" (*i), [r] "=&b" (ret), [o] "=&r" (dummy) : [i] "b" (i), [j] "b" (j) : "cc"); + + return ret; + } + + static inline UInt64 ExchangeAdd_NoSync(volatile UInt64 *i, UInt64 j) + { + UInt64 dummy, ret; + + asm volatile("1:\n\t" + "ldarx %[r],0,%[i]\n\t" + "add %[o],%[r],%[j]\n\t" + "stdcx. %[o],0,%[i]\n\t" + "bne- 1b\n" + : "+m" (*i), [r] "=&b" (ret), [o] "=&r" (dummy) : [i] "b" (i), [j] "b" (j) : "cc"); + + return ret; + } + + static inline bool CompareAndSet_NoSync(volatile UInt64 *i, UInt64 c, UInt64 value) + { + UInt64 ret, dummy; + + asm volatile("1:\n\t" + "ldarx %[r],0,%[i]\n\t" + "cmpw 0,%[r],%[cmp]\n\t" + "mfcr %[r]\n\t" + "bne- 2f\n\t" + "stdcx. %[val],0,%[i]\n\t" + "bne- 1b\n\t" + "2:\n" + : "+m" (*i), [r] "=&b" (ret), [o] "=&r" (dummy) : [i] "b" (i), [cmp] "b" (c), [val] "b" (value) : "cc"); + + return (ret & 0x20000000) ? 1 : 0; + } + +#elif defined(OVR_CC_GNU) && (__GNUC__ >= 4 && __GNUC_MINOR__ >= 1) + + typedef UInt64 T; + + static inline T Exchange_NoSync(volatile T *i, T j) + { + T v; + do { + v = *i; + } while (!__sync_bool_compare_and_swap(i, v, j)); + return v; + } + + static inline T ExchangeAdd_NoSync(volatile T *i, T j) + { + return __sync_fetch_and_add(i, j); + } + + static inline bool CompareAndSet_NoSync(volatile T *i, T c, T value) + { + return __sync_bool_compare_and_swap(i, c, value); + } + +#endif // OS +}; + + +// Default implementation for AtomicOpsRaw; provides implementation of mem-fenced +// atomic operations where fencing is done with a sync object wrapped around a NoSync +// operation implemented in the base class. If such implementation is not possible +// on a given platform, #ifdefs can be used to disable it and then op functions can be +// implemented individually in the appropriate AtomicOpsRaw class. + +template +struct AtomicOpsRaw_DefImpl : public O +{ + typedef typename O::T O_T; + typedef typename O::FullSync O_FullSync; + typedef typename O::AcquireSync O_AcquireSync; + typedef typename O::ReleaseSync O_ReleaseSync; + + // If there is no thread support, provide the default implementation. In this case, + // the base class (0) must still provide the T declaration. +#ifndef OVR_ENABLE_THREADS + + // Atomic exchange of val with argument. Returns old val. + inline static O_T Exchange_NoSync(volatile O_T* p, O_T val) { O_T old = *p; *p = val; return old; } + // Adds a new val to argument; returns its old val. + inline static O_T ExchangeAdd_NoSync(volatile O_T* p, O_T val) { O_T old = *p; *p += val; return old; } + // Compares the argument data with 'c' val. + // If succeeded, stores val int '*p' and returns true; otherwise returns false. + inline static bool CompareAndSet_NoSync(volatile O_T* p, O_T c, O_T val) { if (*p==c) { *p = val; return 1; } return 0; } + +#endif + + // If NoSync wrapped implementation may not be possible, it this block should be + // replaced with per-function implementation in O. + // "AtomicOpsRaw_DefImpl::" prefix in calls below. + inline static O_T Exchange_Sync(volatile O_T* p, O_T val) { O_FullSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl::Exchange_NoSync(p, val); } + inline static O_T Exchange_Release(volatile O_T* p, O_T val) { O_ReleaseSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl::Exchange_NoSync(p, val); } + inline static O_T Exchange_Acquire(volatile O_T* p, O_T val) { O_AcquireSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl::Exchange_NoSync(p, val); } + inline static O_T ExchangeAdd_Sync(volatile O_T* p, O_T val) { O_FullSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl::ExchangeAdd_NoSync(p, val); } + inline static O_T ExchangeAdd_Release(volatile O_T* p, O_T val) { O_ReleaseSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl::ExchangeAdd_NoSync(p, val); } + inline static O_T ExchangeAdd_Acquire(volatile O_T* p, O_T val) { O_AcquireSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl::ExchangeAdd_NoSync(p, val); } + inline static bool CompareAndSet_Sync(volatile O_T* p, O_T c, O_T val) { O_FullSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl::CompareAndSet_NoSync(p,c,val); } + inline static bool CompareAndSet_Release(volatile O_T* p, O_T c, O_T val) { O_ReleaseSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl::CompareAndSet_NoSync(p,c,val); } + inline static bool CompareAndSet_Acquire(volatile O_T* p, O_T c, O_T val) { O_AcquireSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl::CompareAndSet_NoSync(p,c,val); } + + // Loads and stores with memory fence. These have only the relevant versions. +#ifdef OVR_CPU_X86 + // On X86, Store_Release is implemented as exchange. Note that we can also + // consider 'sfence' in the future, although it is not as compatible with older CPUs. + inline static void Store_Release(volatile O_T* p, O_T val) { Exchange_Release(p, val); } +#else + inline static void Store_Release(volatile O_T* p, O_T val) { O_ReleaseSync sync; OVR_UNUSED(sync); *p = val; } +#endif + inline static O_T Load_Acquire(const volatile O_T* p) { O_AcquireSync sync; OVR_UNUSED(sync); return *p; } +}; + + +template +struct AtomicOpsRaw : public AtomicOpsRawBase { }; + +template<> +struct AtomicOpsRaw<4> : public AtomicOpsRaw_DefImpl +{ + // Ensure that assigned type size is correct. + AtomicOpsRaw() + { OVR_COMPILER_ASSERT(sizeof(AtomicOpsRaw_DefImpl::T) == 4); } +}; +template<> +struct AtomicOpsRaw<8> : public AtomicOpsRaw_DefImpl +{ + AtomicOpsRaw() + { OVR_COMPILER_ASSERT(sizeof(AtomicOpsRaw_DefImpl::T) == 8); } +}; + + +// *** AtomicOps - implementation of atomic Ops for specified class + +// Implements atomic ops on a class, provided that the object is either +// 4 or 8 bytes in size (depending on the AtomicOpsRaw specializations +// available). Relies on AtomicOpsRaw for much of implementation. + +template +class AtomicOps +{ + typedef AtomicOpsRaw Ops; + typedef typename Ops::T T; + typedef volatile typename Ops::T* PT; + // We cast through unions to (1) avoid pointer size compiler warnings + // and (2) ensure that there are no problems with strict pointer aliasing. + union C2T_union { C c; T t; }; + +public: + // General purpose implementation for standard syncs. + inline static C Exchange_Sync(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::Exchange_Sync((PT)p, u.t); return u.c; } + inline static C Exchange_Release(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::Exchange_Release((PT)p, u.t); return u.c; } + inline static C Exchange_Acquire(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::Exchange_Acquire((PT)p, u.t); return u.c; } + inline static C Exchange_NoSync(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::Exchange_NoSync((PT)p, u.t); return u.c; } + inline static C ExchangeAdd_Sync(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::ExchangeAdd_Sync((PT)p, u.t); return u.c; } + inline static C ExchangeAdd_Release(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::ExchangeAdd_Release((PT)p, u.t); return u.c; } + inline static C ExchangeAdd_Acquire(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::ExchangeAdd_Acquire((PT)p, u.t); return u.c; } + inline static C ExchangeAdd_NoSync(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::ExchangeAdd_NoSync((PT)p, u.t); return u.c; } + inline static bool CompareAndSet_Sync(volatile C* p, C c, C val) { C2T_union u,cu; u.c = val; cu.c = c; return Ops::CompareAndSet_Sync((PT)p, cu.t, u.t); } + inline static bool CompareAndSet_Release(volatile C* p, C c, C val){ C2T_union u,cu; u.c = val; cu.c = c; return Ops::CompareAndSet_Release((PT)p, cu.t, u.t); } + inline static bool CompareAndSet_Relse(volatile C* p, C c, C val){ C2T_union u,cu; u.c = val; cu.c = c; return Ops::CompareAndSet_Acquire((PT)p, cu.t, u.t); } + inline static bool CompareAndSet_NoSync(volatile C* p, C c, C val) { C2T_union u,cu; u.c = val; cu.c = c; return Ops::CompareAndSet_NoSync((PT)p, cu.t, u.t); } + // Loads and stores with memory fence. These have only the relevant versions. + inline static void Store_Release(volatile C* p, C val) { C2T_union u; u.c = val; Ops::Store_Release((PT)p, u.t); } + inline static C Load_Acquire(const volatile C* p) { C2T_union u; u.t = Ops::Load_Acquire((PT)p); return u.c; } +}; + + + +// Atomic value base class - implements operations shared for integers and pointers. +template +class AtomicValueBase +{ +protected: + typedef AtomicOps Ops; +public: + + volatile T Value; + + inline AtomicValueBase() { } + explicit inline AtomicValueBase(T val) { Ops::Store_Release(&Value, val); } + + // Most libraries (TBB and Joshua Scholar's) library do not do Load_Acquire + // here, since most algorithms do not require atomic loads. Needs some research. + inline operator T() const { return Value; } + + // *** Standard Atomic inlines + inline T Exchange_Sync(T val) { return Ops::Exchange_Sync(&Value, val); } + inline T Exchange_Release(T val) { return Ops::Exchange_Release(&Value, val); } + inline T Exchange_Acquire(T val) { return Ops::Exchange_Acquire(&Value, val); } + inline T Exchange_NoSync(T val) { return Ops::Exchange_NoSync(&Value, val); } + inline bool CompareAndSet_Sync(T c, T val) { return Ops::CompareAndSet_Sync(&Value, c, val); } + inline bool CompareAndSet_Release(T c, T val) { return Ops::CompareAndSet_Release(&Value, c, val); } + inline bool CompareAndSet_Acquire(T c, T val) { return Ops::CompareAndSet_Relse(&Value, c, val); } + inline bool CompareAndSet_NoSync(T c, T val) { return Ops::CompareAndSet_NoSync(&Value, c, val); } + // Load & Store. + inline void Store_Release(T val) { Ops::Store_Release(&Value, val); } + inline T Load_Acquire() const { return Ops::Load_Acquire(&Value); } +}; + + +// ***** AtomicPtr - Atomic pointer template + +// This pointer class supports atomic assignments with release, +// increment / decrement operations, and conditional compare + set. + +template +class AtomicPtr : public AtomicValueBase +{ + typedef typename AtomicValueBase::Ops Ops; + +public: + // Initialize pointer value to 0 by default; use Store_Release only with explicit constructor. + inline AtomicPtr() : AtomicValueBase() { this->Value = 0; } + explicit inline AtomicPtr(T* val) : AtomicValueBase(val) { } + + // Pointer access. + inline T* operator -> () const { return this->Load_Acquire(); } + + // It looks like it is convenient to have Load_Acquire characteristics + // for this, since that is convenient for algorithms such as linked + // list traversals that can be added to bu another thread. + inline operator T* () const { return this->Load_Acquire(); } + + + // *** Standard Atomic inlines (applicable to pointers) + + // ExhangeAdd considers pointer size for pointers. + template + inline T* ExchangeAdd_Sync(I incr) { return Ops::ExchangeAdd_Sync(&this->Value, ((T*)0) + incr); } + template + inline T* ExchangeAdd_Release(I incr) { return Ops::ExchangeAdd_Release(&this->Value, ((T*)0) + incr); } + template + inline T* ExchangeAdd_Acquire(I incr) { return Ops::ExchangeAdd_Acquire(&this->Value, ((T*)0) + incr); } + template + inline T* ExchangeAdd_NoSync(I incr) { return Ops::ExchangeAdd_NoSync(&this->Value, ((T*)0) + incr); } + + // *** Atomic Operators + + inline T* operator = (T* val) { this->Store_Release(val); return val; } + + template + inline T* operator += (I val) { return ExchangeAdd_Sync(val) + val; } + template + inline T* operator -= (I val) { return operator += (-val); } + + inline T* operator ++ () { return ExchangeAdd_Sync(1) + 1; } + inline T* operator -- () { return ExchangeAdd_Sync(-1) - 1; } + inline T* operator ++ (int) { return ExchangeAdd_Sync(1); } + inline T* operator -- (int) { return ExchangeAdd_Sync(-1); } +}; + + +// ***** AtomicInt - Atomic integer template + +// Implements an atomic integer type; the exact type to use is provided +// as an argument. Supports atomic Acquire / Release semantics, atomic +// arithmetic operations, and atomic conditional compare + set. + +template +class AtomicInt : public AtomicValueBase +{ + typedef typename AtomicValueBase::Ops Ops; + +public: + inline AtomicInt() : AtomicValueBase() { } + explicit inline AtomicInt(T val) : AtomicValueBase(val) { } + + + // *** Standard Atomic inlines (applicable to int) + inline T ExchangeAdd_Sync(T val) { return Ops::ExchangeAdd_Sync(&this->Value, val); } + inline T ExchangeAdd_Release(T val) { return Ops::ExchangeAdd_Release(&this->Value, val); } + inline T ExchangeAdd_Acquire(T val) { return Ops::ExchangeAdd_Acquire(&this->Value, val); } + inline T ExchangeAdd_NoSync(T val) { return Ops::ExchangeAdd_NoSync(&this->Value, val); } + // These increments could be more efficient because they don't return a value. + inline void Increment_Sync() { ExchangeAdd_Sync((T)1); } + inline void Increment_Release() { ExchangeAdd_Release((T)1); } + inline void Increment_Acquire() { ExchangeAdd_Acquire((T)1); } + inline void Increment_NoSync() { ExchangeAdd_NoSync((T)1); } + + // *** Atomic Operators + + inline T operator = (T val) { this->Store_Release(val); return val; } + inline T operator += (T val) { return ExchangeAdd_Sync(val) + val; } + inline T operator -= (T val) { return ExchangeAdd_Sync(0 - val) - val; } + + inline T operator ++ () { return ExchangeAdd_Sync((T)1) + 1; } + inline T operator -- () { return ExchangeAdd_Sync(((T)0)-1) - 1; } + inline T operator ++ (int) { return ExchangeAdd_Sync((T)1); } + inline T operator -- (int) { return ExchangeAdd_Sync(((T)0)-1); } + + // More complex atomic operations. Leave it to compiler whether to optimize them or not. + T operator &= (T arg) + { + T comp, newVal; + do { + comp = this->Value; + newVal = comp & arg; + } while(!this->CompareAndSet_Sync(comp, newVal)); + return newVal; + } + + T operator |= (T arg) + { + T comp, newVal; + do { + comp = this->Value; + newVal = comp | arg; + } while(!this->CompareAndSet_Sync(comp, newVal)); + return newVal; + } + + T operator ^= (T arg) + { + T comp, newVal; + do { + comp = this->Value; + newVal = comp ^ arg; + } while(!this->CompareAndSet_Sync(comp, newVal)); + return newVal; + } + + T operator *= (T arg) + { + T comp, newVal; + do { + comp = this->Value; + newVal = comp * arg; + } while(!this->CompareAndSet_Sync(comp, newVal)); + return newVal; + } + + T operator /= (T arg) + { + T comp, newVal; + do { + comp = this->Value; + newVal = comp / arg; + } while(!CompareAndSet_Sync(comp, newVal)); + return newVal; + } + + T operator >>= (unsigned bits) + { + T comp, newVal; + do { + comp = this->Value; + newVal = comp >> bits; + } while(!CompareAndSet_Sync(comp, newVal)); + return newVal; + } + + T operator <<= (unsigned bits) + { + T comp, newVal; + do { + comp = this->Value; + newVal = comp << bits; + } while(!this->CompareAndSet_Sync(comp, newVal)); + return newVal; + } +}; + + + +//----------------------------------------------------------------------------------- +// ***** Lock + +// Lock is a simplest and most efficient mutual-exclusion lock class. +// Unlike Mutex, it cannot be waited on. + +class Lock +{ + // NOTE: Locks are not allocatable and they themselves should not allocate + // memory by standard means. This is the case because StandardAllocator + // relies on this class. + // Make 'delete' private. Don't do this for 'new' since it can be redefined. + void operator delete(void*) {} + + + // *** Lock implementation for various platforms. + +#if !defined(OVR_ENABLE_THREADS) + +public: + // With no thread support, lock does nothing. + inline Lock() { } + inline Lock(unsigned) { } + inline ~Lock() { } + inline void DoLock() { } + inline void Unlock() { } + + // Windows. +#elif defined(OVR_OS_WIN32) + + CRITICAL_SECTION cs; +public: + Lock(unsigned spinCount = 0); + ~Lock(); + // Locking functions. + inline void DoLock() { ::EnterCriticalSection(&cs); } + inline void Unlock() { ::LeaveCriticalSection(&cs); } + +#else + pthread_mutex_t mutex; + +public: + static pthread_mutexattr_t RecursiveAttr; + static bool RecursiveAttrInit; + + Lock (unsigned dummy = 0) + { + if (!RecursiveAttrInit) + { + pthread_mutexattr_init(&RecursiveAttr); + pthread_mutexattr_settype(&RecursiveAttr, PTHREAD_MUTEX_RECURSIVE); + RecursiveAttrInit = 1; + } + pthread_mutex_init(&mutex,&RecursiveAttr); + } + ~Lock () { pthread_mutex_destroy(&mutex); } + inline void DoLock() { pthread_mutex_lock(&mutex); } + inline void Unlock() { pthread_mutex_unlock(&mutex); } + +#endif // OVR_ENABLE_THREDS + + +public: + // Locker class, used for automatic locking + class Locker + { + public: + Lock *pLock; + inline Locker(Lock *plock) + { pLock = plock; pLock->DoLock(); } + inline ~Locker() + { pLock->Unlock(); } + }; +}; + + + +} // OVR + +#endif diff -r 59fc487ba58e -r b069a5c27388 libovr/Src/Kernel/OVR_Color.h --- a/libovr/Src/Kernel/OVR_Color.h Sat Sep 14 17:51:03 2013 +0300 +++ b/libovr/Src/Kernel/OVR_Color.h Sun Sep 15 04:10:05 2013 +0300 @@ -1,1 +1,55 @@ -/************************************************************************************ PublicHeader: OVR.h Filename : OVR_Color.h Content : Contains color struct. Created : February 7, 2013 Notes : Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #ifndef OVR_Color_h #define OVR_Color_h #include "OVR_Types.h" namespace OVR { struct Color { UByte R,G,B,A; Color() {} // Constructs color by channel. Alpha is set to 0xFF (fully visible) // if not specified. Color(unsigned char r,unsigned char g,unsigned char b, unsigned char a = 0xFF) : R(r), G(g), B(b), A(a) { } // 0xAARRGGBB - Common HTML color Hex layout Color(unsigned c) : R((unsigned char)(c>>16)), G((unsigned char)(c>>8)), B((unsigned char)c), A((unsigned char)(c>>24)) { } bool operator==(const Color& b) const { return R == b.R && G == b.G && B == b.B && A == b.A; } void GetRGBA(float *r, float *g, float *b, float* a) const { *r = R / 255.0f; *g = G / 255.0f; *b = B / 255.0f; *a = A / 255.0f; } }; } #endif \ No newline at end of file +/************************************************************************************ + +PublicHeader: OVR.h +Filename : OVR_Color.h +Content : Contains color struct. +Created : February 7, 2013 +Notes : + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ +#ifndef OVR_Color_h +#define OVR_Color_h + +#include "OVR_Types.h" + +namespace OVR { + +struct Color +{ + UByte R,G,B,A; + + Color() {} + + // Constructs color by channel. Alpha is set to 0xFF (fully visible) + // if not specified. + Color(unsigned char r,unsigned char g,unsigned char b, unsigned char a = 0xFF) + : R(r), G(g), B(b), A(a) { } + + // 0xAARRGGBB - Common HTML color Hex layout + Color(unsigned c) + : R((unsigned char)(c>>16)), G((unsigned char)(c>>8)), + B((unsigned char)c), A((unsigned char)(c>>24)) { } + + bool operator==(const Color& b) const + { + return R == b.R && G == b.G && B == b.B && A == b.A; + } + + void GetRGBA(float *r, float *g, float *b, float* a) const + { + *r = R / 255.0f; + *g = G / 255.0f; + *b = B / 255.0f; + *a = A / 255.0f; + } +}; + +} + +#endif diff -r 59fc487ba58e -r b069a5c27388 libovr/Src/Kernel/OVR_ContainerAllocator.h --- a/libovr/Src/Kernel/OVR_ContainerAllocator.h Sat Sep 14 17:51:03 2013 +0300 +++ b/libovr/Src/Kernel/OVR_ContainerAllocator.h Sun Sep 15 04:10:05 2013 +0300 @@ -1,1 +1,256 @@ -/************************************************************************************ PublicHeader: OVR.h Filename : OVR_ContainerAllocator.h Content : Template allocators and constructors for containers. Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #ifndef OVR_ContainerAllocator_h #define OVR_ContainerAllocator_h #include "OVR_Allocator.h" #include namespace OVR { //----------------------------------------------------------------------------------- // ***** Container Allocator // ContainerAllocator serves as a template argument for allocations done by // containers, such as Array and Hash; replacing it could allow allocator // substitution in containers. class ContainerAllocatorBase { public: static void* Alloc(UPInt size) { return OVR_ALLOC(size); } static void* Realloc(void* p, UPInt newSize) { return OVR_REALLOC(p, newSize); } static void Free(void *p) { OVR_FREE(p); } }; //----------------------------------------------------------------------------------- // ***** Constructors, Destructors, Copiers // Plain Old Data - movable, no special constructors/destructor. template class ConstructorPOD { public: static void Construct(void *) {} static void Construct(void *p, const T& source) { *(T*)p = source; } // Same as above, but allows for a different type of constructor. template static void ConstructAlt(void *p, const S& source) { *(T*)p = source; } static void ConstructArray(void*, UPInt) {} static void ConstructArray(void* p, UPInt count, const T& source) { UByte *pdata = (UByte*)p; for (UPInt i=0; i< count; ++i, pdata += sizeof(T)) *(T*)pdata = source; } static void ConstructArray(void* p, UPInt count, const T* psource) { memcpy(p, psource, sizeof(T) * count); } static void Destruct(T*) {} static void DestructArray(T*, UPInt) {} static void CopyArrayForward(T* dst, const T* src, UPInt count) { memmove(dst, src, count * sizeof(T)); } static void CopyArrayBackward(T* dst, const T* src, UPInt count) { memmove(dst, src, count * sizeof(T)); } static bool IsMovable() { return true; } }; //----------------------------------------------------------------------------------- // ***** ConstructorMov // // Correct C++ construction and destruction for movable objects template class ConstructorMov { public: static void Construct(void* p) { OVR::Construct(p); } static void Construct(void* p, const T& source) { OVR::Construct(p, source); } // Same as above, but allows for a different type of constructor. template static void ConstructAlt(void* p, const S& source) { OVR::ConstructAlt(p, source); } static void ConstructArray(void* p, UPInt count) { UByte* pdata = (UByte*)p; for (UPInt i=0; i< count; ++i, pdata += sizeof(T)) Construct(pdata); } static void ConstructArray(void* p, UPInt count, const T& source) { UByte* pdata = (UByte*)p; for (UPInt i=0; i< count; ++i, pdata += sizeof(T)) Construct(pdata, source); } static void ConstructArray(void* p, UPInt count, const T* psource) { UByte* pdata = (UByte*)p; for (UPInt i=0; i< count; ++i, pdata += sizeof(T)) Construct(pdata, *psource++); } static void Destruct(T* p) { p->~T(); OVR_UNUSED(p); // Suppress silly MSVC warning } static void DestructArray(T* p, UPInt count) { p += count - 1; for (UPInt i=0; i~T(); } static void CopyArrayForward(T* dst, const T* src, UPInt count) { memmove(dst, src, count * sizeof(T)); } static void CopyArrayBackward(T* dst, const T* src, UPInt count) { memmove(dst, src, count * sizeof(T)); } static bool IsMovable() { return true; } }; //----------------------------------------------------------------------------------- // ***** ConstructorCPP // // Correct C++ construction and destruction for movable objects template class ConstructorCPP { public: static void Construct(void* p) { OVR::Construct(p); } static void Construct(void* p, const T& source) { OVR::Construct(p, source); } // Same as above, but allows for a different type of constructor. template static void ConstructAlt(void* p, const S& source) { OVR::ConstructAlt(p, source); } static void ConstructArray(void* p, UPInt count) { UByte* pdata = (UByte*)p; for (UPInt i=0; i< count; ++i, pdata += sizeof(T)) Construct(pdata); } static void ConstructArray(void* p, UPInt count, const T& source) { UByte* pdata = (UByte*)p; for (UPInt i=0; i< count; ++i, pdata += sizeof(T)) Construct(pdata, source); } static void ConstructArray(void* p, UPInt count, const T* psource) { UByte* pdata = (UByte*)p; for (UPInt i=0; i< count; ++i, pdata += sizeof(T)) Construct(pdata, *psource++); } static void Destruct(T* p) { p->~T(); OVR_UNUSED(p); // Suppress silly MSVC warning } static void DestructArray(T* p, UPInt count) { p += count - 1; for (UPInt i=0; i~T(); } static void CopyArrayForward(T* dst, const T* src, UPInt count) { for(UPInt i = 0; i < count; ++i) dst[i] = src[i]; } static void CopyArrayBackward(T* dst, const T* src, UPInt count) { for(UPInt i = count; i; --i) dst[i-1] = src[i-1]; } static bool IsMovable() { return false; } }; //----------------------------------------------------------------------------------- // ***** Container Allocator with movement policy // // Simple wraps as specialized allocators template struct ContainerAllocator_POD : ContainerAllocatorBase, ConstructorPOD {}; template struct ContainerAllocator : ContainerAllocatorBase, ConstructorMov {}; template struct ContainerAllocator_CPP : ContainerAllocatorBase, ConstructorCPP {}; } // OVR #endif \ No newline at end of file +/************************************************************************************ + +PublicHeader: OVR.h +Filename : OVR_ContainerAllocator.h +Content : Template allocators and constructors for containers. +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#ifndef OVR_ContainerAllocator_h +#define OVR_ContainerAllocator_h + +#include "OVR_Allocator.h" +#include + + +namespace OVR { + + +//----------------------------------------------------------------------------------- +// ***** Container Allocator + +// ContainerAllocator serves as a template argument for allocations done by +// containers, such as Array and Hash; replacing it could allow allocator +// substitution in containers. + +class ContainerAllocatorBase +{ +public: + static void* Alloc(UPInt size) { return OVR_ALLOC(size); } + static void* Realloc(void* p, UPInt newSize) { return OVR_REALLOC(p, newSize); } + static void Free(void *p) { OVR_FREE(p); } +}; + + + +//----------------------------------------------------------------------------------- +// ***** Constructors, Destructors, Copiers + +// Plain Old Data - movable, no special constructors/destructor. +template +class ConstructorPOD +{ +public: + static void Construct(void *) {} + static void Construct(void *p, const T& source) + { + *(T*)p = source; + } + + // Same as above, but allows for a different type of constructor. + template + static void ConstructAlt(void *p, const S& source) + { + *(T*)p = source; + } + + static void ConstructArray(void*, UPInt) {} + + static void ConstructArray(void* p, UPInt count, const T& source) + { + UByte *pdata = (UByte*)p; + for (UPInt i=0; i< count; ++i, pdata += sizeof(T)) + *(T*)pdata = source; + } + + static void ConstructArray(void* p, UPInt count, const T* psource) + { + memcpy(p, psource, sizeof(T) * count); + } + + static void Destruct(T*) {} + static void DestructArray(T*, UPInt) {} + + static void CopyArrayForward(T* dst, const T* src, UPInt count) + { + memmove(dst, src, count * sizeof(T)); + } + + static void CopyArrayBackward(T* dst, const T* src, UPInt count) + { + memmove(dst, src, count * sizeof(T)); + } + + static bool IsMovable() { return true; } +}; + + +//----------------------------------------------------------------------------------- +// ***** ConstructorMov +// +// Correct C++ construction and destruction for movable objects +template +class ConstructorMov +{ +public: + static void Construct(void* p) + { + OVR::Construct(p); + } + + static void Construct(void* p, const T& source) + { + OVR::Construct(p, source); + } + + // Same as above, but allows for a different type of constructor. + template + static void ConstructAlt(void* p, const S& source) + { + OVR::ConstructAlt(p, source); + } + + static void ConstructArray(void* p, UPInt count) + { + UByte* pdata = (UByte*)p; + for (UPInt i=0; i< count; ++i, pdata += sizeof(T)) + Construct(pdata); + } + + static void ConstructArray(void* p, UPInt count, const T& source) + { + UByte* pdata = (UByte*)p; + for (UPInt i=0; i< count; ++i, pdata += sizeof(T)) + Construct(pdata, source); + } + + static void ConstructArray(void* p, UPInt count, const T* psource) + { + UByte* pdata = (UByte*)p; + for (UPInt i=0; i< count; ++i, pdata += sizeof(T)) + Construct(pdata, *psource++); + } + + static void Destruct(T* p) + { + p->~T(); + OVR_UNUSED(p); // Suppress silly MSVC warning + } + + static void DestructArray(T* p, UPInt count) + { + p += count - 1; + for (UPInt i=0; i~T(); + } + + static void CopyArrayForward(T* dst, const T* src, UPInt count) + { + memmove(dst, src, count * sizeof(T)); + } + + static void CopyArrayBackward(T* dst, const T* src, UPInt count) + { + memmove(dst, src, count * sizeof(T)); + } + + static bool IsMovable() { return true; } +}; + + +//----------------------------------------------------------------------------------- +// ***** ConstructorCPP +// +// Correct C++ construction and destruction for movable objects +template +class ConstructorCPP +{ +public: + static void Construct(void* p) + { + OVR::Construct(p); + } + + static void Construct(void* p, const T& source) + { + OVR::Construct(p, source); + } + + // Same as above, but allows for a different type of constructor. + template + static void ConstructAlt(void* p, const S& source) + { + OVR::ConstructAlt(p, source); + } + + static void ConstructArray(void* p, UPInt count) + { + UByte* pdata = (UByte*)p; + for (UPInt i=0; i< count; ++i, pdata += sizeof(T)) + Construct(pdata); + } + + static void ConstructArray(void* p, UPInt count, const T& source) + { + UByte* pdata = (UByte*)p; + for (UPInt i=0; i< count; ++i, pdata += sizeof(T)) + Construct(pdata, source); + } + + static void ConstructArray(void* p, UPInt count, const T* psource) + { + UByte* pdata = (UByte*)p; + for (UPInt i=0; i< count; ++i, pdata += sizeof(T)) + Construct(pdata, *psource++); + } + + static void Destruct(T* p) + { + p->~T(); + OVR_UNUSED(p); // Suppress silly MSVC warning + } + + static void DestructArray(T* p, UPInt count) + { + p += count - 1; + for (UPInt i=0; i~T(); + } + + static void CopyArrayForward(T* dst, const T* src, UPInt count) + { + for(UPInt i = 0; i < count; ++i) + dst[i] = src[i]; + } + + static void CopyArrayBackward(T* dst, const T* src, UPInt count) + { + for(UPInt i = count; i; --i) + dst[i-1] = src[i-1]; + } + + static bool IsMovable() { return false; } +}; + + +//----------------------------------------------------------------------------------- +// ***** Container Allocator with movement policy +// +// Simple wraps as specialized allocators +template struct ContainerAllocator_POD : ContainerAllocatorBase, ConstructorPOD {}; +template struct ContainerAllocator : ContainerAllocatorBase, ConstructorMov {}; +template struct ContainerAllocator_CPP : ContainerAllocatorBase, ConstructorCPP {}; + + +} // OVR + + +#endif diff -r 59fc487ba58e -r b069a5c27388 libovr/Src/Kernel/OVR_File.cpp --- a/libovr/Src/Kernel/OVR_File.cpp Sat Sep 14 17:51:03 2013 +0300 +++ b/libovr/Src/Kernel/OVR_File.cpp Sun Sep 15 04:10:05 2013 +0300 @@ -1,1 +1,571 @@ -/************************************************************************** Filename : OVR_File.cpp Content : File wrapper class implementation (Win32) Created : April 5, 1999 Authors : Michael Antonov Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. **************************************************************************/ #define GFILE_CXX // Standard C library (Captain Obvious guarantees!) #include #include "OVR_File.h" namespace OVR { // Buffered file adds buffering to an existing file // FILEBUFFER_SIZE defines the size of internal buffer, while // FILEBUFFER_TOLERANCE controls the amount of data we'll effectively try to buffer #define FILEBUFFER_SIZE (8192-8) #define FILEBUFFER_TOLERANCE 4096 // ** Constructor/Destructor // Hidden constructor // Not supposed to be used BufferedFile::BufferedFile() : DelegatedFile(0) { pBuffer = (UByte*)OVR_ALLOC(FILEBUFFER_SIZE); BufferMode = NoBuffer; FilePos = 0; Pos = 0; DataSize = 0; } // Takes another file as source BufferedFile::BufferedFile(File *pfile) : DelegatedFile(pfile) { pBuffer = (UByte*)OVR_ALLOC(FILEBUFFER_SIZE); BufferMode = NoBuffer; FilePos = pfile->LTell(); Pos = 0; DataSize = 0; } // Destructor BufferedFile::~BufferedFile() { // Flush in case there's data if (pFile) FlushBuffer(); // Get rid of buffer if (pBuffer) OVR_FREE(pBuffer); } /* bool BufferedFile::VCopy(const Object &source) { if (!DelegatedFile::VCopy(source)) return 0; // Data members BufferedFile *psource = (BufferedFile*)&source; // Buffer & the mode it's in pBuffer = psource->pBuffer; BufferMode = psource->BufferMode; Pos = psource->Pos; DataSize = psource->DataSize; return 1; } */ // Initializes buffering to a certain mode bool BufferedFile::SetBufferMode(BufferModeType mode) { if (!pBuffer) return false; if (mode == BufferMode) return true; FlushBuffer(); // Can't set write mode if we can't write if ((mode==WriteBuffer) && (!pFile || !pFile->IsWritable()) ) return 0; // And SetMode BufferMode = mode; Pos = 0; DataSize = 0; return 1; } // Flushes buffer void BufferedFile::FlushBuffer() { switch(BufferMode) { case WriteBuffer: // Write data in buffer FilePos += pFile->Write(pBuffer,Pos); Pos = 0; break; case ReadBuffer: // Seek back & reset buffer data if ((DataSize-Pos)>0) FilePos = pFile->LSeek(-(int)(DataSize-Pos), Seek_Cur); DataSize = 0; Pos = 0; break; default: // not handled! break; } } // Reloads data for ReadBuffer void BufferedFile::LoadBuffer() { if (BufferMode == ReadBuffer) { // We should only reload once all of pre-loaded buffer is consumed. OVR_ASSERT(Pos == DataSize); // WARNING: Right now LoadBuffer() assumes the buffer's empty int sz = pFile->Read(pBuffer,FILEBUFFER_SIZE); DataSize = sz<0 ? 0 : (unsigned)sz; Pos = 0; FilePos += DataSize; } } // ** Overridden functions // We override all the functions that can possibly // require buffer mode switch, flush, or extra calculations // Tell() requires buffer adjustment int BufferedFile::Tell() { if (BufferMode == ReadBuffer) return int (FilePos - DataSize + Pos); int pos = pFile->Tell(); // Adjust position based on buffer mode & data if (pos!=-1) { OVR_ASSERT(BufferMode != ReadBuffer); if (BufferMode == WriteBuffer) pos += Pos; } return pos; } SInt64 BufferedFile::LTell() { if (BufferMode == ReadBuffer) return FilePos - DataSize + Pos; SInt64 pos = pFile->LTell(); if (pos!=-1) { OVR_ASSERT(BufferMode != ReadBuffer); if (BufferMode == WriteBuffer) pos += Pos; } return pos; } int BufferedFile::GetLength() { int len = pFile->GetLength(); // If writing through buffer, file length may actually be bigger if ((len!=-1) && (BufferMode==WriteBuffer)) { int currPos = pFile->Tell() + Pos; if (currPos>len) len = currPos; } return len; } SInt64 BufferedFile::LGetLength() { SInt64 len = pFile->LGetLength(); // If writing through buffer, file length may actually be bigger if ((len!=-1) && (BufferMode==WriteBuffer)) { SInt64 currPos = pFile->LTell() + Pos; if (currPos>len) len = currPos; } return len; } /* bool BufferedFile::Stat(FileStats *pfs) { // Have to fix up length is stat if (pFile->Stat(pfs)) { if (BufferMode==WriteBuffer) { SInt64 currPos = pFile->LTell() + Pos; if (currPos > pfs->Size) { pfs->Size = currPos; // ?? pfs->Blocks = (pfs->Size+511) >> 9; } } return 1; } return 0; } */ int BufferedFile::Write(const UByte *psourceBuffer, int numBytes) { if ( (BufferMode==WriteBuffer) || SetBufferMode(WriteBuffer)) { // If not data space in buffer, flush if ((FILEBUFFER_SIZE-(int)Pos)FILEBUFFER_TOLERANCE) { int sz = pFile->Write(psourceBuffer,numBytes); if (sz > 0) FilePos += sz; return sz; } } // Enough space in buffer.. so copy to it memcpy(pBuffer+Pos, psourceBuffer, numBytes); Pos += numBytes; return numBytes; } int sz = pFile->Write(psourceBuffer,numBytes); if (sz > 0) FilePos += sz; return sz; } int BufferedFile::Read(UByte *pdestBuffer, int numBytes) { if ( (BufferMode==ReadBuffer) || SetBufferMode(ReadBuffer)) { // Data in buffer... copy it if ((int)(DataSize-Pos) >= numBytes) { memcpy(pdestBuffer, pBuffer+Pos, numBytes); Pos += numBytes; return numBytes; } // Not enough data in buffer, copy buffer int readBytes = DataSize-Pos; memcpy(pdestBuffer, pBuffer+Pos, readBytes); numBytes -= readBytes; pdestBuffer += readBytes; Pos = DataSize; // Don't reload buffer if more then tolerance // (No major advantage, and we don't want to write a loop) if (numBytes>FILEBUFFER_TOLERANCE) { numBytes = pFile->Read(pdestBuffer,numBytes); if (numBytes > 0) { FilePos += numBytes; Pos = DataSize = 0; } return readBytes + ((numBytes==-1) ? 0 : numBytes); } // Reload the buffer // WARNING: Right now LoadBuffer() assumes the buffer's empty LoadBuffer(); if ((int)(DataSize-Pos) < numBytes) numBytes = (int)DataSize-Pos; memcpy(pdestBuffer, pBuffer+Pos, numBytes); Pos += numBytes; return numBytes + readBytes; /* // Alternative Read implementation. The one above is probably better // due to FILEBUFFER_TOLERANCE. int total = 0; do { int bufferBytes = (int)(DataSize-Pos); int copyBytes = (bufferBytes > numBytes) ? numBytes : bufferBytes; memcpy(pdestBuffer, pBuffer+Pos, copyBytes); numBytes -= copyBytes; pdestBuffer += copyBytes; Pos += copyBytes; total += copyBytes; if (numBytes == 0) break; LoadBuffer(); } while (DataSize > 0); return total; */ } int sz = pFile->Read(pdestBuffer,numBytes); if (sz > 0) FilePos += sz; return sz; } int BufferedFile::SkipBytes(int numBytes) { int skippedBytes = 0; // Special case for skipping a little data in read buffer if (BufferMode==ReadBuffer) { skippedBytes = (((int)DataSize-(int)Pos) >= numBytes) ? numBytes : (DataSize-Pos); Pos += skippedBytes; numBytes -= skippedBytes; } if (numBytes) { numBytes = pFile->SkipBytes(numBytes); // Make sure we return the actual number skipped, or error if (numBytes!=-1) { skippedBytes += numBytes; FilePos += numBytes; Pos = DataSize = 0; } else if (skippedBytes <= 0) skippedBytes = -1; } return skippedBytes; } int BufferedFile::BytesAvailable() { int available = pFile->BytesAvailable(); // Adjust available size based on buffers switch(BufferMode) { case ReadBuffer: available += DataSize-Pos; break; case WriteBuffer: available -= Pos; if (available<0) available= 0; break; default: break; } return available; } bool BufferedFile::Flush() { FlushBuffer(); return pFile->Flush(); } // Seeking could be optimized better.. int BufferedFile::Seek(int offset, int origin) { if (BufferMode == ReadBuffer) { if (origin == Seek_Cur) { // Seek can fall either before or after Pos in the buffer, // but it must be within bounds. if (((unsigned(offset) + Pos)) <= DataSize) { Pos += offset; return int (FilePos - DataSize + Pos); } // Lightweight buffer "Flush". We do this to avoid an extra seek // back operation which would take place if we called FlushBuffer directly. origin = Seek_Set; OVR_ASSERT(((FilePos - DataSize + Pos) + (UInt64)offset) < ~(UInt64)0); offset = (int)(FilePos - DataSize + Pos) + offset; Pos = DataSize = 0; } else if (origin == Seek_Set) { if (((unsigned)offset - (FilePos-DataSize)) <= DataSize) { OVR_ASSERT((FilePos-DataSize) < ~(UInt64)0); Pos = (unsigned)offset - (unsigned)(FilePos-DataSize); return offset; } Pos = DataSize = 0; } else { FlushBuffer(); } } else { FlushBuffer(); } /* // Old Seek Logic if (origin == Seek_Cur && offset + Pos < DataSize) { //OVR_ASSERT((FilePos - DataSize) >= (FilePos - DataSize + Pos + offset)); Pos += offset; OVR_ASSERT(int (Pos) >= 0); return int (FilePos - DataSize + Pos); } else if (origin == Seek_Set && unsigned(offset) >= FilePos - DataSize && unsigned(offset) < FilePos) { Pos = unsigned(offset - FilePos + DataSize); OVR_ASSERT(int (Pos) >= 0); return int (FilePos - DataSize + Pos); } FlushBuffer(); */ FilePos = pFile->Seek(offset,origin); return int (FilePos); } SInt64 BufferedFile::LSeek(SInt64 offset, int origin) { if (BufferMode == ReadBuffer) { if (origin == Seek_Cur) { // Seek can fall either before or after Pos in the buffer, // but it must be within bounds. if (((unsigned(offset) + Pos)) <= DataSize) { Pos += (unsigned)offset; return SInt64(FilePos - DataSize + Pos); } // Lightweight buffer "Flush". We do this to avoid an extra seek // back operation which would take place if we called FlushBuffer directly. origin = Seek_Set; offset = (SInt64)(FilePos - DataSize + Pos) + offset; Pos = DataSize = 0; } else if (origin == Seek_Set) { if (((UInt64)offset - (FilePos-DataSize)) <= DataSize) { Pos = (unsigned)((UInt64)offset - (FilePos-DataSize)); return offset; } Pos = DataSize = 0; } else { FlushBuffer(); } } else { FlushBuffer(); } /* OVR_ASSERT(BufferMode != NoBuffer); if (origin == Seek_Cur && offset + Pos < DataSize) { Pos += int (offset); return FilePos - DataSize + Pos; } else if (origin == Seek_Set && offset >= SInt64(FilePos - DataSize) && offset < SInt64(FilePos)) { Pos = unsigned(offset - FilePos + DataSize); return FilePos - DataSize + Pos; } FlushBuffer(); */ FilePos = pFile->LSeek(offset,origin); return FilePos; } int BufferedFile::CopyFromStream(File *pstream, int byteSize) { // We can't rely on overridden Write() // because delegation doesn't override virtual pointers // So, just re-implement UByte buff[0x4000]; int count = 0; int szRequest, szRead, szWritten; while(byteSize) { szRequest = (byteSize > int(sizeof(buff))) ? int(sizeof(buff)) : byteSize; szRead = pstream->Read(buff,szRequest); szWritten = 0; if (szRead > 0) szWritten = Write(buff,szRead); count +=szWritten; byteSize-=szWritten; if (szWritten < szRequest) break; } return count; } // Closing files bool BufferedFile::Close() { switch(BufferMode) { case WriteBuffer: FlushBuffer(); break; case ReadBuffer: // No need to seek back on close BufferMode = NoBuffer; break; default: break; } return pFile->Close(); } // ***** Global path helpers // Find trailing short filename in a path. const char* OVR_CDECL GetShortFilename(const char* purl) { UPInt len = OVR_strlen(purl); for (UPInt i=len; i>0; i--) if (purl[i]=='\\' || purl[i]=='/') return purl+i+1; return purl; } } // OVR \ No newline at end of file +/************************************************************************** + +Filename : OVR_File.cpp +Content : File wrapper class implementation (Win32) + +Created : April 5, 1999 +Authors : Michael Antonov + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +**************************************************************************/ + +#define GFILE_CXX + +// Standard C library (Captain Obvious guarantees!) +#include + +#include "OVR_File.h" + +namespace OVR { + +// Buffered file adds buffering to an existing file +// FILEBUFFER_SIZE defines the size of internal buffer, while +// FILEBUFFER_TOLERANCE controls the amount of data we'll effectively try to buffer +#define FILEBUFFER_SIZE (8192-8) +#define FILEBUFFER_TOLERANCE 4096 + +// ** Constructor/Destructor + +// Hidden constructor +// Not supposed to be used +BufferedFile::BufferedFile() : DelegatedFile(0) +{ + pBuffer = (UByte*)OVR_ALLOC(FILEBUFFER_SIZE); + BufferMode = NoBuffer; + FilePos = 0; + Pos = 0; + DataSize = 0; +} + +// Takes another file as source +BufferedFile::BufferedFile(File *pfile) : DelegatedFile(pfile) +{ + pBuffer = (UByte*)OVR_ALLOC(FILEBUFFER_SIZE); + BufferMode = NoBuffer; + FilePos = pfile->LTell(); + Pos = 0; + DataSize = 0; +} + + +// Destructor +BufferedFile::~BufferedFile() +{ + // Flush in case there's data + if (pFile) + FlushBuffer(); + // Get rid of buffer + if (pBuffer) + OVR_FREE(pBuffer); +} + +/* +bool BufferedFile::VCopy(const Object &source) +{ + if (!DelegatedFile::VCopy(source)) + return 0; + + // Data members + BufferedFile *psource = (BufferedFile*)&source; + + // Buffer & the mode it's in + pBuffer = psource->pBuffer; + BufferMode = psource->BufferMode; + Pos = psource->Pos; + DataSize = psource->DataSize; + return 1; +} +*/ + +// Initializes buffering to a certain mode +bool BufferedFile::SetBufferMode(BufferModeType mode) +{ + if (!pBuffer) + return false; + if (mode == BufferMode) + return true; + + FlushBuffer(); + + // Can't set write mode if we can't write + if ((mode==WriteBuffer) && (!pFile || !pFile->IsWritable()) ) + return 0; + + // And SetMode + BufferMode = mode; + Pos = 0; + DataSize = 0; + return 1; +} + +// Flushes buffer +void BufferedFile::FlushBuffer() +{ + switch(BufferMode) + { + case WriteBuffer: + // Write data in buffer + FilePos += pFile->Write(pBuffer,Pos); + Pos = 0; + break; + + case ReadBuffer: + // Seek back & reset buffer data + if ((DataSize-Pos)>0) + FilePos = pFile->LSeek(-(int)(DataSize-Pos), Seek_Cur); + DataSize = 0; + Pos = 0; + break; + default: + // not handled! + break; + } +} + +// Reloads data for ReadBuffer +void BufferedFile::LoadBuffer() +{ + if (BufferMode == ReadBuffer) + { + // We should only reload once all of pre-loaded buffer is consumed. + OVR_ASSERT(Pos == DataSize); + + // WARNING: Right now LoadBuffer() assumes the buffer's empty + int sz = pFile->Read(pBuffer,FILEBUFFER_SIZE); + DataSize = sz<0 ? 0 : (unsigned)sz; + Pos = 0; + FilePos += DataSize; + } +} + + +// ** Overridden functions + +// We override all the functions that can possibly +// require buffer mode switch, flush, or extra calculations + +// Tell() requires buffer adjustment +int BufferedFile::Tell() +{ + if (BufferMode == ReadBuffer) + return int (FilePos - DataSize + Pos); + + int pos = pFile->Tell(); + // Adjust position based on buffer mode & data + if (pos!=-1) + { + OVR_ASSERT(BufferMode != ReadBuffer); + if (BufferMode == WriteBuffer) + pos += Pos; + } + return pos; +} + +SInt64 BufferedFile::LTell() +{ + if (BufferMode == ReadBuffer) + return FilePos - DataSize + Pos; + + SInt64 pos = pFile->LTell(); + if (pos!=-1) + { + OVR_ASSERT(BufferMode != ReadBuffer); + if (BufferMode == WriteBuffer) + pos += Pos; + } + return pos; +} + +int BufferedFile::GetLength() +{ + int len = pFile->GetLength(); + // If writing through buffer, file length may actually be bigger + if ((len!=-1) && (BufferMode==WriteBuffer)) + { + int currPos = pFile->Tell() + Pos; + if (currPos>len) + len = currPos; + } + return len; +} +SInt64 BufferedFile::LGetLength() +{ + SInt64 len = pFile->LGetLength(); + // If writing through buffer, file length may actually be bigger + if ((len!=-1) && (BufferMode==WriteBuffer)) + { + SInt64 currPos = pFile->LTell() + Pos; + if (currPos>len) + len = currPos; + } + return len; +} + +/* +bool BufferedFile::Stat(FileStats *pfs) +{ + // Have to fix up length is stat + if (pFile->Stat(pfs)) + { + if (BufferMode==WriteBuffer) + { + SInt64 currPos = pFile->LTell() + Pos; + if (currPos > pfs->Size) + { + pfs->Size = currPos; + // ?? + pfs->Blocks = (pfs->Size+511) >> 9; + } + } + return 1; + } + return 0; +} +*/ + +int BufferedFile::Write(const UByte *psourceBuffer, int numBytes) +{ + if ( (BufferMode==WriteBuffer) || SetBufferMode(WriteBuffer)) + { + // If not data space in buffer, flush + if ((FILEBUFFER_SIZE-(int)Pos)FILEBUFFER_TOLERANCE) + { + int sz = pFile->Write(psourceBuffer,numBytes); + if (sz > 0) + FilePos += sz; + return sz; + } + } + + // Enough space in buffer.. so copy to it + memcpy(pBuffer+Pos, psourceBuffer, numBytes); + Pos += numBytes; + return numBytes; + } + int sz = pFile->Write(psourceBuffer,numBytes); + if (sz > 0) + FilePos += sz; + return sz; +} + +int BufferedFile::Read(UByte *pdestBuffer, int numBytes) +{ + if ( (BufferMode==ReadBuffer) || SetBufferMode(ReadBuffer)) + { + // Data in buffer... copy it + if ((int)(DataSize-Pos) >= numBytes) + { + memcpy(pdestBuffer, pBuffer+Pos, numBytes); + Pos += numBytes; + return numBytes; + } + + // Not enough data in buffer, copy buffer + int readBytes = DataSize-Pos; + memcpy(pdestBuffer, pBuffer+Pos, readBytes); + numBytes -= readBytes; + pdestBuffer += readBytes; + Pos = DataSize; + + // Don't reload buffer if more then tolerance + // (No major advantage, and we don't want to write a loop) + if (numBytes>FILEBUFFER_TOLERANCE) + { + numBytes = pFile->Read(pdestBuffer,numBytes); + if (numBytes > 0) + { + FilePos += numBytes; + Pos = DataSize = 0; + } + return readBytes + ((numBytes==-1) ? 0 : numBytes); + } + + // Reload the buffer + // WARNING: Right now LoadBuffer() assumes the buffer's empty + LoadBuffer(); + if ((int)(DataSize-Pos) < numBytes) + numBytes = (int)DataSize-Pos; + + memcpy(pdestBuffer, pBuffer+Pos, numBytes); + Pos += numBytes; + return numBytes + readBytes; + + /* + // Alternative Read implementation. The one above is probably better + // due to FILEBUFFER_TOLERANCE. + int total = 0; + + do { + int bufferBytes = (int)(DataSize-Pos); + int copyBytes = (bufferBytes > numBytes) ? numBytes : bufferBytes; + + memcpy(pdestBuffer, pBuffer+Pos, copyBytes); + numBytes -= copyBytes; + pdestBuffer += copyBytes; + Pos += copyBytes; + total += copyBytes; + + if (numBytes == 0) + break; + LoadBuffer(); + + } while (DataSize > 0); + + return total; + */ + } + int sz = pFile->Read(pdestBuffer,numBytes); + if (sz > 0) + FilePos += sz; + return sz; +} + + +int BufferedFile::SkipBytes(int numBytes) +{ + int skippedBytes = 0; + + // Special case for skipping a little data in read buffer + if (BufferMode==ReadBuffer) + { + skippedBytes = (((int)DataSize-(int)Pos) >= numBytes) ? numBytes : (DataSize-Pos); + Pos += skippedBytes; + numBytes -= skippedBytes; + } + + if (numBytes) + { + numBytes = pFile->SkipBytes(numBytes); + // Make sure we return the actual number skipped, or error + if (numBytes!=-1) + { + skippedBytes += numBytes; + FilePos += numBytes; + Pos = DataSize = 0; + } + else if (skippedBytes <= 0) + skippedBytes = -1; + } + return skippedBytes; +} + +int BufferedFile::BytesAvailable() +{ + int available = pFile->BytesAvailable(); + // Adjust available size based on buffers + switch(BufferMode) + { + case ReadBuffer: + available += DataSize-Pos; + break; + case WriteBuffer: + available -= Pos; + if (available<0) + available= 0; + break; + default: + break; + } + return available; +} + +bool BufferedFile::Flush() +{ + FlushBuffer(); + return pFile->Flush(); +} + +// Seeking could be optimized better.. +int BufferedFile::Seek(int offset, int origin) +{ + if (BufferMode == ReadBuffer) + { + if (origin == Seek_Cur) + { + // Seek can fall either before or after Pos in the buffer, + // but it must be within bounds. + if (((unsigned(offset) + Pos)) <= DataSize) + { + Pos += offset; + return int (FilePos - DataSize + Pos); + } + + // Lightweight buffer "Flush". We do this to avoid an extra seek + // back operation which would take place if we called FlushBuffer directly. + origin = Seek_Set; + OVR_ASSERT(((FilePos - DataSize + Pos) + (UInt64)offset) < ~(UInt64)0); + offset = (int)(FilePos - DataSize + Pos) + offset; + Pos = DataSize = 0; + } + else if (origin == Seek_Set) + { + if (((unsigned)offset - (FilePos-DataSize)) <= DataSize) + { + OVR_ASSERT((FilePos-DataSize) < ~(UInt64)0); + Pos = (unsigned)offset - (unsigned)(FilePos-DataSize); + return offset; + } + Pos = DataSize = 0; + } + else + { + FlushBuffer(); + } + } + else + { + FlushBuffer(); + } + + /* + // Old Seek Logic + if (origin == Seek_Cur && offset + Pos < DataSize) + { + //OVR_ASSERT((FilePos - DataSize) >= (FilePos - DataSize + Pos + offset)); + Pos += offset; + OVR_ASSERT(int (Pos) >= 0); + return int (FilePos - DataSize + Pos); + } + else if (origin == Seek_Set && unsigned(offset) >= FilePos - DataSize && unsigned(offset) < FilePos) + { + Pos = unsigned(offset - FilePos + DataSize); + OVR_ASSERT(int (Pos) >= 0); + return int (FilePos - DataSize + Pos); + } + + FlushBuffer(); + */ + + + FilePos = pFile->Seek(offset,origin); + return int (FilePos); +} + +SInt64 BufferedFile::LSeek(SInt64 offset, int origin) +{ + if (BufferMode == ReadBuffer) + { + if (origin == Seek_Cur) + { + // Seek can fall either before or after Pos in the buffer, + // but it must be within bounds. + if (((unsigned(offset) + Pos)) <= DataSize) + { + Pos += (unsigned)offset; + return SInt64(FilePos - DataSize + Pos); + } + + // Lightweight buffer "Flush". We do this to avoid an extra seek + // back operation which would take place if we called FlushBuffer directly. + origin = Seek_Set; + offset = (SInt64)(FilePos - DataSize + Pos) + offset; + Pos = DataSize = 0; + } + else if (origin == Seek_Set) + { + if (((UInt64)offset - (FilePos-DataSize)) <= DataSize) + { + Pos = (unsigned)((UInt64)offset - (FilePos-DataSize)); + return offset; + } + Pos = DataSize = 0; + } + else + { + FlushBuffer(); + } + } + else + { + FlushBuffer(); + } + +/* + OVR_ASSERT(BufferMode != NoBuffer); + + if (origin == Seek_Cur && offset + Pos < DataSize) + { + Pos += int (offset); + return FilePos - DataSize + Pos; + } + else if (origin == Seek_Set && offset >= SInt64(FilePos - DataSize) && offset < SInt64(FilePos)) + { + Pos = unsigned(offset - FilePos + DataSize); + return FilePos - DataSize + Pos; + } + + FlushBuffer(); + */ + + FilePos = pFile->LSeek(offset,origin); + return FilePos; +} + +int BufferedFile::CopyFromStream(File *pstream, int byteSize) +{ + // We can't rely on overridden Write() + // because delegation doesn't override virtual pointers + // So, just re-implement + UByte buff[0x4000]; + int count = 0; + int szRequest, szRead, szWritten; + + while(byteSize) + { + szRequest = (byteSize > int(sizeof(buff))) ? int(sizeof(buff)) : byteSize; + + szRead = pstream->Read(buff,szRequest); + szWritten = 0; + if (szRead > 0) + szWritten = Write(buff,szRead); + + count +=szWritten; + byteSize-=szWritten; + if (szWritten < szRequest) + break; + } + return count; +} + +// Closing files +bool BufferedFile::Close() +{ + switch(BufferMode) + { + case WriteBuffer: + FlushBuffer(); + break; + case ReadBuffer: + // No need to seek back on close + BufferMode = NoBuffer; + break; + default: + break; + } + return pFile->Close(); +} + + +// ***** Global path helpers + +// Find trailing short filename in a path. +const char* OVR_CDECL GetShortFilename(const char* purl) +{ + UPInt len = OVR_strlen(purl); + for (UPInt i=len; i>0; i--) + if (purl[i]=='\\' || purl[i]=='/') + return purl+i+1; + return purl; +} + +} // OVR + diff -r 59fc487ba58e -r b069a5c27388 libovr/Src/Kernel/OVR_File.h --- a/libovr/Src/Kernel/OVR_File.h Sat Sep 14 17:51:03 2013 +0300 +++ b/libovr/Src/Kernel/OVR_File.h Sun Sep 15 04:10:05 2013 +0300 @@ -1,1 +1,518 @@ -/************************************************************************************ PublicHeader: Kernel Filename : OVR_File.h Content : Header for all internal file management - functions and structures to be inherited by OS specific subclasses. Created : September 19, 2012 Notes : Notes : errno may not be preserved across use of BaseFile member functions : Directories cannot be deleted while files opened from them are in use (For the GetFullName function) Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #ifndef OVR_File_h #define OVR_File_h #include "OVR_RefCount.h" #include "OVR_Std.h" #include "OVR_Alg.h" #include #include "OVR_String.h" namespace OVR { // ***** Declared classes class FileConstants; class File; class DelegatedFile; class BufferedFile; // ***** Flags for File & Directory accesses class FileConstants { public: // *** File open flags enum OpenFlags { Open_Read = 1, Open_Write = 2, Open_ReadWrite = 3, // Opens file and truncates it to zero length // - file must have write permission // - when used with Create, it opens an existing // file and empties it or creates a new file Open_Truncate = 4, // Creates and opens new file // - does not erase contents if file already // exists unless combined with Truncate Open_Create = 8, // Returns an error value if the file already exists Open_CreateOnly = 24, // Open file with buffering Open_Buffered = 32 }; // *** File Mode flags enum Modes { Mode_Read = 0444, Mode_Write = 0222, Mode_Execute = 0111, Mode_ReadWrite = 0666 }; // *** Seek operations enum SeekOps { Seek_Set = 0, Seek_Cur = 1, Seek_End = 2 }; // *** Errors enum Errors { Error_FileNotFound = 0x1001, Error_Access = 0x1002, Error_IOError = 0x1003, Error_DiskFull = 0x1004 }; }; //----------------------------------------------------------------------------------- // ***** File Class // The pure virtual base random-access file // This is a base class to all files class File : public RefCountBase, public FileConstants { public: File() { } // ** Location Information // Returns a file name path relative to the 'reference' directory // This is often a path that was used to create a file // (this is not a global path, global path can be obtained with help of directory) virtual const char* GetFilePath() = 0; // ** File Information // Return 1 if file's usable (open) virtual bool IsValid() = 0; // Return 1 if file's writable, otherwise 0 virtual bool IsWritable() = 0; // Return position virtual int Tell() = 0; virtual SInt64 LTell() = 0; // File size virtual int GetLength() = 0; virtual SInt64 LGetLength() = 0; // Returns file stats // 0 for failure //virtual bool Stat(FileStats *pfs) = 0; // Return errno-based error code // Useful if any other function failed virtual int GetErrorCode() = 0; // ** Stream implementation & I/O // Blocking write, will write in the given number of bytes to the stream // Returns : -1 for error // Otherwise number of bytes read virtual int Write(const UByte *pbufer, int numBytes) = 0; // Blocking read, will read in the given number of bytes or less from the stream // Returns : -1 for error // Otherwise number of bytes read, // if 0 or < numBytes, no more bytes available; end of file or the other side of stream is closed virtual int Read(UByte *pbufer, int numBytes) = 0; // Skips (ignores) a given # of bytes // Same return values as Read virtual int SkipBytes(int numBytes) = 0; // Returns the number of bytes available to read from a stream without blocking // For a file, this should generally be number of bytes to the end virtual int BytesAvailable() = 0; // Causes any implementation's buffered data to be delivered to destination // Return 0 for error virtual bool Flush() = 0; // Need to provide a more optimized implementation that doe snot necessarily involve a lot of seeking inline bool IsEOF() { return !BytesAvailable(); } // Seeking // Returns new position, -1 for error virtual int Seek(int offset, int origin=Seek_Set) = 0; virtual SInt64 LSeek(SInt64 offset, int origin=Seek_Set) = 0; // Seek simplification int SeekToBegin() {return Seek(0); } int SeekToEnd() {return Seek(0,Seek_End); } int Skip(int numBytes) {return Seek(numBytes,Seek_Cur); } // Appends other file data from a stream // Return -1 for error, else # of bytes written virtual int CopyFromStream(File *pstream, int byteSize) = 0; // Closes the file // After close, file cannot be accessed virtual bool Close() = 0; // ***** Inlines for convenient primitive type serialization // Read/Write helpers private: UInt64 PRead64() { UInt64 v = 0; Read((UByte*)&v, 8); return v; } UInt32 PRead32() { UInt32 v = 0; Read((UByte*)&v, 4); return v; } UInt16 PRead16() { UInt16 v = 0; Read((UByte*)&v, 2); return v; } UByte PRead8() { UByte v = 0; Read((UByte*)&v, 1); return v; } void PWrite64(UInt64 v) { Write((UByte*)&v, 8); } void PWrite32(UInt32 v) { Write((UByte*)&v, 4); } void PWrite16(UInt16 v) { Write((UByte*)&v, 2); } void PWrite8(UByte v) { Write((UByte*)&v, 1); } public: // Writing primitive types - Little Endian inline void WriteUByte(UByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToLE(v)); } inline void WriteSByte(SByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToLE(v)); } inline void WriteUInt8(UByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToLE(v)); } inline void WriteSInt8(SByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToLE(v)); } inline void WriteUInt16(UInt16 v) { PWrite16((UInt16)Alg::ByteUtil::SystemToLE(v)); } inline void WriteSInt16(SInt16 v) { PWrite16((UInt16)Alg::ByteUtil::SystemToLE(v)); } inline void WriteUInt32(UInt32 v) { PWrite32((UInt32)Alg::ByteUtil::SystemToLE(v)); } inline void WriteSInt32(SInt32 v) { PWrite32((UInt32)Alg::ByteUtil::SystemToLE(v)); } inline void WriteUInt64(UInt64 v) { PWrite64((UInt64)Alg::ByteUtil::SystemToLE(v)); } inline void WriteSInt64(SInt64 v) { PWrite64((UInt64)Alg::ByteUtil::SystemToLE(v)); } inline void WriteFloat(float v) { v = Alg::ByteUtil::SystemToLE(v); Write((UByte*)&v, 4); } inline void WriteDouble(double v) { v = Alg::ByteUtil::SystemToLE(v); Write((UByte*)&v, 8); } // Writing primitive types - Big Endian inline void WriteUByteBE(UByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToBE(v)); } inline void WriteSByteBE(SByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToBE(v)); } inline void WriteUInt8BE(UInt16 v) { PWrite8((UByte)Alg::ByteUtil::SystemToBE(v)); } inline void WriteSInt8BE(SInt16 v) { PWrite8((UByte)Alg::ByteUtil::SystemToBE(v)); } inline void WriteUInt16BE(UInt16 v) { PWrite16((UInt16)Alg::ByteUtil::SystemToBE(v)); } inline void WriteSInt16BE(UInt16 v) { PWrite16((UInt16)Alg::ByteUtil::SystemToBE(v)); } inline void WriteUInt32BE(UInt32 v) { PWrite32((UInt32)Alg::ByteUtil::SystemToBE(v)); } inline void WriteSInt32BE(UInt32 v) { PWrite32((UInt32)Alg::ByteUtil::SystemToBE(v)); } inline void WriteUInt64BE(UInt64 v) { PWrite64((UInt64)Alg::ByteUtil::SystemToBE(v)); } inline void WriteSInt64BE(UInt64 v) { PWrite64((UInt64)Alg::ByteUtil::SystemToBE(v)); } inline void WriteFloatBE(float v) { v = Alg::ByteUtil::SystemToBE(v); Write((UByte*)&v, 4); } inline void WriteDoubleBE(double v) { v = Alg::ByteUtil::SystemToBE(v); Write((UByte*)&v, 8); } // Reading primitive types - Little Endian inline UByte ReadUByte() { return (UByte)Alg::ByteUtil::LEToSystem(PRead8()); } inline SByte ReadSByte() { return (SByte)Alg::ByteUtil::LEToSystem(PRead8()); } inline UByte ReadUInt8() { return (UByte)Alg::ByteUtil::LEToSystem(PRead8()); } inline SByte ReadSInt8() { return (SByte)Alg::ByteUtil::LEToSystem(PRead8()); } inline UInt16 ReadUInt16() { return (UInt16)Alg::ByteUtil::LEToSystem(PRead16()); } inline SInt16 ReadSInt16() { return (SInt16)Alg::ByteUtil::LEToSystem(PRead16()); } inline UInt32 ReadUInt32() { return (UInt32)Alg::ByteUtil::LEToSystem(PRead32()); } inline SInt32 ReadSInt32() { return (SInt32)Alg::ByteUtil::LEToSystem(PRead32()); } inline UInt64 ReadUInt64() { return (UInt64)Alg::ByteUtil::LEToSystem(PRead64()); } inline SInt64 ReadSInt64() { return (SInt64)Alg::ByteUtil::LEToSystem(PRead64()); } inline float ReadFloat() { float v = 0.0f; Read((UByte*)&v, 4); return Alg::ByteUtil::LEToSystem(v); } inline double ReadDouble() { double v = 0.0; Read((UByte*)&v, 8); return Alg::ByteUtil::LEToSystem(v); } // Reading primitive types - Big Endian inline UByte ReadUByteBE() { return (UByte)Alg::ByteUtil::BEToSystem(PRead8()); } inline SByte ReadSByteBE() { return (SByte)Alg::ByteUtil::BEToSystem(PRead8()); } inline UByte ReadUInt8BE() { return (UByte)Alg::ByteUtil::BEToSystem(PRead8()); } inline SByte ReadSInt8BE() { return (SByte)Alg::ByteUtil::BEToSystem(PRead8()); } inline UInt16 ReadUInt16BE() { return (UInt16)Alg::ByteUtil::BEToSystem(PRead16()); } inline SInt16 ReadSInt16BE() { return (SInt16)Alg::ByteUtil::BEToSystem(PRead16()); } inline UInt32 ReadUInt32BE() { return (UInt32)Alg::ByteUtil::BEToSystem(PRead32()); } inline SInt32 ReadSInt32BE() { return (SInt32)Alg::ByteUtil::BEToSystem(PRead32()); } inline UInt64 ReadUInt64BE() { return (UInt64)Alg::ByteUtil::BEToSystem(PRead64()); } inline SInt64 ReadSInt64BE() { return (SInt64)Alg::ByteUtil::BEToSystem(PRead64()); } inline float ReadFloatBE() { float v = 0.0f; Read((UByte*)&v, 4); return Alg::ByteUtil::BEToSystem(v); } inline double ReadDoubleBE() { double v = 0.0; Read((UByte*)&v, 8); return Alg::ByteUtil::BEToSystem(v); } }; // *** Delegated File class DelegatedFile : public File { protected: // Delegating file pointer Ptr pFile; // Hidden default constructor DelegatedFile() : pFile(0) { } DelegatedFile(const DelegatedFile &source) : File() { OVR_UNUSED(source); } public: // Constructors DelegatedFile(File *pfile) : pFile(pfile) { } // ** Location Information virtual const char* GetFilePath() { return pFile->GetFilePath(); } // ** File Information virtual bool IsValid() { return pFile && pFile->IsValid(); } virtual bool IsWritable() { return pFile->IsWritable(); } // virtual bool IsRecoverable() { return pFile->IsRecoverable(); } virtual int Tell() { return pFile->Tell(); } virtual SInt64 LTell() { return pFile->LTell(); } virtual int GetLength() { return pFile->GetLength(); } virtual SInt64 LGetLength() { return pFile->LGetLength(); } //virtual bool Stat(FileStats *pfs) { return pFile->Stat(pfs); } virtual int GetErrorCode() { return pFile->GetErrorCode(); } // ** Stream implementation & I/O virtual int Write(const UByte *pbuffer, int numBytes) { return pFile->Write(pbuffer,numBytes); } virtual int Read(UByte *pbuffer, int numBytes) { return pFile->Read(pbuffer,numBytes); } virtual int SkipBytes(int numBytes) { return pFile->SkipBytes(numBytes); } virtual int BytesAvailable() { return pFile->BytesAvailable(); } virtual bool Flush() { return pFile->Flush(); } // Seeking virtual int Seek(int offset, int origin=Seek_Set) { return pFile->Seek(offset,origin); } virtual SInt64 LSeek(SInt64 offset, int origin=Seek_Set) { return pFile->LSeek(offset,origin); } virtual int CopyFromStream(File *pstream, int byteSize) { return pFile->CopyFromStream(pstream,byteSize); } // Closing the file virtual bool Close() { return pFile->Close(); } }; //----------------------------------------------------------------------------------- // ***** Buffered File // This file class adds buffering to an existing file // Buffered file never fails by itself; if there's not // enough memory for buffer, no buffer's used class BufferedFile : public DelegatedFile { protected: enum BufferModeType { NoBuffer, ReadBuffer, WriteBuffer }; // Buffer & the mode it's in UByte* pBuffer; BufferModeType BufferMode; // Position in buffer unsigned Pos; // Data in buffer if reading unsigned DataSize; // Underlying file position UInt64 FilePos; // Initializes buffering to a certain mode bool SetBufferMode(BufferModeType mode); // Flushes buffer // WriteBuffer - write data to disk, ReadBuffer - reset buffer & fix file position void FlushBuffer(); // Loads data into ReadBuffer // WARNING: Right now LoadBuffer() assumes the buffer's empty void LoadBuffer(); // Hidden constructor BufferedFile(); inline BufferedFile(const BufferedFile &source) : DelegatedFile() { OVR_UNUSED(source); } public: // Constructor // - takes another file as source BufferedFile(File *pfile); ~BufferedFile(); // ** Overridden functions // We override all the functions that can possibly // require buffer mode switch, flush, or extra calculations virtual int Tell(); virtual SInt64 LTell(); virtual int GetLength(); virtual SInt64 LGetLength(); // virtual bool Stat(GFileStats *pfs); virtual int Write(const UByte *pbufer, int numBytes); virtual int Read(UByte *pbufer, int numBytes); virtual int SkipBytes(int numBytes); virtual int BytesAvailable(); virtual bool Flush(); virtual int Seek(int offset, int origin=Seek_Set); virtual SInt64 LSeek(SInt64 offset, int origin=Seek_Set); virtual int CopyFromStream(File *pstream, int byteSize); virtual bool Close(); }; //----------------------------------------------------------------------------------- // ***** Memory File class MemoryFile : public File { public: const char* GetFilePath() { return FilePath.ToCStr(); } bool IsValid() { return Valid; } bool IsWritable() { return false; } bool Flush() { return true; } int GetErrorCode() { return 0; } int Tell() { return FileIndex; } SInt64 LTell() { return (SInt64) FileIndex; } int GetLength() { return FileSize; } SInt64 LGetLength() { return (SInt64) FileSize; } bool Close() { Valid = false; return false; } int CopyFromStream(File *pstream, int byteSize) { OVR_UNUSED2(pstream, byteSize); return 0; } int Write(const UByte *pbuffer, int numBytes) { OVR_UNUSED2(pbuffer, numBytes); return 0; } int Read(UByte *pbufer, int numBytes) { if (FileIndex + numBytes > FileSize) { numBytes = FileSize - FileIndex; } if (numBytes > 0) { ::memcpy (pbufer, &FileData [FileIndex], numBytes); FileIndex += numBytes; } return numBytes; } int SkipBytes(int numBytes) { if (FileIndex + numBytes > FileSize) { numBytes = FileSize - FileIndex; } FileIndex += numBytes; return numBytes; } int BytesAvailable() { return (FileSize - FileIndex); } int Seek(int offset, int origin = Seek_Set) { switch (origin) { case Seek_Set : FileIndex = offset; break; case Seek_Cur : FileIndex += offset; break; case Seek_End : FileIndex = FileSize - offset; break; } return FileIndex; } SInt64 LSeek(SInt64 offset, int origin = Seek_Set) { return (SInt64) Seek((int) offset, origin); } public: MemoryFile (const String& fileName, const UByte *pBuffer, int buffSize) : FilePath(fileName) { FileData = pBuffer; FileSize = buffSize; FileIndex = 0; Valid = (!fileName.IsEmpty() && pBuffer && buffSize > 0) ? true : false; } // pfileName should be encoded as UTF-8 to support international file names. MemoryFile (const char* pfileName, const UByte *pBuffer, int buffSize) : FilePath(pfileName) { FileData = pBuffer; FileSize = buffSize; FileIndex = 0; Valid = (pfileName && pBuffer && buffSize > 0) ? true : false; } private: String FilePath; const UByte *FileData; int FileSize; int FileIndex; bool Valid; }; // ***** Global path helpers // Find trailing short filename in a path. const char* OVR_CDECL GetShortFilename(const char* purl); } // OVR #endif \ No newline at end of file +/************************************************************************************ + +PublicHeader: Kernel +Filename : OVR_File.h +Content : Header for all internal file management - functions and structures + to be inherited by OS specific subclasses. +Created : September 19, 2012 +Notes : + +Notes : errno may not be preserved across use of BaseFile member functions + : Directories cannot be deleted while files opened from them are in use + (For the GetFullName function) + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#ifndef OVR_File_h +#define OVR_File_h + +#include "OVR_RefCount.h" +#include "OVR_Std.h" +#include "OVR_Alg.h" + +#include +#include "OVR_String.h" + +namespace OVR { + +// ***** Declared classes +class FileConstants; +class File; +class DelegatedFile; +class BufferedFile; + + +// ***** Flags for File & Directory accesses + +class FileConstants +{ +public: + + // *** File open flags + enum OpenFlags + { + Open_Read = 1, + Open_Write = 2, + Open_ReadWrite = 3, + + // Opens file and truncates it to zero length + // - file must have write permission + // - when used with Create, it opens an existing + // file and empties it or creates a new file + Open_Truncate = 4, + + // Creates and opens new file + // - does not erase contents if file already + // exists unless combined with Truncate + Open_Create = 8, + + // Returns an error value if the file already exists + Open_CreateOnly = 24, + + // Open file with buffering + Open_Buffered = 32 + }; + + // *** File Mode flags + enum Modes + { + Mode_Read = 0444, + Mode_Write = 0222, + Mode_Execute = 0111, + + Mode_ReadWrite = 0666 + }; + + // *** Seek operations + enum SeekOps + { + Seek_Set = 0, + Seek_Cur = 1, + Seek_End = 2 + }; + + // *** Errors + enum Errors + { + Error_FileNotFound = 0x1001, + Error_Access = 0x1002, + Error_IOError = 0x1003, + Error_DiskFull = 0x1004 + }; +}; + + +//----------------------------------------------------------------------------------- +// ***** File Class + +// The pure virtual base random-access file +// This is a base class to all files + +class File : public RefCountBase, public FileConstants +{ +public: + File() { } + // ** Location Information + + // Returns a file name path relative to the 'reference' directory + // This is often a path that was used to create a file + // (this is not a global path, global path can be obtained with help of directory) + virtual const char* GetFilePath() = 0; + + + // ** File Information + + // Return 1 if file's usable (open) + virtual bool IsValid() = 0; + // Return 1 if file's writable, otherwise 0 + virtual bool IsWritable() = 0; + + // Return position + virtual int Tell() = 0; + virtual SInt64 LTell() = 0; + + // File size + virtual int GetLength() = 0; + virtual SInt64 LGetLength() = 0; + + // Returns file stats + // 0 for failure + //virtual bool Stat(FileStats *pfs) = 0; + + // Return errno-based error code + // Useful if any other function failed + virtual int GetErrorCode() = 0; + + + // ** Stream implementation & I/O + + // Blocking write, will write in the given number of bytes to the stream + // Returns : -1 for error + // Otherwise number of bytes read + virtual int Write(const UByte *pbufer, int numBytes) = 0; + // Blocking read, will read in the given number of bytes or less from the stream + // Returns : -1 for error + // Otherwise number of bytes read, + // if 0 or < numBytes, no more bytes available; end of file or the other side of stream is closed + virtual int Read(UByte *pbufer, int numBytes) = 0; + + // Skips (ignores) a given # of bytes + // Same return values as Read + virtual int SkipBytes(int numBytes) = 0; + + // Returns the number of bytes available to read from a stream without blocking + // For a file, this should generally be number of bytes to the end + virtual int BytesAvailable() = 0; + + // Causes any implementation's buffered data to be delivered to destination + // Return 0 for error + virtual bool Flush() = 0; + + + // Need to provide a more optimized implementation that doe snot necessarily involve a lot of seeking + inline bool IsEOF() { return !BytesAvailable(); } + + + // Seeking + // Returns new position, -1 for error + virtual int Seek(int offset, int origin=Seek_Set) = 0; + virtual SInt64 LSeek(SInt64 offset, int origin=Seek_Set) = 0; + // Seek simplification + int SeekToBegin() {return Seek(0); } + int SeekToEnd() {return Seek(0,Seek_End); } + int Skip(int numBytes) {return Seek(numBytes,Seek_Cur); } + + + // Appends other file data from a stream + // Return -1 for error, else # of bytes written + virtual int CopyFromStream(File *pstream, int byteSize) = 0; + + // Closes the file + // After close, file cannot be accessed + virtual bool Close() = 0; + + + // ***** Inlines for convenient primitive type serialization + + // Read/Write helpers +private: + UInt64 PRead64() { UInt64 v = 0; Read((UByte*)&v, 8); return v; } + UInt32 PRead32() { UInt32 v = 0; Read((UByte*)&v, 4); return v; } + UInt16 PRead16() { UInt16 v = 0; Read((UByte*)&v, 2); return v; } + UByte PRead8() { UByte v = 0; Read((UByte*)&v, 1); return v; } + void PWrite64(UInt64 v) { Write((UByte*)&v, 8); } + void PWrite32(UInt32 v) { Write((UByte*)&v, 4); } + void PWrite16(UInt16 v) { Write((UByte*)&v, 2); } + void PWrite8(UByte v) { Write((UByte*)&v, 1); } + +public: + + // Writing primitive types - Little Endian + inline void WriteUByte(UByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToLE(v)); } + inline void WriteSByte(SByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToLE(v)); } + inline void WriteUInt8(UByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToLE(v)); } + inline void WriteSInt8(SByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToLE(v)); } + inline void WriteUInt16(UInt16 v) { PWrite16((UInt16)Alg::ByteUtil::SystemToLE(v)); } + inline void WriteSInt16(SInt16 v) { PWrite16((UInt16)Alg::ByteUtil::SystemToLE(v)); } + inline void WriteUInt32(UInt32 v) { PWrite32((UInt32)Alg::ByteUtil::SystemToLE(v)); } + inline void WriteSInt32(SInt32 v) { PWrite32((UInt32)Alg::ByteUtil::SystemToLE(v)); } + inline void WriteUInt64(UInt64 v) { PWrite64((UInt64)Alg::ByteUtil::SystemToLE(v)); } + inline void WriteSInt64(SInt64 v) { PWrite64((UInt64)Alg::ByteUtil::SystemToLE(v)); } + inline void WriteFloat(float v) { v = Alg::ByteUtil::SystemToLE(v); Write((UByte*)&v, 4); } + inline void WriteDouble(double v) { v = Alg::ByteUtil::SystemToLE(v); Write((UByte*)&v, 8); } + // Writing primitive types - Big Endian + inline void WriteUByteBE(UByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToBE(v)); } + inline void WriteSByteBE(SByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToBE(v)); } + inline void WriteUInt8BE(UInt16 v) { PWrite8((UByte)Alg::ByteUtil::SystemToBE(v)); } + inline void WriteSInt8BE(SInt16 v) { PWrite8((UByte)Alg::ByteUtil::SystemToBE(v)); } + inline void WriteUInt16BE(UInt16 v) { PWrite16((UInt16)Alg::ByteUtil::SystemToBE(v)); } + inline void WriteSInt16BE(UInt16 v) { PWrite16((UInt16)Alg::ByteUtil::SystemToBE(v)); } + inline void WriteUInt32BE(UInt32 v) { PWrite32((UInt32)Alg::ByteUtil::SystemToBE(v)); } + inline void WriteSInt32BE(UInt32 v) { PWrite32((UInt32)Alg::ByteUtil::SystemToBE(v)); } + inline void WriteUInt64BE(UInt64 v) { PWrite64((UInt64)Alg::ByteUtil::SystemToBE(v)); } + inline void WriteSInt64BE(UInt64 v) { PWrite64((UInt64)Alg::ByteUtil::SystemToBE(v)); } + inline void WriteFloatBE(float v) { v = Alg::ByteUtil::SystemToBE(v); Write((UByte*)&v, 4); } + inline void WriteDoubleBE(double v) { v = Alg::ByteUtil::SystemToBE(v); Write((UByte*)&v, 8); } + + // Reading primitive types - Little Endian + inline UByte ReadUByte() { return (UByte)Alg::ByteUtil::LEToSystem(PRead8()); } + inline SByte ReadSByte() { return (SByte)Alg::ByteUtil::LEToSystem(PRead8()); } + inline UByte ReadUInt8() { return (UByte)Alg::ByteUtil::LEToSystem(PRead8()); } + inline SByte ReadSInt8() { return (SByte)Alg::ByteUtil::LEToSystem(PRead8()); } + inline UInt16 ReadUInt16() { return (UInt16)Alg::ByteUtil::LEToSystem(PRead16()); } + inline SInt16 ReadSInt16() { return (SInt16)Alg::ByteUtil::LEToSystem(PRead16()); } + inline UInt32 ReadUInt32() { return (UInt32)Alg::ByteUtil::LEToSystem(PRead32()); } + inline SInt32 ReadSInt32() { return (SInt32)Alg::ByteUtil::LEToSystem(PRead32()); } + inline UInt64 ReadUInt64() { return (UInt64)Alg::ByteUtil::LEToSystem(PRead64()); } + inline SInt64 ReadSInt64() { return (SInt64)Alg::ByteUtil::LEToSystem(PRead64()); } + inline float ReadFloat() { float v = 0.0f; Read((UByte*)&v, 4); return Alg::ByteUtil::LEToSystem(v); } + inline double ReadDouble() { double v = 0.0; Read((UByte*)&v, 8); return Alg::ByteUtil::LEToSystem(v); } + // Reading primitive types - Big Endian + inline UByte ReadUByteBE() { return (UByte)Alg::ByteUtil::BEToSystem(PRead8()); } + inline SByte ReadSByteBE() { return (SByte)Alg::ByteUtil::BEToSystem(PRead8()); } + inline UByte ReadUInt8BE() { return (UByte)Alg::ByteUtil::BEToSystem(PRead8()); } + inline SByte ReadSInt8BE() { return (SByte)Alg::ByteUtil::BEToSystem(PRead8()); } + inline UInt16 ReadUInt16BE() { return (UInt16)Alg::ByteUtil::BEToSystem(PRead16()); } + inline SInt16 ReadSInt16BE() { return (SInt16)Alg::ByteUtil::BEToSystem(PRead16()); } + inline UInt32 ReadUInt32BE() { return (UInt32)Alg::ByteUtil::BEToSystem(PRead32()); } + inline SInt32 ReadSInt32BE() { return (SInt32)Alg::ByteUtil::BEToSystem(PRead32()); } + inline UInt64 ReadUInt64BE() { return (UInt64)Alg::ByteUtil::BEToSystem(PRead64()); } + inline SInt64 ReadSInt64BE() { return (SInt64)Alg::ByteUtil::BEToSystem(PRead64()); } + inline float ReadFloatBE() { float v = 0.0f; Read((UByte*)&v, 4); return Alg::ByteUtil::BEToSystem(v); } + inline double ReadDoubleBE() { double v = 0.0; Read((UByte*)&v, 8); return Alg::ByteUtil::BEToSystem(v); } +}; + + +// *** Delegated File + +class DelegatedFile : public File +{ +protected: + // Delegating file pointer + Ptr pFile; + + // Hidden default constructor + DelegatedFile() : pFile(0) { } + DelegatedFile(const DelegatedFile &source) : File() { OVR_UNUSED(source); } +public: + // Constructors + DelegatedFile(File *pfile) : pFile(pfile) { } + + // ** Location Information + virtual const char* GetFilePath() { return pFile->GetFilePath(); } + + // ** File Information + virtual bool IsValid() { return pFile && pFile->IsValid(); } + virtual bool IsWritable() { return pFile->IsWritable(); } +// virtual bool IsRecoverable() { return pFile->IsRecoverable(); } + + virtual int Tell() { return pFile->Tell(); } + virtual SInt64 LTell() { return pFile->LTell(); } + + virtual int GetLength() { return pFile->GetLength(); } + virtual SInt64 LGetLength() { return pFile->LGetLength(); } + + //virtual bool Stat(FileStats *pfs) { return pFile->Stat(pfs); } + + virtual int GetErrorCode() { return pFile->GetErrorCode(); } + + // ** Stream implementation & I/O + virtual int Write(const UByte *pbuffer, int numBytes) { return pFile->Write(pbuffer,numBytes); } + virtual int Read(UByte *pbuffer, int numBytes) { return pFile->Read(pbuffer,numBytes); } + + virtual int SkipBytes(int numBytes) { return pFile->SkipBytes(numBytes); } + + virtual int BytesAvailable() { return pFile->BytesAvailable(); } + + virtual bool Flush() { return pFile->Flush(); } + + // Seeking + virtual int Seek(int offset, int origin=Seek_Set) { return pFile->Seek(offset,origin); } + virtual SInt64 LSeek(SInt64 offset, int origin=Seek_Set) { return pFile->LSeek(offset,origin); } + + virtual int CopyFromStream(File *pstream, int byteSize) { return pFile->CopyFromStream(pstream,byteSize); } + + // Closing the file + virtual bool Close() { return pFile->Close(); } +}; + + +//----------------------------------------------------------------------------------- +// ***** Buffered File + +// This file class adds buffering to an existing file +// Buffered file never fails by itself; if there's not +// enough memory for buffer, no buffer's used + +class BufferedFile : public DelegatedFile +{ +protected: + enum BufferModeType + { + NoBuffer, + ReadBuffer, + WriteBuffer + }; + + // Buffer & the mode it's in + UByte* pBuffer; + BufferModeType BufferMode; + // Position in buffer + unsigned Pos; + // Data in buffer if reading + unsigned DataSize; + // Underlying file position + UInt64 FilePos; + + // Initializes buffering to a certain mode + bool SetBufferMode(BufferModeType mode); + // Flushes buffer + // WriteBuffer - write data to disk, ReadBuffer - reset buffer & fix file position + void FlushBuffer(); + // Loads data into ReadBuffer + // WARNING: Right now LoadBuffer() assumes the buffer's empty + void LoadBuffer(); + + // Hidden constructor + BufferedFile(); + inline BufferedFile(const BufferedFile &source) : DelegatedFile() { OVR_UNUSED(source); } +public: + + // Constructor + // - takes another file as source + BufferedFile(File *pfile); + ~BufferedFile(); + + + // ** Overridden functions + + // We override all the functions that can possibly + // require buffer mode switch, flush, or extra calculations + virtual int Tell(); + virtual SInt64 LTell(); + + virtual int GetLength(); + virtual SInt64 LGetLength(); + +// virtual bool Stat(GFileStats *pfs); + + virtual int Write(const UByte *pbufer, int numBytes); + virtual int Read(UByte *pbufer, int numBytes); + + virtual int SkipBytes(int numBytes); + + virtual int BytesAvailable(); + + virtual bool Flush(); + + virtual int Seek(int offset, int origin=Seek_Set); + virtual SInt64 LSeek(SInt64 offset, int origin=Seek_Set); + + virtual int CopyFromStream(File *pstream, int byteSize); + + virtual bool Close(); +}; + + +//----------------------------------------------------------------------------------- +// ***** Memory File + +class MemoryFile : public File +{ +public: + + const char* GetFilePath() { return FilePath.ToCStr(); } + + bool IsValid() { return Valid; } + bool IsWritable() { return false; } + + bool Flush() { return true; } + int GetErrorCode() { return 0; } + + int Tell() { return FileIndex; } + SInt64 LTell() { return (SInt64) FileIndex; } + + int GetLength() { return FileSize; } + SInt64 LGetLength() { return (SInt64) FileSize; } + + bool Close() + { + Valid = false; + return false; + } + + int CopyFromStream(File *pstream, int byteSize) + { OVR_UNUSED2(pstream, byteSize); + return 0; + } + + int Write(const UByte *pbuffer, int numBytes) + { OVR_UNUSED2(pbuffer, numBytes); + return 0; + } + + int Read(UByte *pbufer, int numBytes) + { + if (FileIndex + numBytes > FileSize) + { + numBytes = FileSize - FileIndex; + } + + if (numBytes > 0) + { + ::memcpy (pbufer, &FileData [FileIndex], numBytes); + + FileIndex += numBytes; + } + + return numBytes; + } + + int SkipBytes(int numBytes) + { + if (FileIndex + numBytes > FileSize) + { + numBytes = FileSize - FileIndex; + } + + FileIndex += numBytes; + + return numBytes; + } + + int BytesAvailable() + { + return (FileSize - FileIndex); + } + + int Seek(int offset, int origin = Seek_Set) + { + switch (origin) + { + case Seek_Set : FileIndex = offset; break; + case Seek_Cur : FileIndex += offset; break; + case Seek_End : FileIndex = FileSize - offset; break; + } + + return FileIndex; + } + + SInt64 LSeek(SInt64 offset, int origin = Seek_Set) + { + return (SInt64) Seek((int) offset, origin); + } + +public: + + MemoryFile (const String& fileName, const UByte *pBuffer, int buffSize) + : FilePath(fileName) + { + FileData = pBuffer; + FileSize = buffSize; + FileIndex = 0; + Valid = (!fileName.IsEmpty() && pBuffer && buffSize > 0) ? true : false; + } + + // pfileName should be encoded as UTF-8 to support international file names. + MemoryFile (const char* pfileName, const UByte *pBuffer, int buffSize) + : FilePath(pfileName) + { + FileData = pBuffer; + FileSize = buffSize; + FileIndex = 0; + Valid = (pfileName && pBuffer && buffSize > 0) ? true : false; + } +private: + + String FilePath; + const UByte *FileData; + int FileSize; + int FileIndex; + bool Valid; +}; + + +// ***** Global path helpers + +// Find trailing short filename in a path. +const char* OVR_CDECL GetShortFilename(const char* purl); + +} // OVR + +#endif diff -r 59fc487ba58e -r b069a5c27388 libovr/Src/Kernel/OVR_FileFILE.cpp --- a/libovr/Src/Kernel/OVR_FileFILE.cpp Sat Sep 14 17:51:03 2013 +0300 +++ b/libovr/Src/Kernel/OVR_FileFILE.cpp Sun Sep 15 04:10:05 2013 +0300 @@ -1,1 +1,583 @@ -/************************************************************************** Filename : OVR_FileFILE.cpp Content : File wrapper class implementation (Win32) Created : April 5, 1999 Authors : Michael Antonov Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. **************************************************************************/ #define GFILE_CXX #include "OVR_Types.h" #include "OVR_Log.h" // Standard C library (Captain Obvious guarantees!) #include #ifndef OVR_OS_WINCE #include #endif #include "OVR_SysFile.h" #ifndef OVR_OS_WINCE #include #endif namespace OVR { // ***** File interface // ***** FILEFile - C streams file static int SFerror () { if (errno == ENOENT) return FileConstants::Error_FileNotFound; else if (errno == EACCES || errno == EPERM) return FileConstants::Error_Access; else if (errno == ENOSPC) return FileConstants::Error_DiskFull; else return FileConstants::Error_IOError; }; #ifdef OVR_OS_WIN32 #include "windows.h" // A simple helper class to disable/enable system error mode, if necessary // Disabling happens conditionally only if a drive name is involved class SysErrorModeDisabler { BOOL Disabled; UINT OldMode; public: SysErrorModeDisabler(const char* pfileName) { if (pfileName && (pfileName[0]!=0) && pfileName[1]==':') { Disabled = 1; OldMode = ::SetErrorMode(SEM_FAILCRITICALERRORS); } else Disabled = 0; } ~SysErrorModeDisabler() { if (Disabled) ::SetErrorMode(OldMode); } }; #else class SysErrorModeDisabler { public: SysErrorModeDisabler(const char* pfileName) { } }; #endif // OVR_OS_WIN32 // This macro enables verification of I/O results after seeks against a pre-loaded // full file buffer copy. This is generally not necessary, but can been used to debug // memory corruptions; we've seen this fail due to EAX2/DirectSound corrupting memory // under FMOD with XP64 (32-bit) and Realtek HA Audio driver. //#define GFILE_VERIFY_SEEK_ERRORS // This is the simplest possible file implementation, it wraps around the descriptor // This file is delegated to by SysFile. class FILEFile : public File { protected: // Allocated filename String FileName; // File handle & open mode bool Opened; FILE* fs; int OpenFlags; // Error code for last request int ErrorCode; int LastOp; #ifdef OVR_FILE_VERIFY_SEEK_ERRORS UByte* pFileTestBuffer; unsigned FileTestLength; unsigned TestPos; // File pointer position during tests. #endif public: FILEFile() { Opened = 0; FileName = ""; #ifdef OVR_FILE_VERIFY_SEEK_ERRORS pFileTestBuffer =0; FileTestLength =0; TestPos =0; #endif } // Initialize file by opening it FILEFile(const String& fileName, int flags, int Mode); // The 'pfileName' should be encoded as UTF-8 to support international file names. FILEFile(const char* pfileName, int flags, int Mode); ~FILEFile() { if (Opened) Close(); } virtual const char* GetFilePath(); // ** File Information virtual bool IsValid(); virtual bool IsWritable(); // Return position / file size virtual int Tell(); virtual SInt64 LTell(); virtual int GetLength(); virtual SInt64 LGetLength(); // virtual bool Stat(FileStats *pfs); virtual int GetErrorCode(); // ** Stream implementation & I/O virtual int Write(const UByte *pbuffer, int numBytes); virtual int Read(UByte *pbuffer, int numBytes); virtual int SkipBytes(int numBytes); virtual int BytesAvailable(); virtual bool Flush(); virtual int Seek(int offset, int origin); virtual SInt64 LSeek(SInt64 offset, int origin); virtual int CopyFromStream(File *pStream, int byteSize); virtual bool Close(); private: void init(); }; // Initialize file by opening it FILEFile::FILEFile(const String& fileName, int flags, int mode) : FileName(fileName), OpenFlags(flags) { OVR_UNUSED(mode); init(); } // The 'pfileName' should be encoded as UTF-8 to support international file names. FILEFile::FILEFile(const char* pfileName, int flags, int mode) : FileName(pfileName), OpenFlags(flags) { OVR_UNUSED(mode); init(); } void FILEFile::init() { // Open mode for file's open const char *omode = "rb"; if (OpenFlags & Open_Truncate) { if (OpenFlags & Open_Read) omode = "w+b"; else omode = "wb"; } else if (OpenFlags & Open_Create) { if (OpenFlags & Open_Read) omode = "a+b"; else omode = "ab"; } else if (OpenFlags & Open_Write) omode = "r+b"; #ifdef OVR_OS_WIN32 SysErrorModeDisabler disabler(FileName.ToCStr()); #endif #if defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400) wchar_t womode[16]; wchar_t *pwFileName = (wchar_t*)OVR_ALLOC((UTF8Util::GetLength(FileName.ToCStr())+1) * sizeof(wchar_t)); UTF8Util::DecodeString(pwFileName, FileName.ToCStr()); OVR_ASSERT(strlen(omode) < sizeof(womode)/sizeof(womode[0])); UTF8Util::DecodeString(womode, omode); _wfopen_s(&fs, pwFileName, womode); OVR_FREE(pwFileName); #else fs = fopen(FileName.ToCStr(), omode); #endif if (fs) rewind (fs); Opened = (fs != NULL); // Set error code if (!Opened) ErrorCode = SFerror(); else { // If we are testing file seek correctness, pre-load the entire file so // that we can do comparison tests later. #ifdef OVR_FILE_VERIFY_SEEK_ERRORS TestPos = 0; fseek(fs, 0, SEEK_END); FileTestLength = ftell(fs); fseek(fs, 0, SEEK_SET); pFileTestBuffer = (UByte*)OVR_ALLOC(FileTestLength); if (pFileTestBuffer) { OVR_ASSERT(FileTestLength == (unsigned)Read(pFileTestBuffer, FileTestLength)); Seek(0, Seek_Set); } #endif ErrorCode = 0; } LastOp = 0; } const char* FILEFile::GetFilePath() { return FileName.ToCStr(); } // ** File Information bool FILEFile::IsValid() { return Opened; } bool FILEFile::IsWritable() { return IsValid() && (OpenFlags&Open_Write); } /* bool FILEFile::IsRecoverable() { return IsValid() && ((OpenFlags&OVR_FO_SAFETRUNC) == OVR_FO_SAFETRUNC); } */ // Return position / file size int FILEFile::Tell() { int pos = (int)ftell (fs); if (pos < 0) ErrorCode = SFerror(); return pos; } SInt64 FILEFile::LTell() { SInt64 pos = ftell(fs); if (pos < 0) ErrorCode = SFerror(); return pos; } int FILEFile::GetLength() { int pos = Tell(); if (pos >= 0) { Seek (0, Seek_End); int size = Tell(); Seek (pos, Seek_Set); return size; } return -1; } SInt64 FILEFile::LGetLength() { SInt64 pos = LTell(); if (pos >= 0) { LSeek (0, Seek_End); SInt64 size = LTell(); LSeek (pos, Seek_Set); return size; } return -1; } int FILEFile::GetErrorCode() { return ErrorCode; } // ** Stream implementation & I/O int FILEFile::Write(const UByte *pbuffer, int numBytes) { if (LastOp && LastOp != Open_Write) fflush(fs); LastOp = Open_Write; int written = (int) fwrite(pbuffer, 1, numBytes, fs); if (written < numBytes) ErrorCode = SFerror(); #ifdef OVR_FILE_VERIFY_SEEK_ERRORS if (written > 0) TestPos += written; #endif return written; } int FILEFile::Read(UByte *pbuffer, int numBytes) { if (LastOp && LastOp != Open_Read) fflush(fs); LastOp = Open_Read; int read = (int) fread(pbuffer, 1, numBytes, fs); if (read < numBytes) ErrorCode = SFerror(); #ifdef OVR_FILE_VERIFY_SEEK_ERRORS if (read > 0) { // Read-in data must match our pre-loaded buffer data! UByte* pcompareBuffer = pFileTestBuffer + TestPos; for (int i=0; i< read; i++) { OVR_ASSERT(pcompareBuffer[i] == pbuffer[i]); } //OVR_ASSERT(!memcmp(pFileTestBuffer + TestPos, pbuffer, read)); TestPos += read; OVR_ASSERT(ftell(fs) == (int)TestPos); } #endif return read; } // Seeks ahead to skip bytes int FILEFile::SkipBytes(int numBytes) { SInt64 pos = LTell(); SInt64 newPos = LSeek(numBytes, Seek_Cur); // Return -1 for major error if ((pos==-1) || (newPos==-1)) { return -1; } //ErrorCode = ((NewPos-Pos) int(sizeof(buff))) ? int(sizeof(buff)) : byteSize; szRead = pstream->Read(buff, szRequest); szWritten = 0; if (szRead > 0) szWritten = Write(buff, szRead); count += szWritten; byteSize -= szWritten; if (szWritten < szRequest) break; } return count; } bool FILEFile::Close() { #ifdef OVR_FILE_VERIFY_SEEK_ERRORS if (pFileTestBuffer) { OVR_FREE(pFileTestBuffer); pFileTestBuffer = 0; FileTestLength = 0; } #endif bool closeRet = !fclose(fs); if (!closeRet) { ErrorCode = SFerror(); return 0; } else { Opened = 0; fs = 0; ErrorCode = 0; } // Handle safe truncate /* if ((OpenFlags & OVR_FO_SAFETRUNC) == OVR_FO_SAFETRUNC) { // Delete original file (if it existed) DWORD oldAttributes = FileUtilWin32::GetFileAttributes(FileName); if (oldAttributes!=0xFFFFFFFF) if (!FileUtilWin32::DeleteFile(FileName)) { // Try to remove the readonly attribute FileUtilWin32::SetFileAttributes(FileName, oldAttributes & (~FILE_ATTRIBUTE_READONLY) ); // And delete the file again if (!FileUtilWin32::DeleteFile(FileName)) return 0; } // Rename temp file to real filename if (!FileUtilWin32::MoveFile(TempName, FileName)) { //ErrorCode = errno; return 0; } } */ return 1; } /* bool FILEFile::CloseCancel() { bool closeRet = (bool)::CloseHandle(fd); if (!closeRet) { //ErrorCode = errno; return 0; } else { Opened = 0; fd = INVALID_HANDLE_VALUE; ErrorCode = 0; } // Handle safe truncate (delete tmp file, leave original unchanged) if ((OpenFlags&OVR_FO_SAFETRUNC) == OVR_FO_SAFETRUNC) if (!FileUtilWin32::DeleteFile(TempName)) { //ErrorCode = errno; return 0; } return 1; } */ File *FileFILEOpen(const String& path, int flags, int mode) { return new FILEFile(path, flags, mode); } // Helper function: obtain file information time. bool SysFile::GetFileStat(FileStat* pfileStat, const String& path) { #if defined(OVR_OS_WIN32) // 64-bit implementation on Windows. struct __stat64 fileStat; // Stat returns 0 for success. wchar_t *pwpath = (wchar_t*)OVR_ALLOC((UTF8Util::GetLength(path.ToCStr())+1)*sizeof(wchar_t)); UTF8Util::DecodeString(pwpath, path.ToCStr()); int ret = _wstat64(pwpath, &fileStat); OVR_FREE(pwpath); if (ret) return false; #else struct stat fileStat; // Stat returns 0 for success. if (stat(path, &fileStat) != 0) return false; #endif pfileStat->AccessTime = fileStat.st_atime; pfileStat->ModifyTime = fileStat.st_mtime; pfileStat->FileSize = fileStat.st_size; return true; } } // Scaleform \ No newline at end of file +/************************************************************************** + +Filename : OVR_FileFILE.cpp +Content : File wrapper class implementation (Win32) + +Created : April 5, 1999 +Authors : Michael Antonov + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +**************************************************************************/ + +#define GFILE_CXX + +#include "OVR_Types.h" +#include "OVR_Log.h" + +// Standard C library (Captain Obvious guarantees!) +#include +#ifndef OVR_OS_WINCE +#include +#endif + +#include "OVR_SysFile.h" + +#ifndef OVR_OS_WINCE +#include +#endif + +namespace OVR { + +// ***** File interface + +// ***** FILEFile - C streams file + +static int SFerror () +{ + if (errno == ENOENT) + return FileConstants::Error_FileNotFound; + else if (errno == EACCES || errno == EPERM) + return FileConstants::Error_Access; + else if (errno == ENOSPC) + return FileConstants::Error_DiskFull; + else + return FileConstants::Error_IOError; +}; + +#ifdef OVR_OS_WIN32 +#include "windows.h" +// A simple helper class to disable/enable system error mode, if necessary +// Disabling happens conditionally only if a drive name is involved +class SysErrorModeDisabler +{ + BOOL Disabled; + UINT OldMode; +public: + SysErrorModeDisabler(const char* pfileName) + { + if (pfileName && (pfileName[0]!=0) && pfileName[1]==':') + { + Disabled = 1; + OldMode = ::SetErrorMode(SEM_FAILCRITICALERRORS); + } + else + Disabled = 0; + } + + ~SysErrorModeDisabler() + { + if (Disabled) ::SetErrorMode(OldMode); + } +}; +#else +class SysErrorModeDisabler +{ +public: + SysErrorModeDisabler(const char* pfileName) { } +}; +#endif // OVR_OS_WIN32 + + +// This macro enables verification of I/O results after seeks against a pre-loaded +// full file buffer copy. This is generally not necessary, but can been used to debug +// memory corruptions; we've seen this fail due to EAX2/DirectSound corrupting memory +// under FMOD with XP64 (32-bit) and Realtek HA Audio driver. +//#define GFILE_VERIFY_SEEK_ERRORS + + +// This is the simplest possible file implementation, it wraps around the descriptor +// This file is delegated to by SysFile. + +class FILEFile : public File +{ +protected: + + // Allocated filename + String FileName; + + // File handle & open mode + bool Opened; + FILE* fs; + int OpenFlags; + // Error code for last request + int ErrorCode; + + int LastOp; + +#ifdef OVR_FILE_VERIFY_SEEK_ERRORS + UByte* pFileTestBuffer; + unsigned FileTestLength; + unsigned TestPos; // File pointer position during tests. +#endif + +public: + + FILEFile() + { + Opened = 0; FileName = ""; + +#ifdef OVR_FILE_VERIFY_SEEK_ERRORS + pFileTestBuffer =0; + FileTestLength =0; + TestPos =0; +#endif + } + // Initialize file by opening it + FILEFile(const String& fileName, int flags, int Mode); + // The 'pfileName' should be encoded as UTF-8 to support international file names. + FILEFile(const char* pfileName, int flags, int Mode); + + ~FILEFile() + { + if (Opened) + Close(); + } + + virtual const char* GetFilePath(); + + // ** File Information + virtual bool IsValid(); + virtual bool IsWritable(); + + // Return position / file size + virtual int Tell(); + virtual SInt64 LTell(); + virtual int GetLength(); + virtual SInt64 LGetLength(); + +// virtual bool Stat(FileStats *pfs); + virtual int GetErrorCode(); + + // ** Stream implementation & I/O + virtual int Write(const UByte *pbuffer, int numBytes); + virtual int Read(UByte *pbuffer, int numBytes); + virtual int SkipBytes(int numBytes); + virtual int BytesAvailable(); + virtual bool Flush(); + virtual int Seek(int offset, int origin); + virtual SInt64 LSeek(SInt64 offset, int origin); + + virtual int CopyFromStream(File *pStream, int byteSize); + virtual bool Close(); +private: + void init(); +}; + + +// Initialize file by opening it +FILEFile::FILEFile(const String& fileName, int flags, int mode) + : FileName(fileName), OpenFlags(flags) +{ + OVR_UNUSED(mode); + init(); +} + +// The 'pfileName' should be encoded as UTF-8 to support international file names. +FILEFile::FILEFile(const char* pfileName, int flags, int mode) + : FileName(pfileName), OpenFlags(flags) +{ + OVR_UNUSED(mode); + init(); +} + +void FILEFile::init() +{ + // Open mode for file's open + const char *omode = "rb"; + + if (OpenFlags & Open_Truncate) + { + if (OpenFlags & Open_Read) + omode = "w+b"; + else + omode = "wb"; + } + else if (OpenFlags & Open_Create) + { + if (OpenFlags & Open_Read) + omode = "a+b"; + else + omode = "ab"; + } + else if (OpenFlags & Open_Write) + omode = "r+b"; + +#ifdef OVR_OS_WIN32 + SysErrorModeDisabler disabler(FileName.ToCStr()); +#endif + +#if defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400) + wchar_t womode[16]; + wchar_t *pwFileName = (wchar_t*)OVR_ALLOC((UTF8Util::GetLength(FileName.ToCStr())+1) * sizeof(wchar_t)); + UTF8Util::DecodeString(pwFileName, FileName.ToCStr()); + OVR_ASSERT(strlen(omode) < sizeof(womode)/sizeof(womode[0])); + UTF8Util::DecodeString(womode, omode); + _wfopen_s(&fs, pwFileName, womode); + OVR_FREE(pwFileName); +#else + fs = fopen(FileName.ToCStr(), omode); +#endif + if (fs) + rewind (fs); + Opened = (fs != NULL); + // Set error code + if (!Opened) + ErrorCode = SFerror(); + else + { + // If we are testing file seek correctness, pre-load the entire file so + // that we can do comparison tests later. +#ifdef OVR_FILE_VERIFY_SEEK_ERRORS + TestPos = 0; + fseek(fs, 0, SEEK_END); + FileTestLength = ftell(fs); + fseek(fs, 0, SEEK_SET); + pFileTestBuffer = (UByte*)OVR_ALLOC(FileTestLength); + if (pFileTestBuffer) + { + OVR_ASSERT(FileTestLength == (unsigned)Read(pFileTestBuffer, FileTestLength)); + Seek(0, Seek_Set); + } +#endif + + ErrorCode = 0; + } + LastOp = 0; +} + + +const char* FILEFile::GetFilePath() +{ + return FileName.ToCStr(); +} + + +// ** File Information +bool FILEFile::IsValid() +{ + return Opened; +} +bool FILEFile::IsWritable() +{ + return IsValid() && (OpenFlags&Open_Write); +} +/* +bool FILEFile::IsRecoverable() +{ + return IsValid() && ((OpenFlags&OVR_FO_SAFETRUNC) == OVR_FO_SAFETRUNC); +} +*/ + +// Return position / file size +int FILEFile::Tell() +{ + int pos = (int)ftell (fs); + if (pos < 0) + ErrorCode = SFerror(); + return pos; +} + +SInt64 FILEFile::LTell() +{ + SInt64 pos = ftell(fs); + if (pos < 0) + ErrorCode = SFerror(); + return pos; +} + +int FILEFile::GetLength() +{ + int pos = Tell(); + if (pos >= 0) + { + Seek (0, Seek_End); + int size = Tell(); + Seek (pos, Seek_Set); + return size; + } + return -1; +} +SInt64 FILEFile::LGetLength() +{ + SInt64 pos = LTell(); + if (pos >= 0) + { + LSeek (0, Seek_End); + SInt64 size = LTell(); + LSeek (pos, Seek_Set); + return size; + } + return -1; +} + +int FILEFile::GetErrorCode() +{ + return ErrorCode; +} + +// ** Stream implementation & I/O +int FILEFile::Write(const UByte *pbuffer, int numBytes) +{ + if (LastOp && LastOp != Open_Write) + fflush(fs); + LastOp = Open_Write; + int written = (int) fwrite(pbuffer, 1, numBytes, fs); + if (written < numBytes) + ErrorCode = SFerror(); + +#ifdef OVR_FILE_VERIFY_SEEK_ERRORS + if (written > 0) + TestPos += written; +#endif + + return written; +} + +int FILEFile::Read(UByte *pbuffer, int numBytes) +{ + if (LastOp && LastOp != Open_Read) + fflush(fs); + LastOp = Open_Read; + int read = (int) fread(pbuffer, 1, numBytes, fs); + if (read < numBytes) + ErrorCode = SFerror(); + +#ifdef OVR_FILE_VERIFY_SEEK_ERRORS + if (read > 0) + { + // Read-in data must match our pre-loaded buffer data! + UByte* pcompareBuffer = pFileTestBuffer + TestPos; + for (int i=0; i< read; i++) + { + OVR_ASSERT(pcompareBuffer[i] == pbuffer[i]); + } + + //OVR_ASSERT(!memcmp(pFileTestBuffer + TestPos, pbuffer, read)); + TestPos += read; + OVR_ASSERT(ftell(fs) == (int)TestPos); + } +#endif + + return read; +} + +// Seeks ahead to skip bytes +int FILEFile::SkipBytes(int numBytes) +{ + SInt64 pos = LTell(); + SInt64 newPos = LSeek(numBytes, Seek_Cur); + + // Return -1 for major error + if ((pos==-1) || (newPos==-1)) + { + return -1; + } + //ErrorCode = ((NewPos-Pos) int(sizeof(buff))) ? int(sizeof(buff)) : byteSize; + + szRead = pstream->Read(buff, szRequest); + szWritten = 0; + if (szRead > 0) + szWritten = Write(buff, szRead); + + count += szWritten; + byteSize -= szWritten; + if (szWritten < szRequest) + break; + } + return count; +} + + +bool FILEFile::Close() +{ +#ifdef OVR_FILE_VERIFY_SEEK_ERRORS + if (pFileTestBuffer) + { + OVR_FREE(pFileTestBuffer); + pFileTestBuffer = 0; + FileTestLength = 0; + } +#endif + + bool closeRet = !fclose(fs); + + if (!closeRet) + { + ErrorCode = SFerror(); + return 0; + } + else + { + Opened = 0; + fs = 0; + ErrorCode = 0; + } + + // Handle safe truncate + /* + if ((OpenFlags & OVR_FO_SAFETRUNC) == OVR_FO_SAFETRUNC) + { + // Delete original file (if it existed) + DWORD oldAttributes = FileUtilWin32::GetFileAttributes(FileName); + if (oldAttributes!=0xFFFFFFFF) + if (!FileUtilWin32::DeleteFile(FileName)) + { + // Try to remove the readonly attribute + FileUtilWin32::SetFileAttributes(FileName, oldAttributes & (~FILE_ATTRIBUTE_READONLY) ); + // And delete the file again + if (!FileUtilWin32::DeleteFile(FileName)) + return 0; + } + + // Rename temp file to real filename + if (!FileUtilWin32::MoveFile(TempName, FileName)) + { + //ErrorCode = errno; + return 0; + } + } + */ + return 1; +} + +/* +bool FILEFile::CloseCancel() +{ + bool closeRet = (bool)::CloseHandle(fd); + + if (!closeRet) + { + //ErrorCode = errno; + return 0; + } + else + { + Opened = 0; + fd = INVALID_HANDLE_VALUE; + ErrorCode = 0; + } + + // Handle safe truncate (delete tmp file, leave original unchanged) + if ((OpenFlags&OVR_FO_SAFETRUNC) == OVR_FO_SAFETRUNC) + if (!FileUtilWin32::DeleteFile(TempName)) + { + //ErrorCode = errno; + return 0; + } + return 1; +} +*/ + +File *FileFILEOpen(const String& path, int flags, int mode) +{ + return new FILEFile(path, flags, mode); +} + +// Helper function: obtain file information time. +bool SysFile::GetFileStat(FileStat* pfileStat, const String& path) +{ +#if defined(OVR_OS_WIN32) + // 64-bit implementation on Windows. + struct __stat64 fileStat; + // Stat returns 0 for success. + wchar_t *pwpath = (wchar_t*)OVR_ALLOC((UTF8Util::GetLength(path.ToCStr())+1)*sizeof(wchar_t)); + UTF8Util::DecodeString(pwpath, path.ToCStr()); + + int ret = _wstat64(pwpath, &fileStat); + OVR_FREE(pwpath); + if (ret) return false; +#else + struct stat fileStat; + // Stat returns 0 for success. + if (stat(path, &fileStat) != 0) + return false; +#endif + pfileStat->AccessTime = fileStat.st_atime; + pfileStat->ModifyTime = fileStat.st_mtime; + pfileStat->FileSize = fileStat.st_size; + return true; +} + +} // Scaleform diff -r 59fc487ba58e -r b069a5c27388 libovr/Src/Kernel/OVR_Hash.h --- a/libovr/Src/Kernel/OVR_Hash.h Sat Sep 14 17:51:03 2013 +0300 +++ b/libovr/Src/Kernel/OVR_Hash.h Sun Sep 15 04:10:05 2013 +0300 @@ -1,1 +1,1291 @@ -/************************************************************************************ PublicHeader: None Filename : OVR_Hash.h Content : Template hash-table/set implementation Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #ifndef OVR_Hash_h #define OVR_Hash_h #include "OVR_ContainerAllocator.h" #include "OVR_Alg.h" // 'new' operator is redefined/used in this file. #undef new namespace OVR { //----------------------------------------------------------------------------------- // ***** Hash Table Implementation // HastSet and Hash. // // Hash table, linear probing, internal chaining. One interesting/nice thing // about this implementation is that the table itself is a flat chunk of memory // containing no pointers, only relative indices. If the key and value types // of the Hash contain no pointers, then the Hash can be serialized using raw IO. // // Never shrinks, unless you explicitly Clear() it. Expands on // demand, though. For best results, if you know roughly how big your // table will be, default it to that size when you create it. // // Key usability feature: // // 1. Allows node hash values to either be cached or not. // // 2. Allows for alternative keys with methods such as GetAlt(). Handy // if you need to search nodes by their components; no need to create // temporary nodes. // // *** Hash functors: // // IdentityHash - use when the key is already a good hash // HFixedSizeHash - general hash based on object's in-memory representation. // Hash is just the input value; can use this for integer-indexed hash tables. template class IdentityHash { public: UPInt operator()(const C& data) const { return (UPInt) data; } }; // Computes a hash of an object's representation. template class FixedSizeHash { public: // Alternative: "sdbm" hash function, suggested at same web page // above, http::/www.cs.yorku.ca/~oz/hash.html // This is somewhat slower then Bernstein, but it works way better than the above // hash function for hashing large numbers of 32-bit ints. static OVR_FORCE_INLINE UPInt SDBM_Hash(const void* data_in, UPInt size, UPInt seed = 5381) { const UByte* data = (const UByte*) data_in; UPInt h = seed; while (size > 0) { size--; h = (h << 16) + (h << 6) - h + (UPInt)data[size]; } return h; } UPInt operator()(const C& data) const { unsigned char* p = (unsigned char*) &data; int size = sizeof(C); return SDBM_Hash(p, size); } }; // *** HashsetEntry Entry types. // Compact hash table Entry type that re-computes hash keys during hash traversal. // Good to use if the hash function is cheap or the hash value is already cached in C. template class HashsetEntry { public: // Internal chaining for collisions. SPInt NextInChain; C Value; HashsetEntry() : NextInChain(-2) { } HashsetEntry(const HashsetEntry& e) : NextInChain(e.NextInChain), Value(e.Value) { } HashsetEntry(const C& key, SPInt next) : NextInChain(next), Value(key) { } bool IsEmpty() const { return NextInChain == -2; } bool IsEndOfChain() const { return NextInChain == -1; } // Cached hash value access - can be optimized bu storing hash locally. // Mask value only needs to be used if SetCachedHash is not implemented. UPInt GetCachedHash(UPInt maskValue) const { return HashF()(Value) & maskValue; } void SetCachedHash(UPInt) {} void Clear() { Value.~C(); // placement delete NextInChain = -2; } // Free is only used from dtor of hash; Clear is used during regular operations: // assignment, hash reallocations, value reassignments, so on. void Free() { Clear(); } }; // Hash table Entry type that caches the Entry hash value for nodes, so that it // does not need to be re-computed during access. template class HashsetCachedEntry { public: // Internal chaining for collisions. SPInt NextInChain; UPInt HashValue; C Value; HashsetCachedEntry() : NextInChain(-2) { } HashsetCachedEntry(const HashsetCachedEntry& e) : NextInChain(e.NextInChain), HashValue(e.HashValue), Value(e.Value) { } HashsetCachedEntry(const C& key, SPInt next) : NextInChain(next), Value(key) { } bool IsEmpty() const { return NextInChain == -2; } bool IsEndOfChain() const { return NextInChain == -1; } // Cached hash value access - can be optimized bu storing hash locally. // Mask value only needs to be used if SetCachedHash is not implemented. UPInt GetCachedHash(UPInt maskValue) const { OVR_UNUSED(maskValue); return HashValue; } void SetCachedHash(UPInt hashValue) { HashValue = hashValue; } void Clear() { Value.~C(); NextInChain = -2; } // Free is only used from dtor of hash; Clear is used during regular operations: // assignment, hash reallocations, value reassignments, so on. void Free() { Clear(); } }; //----------------------------------------------------------------------------------- // *** HashSet implementation - relies on either cached or regular entries. // // Use: Entry = HashsetCachedEntry if hashes are expensive to // compute and thus need caching in entries. // Entry = HashsetEntry if hashes are already externally cached. // template, class AltHashF = HashF, class Allocator = ContainerAllocator, class Entry = HashsetCachedEntry > class HashSetBase { enum { HashMinSize = 8 }; public: OVR_MEMORY_REDEFINE_NEW(HashSetBase) typedef HashSetBase SelfType; HashSetBase() : pTable(NULL) { } HashSetBase(int sizeHint) : pTable(NULL) { SetCapacity(this, sizeHint); } HashSetBase(const SelfType& src) : pTable(NULL) { Assign(this, src); } ~HashSetBase() { if (pTable) { // Delete the entries. for (UPInt i = 0, n = pTable->SizeMask; i <= n; i++) { Entry* e = &E(i); if (!e->IsEmpty()) e->Free(); } Allocator::Free(pTable); pTable = NULL; } } void Assign(const SelfType& src) { Clear(); if (src.IsEmpty() == false) { SetCapacity(src.GetSize()); for (ConstIterator it = src.Begin(); it != src.End(); ++it) { Add(*it); } } } // Remove all entries from the HashSet table. void Clear() { if (pTable) { // Delete the entries. for (UPInt i = 0, n = pTable->SizeMask; i <= n; i++) { Entry* e = &E(i); if (!e->IsEmpty()) e->Clear(); } Allocator::Free(pTable); pTable = NULL; } } // Returns true if the HashSet is empty. bool IsEmpty() const { return pTable == NULL || pTable->EntryCount == 0; } // Set a new or existing value under the key, to the value. // Pass a different class of 'key' so that assignment reference object // can be passed instead of the actual object. template void Set(const CRef& key) { UPInt hashValue = HashF()(key); SPInt index = (SPInt)-1; if (pTable != NULL) index = findIndexCore(key, hashValue & pTable->SizeMask); if (index >= 0) { E(index).Value = key; } else { // Entry under key doesn't exist. add(key, hashValue); } } template inline void Add(const CRef& key) { UPInt hashValue = HashF()(key); add(key, hashValue); } // Remove by alternative key. template void RemoveAlt(const K& key) { if (pTable == NULL) return; UPInt hashValue = AltHashF()(key); SPInt index = hashValue & pTable->SizeMask; Entry* e = &E(index); // If empty node or occupied by collider, we have nothing to remove. if (e->IsEmpty() || (e->GetCachedHash(pTable->SizeMask) != (UPInt)index)) return; // Save index SPInt naturalIndex = index; SPInt prevIndex = -1; while ((e->GetCachedHash(pTable->SizeMask) != (UPInt)naturalIndex) || !(e->Value == key)) { // Keep looking through the chain. prevIndex = index; index = e->NextInChain; if (index == -1) return; // End of chain, item not found e = &E(index); } // Found it - our item is at index if (naturalIndex == index) { // If we have a follower, move it to us if (!e->IsEndOfChain()) { Entry* enext = &E(e->NextInChain); e->Clear(); new (e) Entry(*enext); // Point us to the follower's cell that will be cleared e = enext; } } else { // We are not at natural index, so deal with the prev items next index E(prevIndex).NextInChain = e->NextInChain; } // Clear us, of the follower cell that was moved. e->Clear(); pTable->EntryCount --; // Should we check the size to condense hash? ... } // Remove by main key. template void Remove(const CRef& key) { RemoveAlt(key); } // Retrieve the pointer to a value under the given key. // - If there's no value under the key, then return NULL. // - If there is a value, return the pointer. template C* Get(const K& key) { SPInt index = findIndex(key); if (index >= 0) return &E(index).Value; return 0; } template const C* Get(const K& key) const { SPInt index = findIndex(key); if (index >= 0) return &E(index).Value; return 0; } // Alternative key versions of Get. Used by Hash. template const C* GetAlt(const K& key) const { SPInt index = findIndexAlt(key); if (index >= 0) return &E(index).Value; return 0; } template C* GetAlt(const K& key) { SPInt index = findIndexAlt(key); if (index >= 0) return &E(index).Value; return 0; } template bool GetAlt(const K& key, C* pval) const { SPInt index = findIndexAlt(key); if (index >= 0) { if (pval) *pval = E(index).Value; return true; } return false; } UPInt GetSize() const { return pTable == NULL ? 0 : (UPInt)pTable->EntryCount; } // Resize the HashSet table to fit one more Entry. Often this // doesn't involve any action. void CheckExpand() { if (pTable == NULL) { // Initial creation of table. Make a minimum-sized table. setRawCapacity(HashMinSize); } else if (pTable->EntryCount * 5 > (pTable->SizeMask + 1) * 4) { // pTable is more than 5/4 ths full. Expand. setRawCapacity((pTable->SizeMask + 1) * 2); } } // Hint the bucket count to >= n. void Resize(UPInt n) { // Not really sure what this means in relation to // STLport's hash_map... they say they "increase the // bucket count to at least n" -- but does that mean // their real capacity after Resize(n) is more like // n*2 (since they do linked-list chaining within // buckets?). SetCapacity(n); } // Size the HashSet so that it can comfortably contain the given // number of elements. If the HashSet already contains more // elements than newSize, then this may be a no-op. void SetCapacity(UPInt newSize) { UPInt newRawSize = (newSize * 5) / 4; if (newRawSize <= GetSize()) return; setRawCapacity(newRawSize); } // Disable inappropriate 'operator ->' warning on MSVC6. #ifdef OVR_CC_MSVC #if (OVR_CC_MSVC < 1300) # pragma warning(disable : 4284) #endif #endif // Iterator API, like STL. struct ConstIterator { const C& operator * () const { OVR_ASSERT(Index >= 0 && Index <= (SPInt)pHash->pTable->SizeMask); return pHash->E(Index).Value; } const C* operator -> () const { OVR_ASSERT(Index >= 0 && Index <= (SPInt)pHash->pTable->SizeMask); return &pHash->E(Index).Value; } void operator ++ () { // Find next non-empty Entry. if (Index <= (SPInt)pHash->pTable->SizeMask) { Index++; while ((UPInt)Index <= pHash->pTable->SizeMask && pHash->E(Index).IsEmpty()) { Index++; } } } bool operator == (const ConstIterator& it) const { if (IsEnd() && it.IsEnd()) { return true; } else { return (pHash == it.pHash) && (Index == it.Index); } } bool operator != (const ConstIterator& it) const { return ! (*this == it); } bool IsEnd() const { return (pHash == NULL) || (pHash->pTable == NULL) || (Index > (SPInt)pHash->pTable->SizeMask); } ConstIterator() : pHash(NULL), Index(0) { } public: // Constructor was intentionally made public to allow create // iterator with arbitrary index. ConstIterator(const SelfType* h, SPInt index) : pHash(h), Index(index) { } const SelfType* GetContainer() const { return pHash; } SPInt GetIndex() const { return Index; } protected: friend class HashSetBase; const SelfType* pHash; SPInt Index; }; friend struct ConstIterator; // Non-const Iterator; Get most of it from ConstIterator. struct Iterator : public ConstIterator { // Allow non-const access to entries. C& operator*() const { OVR_ASSERT(ConstIterator::Index >= 0 && ConstIterator::Index <= (SPInt)ConstIterator::pHash->pTable->SizeMask); return const_cast(ConstIterator::pHash)->E(ConstIterator::Index).Value; } C* operator->() const { return &(operator*()); } Iterator() : ConstIterator(NULL, 0) { } // Removes current element from Hash void Remove() { RemoveAlt(operator*()); } template void RemoveAlt(const K& key) { SelfType* phash = const_cast(ConstIterator::pHash); //Entry* ee = &phash->E(ConstIterator::Index); //const C& key = ee->Value; UPInt hashValue = AltHashF()(key); SPInt index = hashValue & phash->pTable->SizeMask; Entry* e = &phash->E(index); // If empty node or occupied by collider, we have nothing to remove. if (e->IsEmpty() || (e->GetCachedHash(phash->pTable->SizeMask) != (UPInt)index)) return; // Save index SPInt naturalIndex = index; SPInt prevIndex = -1; while ((e->GetCachedHash(phash->pTable->SizeMask) != (UPInt)naturalIndex) || !(e->Value == key)) { // Keep looking through the chain. prevIndex = index; index = e->NextInChain; if (index == -1) return; // End of chain, item not found e = &phash->E(index); } if (index == (SPInt)ConstIterator::Index) { // Found it - our item is at index if (naturalIndex == index) { // If we have a follower, move it to us if (!e->IsEndOfChain()) { Entry* enext = &phash->E(e->NextInChain); e->Clear(); new (e) Entry(*enext); // Point us to the follower's cell that will be cleared e = enext; --ConstIterator::Index; } } else { // We are not at natural index, so deal with the prev items next index phash->E(prevIndex).NextInChain = e->NextInChain; } // Clear us, of the follower cell that was moved. e->Clear(); phash->pTable->EntryCount --; } else OVR_ASSERT(0); //? } private: friend class HashSetBase; Iterator(SelfType* h, SPInt i0) : ConstIterator(h, i0) { } }; friend struct Iterator; Iterator Begin() { if (pTable == 0) return Iterator(NULL, 0); // Scan till we hit the First valid Entry. UPInt i0 = 0; while (i0 <= pTable->SizeMask && E(i0).IsEmpty()) { i0++; } return Iterator(this, i0); } Iterator End() { return Iterator(NULL, 0); } ConstIterator Begin() const { return const_cast(this)->Begin(); } ConstIterator End() const { return const_cast(this)->End(); } template Iterator Find(const K& key) { SPInt index = findIndex(key); if (index >= 0) return Iterator(this, index); return Iterator(NULL, 0); } template Iterator FindAlt(const K& key) { SPInt index = findIndexAlt(key); if (index >= 0) return Iterator(this, index); return Iterator(NULL, 0); } template ConstIterator Find(const K& key) const { return const_cast(this)->Find(key); } template ConstIterator FindAlt(const K& key) const { return const_cast(this)->FindAlt(key); } private: // Find the index of the matching Entry. If no match, then return -1. template SPInt findIndex(const K& key) const { if (pTable == NULL) return -1; UPInt hashValue = HashF()(key) & pTable->SizeMask; return findIndexCore(key, hashValue); } template SPInt findIndexAlt(const K& key) const { if (pTable == NULL) return -1; UPInt hashValue = AltHashF()(key) & pTable->SizeMask; return findIndexCore(key, hashValue); } // Find the index of the matching Entry. If no match, then return -1. template SPInt findIndexCore(const K& key, UPInt hashValue) const { // Table must exist. OVR_ASSERT(pTable != 0); // Hash key must be 'and-ed' by the caller. OVR_ASSERT((hashValue & ~pTable->SizeMask) == 0); UPInt index = hashValue; const Entry* e = &E(index); // If empty or occupied by a collider, not found. if (e->IsEmpty() || (e->GetCachedHash(pTable->SizeMask) != index)) return -1; while(1) { OVR_ASSERT(e->GetCachedHash(pTable->SizeMask) == hashValue); if (e->GetCachedHash(pTable->SizeMask) == hashValue && e->Value == key) { // Found it. return index; } // Values can not be equal at this point. // That would mean that the hash key for the same value differs. OVR_ASSERT(!(e->Value == key)); // Keep looking through the chain. index = e->NextInChain; if (index == (UPInt)-1) break; // end of chain e = &E(index); OVR_ASSERT(!e->IsEmpty()); } return -1; } // Add a new value to the HashSet table, under the specified key. template void add(const CRef& key, UPInt hashValue) { CheckExpand(); hashValue &= pTable->SizeMask; pTable->EntryCount++; SPInt index = hashValue; Entry* naturalEntry = &(E(index)); if (naturalEntry->IsEmpty()) { // Put the new Entry in. new (naturalEntry) Entry(key, -1); } else { // Find a blank spot. SPInt blankIndex = index; do { blankIndex = (blankIndex + 1) & pTable->SizeMask; } while(!E(blankIndex).IsEmpty()); Entry* blankEntry = &E(blankIndex); if (naturalEntry->GetCachedHash(pTable->SizeMask) == (UPInt)index) { // Collision. Link into this chain. // Move existing list head. new (blankEntry) Entry(*naturalEntry); // placement new, copy ctor // Put the new info in the natural Entry. naturalEntry->Value = key; naturalEntry->NextInChain = blankIndex; } else { // Existing Entry does not naturally // belong in this slot. Existing // Entry must be moved. // Find natural location of collided element (i.e. root of chain) SPInt collidedIndex = naturalEntry->GetCachedHash(pTable->SizeMask); OVR_ASSERT(collidedIndex >= 0 && collidedIndex <= (SPInt)pTable->SizeMask); for (;;) { Entry* e = &E(collidedIndex); if (e->NextInChain == index) { // Here's where we need to splice. new (blankEntry) Entry(*naturalEntry); e->NextInChain = blankIndex; break; } collidedIndex = e->NextInChain; OVR_ASSERT(collidedIndex >= 0 && collidedIndex <= (SPInt)pTable->SizeMask); } // Put the new data in the natural Entry. naturalEntry->Value = key; naturalEntry->NextInChain = -1; } } // Record hash value: has effect only if cached node is used. naturalEntry->SetCachedHash(hashValue); } // Index access helpers. Entry& E(UPInt index) { // Must have pTable and access needs to be within bounds. OVR_ASSERT(index <= pTable->SizeMask); return *(((Entry*) (pTable + 1)) + index); } const Entry& E(UPInt index) const { OVR_ASSERT(index <= pTable->SizeMask); return *(((Entry*) (pTable + 1)) + index); } // Resize the HashSet table to the given size (Rehash the // contents of the current table). The arg is the number of // HashSet table entries, not the number of elements we should // actually contain (which will be less than this). void setRawCapacity(UPInt newSize) { if (newSize == 0) { // Special case. Clear(); return; } // Minimum size; don't incur rehashing cost when expanding // very small tables. Not that we perform this check before // 'log2f' call to avoid fp exception with newSize == 1. if (newSize < HashMinSize) newSize = HashMinSize; else { // Force newSize to be a power of two. int bits = Alg::UpperBit(newSize-1) + 1; // Chop( Log2f((float)(newSize-1)) + 1); OVR_ASSERT((UPInt(1) << bits) >= newSize); newSize = UPInt(1) << bits; } SelfType newHash; newHash.pTable = (TableType*) Allocator::Alloc( sizeof(TableType) + sizeof(Entry) * newSize); // Need to do something on alloc failure! OVR_ASSERT(newHash.pTable); newHash.pTable->EntryCount = 0; newHash.pTable->SizeMask = newSize - 1; UPInt i, n; // Mark all entries as empty. for (i = 0; i < newSize; i++) newHash.E(i).NextInChain = -2; // Copy stuff to newHash if (pTable) { for (i = 0, n = pTable->SizeMask; i <= n; i++) { Entry* e = &E(i); if (e->IsEmpty() == false) { // Insert old Entry into new HashSet. newHash.Add(e->Value); // placement delete of old element e->Clear(); } } // Delete our old data buffer. Allocator::Free(pTable); } // Steal newHash's data. pTable = newHash.pTable; newHash.pTable = NULL; } struct TableType { UPInt EntryCount; UPInt SizeMask; // Entry array follows this structure // in memory. }; TableType* pTable; }; //----------------------------------------------------------------------------------- template, class AltHashF = HashF, class Allocator = ContainerAllocator, class Entry = HashsetCachedEntry > class HashSet : public HashSetBase { public: typedef HashSetBase BaseType; typedef HashSet SelfType; HashSet() { } HashSet(int sizeHint) : BaseType(sizeHint) { } HashSet(const SelfType& src) : BaseType(src) { } ~HashSet() { } void operator = (const SelfType& src) { BaseType::Assign(src); } // Set a new or existing value under the key, to the value. // Pass a different class of 'key' so that assignment reference object // can be passed instead of the actual object. template void Set(const CRef& key) { BaseType::Set(key); } template inline void Add(const CRef& key) { BaseType::Add(key); } // Hint the bucket count to >= n. void Resize(UPInt n) { BaseType::SetCapacity(n); } // Size the HashSet so that it can comfortably contain the given // number of elements. If the HashSet already contains more // elements than newSize, then this may be a no-op. void SetCapacity(UPInt newSize) { BaseType::SetCapacity(newSize); } }; // HashSet with uncached hash code; declared for convenience. template, class AltHashF = HashF, class Allocator = ContainerAllocator > class HashSetUncached : public HashSet > { public: typedef HashSetUncached SelfType; typedef HashSet > BaseType; // Delegated constructors. HashSetUncached() { } HashSetUncached(int sizeHint) : BaseType(sizeHint) { } HashSetUncached(const SelfType& src) : BaseType(src) { } ~HashSetUncached() { } void operator = (const SelfType& src) { BaseType::operator = (src); } }; //----------------------------------------------------------------------------------- // ***** Hash hash table implementation // Node for Hash - necessary so that Hash can delegate its implementation // to HashSet. template struct HashNode { typedef HashNode SelfType; typedef C FirstType; typedef U SecondType; C First; U Second; // NodeRef is used to allow passing of elements into HashSet // without using a temporary object. struct NodeRef { const C* pFirst; const U* pSecond; NodeRef(const C& f, const U& s) : pFirst(&f), pSecond(&s) { } NodeRef(const NodeRef& src) : pFirst(src.pFirst), pSecond(src.pSecond) { } // Enable computation of ghash_node_hashf. inline UPInt GetHash() const { return HashF()(*pFirst); } // Necessary conversion to allow HashNode::operator == to work. operator const C& () const { return *pFirst; } }; // Note: No default constructor is necessary. HashNode(const HashNode& src) : First(src.First), Second(src.Second) { } HashNode(const NodeRef& src) : First(*src.pFirst), Second(*src.pSecond) { } void operator = (const NodeRef& src) { First = *src.pFirst; Second = *src.pSecond; } template bool operator == (const K& src) const { return (First == src); } template static UPInt CalcHash(const K& data) { return HashF()(data); } inline UPInt GetHash() const { return HashF()(First); } // Hash functors used with this node. A separate functor is used for alternative // key lookup so that it does not need to access the '.First' element. struct NodeHashF { template UPInt operator()(const K& data) const { return data.GetHash(); } }; struct NodeAltHashF { template UPInt operator()(const K& data) const { return HashNode::CalcHash(data); } }; }; // **** Extra hashset_entry types to allow NodeRef construction. // The big difference between the below types and the ones used in hash_set is that // these allow initializing the node with 'typename C::NodeRef& keyRef', which // is critical to avoid temporary node allocation on stack when using placement new. // Compact hash table Entry type that re-computes hash keys during hash traversal. // Good to use if the hash function is cheap or the hash value is already cached in C. template class HashsetNodeEntry { public: // Internal chaining for collisions. SPInt NextInChain; C Value; HashsetNodeEntry() : NextInChain(-2) { } HashsetNodeEntry(const HashsetNodeEntry& e) : NextInChain(e.NextInChain), Value(e.Value) { } HashsetNodeEntry(const C& key, SPInt next) : NextInChain(next), Value(key) { } HashsetNodeEntry(const typename C::NodeRef& keyRef, SPInt next) : NextInChain(next), Value(keyRef) { } bool IsEmpty() const { return NextInChain == -2; } bool IsEndOfChain() const { return NextInChain == -1; } UPInt GetCachedHash(UPInt maskValue) const { return HashF()(Value) & maskValue; } void SetCachedHash(UPInt hashValue) { OVR_UNUSED(hashValue); } void Clear() { Value.~C(); // placement delete NextInChain = -2; } // Free is only used from dtor of hash; Clear is used during regular operations: // assignment, hash reallocations, value reassignments, so on. void Free() { Clear(); } }; // Hash table Entry type that caches the Entry hash value for nodes, so that it // does not need to be re-computed during access. template class HashsetCachedNodeEntry { public: // Internal chaining for collisions. SPInt NextInChain; UPInt HashValue; C Value; HashsetCachedNodeEntry() : NextInChain(-2) { } HashsetCachedNodeEntry(const HashsetCachedNodeEntry& e) : NextInChain(e.NextInChain), HashValue(e.HashValue), Value(e.Value) { } HashsetCachedNodeEntry(const C& key, SPInt next) : NextInChain(next), Value(key) { } HashsetCachedNodeEntry(const typename C::NodeRef& keyRef, SPInt next) : NextInChain(next), Value(keyRef) { } bool IsEmpty() const { return NextInChain == -2; } bool IsEndOfChain() const { return NextInChain == -1; } UPInt GetCachedHash(UPInt maskValue) const { OVR_UNUSED(maskValue); return HashValue; } void SetCachedHash(UPInt hashValue) { HashValue = hashValue; } void Clear() { Value.~C(); NextInChain = -2; } // Free is only used from dtor of hash; Clear is used during regular operations: // assignment, hash reallocations, value reassignments, so on. void Free() { Clear(); } }; //----------------------------------------------------------------------------------- template, class Allocator = ContainerAllocator, class HashNode = OVR::HashNode, class Entry = HashsetCachedNodeEntry, class Container = HashSet > class Hash { public: OVR_MEMORY_REDEFINE_NEW(Hash) // Types used for hash_set. typedef U ValueType; typedef Hash SelfType; // Actual hash table itself, implemented as hash_set. Container mHash; public: Hash() { } Hash(int sizeHint) : mHash(sizeHint) { } Hash(const SelfType& src) : mHash(src.mHash) { } ~Hash() { } void operator = (const SelfType& src) { mHash = src.mHash; } // Remove all entries from the Hash table. inline void Clear() { mHash.Clear(); } // Returns true if the Hash is empty. inline bool IsEmpty() const { return mHash.IsEmpty(); } // Access (set). inline void Set(const C& key, const U& value) { typename HashNode::NodeRef e(key, value); mHash.Set(e); } inline void Add(const C& key, const U& value) { typename HashNode::NodeRef e(key, value); mHash.Add(e); } // Removes an element by clearing its Entry. inline void Remove(const C& key) { mHash.RemoveAlt(key); } template inline void RemoveAlt(const K& key) { mHash.RemoveAlt(key); } // Retrieve the value under the given key. // - If there's no value under the key, then return false and leave *pvalue alone. // - If there is a value, return true, and Set *Pvalue to the Entry's value. // - If value == NULL, return true or false according to the presence of the key. bool Get(const C& key, U* pvalue) const { const HashNode* p = mHash.GetAlt(key); if (p) { if (pvalue) *pvalue = p->Second; return true; } return false; } template bool GetAlt(const K& key, U* pvalue) const { const HashNode* p = mHash.GetAlt(key); if (p) { if (pvalue) *pvalue = p->Second; return true; } return false; } // Retrieve the pointer to a value under the given key. // - If there's no value under the key, then return NULL. // - If there is a value, return the pointer. inline U* Get(const C& key) { HashNode* p = mHash.GetAlt(key); return p ? &p->Second : 0; } inline const U* Get(const C& key) const { const HashNode* p = mHash.GetAlt(key); return p ? &p->Second : 0; } template inline U* GetAlt(const K& key) { HashNode* p = mHash.GetAlt(key); return p ? &p->Second : 0; } template inline const U* GetAlt(const K& key) const { const HashNode* p = mHash.GetAlt(key); return p ? &p->Second : 0; } // Sizing methods - delegate to Hash. inline UPInt GetSize() const { return mHash.GetSize(); } inline void Resize(UPInt n) { mHash.Resize(n); } inline void SetCapacity(UPInt newSize) { mHash.SetCapacity(newSize); } // Iterator API, like STL. typedef typename Container::ConstIterator ConstIterator; typedef typename Container::Iterator Iterator; inline Iterator Begin() { return mHash.Begin(); } inline Iterator End() { return mHash.End(); } inline ConstIterator Begin() const { return mHash.Begin(); } inline ConstIterator End() const { return mHash.End(); } Iterator Find(const C& key) { return mHash.FindAlt(key); } ConstIterator Find(const C& key) const { return mHash.FindAlt(key); } template Iterator FindAlt(const K& key) { return mHash.FindAlt(key); } template ConstIterator FindAlt(const K& key) const { return mHash.FindAlt(key); } }; // Hash with uncached hash code; declared for convenience. template, class Allocator = ContainerAllocator > class HashUncached : public Hash, HashsetNodeEntry, typename HashNode::NodeHashF> > { public: typedef HashUncached SelfType; typedef Hash, HashsetNodeEntry, typename HashNode::NodeHashF> > BaseType; // Delegated constructors. HashUncached() { } HashUncached(int sizeHint) : BaseType(sizeHint) { } HashUncached(const SelfType& src) : BaseType(src) { } ~HashUncached() { } void operator = (const SelfType& src) { BaseType::operator = (src); } }; // And identity hash in which keys serve as hash value. Can be uncached, // since hash computation is assumed cheap. template, class HashF = IdentityHash > class HashIdentity : public HashUncached { public: typedef HashIdentity SelfType; typedef HashUncached BaseType; // Delegated constructors. HashIdentity() { } HashIdentity(int sizeHint) : BaseType(sizeHint) { } HashIdentity(const SelfType& src) : BaseType(src) { } ~HashIdentity() { } void operator = (const SelfType& src) { BaseType::operator = (src); } }; } // OVR #ifdef OVR_DEFINE_NEW #define new OVR_DEFINE_NEW #endif #endif \ No newline at end of file +/************************************************************************************ + +PublicHeader: None +Filename : OVR_Hash.h +Content : Template hash-table/set implementation +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#ifndef OVR_Hash_h +#define OVR_Hash_h + +#include "OVR_ContainerAllocator.h" +#include "OVR_Alg.h" + +// 'new' operator is redefined/used in this file. +#undef new + +namespace OVR { + +//----------------------------------------------------------------------------------- +// ***** Hash Table Implementation + +// HastSet and Hash. +// +// Hash table, linear probing, internal chaining. One interesting/nice thing +// about this implementation is that the table itself is a flat chunk of memory +// containing no pointers, only relative indices. If the key and value types +// of the Hash contain no pointers, then the Hash can be serialized using raw IO. +// +// Never shrinks, unless you explicitly Clear() it. Expands on +// demand, though. For best results, if you know roughly how big your +// table will be, default it to that size when you create it. +// +// Key usability feature: +// +// 1. Allows node hash values to either be cached or not. +// +// 2. Allows for alternative keys with methods such as GetAlt(). Handy +// if you need to search nodes by their components; no need to create +// temporary nodes. +// + + +// *** Hash functors: +// +// IdentityHash - use when the key is already a good hash +// HFixedSizeHash - general hash based on object's in-memory representation. + + +// Hash is just the input value; can use this for integer-indexed hash tables. +template +class IdentityHash +{ +public: + UPInt operator()(const C& data) const + { return (UPInt) data; } +}; + +// Computes a hash of an object's representation. +template +class FixedSizeHash +{ +public: + // Alternative: "sdbm" hash function, suggested at same web page + // above, http::/www.cs.yorku.ca/~oz/hash.html + // This is somewhat slower then Bernstein, but it works way better than the above + // hash function for hashing large numbers of 32-bit ints. + static OVR_FORCE_INLINE UPInt SDBM_Hash(const void* data_in, UPInt size, UPInt seed = 5381) + { + const UByte* data = (const UByte*) data_in; + UPInt h = seed; + while (size > 0) + { + size--; + h = (h << 16) + (h << 6) - h + (UPInt)data[size]; + } + return h; + } + + UPInt operator()(const C& data) const + { + unsigned char* p = (unsigned char*) &data; + int size = sizeof(C); + + return SDBM_Hash(p, size); + } +}; + + + +// *** HashsetEntry Entry types. + +// Compact hash table Entry type that re-computes hash keys during hash traversal. +// Good to use if the hash function is cheap or the hash value is already cached in C. +template +class HashsetEntry +{ +public: + // Internal chaining for collisions. + SPInt NextInChain; + C Value; + + HashsetEntry() + : NextInChain(-2) { } + HashsetEntry(const HashsetEntry& e) + : NextInChain(e.NextInChain), Value(e.Value) { } + HashsetEntry(const C& key, SPInt next) + : NextInChain(next), Value(key) { } + + bool IsEmpty() const { return NextInChain == -2; } + bool IsEndOfChain() const { return NextInChain == -1; } + + // Cached hash value access - can be optimized bu storing hash locally. + // Mask value only needs to be used if SetCachedHash is not implemented. + UPInt GetCachedHash(UPInt maskValue) const { return HashF()(Value) & maskValue; } + void SetCachedHash(UPInt) {} + + void Clear() + { + Value.~C(); // placement delete + NextInChain = -2; + } + // Free is only used from dtor of hash; Clear is used during regular operations: + // assignment, hash reallocations, value reassignments, so on. + void Free() { Clear(); } +}; + +// Hash table Entry type that caches the Entry hash value for nodes, so that it +// does not need to be re-computed during access. +template +class HashsetCachedEntry +{ +public: + // Internal chaining for collisions. + SPInt NextInChain; + UPInt HashValue; + C Value; + + HashsetCachedEntry() + : NextInChain(-2) { } + HashsetCachedEntry(const HashsetCachedEntry& e) + : NextInChain(e.NextInChain), HashValue(e.HashValue), Value(e.Value) { } + HashsetCachedEntry(const C& key, SPInt next) + : NextInChain(next), Value(key) { } + + bool IsEmpty() const { return NextInChain == -2; } + bool IsEndOfChain() const { return NextInChain == -1; } + + // Cached hash value access - can be optimized bu storing hash locally. + // Mask value only needs to be used if SetCachedHash is not implemented. + UPInt GetCachedHash(UPInt maskValue) const { OVR_UNUSED(maskValue); return HashValue; } + void SetCachedHash(UPInt hashValue) { HashValue = hashValue; } + + void Clear() + { + Value.~C(); + NextInChain = -2; + } + // Free is only used from dtor of hash; Clear is used during regular operations: + // assignment, hash reallocations, value reassignments, so on. + void Free() { Clear(); } +}; + + +//----------------------------------------------------------------------------------- +// *** HashSet implementation - relies on either cached or regular entries. +// +// Use: Entry = HashsetCachedEntry if hashes are expensive to +// compute and thus need caching in entries. +// Entry = HashsetEntry if hashes are already externally cached. +// +template, + class AltHashF = HashF, + class Allocator = ContainerAllocator, + class Entry = HashsetCachedEntry > +class HashSetBase +{ + enum { HashMinSize = 8 }; + +public: + OVR_MEMORY_REDEFINE_NEW(HashSetBase) + + typedef HashSetBase SelfType; + + HashSetBase() : pTable(NULL) { } + HashSetBase(int sizeHint) : pTable(NULL) { SetCapacity(this, sizeHint); } + HashSetBase(const SelfType& src) : pTable(NULL) { Assign(this, src); } + + ~HashSetBase() + { + if (pTable) + { + // Delete the entries. + for (UPInt i = 0, n = pTable->SizeMask; i <= n; i++) + { + Entry* e = &E(i); + if (!e->IsEmpty()) + e->Free(); + } + + Allocator::Free(pTable); + pTable = NULL; + } + } + + + void Assign(const SelfType& src) + { + Clear(); + if (src.IsEmpty() == false) + { + SetCapacity(src.GetSize()); + + for (ConstIterator it = src.Begin(); it != src.End(); ++it) + { + Add(*it); + } + } + } + + + // Remove all entries from the HashSet table. + void Clear() + { + if (pTable) + { + // Delete the entries. + for (UPInt i = 0, n = pTable->SizeMask; i <= n; i++) + { + Entry* e = &E(i); + if (!e->IsEmpty()) + e->Clear(); + } + + Allocator::Free(pTable); + pTable = NULL; + } + } + + // Returns true if the HashSet is empty. + bool IsEmpty() const + { + return pTable == NULL || pTable->EntryCount == 0; + } + + + // Set a new or existing value under the key, to the value. + // Pass a different class of 'key' so that assignment reference object + // can be passed instead of the actual object. + template + void Set(const CRef& key) + { + UPInt hashValue = HashF()(key); + SPInt index = (SPInt)-1; + + if (pTable != NULL) + index = findIndexCore(key, hashValue & pTable->SizeMask); + + if (index >= 0) + { + E(index).Value = key; + } + else + { + // Entry under key doesn't exist. + add(key, hashValue); + } + } + + template + inline void Add(const CRef& key) + { + UPInt hashValue = HashF()(key); + add(key, hashValue); + } + + // Remove by alternative key. + template + void RemoveAlt(const K& key) + { + if (pTable == NULL) + return; + + UPInt hashValue = AltHashF()(key); + SPInt index = hashValue & pTable->SizeMask; + + Entry* e = &E(index); + + // If empty node or occupied by collider, we have nothing to remove. + if (e->IsEmpty() || (e->GetCachedHash(pTable->SizeMask) != (UPInt)index)) + return; + + // Save index + SPInt naturalIndex = index; + SPInt prevIndex = -1; + + while ((e->GetCachedHash(pTable->SizeMask) != (UPInt)naturalIndex) || !(e->Value == key)) + { + // Keep looking through the chain. + prevIndex = index; + index = e->NextInChain; + if (index == -1) + return; // End of chain, item not found + e = &E(index); + } + + // Found it - our item is at index + if (naturalIndex == index) + { + // If we have a follower, move it to us + if (!e->IsEndOfChain()) + { + Entry* enext = &E(e->NextInChain); + e->Clear(); + new (e) Entry(*enext); + // Point us to the follower's cell that will be cleared + e = enext; + } + } + else + { + // We are not at natural index, so deal with the prev items next index + E(prevIndex).NextInChain = e->NextInChain; + } + + // Clear us, of the follower cell that was moved. + e->Clear(); + pTable->EntryCount --; + // Should we check the size to condense hash? ... + } + + // Remove by main key. + template + void Remove(const CRef& key) + { + RemoveAlt(key); + } + + // Retrieve the pointer to a value under the given key. + // - If there's no value under the key, then return NULL. + // - If there is a value, return the pointer. + template + C* Get(const K& key) + { + SPInt index = findIndex(key); + if (index >= 0) + return &E(index).Value; + return 0; + } + + template + const C* Get(const K& key) const + { + SPInt index = findIndex(key); + if (index >= 0) + return &E(index).Value; + return 0; + } + + // Alternative key versions of Get. Used by Hash. + template + const C* GetAlt(const K& key) const + { + SPInt index = findIndexAlt(key); + if (index >= 0) + return &E(index).Value; + return 0; + } + + template + C* GetAlt(const K& key) + { + SPInt index = findIndexAlt(key); + if (index >= 0) + return &E(index).Value; + return 0; + } + + template + bool GetAlt(const K& key, C* pval) const + { + SPInt index = findIndexAlt(key); + if (index >= 0) + { + if (pval) + *pval = E(index).Value; + return true; + } + return false; + } + + + UPInt GetSize() const + { + return pTable == NULL ? 0 : (UPInt)pTable->EntryCount; + } + + + // Resize the HashSet table to fit one more Entry. Often this + // doesn't involve any action. + void CheckExpand() + { + if (pTable == NULL) + { + // Initial creation of table. Make a minimum-sized table. + setRawCapacity(HashMinSize); + } + else if (pTable->EntryCount * 5 > (pTable->SizeMask + 1) * 4) + { + // pTable is more than 5/4 ths full. Expand. + setRawCapacity((pTable->SizeMask + 1) * 2); + } + } + + // Hint the bucket count to >= n. + void Resize(UPInt n) + { + // Not really sure what this means in relation to + // STLport's hash_map... they say they "increase the + // bucket count to at least n" -- but does that mean + // their real capacity after Resize(n) is more like + // n*2 (since they do linked-list chaining within + // buckets?). + SetCapacity(n); + } + + // Size the HashSet so that it can comfortably contain the given + // number of elements. If the HashSet already contains more + // elements than newSize, then this may be a no-op. + void SetCapacity(UPInt newSize) + { + UPInt newRawSize = (newSize * 5) / 4; + if (newRawSize <= GetSize()) + return; + setRawCapacity(newRawSize); + } + + // Disable inappropriate 'operator ->' warning on MSVC6. +#ifdef OVR_CC_MSVC +#if (OVR_CC_MSVC < 1300) +# pragma warning(disable : 4284) +#endif +#endif + + // Iterator API, like STL. + struct ConstIterator + { + const C& operator * () const + { + OVR_ASSERT(Index >= 0 && Index <= (SPInt)pHash->pTable->SizeMask); + return pHash->E(Index).Value; + } + + const C* operator -> () const + { + OVR_ASSERT(Index >= 0 && Index <= (SPInt)pHash->pTable->SizeMask); + return &pHash->E(Index).Value; + } + + void operator ++ () + { + // Find next non-empty Entry. + if (Index <= (SPInt)pHash->pTable->SizeMask) + { + Index++; + while ((UPInt)Index <= pHash->pTable->SizeMask && + pHash->E(Index).IsEmpty()) + { + Index++; + } + } + } + + bool operator == (const ConstIterator& it) const + { + if (IsEnd() && it.IsEnd()) + { + return true; + } + else + { + return (pHash == it.pHash) && (Index == it.Index); + } + } + + bool operator != (const ConstIterator& it) const + { + return ! (*this == it); + } + + + bool IsEnd() const + { + return (pHash == NULL) || + (pHash->pTable == NULL) || + (Index > (SPInt)pHash->pTable->SizeMask); + } + + ConstIterator() + : pHash(NULL), Index(0) + { } + + public: + // Constructor was intentionally made public to allow create + // iterator with arbitrary index. + ConstIterator(const SelfType* h, SPInt index) + : pHash(h), Index(index) + { } + + const SelfType* GetContainer() const + { + return pHash; + } + SPInt GetIndex() const + { + return Index; + } + + protected: + friend class HashSetBase; + + const SelfType* pHash; + SPInt Index; + }; + + friend struct ConstIterator; + + + // Non-const Iterator; Get most of it from ConstIterator. + struct Iterator : public ConstIterator + { + // Allow non-const access to entries. + C& operator*() const + { + OVR_ASSERT(ConstIterator::Index >= 0 && ConstIterator::Index <= (SPInt)ConstIterator::pHash->pTable->SizeMask); + return const_cast(ConstIterator::pHash)->E(ConstIterator::Index).Value; + } + + C* operator->() const + { + return &(operator*()); + } + + Iterator() + : ConstIterator(NULL, 0) + { } + + // Removes current element from Hash + void Remove() + { + RemoveAlt(operator*()); + } + + template + void RemoveAlt(const K& key) + { + SelfType* phash = const_cast(ConstIterator::pHash); + //Entry* ee = &phash->E(ConstIterator::Index); + //const C& key = ee->Value; + + UPInt hashValue = AltHashF()(key); + SPInt index = hashValue & phash->pTable->SizeMask; + + Entry* e = &phash->E(index); + + // If empty node or occupied by collider, we have nothing to remove. + if (e->IsEmpty() || (e->GetCachedHash(phash->pTable->SizeMask) != (UPInt)index)) + return; + + // Save index + SPInt naturalIndex = index; + SPInt prevIndex = -1; + + while ((e->GetCachedHash(phash->pTable->SizeMask) != (UPInt)naturalIndex) || !(e->Value == key)) + { + // Keep looking through the chain. + prevIndex = index; + index = e->NextInChain; + if (index == -1) + return; // End of chain, item not found + e = &phash->E(index); + } + + if (index == (SPInt)ConstIterator::Index) + { + // Found it - our item is at index + if (naturalIndex == index) + { + // If we have a follower, move it to us + if (!e->IsEndOfChain()) + { + Entry* enext = &phash->E(e->NextInChain); + e->Clear(); + new (e) Entry(*enext); + // Point us to the follower's cell that will be cleared + e = enext; + --ConstIterator::Index; + } + } + else + { + // We are not at natural index, so deal with the prev items next index + phash->E(prevIndex).NextInChain = e->NextInChain; + } + + // Clear us, of the follower cell that was moved. + e->Clear(); + phash->pTable->EntryCount --; + } + else + OVR_ASSERT(0); //? + } + + private: + friend class HashSetBase; + + Iterator(SelfType* h, SPInt i0) + : ConstIterator(h, i0) + { } + }; + + friend struct Iterator; + + Iterator Begin() + { + if (pTable == 0) + return Iterator(NULL, 0); + + // Scan till we hit the First valid Entry. + UPInt i0 = 0; + while (i0 <= pTable->SizeMask && E(i0).IsEmpty()) + { + i0++; + } + return Iterator(this, i0); + } + Iterator End() { return Iterator(NULL, 0); } + + ConstIterator Begin() const { return const_cast(this)->Begin(); } + ConstIterator End() const { return const_cast(this)->End(); } + + template + Iterator Find(const K& key) + { + SPInt index = findIndex(key); + if (index >= 0) + return Iterator(this, index); + return Iterator(NULL, 0); + } + + template + Iterator FindAlt(const K& key) + { + SPInt index = findIndexAlt(key); + if (index >= 0) + return Iterator(this, index); + return Iterator(NULL, 0); + } + + template + ConstIterator Find(const K& key) const { return const_cast(this)->Find(key); } + + template + ConstIterator FindAlt(const K& key) const { return const_cast(this)->FindAlt(key); } + +private: + // Find the index of the matching Entry. If no match, then return -1. + template + SPInt findIndex(const K& key) const + { + if (pTable == NULL) + return -1; + UPInt hashValue = HashF()(key) & pTable->SizeMask; + return findIndexCore(key, hashValue); + } + + template + SPInt findIndexAlt(const K& key) const + { + if (pTable == NULL) + return -1; + UPInt hashValue = AltHashF()(key) & pTable->SizeMask; + return findIndexCore(key, hashValue); + } + + // Find the index of the matching Entry. If no match, then return -1. + template + SPInt findIndexCore(const K& key, UPInt hashValue) const + { + // Table must exist. + OVR_ASSERT(pTable != 0); + // Hash key must be 'and-ed' by the caller. + OVR_ASSERT((hashValue & ~pTable->SizeMask) == 0); + + UPInt index = hashValue; + const Entry* e = &E(index); + + // If empty or occupied by a collider, not found. + if (e->IsEmpty() || (e->GetCachedHash(pTable->SizeMask) != index)) + return -1; + + while(1) + { + OVR_ASSERT(e->GetCachedHash(pTable->SizeMask) == hashValue); + + if (e->GetCachedHash(pTable->SizeMask) == hashValue && e->Value == key) + { + // Found it. + return index; + } + // Values can not be equal at this point. + // That would mean that the hash key for the same value differs. + OVR_ASSERT(!(e->Value == key)); + + // Keep looking through the chain. + index = e->NextInChain; + if (index == (UPInt)-1) + break; // end of chain + + e = &E(index); + OVR_ASSERT(!e->IsEmpty()); + } + return -1; + } + + + // Add a new value to the HashSet table, under the specified key. + template + void add(const CRef& key, UPInt hashValue) + { + CheckExpand(); + hashValue &= pTable->SizeMask; + + pTable->EntryCount++; + + SPInt index = hashValue; + Entry* naturalEntry = &(E(index)); + + if (naturalEntry->IsEmpty()) + { + // Put the new Entry in. + new (naturalEntry) Entry(key, -1); + } + else + { + // Find a blank spot. + SPInt blankIndex = index; + do { + blankIndex = (blankIndex + 1) & pTable->SizeMask; + } while(!E(blankIndex).IsEmpty()); + + Entry* blankEntry = &E(blankIndex); + + if (naturalEntry->GetCachedHash(pTable->SizeMask) == (UPInt)index) + { + // Collision. Link into this chain. + + // Move existing list head. + new (blankEntry) Entry(*naturalEntry); // placement new, copy ctor + + // Put the new info in the natural Entry. + naturalEntry->Value = key; + naturalEntry->NextInChain = blankIndex; + } + else + { + // Existing Entry does not naturally + // belong in this slot. Existing + // Entry must be moved. + + // Find natural location of collided element (i.e. root of chain) + SPInt collidedIndex = naturalEntry->GetCachedHash(pTable->SizeMask); + OVR_ASSERT(collidedIndex >= 0 && collidedIndex <= (SPInt)pTable->SizeMask); + for (;;) + { + Entry* e = &E(collidedIndex); + if (e->NextInChain == index) + { + // Here's where we need to splice. + new (blankEntry) Entry(*naturalEntry); + e->NextInChain = blankIndex; + break; + } + collidedIndex = e->NextInChain; + OVR_ASSERT(collidedIndex >= 0 && collidedIndex <= (SPInt)pTable->SizeMask); + } + + // Put the new data in the natural Entry. + naturalEntry->Value = key; + naturalEntry->NextInChain = -1; + } + } + + // Record hash value: has effect only if cached node is used. + naturalEntry->SetCachedHash(hashValue); + } + + // Index access helpers. + Entry& E(UPInt index) + { + // Must have pTable and access needs to be within bounds. + OVR_ASSERT(index <= pTable->SizeMask); + return *(((Entry*) (pTable + 1)) + index); + } + const Entry& E(UPInt index) const + { + OVR_ASSERT(index <= pTable->SizeMask); + return *(((Entry*) (pTable + 1)) + index); + } + + + // Resize the HashSet table to the given size (Rehash the + // contents of the current table). The arg is the number of + // HashSet table entries, not the number of elements we should + // actually contain (which will be less than this). + void setRawCapacity(UPInt newSize) + { + if (newSize == 0) + { + // Special case. + Clear(); + return; + } + + // Minimum size; don't incur rehashing cost when expanding + // very small tables. Not that we perform this check before + // 'log2f' call to avoid fp exception with newSize == 1. + if (newSize < HashMinSize) + newSize = HashMinSize; + else + { + // Force newSize to be a power of two. + int bits = Alg::UpperBit(newSize-1) + 1; // Chop( Log2f((float)(newSize-1)) + 1); + OVR_ASSERT((UPInt(1) << bits) >= newSize); + newSize = UPInt(1) << bits; + } + + SelfType newHash; + newHash.pTable = (TableType*) + Allocator::Alloc( + sizeof(TableType) + sizeof(Entry) * newSize); + // Need to do something on alloc failure! + OVR_ASSERT(newHash.pTable); + + newHash.pTable->EntryCount = 0; + newHash.pTable->SizeMask = newSize - 1; + UPInt i, n; + + // Mark all entries as empty. + for (i = 0; i < newSize; i++) + newHash.E(i).NextInChain = -2; + + // Copy stuff to newHash + if (pTable) + { + for (i = 0, n = pTable->SizeMask; i <= n; i++) + { + Entry* e = &E(i); + if (e->IsEmpty() == false) + { + // Insert old Entry into new HashSet. + newHash.Add(e->Value); + // placement delete of old element + e->Clear(); + } + } + + // Delete our old data buffer. + Allocator::Free(pTable); + } + + // Steal newHash's data. + pTable = newHash.pTable; + newHash.pTable = NULL; + } + + struct TableType + { + UPInt EntryCount; + UPInt SizeMask; + // Entry array follows this structure + // in memory. + }; + TableType* pTable; +}; + + + +//----------------------------------------------------------------------------------- +template, + class AltHashF = HashF, + class Allocator = ContainerAllocator, + class Entry = HashsetCachedEntry > +class HashSet : public HashSetBase +{ +public: + typedef HashSetBase BaseType; + typedef HashSet SelfType; + + HashSet() { } + HashSet(int sizeHint) : BaseType(sizeHint) { } + HashSet(const SelfType& src) : BaseType(src) { } + ~HashSet() { } + + void operator = (const SelfType& src) { BaseType::Assign(src); } + + // Set a new or existing value under the key, to the value. + // Pass a different class of 'key' so that assignment reference object + // can be passed instead of the actual object. + template + void Set(const CRef& key) + { + BaseType::Set(key); + } + + template + inline void Add(const CRef& key) + { + BaseType::Add(key); + } + + // Hint the bucket count to >= n. + void Resize(UPInt n) + { + BaseType::SetCapacity(n); + } + + // Size the HashSet so that it can comfortably contain the given + // number of elements. If the HashSet already contains more + // elements than newSize, then this may be a no-op. + void SetCapacity(UPInt newSize) + { + BaseType::SetCapacity(newSize); + } + +}; + +// HashSet with uncached hash code; declared for convenience. +template, + class AltHashF = HashF, + class Allocator = ContainerAllocator > +class HashSetUncached : public HashSet > +{ +public: + + typedef HashSetUncached SelfType; + typedef HashSet > BaseType; + + // Delegated constructors. + HashSetUncached() { } + HashSetUncached(int sizeHint) : BaseType(sizeHint) { } + HashSetUncached(const SelfType& src) : BaseType(src) { } + ~HashSetUncached() { } + + void operator = (const SelfType& src) + { + BaseType::operator = (src); + } +}; + + +//----------------------------------------------------------------------------------- +// ***** Hash hash table implementation + +// Node for Hash - necessary so that Hash can delegate its implementation +// to HashSet. +template +struct HashNode +{ + typedef HashNode SelfType; + typedef C FirstType; + typedef U SecondType; + + C First; + U Second; + + // NodeRef is used to allow passing of elements into HashSet + // without using a temporary object. + struct NodeRef + { + const C* pFirst; + const U* pSecond; + + NodeRef(const C& f, const U& s) : pFirst(&f), pSecond(&s) { } + NodeRef(const NodeRef& src) : pFirst(src.pFirst), pSecond(src.pSecond) { } + + // Enable computation of ghash_node_hashf. + inline UPInt GetHash() const { return HashF()(*pFirst); } + // Necessary conversion to allow HashNode::operator == to work. + operator const C& () const { return *pFirst; } + }; + + // Note: No default constructor is necessary. + HashNode(const HashNode& src) : First(src.First), Second(src.Second) { } + HashNode(const NodeRef& src) : First(*src.pFirst), Second(*src.pSecond) { } + void operator = (const NodeRef& src) { First = *src.pFirst; Second = *src.pSecond; } + + template + bool operator == (const K& src) const { return (First == src); } + + template + static UPInt CalcHash(const K& data) { return HashF()(data); } + inline UPInt GetHash() const { return HashF()(First); } + + // Hash functors used with this node. A separate functor is used for alternative + // key lookup so that it does not need to access the '.First' element. + struct NodeHashF + { + template + UPInt operator()(const K& data) const { return data.GetHash(); } + }; + struct NodeAltHashF + { + template + UPInt operator()(const K& data) const { return HashNode::CalcHash(data); } + }; +}; + + + +// **** Extra hashset_entry types to allow NodeRef construction. + +// The big difference between the below types and the ones used in hash_set is that +// these allow initializing the node with 'typename C::NodeRef& keyRef', which +// is critical to avoid temporary node allocation on stack when using placement new. + +// Compact hash table Entry type that re-computes hash keys during hash traversal. +// Good to use if the hash function is cheap or the hash value is already cached in C. +template +class HashsetNodeEntry +{ +public: + // Internal chaining for collisions. + SPInt NextInChain; + C Value; + + HashsetNodeEntry() + : NextInChain(-2) { } + HashsetNodeEntry(const HashsetNodeEntry& e) + : NextInChain(e.NextInChain), Value(e.Value) { } + HashsetNodeEntry(const C& key, SPInt next) + : NextInChain(next), Value(key) { } + HashsetNodeEntry(const typename C::NodeRef& keyRef, SPInt next) + : NextInChain(next), Value(keyRef) { } + + bool IsEmpty() const { return NextInChain == -2; } + bool IsEndOfChain() const { return NextInChain == -1; } + UPInt GetCachedHash(UPInt maskValue) const { return HashF()(Value) & maskValue; } + void SetCachedHash(UPInt hashValue) { OVR_UNUSED(hashValue); } + + void Clear() + { + Value.~C(); // placement delete + NextInChain = -2; + } + // Free is only used from dtor of hash; Clear is used during regular operations: + // assignment, hash reallocations, value reassignments, so on. + void Free() { Clear(); } +}; + +// Hash table Entry type that caches the Entry hash value for nodes, so that it +// does not need to be re-computed during access. +template +class HashsetCachedNodeEntry +{ +public: + // Internal chaining for collisions. + SPInt NextInChain; + UPInt HashValue; + C Value; + + HashsetCachedNodeEntry() + : NextInChain(-2) { } + HashsetCachedNodeEntry(const HashsetCachedNodeEntry& e) + : NextInChain(e.NextInChain), HashValue(e.HashValue), Value(e.Value) { } + HashsetCachedNodeEntry(const C& key, SPInt next) + : NextInChain(next), Value(key) { } + HashsetCachedNodeEntry(const typename C::NodeRef& keyRef, SPInt next) + : NextInChain(next), Value(keyRef) { } + + bool IsEmpty() const { return NextInChain == -2; } + bool IsEndOfChain() const { return NextInChain == -1; } + UPInt GetCachedHash(UPInt maskValue) const { OVR_UNUSED(maskValue); return HashValue; } + void SetCachedHash(UPInt hashValue) { HashValue = hashValue; } + + void Clear() + { + Value.~C(); + NextInChain = -2; + } + // Free is only used from dtor of hash; Clear is used during regular operations: + // assignment, hash reallocations, value reassignments, so on. + void Free() { Clear(); } +}; + + +//----------------------------------------------------------------------------------- +template, + class Allocator = ContainerAllocator, + class HashNode = OVR::HashNode, + class Entry = HashsetCachedNodeEntry, + class Container = HashSet > +class Hash +{ +public: + OVR_MEMORY_REDEFINE_NEW(Hash) + + // Types used for hash_set. + typedef U ValueType; + typedef Hash SelfType; + + // Actual hash table itself, implemented as hash_set. + Container mHash; + +public: + Hash() { } + Hash(int sizeHint) : mHash(sizeHint) { } + Hash(const SelfType& src) : mHash(src.mHash) { } + ~Hash() { } + + void operator = (const SelfType& src) { mHash = src.mHash; } + + // Remove all entries from the Hash table. + inline void Clear() { mHash.Clear(); } + // Returns true if the Hash is empty. + inline bool IsEmpty() const { return mHash.IsEmpty(); } + + // Access (set). + inline void Set(const C& key, const U& value) + { + typename HashNode::NodeRef e(key, value); + mHash.Set(e); + } + inline void Add(const C& key, const U& value) + { + typename HashNode::NodeRef e(key, value); + mHash.Add(e); + } + + // Removes an element by clearing its Entry. + inline void Remove(const C& key) + { + mHash.RemoveAlt(key); + } + template + inline void RemoveAlt(const K& key) + { + mHash.RemoveAlt(key); + } + + // Retrieve the value under the given key. + // - If there's no value under the key, then return false and leave *pvalue alone. + // - If there is a value, return true, and Set *Pvalue to the Entry's value. + // - If value == NULL, return true or false according to the presence of the key. + bool Get(const C& key, U* pvalue) const + { + const HashNode* p = mHash.GetAlt(key); + if (p) + { + if (pvalue) + *pvalue = p->Second; + return true; + } + return false; + } + + template + bool GetAlt(const K& key, U* pvalue) const + { + const HashNode* p = mHash.GetAlt(key); + if (p) + { + if (pvalue) + *pvalue = p->Second; + return true; + } + return false; + } + + // Retrieve the pointer to a value under the given key. + // - If there's no value under the key, then return NULL. + // - If there is a value, return the pointer. + inline U* Get(const C& key) + { + HashNode* p = mHash.GetAlt(key); + return p ? &p->Second : 0; + } + inline const U* Get(const C& key) const + { + const HashNode* p = mHash.GetAlt(key); + return p ? &p->Second : 0; + } + + template + inline U* GetAlt(const K& key) + { + HashNode* p = mHash.GetAlt(key); + return p ? &p->Second : 0; + } + template + inline const U* GetAlt(const K& key) const + { + const HashNode* p = mHash.GetAlt(key); + return p ? &p->Second : 0; + } + + // Sizing methods - delegate to Hash. + inline UPInt GetSize() const { return mHash.GetSize(); } + inline void Resize(UPInt n) { mHash.Resize(n); } + inline void SetCapacity(UPInt newSize) { mHash.SetCapacity(newSize); } + + // Iterator API, like STL. + typedef typename Container::ConstIterator ConstIterator; + typedef typename Container::Iterator Iterator; + + inline Iterator Begin() { return mHash.Begin(); } + inline Iterator End() { return mHash.End(); } + inline ConstIterator Begin() const { return mHash.Begin(); } + inline ConstIterator End() const { return mHash.End(); } + + Iterator Find(const C& key) { return mHash.FindAlt(key); } + ConstIterator Find(const C& key) const { return mHash.FindAlt(key); } + + template + Iterator FindAlt(const K& key) { return mHash.FindAlt(key); } + template + ConstIterator FindAlt(const K& key) const { return mHash.FindAlt(key); } +}; + + + +// Hash with uncached hash code; declared for convenience. +template, class Allocator = ContainerAllocator > +class HashUncached + : public Hash, + HashsetNodeEntry, typename HashNode::NodeHashF> > +{ +public: + typedef HashUncached SelfType; + typedef Hash, + HashsetNodeEntry, + typename HashNode::NodeHashF> > BaseType; + + // Delegated constructors. + HashUncached() { } + HashUncached(int sizeHint) : BaseType(sizeHint) { } + HashUncached(const SelfType& src) : BaseType(src) { } + ~HashUncached() { } + void operator = (const SelfType& src) { BaseType::operator = (src); } +}; + + + +// And identity hash in which keys serve as hash value. Can be uncached, +// since hash computation is assumed cheap. +template, class HashF = IdentityHash > +class HashIdentity + : public HashUncached +{ +public: + typedef HashIdentity SelfType; + typedef HashUncached BaseType; + + // Delegated constructors. + HashIdentity() { } + HashIdentity(int sizeHint) : BaseType(sizeHint) { } + HashIdentity(const SelfType& src) : BaseType(src) { } + ~HashIdentity() { } + void operator = (const SelfType& src) { BaseType::operator = (src); } +}; + + +} // OVR + + +#ifdef OVR_DEFINE_NEW +#define new OVR_DEFINE_NEW +#endif + +#endif diff -r 59fc487ba58e -r b069a5c27388 libovr/Src/Kernel/OVR_KeyCodes.h --- a/libovr/Src/Kernel/OVR_KeyCodes.h Sat Sep 14 17:51:03 2013 +0300 +++ b/libovr/Src/Kernel/OVR_KeyCodes.h Sun Sep 15 04:10:05 2013 +0300 @@ -1,1 +1,240 @@ -/************************************************************************************ PublicHeader: OVR.h Filename : OVR_KeyCodes.h Content : Common keyboard constants Created : September 19, 2012 Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #ifndef OVR_KeyCodes_h #define OVR_KeyCodes_h namespace OVR { //----------------------------------------------------------------------------------- // ***** KeyCode // KeyCode enumeration defines platform-independent keyboard key constants. // Note that Key_A through Key_Z are mapped to capital ascii constants. enum KeyCode { // Key_None indicates that no key was specified. Key_None = 0, // A through Z and numbers 0 through 9. Key_A = 65, Key_B, Key_C, Key_D, Key_E, Key_F, Key_G, Key_H, Key_I, Key_J, Key_K, Key_L, Key_M, Key_N, Key_O, Key_P, Key_Q, Key_R, Key_S, Key_T, Key_U, Key_V, Key_W, Key_X, Key_Y, Key_Z, Key_Num0 = 48, Key_Num1, Key_Num2, Key_Num3, Key_Num4, Key_Num5, Key_Num6, Key_Num7, Key_Num8, Key_Num9, // Numeric keypad. Key_KP_0 = 0xa0, Key_KP_1, Key_KP_2, Key_KP_3, Key_KP_4, Key_KP_5, Key_KP_6, Key_KP_7, Key_KP_8, Key_KP_9, Key_KP_Multiply, Key_KP_Add, Key_KP_Enter, Key_KP_Subtract, Key_KP_Decimal, Key_KP_Divide, // Function keys. Key_F1 = 0xb0, Key_F2, Key_F3, Key_F4, Key_F5, Key_F6, Key_F7, Key_F8, Key_F9, Key_F10, Key_F11, Key_F12, Key_F13, Key_F14, Key_F15, // Other keys. Key_Backspace = 8, Key_Tab, Key_Clear = 12, Key_Return, Key_Shift = 16, Key_Control, Key_Alt, Key_Pause, Key_CapsLock = 20, // Toggle Key_Escape = 27, Key_Space = 32, Key_Quote = 39, Key_PageUp = 0xc0, Key_PageDown, Key_End, Key_Home, Key_Left, Key_Up, Key_Right, Key_Down, Key_Insert, Key_Delete, Key_Help, Key_Comma = 44, Key_Minus, Key_Slash = 47, Key_Period, Key_NumLock = 144, // Toggle Key_ScrollLock = 145, // Toggle Key_Semicolon = 59, Key_Equal = 61, Key_Bar = 192, Key_BracketLeft = 91, Key_Backslash, Key_BracketRight, Key_OEM_AX = 0xE1, // 'AX' key on Japanese AX keyboard Key_OEM_102 = 0xE2, // "<>" or "\|" on RT 102-key keyboard. Key_ICO_HELP = 0xE3, // Help key on ICO Key_ICO_00 = 0xE4, // 00 key on ICO Key_Meta, // Total number of keys. Key_CodeCount }; //----------------------------------------------------------------------------------- class KeyModifiers { public: enum { Key_ShiftPressed = 0x01, Key_CtrlPressed = 0x02, Key_AltPressed = 0x04, Key_MetaPressed = 0x08, Key_CapsToggled = 0x10, Key_NumToggled = 0x20, Key_ScrollToggled = 0x40, Initialized_Bit = 0x80, Initialized_Mask = 0xFF }; unsigned char States; KeyModifiers() : States(0) { } KeyModifiers(unsigned char st) : States((unsigned char)(st | Initialized_Bit)) { } void Reset() { States = 0; } bool IsShiftPressed() const { return (States & Key_ShiftPressed) != 0; } bool IsCtrlPressed() const { return (States & Key_CtrlPressed) != 0; } bool IsAltPressed() const { return (States & Key_AltPressed) != 0; } bool IsMetaPressed() const { return (States & Key_MetaPressed) != 0; } bool IsCapsToggled() const { return (States & Key_CapsToggled) != 0; } bool IsNumToggled() const { return (States & Key_NumToggled) != 0; } bool IsScrollToggled() const{ return (States & Key_ScrollToggled) != 0; } void SetShiftPressed(bool v = true) { (v) ? States |= Key_ShiftPressed : States &= ~Key_ShiftPressed; } void SetCtrlPressed(bool v = true) { (v) ? States |= Key_CtrlPressed : States &= ~Key_CtrlPressed; } void SetAltPressed(bool v = true) { (v) ? States |= Key_AltPressed : States &= ~Key_AltPressed; } void SetMetaPressed(bool v = true) { (v) ? States |= Key_MetaPressed : States &= ~Key_MetaPressed; } void SetCapsToggled(bool v = true) { (v) ? States |= Key_CapsToggled : States &= ~Key_CapsToggled; } void SetNumToggled(bool v = true) { (v) ? States |= Key_NumToggled : States &= ~Key_NumToggled; } void SetScrollToggled(bool v = true) { (v) ? States |= Key_ScrollToggled: States &= ~Key_ScrollToggled; } bool IsInitialized() const { return (States & Initialized_Mask) != 0; } }; //----------------------------------------------------------------------------------- /* enum PadKeyCode { Pad_None, // Indicates absence of key code. Pad_Back, Pad_Start, Pad_A, Pad_B, Pad_X, Pad_Y, Pad_R1, // RightShoulder; Pad_L1, // LeftShoulder; Pad_R2, // RightTrigger; Pad_L2, // LeftTrigger; Pad_Up, Pad_Down, Pad_Right, Pad_Left, Pad_Plus, Pad_Minus, Pad_1, Pad_2, Pad_H, Pad_C, Pad_Z, Pad_O, Pad_T, Pad_S, Pad_Select, Pad_Home, Pad_RT, // RightThumb; Pad_LT // LeftThumb; }; */ } // OVR #endif \ No newline at end of file +/************************************************************************************ + +PublicHeader: OVR.h +Filename : OVR_KeyCodes.h +Content : Common keyboard constants +Created : September 19, 2012 + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#ifndef OVR_KeyCodes_h +#define OVR_KeyCodes_h + +namespace OVR { + +//----------------------------------------------------------------------------------- +// ***** KeyCode + +// KeyCode enumeration defines platform-independent keyboard key constants. +// Note that Key_A through Key_Z are mapped to capital ascii constants. + +enum KeyCode +{ + // Key_None indicates that no key was specified. + Key_None = 0, + + // A through Z and numbers 0 through 9. + Key_A = 65, + Key_B, + Key_C, + Key_D, + Key_E, + Key_F, + Key_G, + Key_H, + Key_I, + Key_J, + Key_K, + Key_L, + Key_M, + Key_N, + Key_O, + Key_P, + Key_Q, + Key_R, + Key_S, + Key_T, + Key_U, + Key_V, + Key_W, + Key_X, + Key_Y, + Key_Z, + Key_Num0 = 48, + Key_Num1, + Key_Num2, + Key_Num3, + Key_Num4, + Key_Num5, + Key_Num6, + Key_Num7, + Key_Num8, + Key_Num9, + + // Numeric keypad. + Key_KP_0 = 0xa0, + Key_KP_1, + Key_KP_2, + Key_KP_3, + Key_KP_4, + Key_KP_5, + Key_KP_6, + Key_KP_7, + Key_KP_8, + Key_KP_9, + Key_KP_Multiply, + Key_KP_Add, + Key_KP_Enter, + Key_KP_Subtract, + Key_KP_Decimal, + Key_KP_Divide, + + // Function keys. + Key_F1 = 0xb0, + Key_F2, + Key_F3, + Key_F4, + Key_F5, + Key_F6, + Key_F7, + Key_F8, + Key_F9, + Key_F10, + Key_F11, + Key_F12, + Key_F13, + Key_F14, + Key_F15, + + // Other keys. + Key_Backspace = 8, + Key_Tab, + Key_Clear = 12, + Key_Return, + Key_Shift = 16, + Key_Control, + Key_Alt, + Key_Pause, + Key_CapsLock = 20, // Toggle + Key_Escape = 27, + Key_Space = 32, + Key_Quote = 39, + Key_PageUp = 0xc0, + Key_PageDown, + Key_End, + Key_Home, + Key_Left, + Key_Up, + Key_Right, + Key_Down, + Key_Insert, + Key_Delete, + Key_Help, + + Key_Comma = 44, + Key_Minus, + Key_Slash = 47, + Key_Period, + Key_NumLock = 144, // Toggle + Key_ScrollLock = 145, // Toggle + + Key_Semicolon = 59, + Key_Equal = 61, + Key_Bar = 192, + Key_BracketLeft = 91, + Key_Backslash, + Key_BracketRight, + + Key_OEM_AX = 0xE1, // 'AX' key on Japanese AX keyboard + Key_OEM_102 = 0xE2, // "<>" or "\|" on RT 102-key keyboard. + Key_ICO_HELP = 0xE3, // Help key on ICO + Key_ICO_00 = 0xE4, // 00 key on ICO + + Key_Meta, + + // Total number of keys. + Key_CodeCount +}; + + +//----------------------------------------------------------------------------------- + +class KeyModifiers +{ +public: + enum + { + Key_ShiftPressed = 0x01, + Key_CtrlPressed = 0x02, + Key_AltPressed = 0x04, + Key_MetaPressed = 0x08, + Key_CapsToggled = 0x10, + Key_NumToggled = 0x20, + Key_ScrollToggled = 0x40, + + Initialized_Bit = 0x80, + Initialized_Mask = 0xFF + }; + unsigned char States; + + KeyModifiers() : States(0) { } + KeyModifiers(unsigned char st) : States((unsigned char)(st | Initialized_Bit)) { } + + void Reset() { States = 0; } + + bool IsShiftPressed() const { return (States & Key_ShiftPressed) != 0; } + bool IsCtrlPressed() const { return (States & Key_CtrlPressed) != 0; } + bool IsAltPressed() const { return (States & Key_AltPressed) != 0; } + bool IsMetaPressed() const { return (States & Key_MetaPressed) != 0; } + bool IsCapsToggled() const { return (States & Key_CapsToggled) != 0; } + bool IsNumToggled() const { return (States & Key_NumToggled) != 0; } + bool IsScrollToggled() const{ return (States & Key_ScrollToggled) != 0; } + + void SetShiftPressed(bool v = true) { (v) ? States |= Key_ShiftPressed : States &= ~Key_ShiftPressed; } + void SetCtrlPressed(bool v = true) { (v) ? States |= Key_CtrlPressed : States &= ~Key_CtrlPressed; } + void SetAltPressed(bool v = true) { (v) ? States |= Key_AltPressed : States &= ~Key_AltPressed; } + void SetMetaPressed(bool v = true) { (v) ? States |= Key_MetaPressed : States &= ~Key_MetaPressed; } + void SetCapsToggled(bool v = true) { (v) ? States |= Key_CapsToggled : States &= ~Key_CapsToggled; } + void SetNumToggled(bool v = true) { (v) ? States |= Key_NumToggled : States &= ~Key_NumToggled; } + void SetScrollToggled(bool v = true) { (v) ? States |= Key_ScrollToggled: States &= ~Key_ScrollToggled; } + + bool IsInitialized() const { return (States & Initialized_Mask) != 0; } +}; + + +//----------------------------------------------------------------------------------- + +/* +enum PadKeyCode +{ + Pad_None, // Indicates absence of key code. + Pad_Back, + Pad_Start, + Pad_A, + Pad_B, + Pad_X, + Pad_Y, + Pad_R1, // RightShoulder; + Pad_L1, // LeftShoulder; + Pad_R2, // RightTrigger; + Pad_L2, // LeftTrigger; + Pad_Up, + Pad_Down, + Pad_Right, + Pad_Left, + Pad_Plus, + Pad_Minus, + Pad_1, + Pad_2, + Pad_H, + Pad_C, + Pad_Z, + Pad_O, + Pad_T, + Pad_S, + Pad_Select, + Pad_Home, + Pad_RT, // RightThumb; + Pad_LT // LeftThumb; +}; +*/ + +} // OVR + +#endif diff -r 59fc487ba58e -r b069a5c27388 libovr/Src/Kernel/OVR_List.h --- a/libovr/Src/Kernel/OVR_List.h Sat Sep 14 17:51:03 2013 +0300 +++ b/libovr/Src/Kernel/OVR_List.h Sun Sep 15 04:10:05 2013 +0300 @@ -1,1 +1,325 @@ -/************************************************************************************ PublicHeader: OVR Filename : OVR_List.h Content : Template implementation for doubly-connected linked List Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #ifndef OVR_List_h #define OVR_List_h #include "OVR_Types.h" namespace OVR { //----------------------------------------------------------------------------------- // ***** ListNode // // Base class for the elements of the intrusive linked list. // To store elements in the List do: // // struct MyData : ListNode // { // . . . // }; template struct ListNode { union { T* pPrev; void* pVoidPrev; }; union { T* pNext; void* pVoidNext; }; void RemoveNode() { pPrev->pNext = pNext; pNext->pPrev = pPrev; } // Removes us from the list and inserts pnew there instead. void ReplaceNodeWith(T* pnew) { pPrev->pNext = pnew; pNext->pPrev = pnew; pnew->pPrev = pPrev; pnew->pNext = pNext; } // Inserts the argument linked list node after us in the list. void InsertNodeAfter(T* p) { p->pPrev = pNext->pPrev; // this p->pNext = pNext; pNext->pPrev = p; pNext = p; } // Inserts the argument linked list node before us in the list. void InsertNodeBefore(T* p) { p->pNext = pNext->pPrev; // this p->pPrev = pPrev; pPrev->pNext = p; pPrev = p; } void Alloc_MoveTo(ListNode* pdest) { pdest->pNext = pNext; pdest->pPrev = pPrev; pPrev->pNext = (T*)pdest; pNext->pPrev = (T*)pdest; } }; //------------------------------------------------------------------------ // ***** List // // Doubly linked intrusive list. // The data type must be derived from ListNode. // // Adding: PushFront(), PushBack(). // Removing: Remove() - the element must be in the list! // Moving: BringToFront(), SendToBack() - the element must be in the list! // // Iterating: // MyData* data = MyList.GetFirst(); // while (!MyList.IsNull(data)) // { // . . . // data = MyList.GetNext(data); // } // // Removing: // MyData* data = MyList.GetFirst(); // while (!MyList.IsNull(data)) // { // MyData* next = MyList.GetNext(data); // if (ToBeRemoved(data)) // MyList.Remove(data); // data = next; // } // // List<> represents a doubly-linked list of T, where each T must derive // from ListNode. B specifies the base class that was directly // derived from ListNode, and is only necessary if there is an intermediate // inheritance chain. template class List { public: typedef T ValueType; List() { Root.pNext = Root.pPrev = (ValueType*)&Root; } void Clear() { Root.pNext = Root.pPrev = (ValueType*)&Root; } const ValueType* GetFirst() const { return (const ValueType*)Root.pNext; } const ValueType* GetLast () const { return (const ValueType*)Root.pPrev; } ValueType* GetFirst() { return (ValueType*)Root.pNext; } ValueType* GetLast () { return (ValueType*)Root.pPrev; } // Determine if list is empty (i.e.) points to itself. // Go through void* access to avoid issues with strict-aliasing optimizing out the // access after RemoveNode(), etc. bool IsEmpty() const { return Root.pVoidNext == (const T*)(const B*)&Root; } bool IsFirst(const ValueType* p) const { return p == Root.pNext; } bool IsLast (const ValueType* p) const { return p == Root.pPrev; } bool IsNull (const ValueType* p) const { return p == (const T*)(const B*)&Root; } inline static const ValueType* GetPrev(const ValueType* p) { return (const ValueType*)p->pPrev; } inline static const ValueType* GetNext(const ValueType* p) { return (const ValueType*)p->pNext; } inline static ValueType* GetPrev( ValueType* p) { return (ValueType*)p->pPrev; } inline static ValueType* GetNext( ValueType* p) { return (ValueType*)p->pNext; } void PushFront(ValueType* p) { p->pNext = Root.pNext; p->pPrev = (ValueType*)&Root; Root.pNext->pPrev = p; Root.pNext = p; } void PushBack(ValueType* p) { p->pPrev = Root.pPrev; p->pNext = (ValueType*)&Root; Root.pPrev->pNext = p; Root.pPrev = p; } static void Remove(ValueType* p) { p->pPrev->pNext = p->pNext; p->pNext->pPrev = p->pPrev; } void BringToFront(ValueType* p) { Remove(p); PushFront(p); } void SendToBack(ValueType* p) { Remove(p); PushBack(p); } // Appends the contents of the argument list to the front of this list; // items are removed from the argument list. void PushListToFront(List& src) { if (!src.IsEmpty()) { ValueType* pfirst = src.GetFirst(); ValueType* plast = src.GetLast(); src.Clear(); plast->pNext = Root.pNext; pfirst->pPrev = (ValueType*)&Root; Root.pNext->pPrev = plast; Root.pNext = pfirst; } } void PushListToBack(List& src) { if (!src.IsEmpty()) { ValueType* pfirst = src.GetFirst(); ValueType* plast = src.GetLast(); src.Clear(); plast->pNext = (ValueType*)&Root; pfirst->pPrev = Root.pPrev; Root.pPrev->pNext = pfirst; Root.pPrev = plast; } } // Removes all source list items after (and including) the 'pfirst' node from the // source list and adds them to out list. void PushFollowingListItemsToFront(List& src, ValueType *pfirst) { if (pfirst != &src.Root) { ValueType *plast = src.Root.pPrev; // Remove list remainder from source. pfirst->pPrev->pNext = (ValueType*)&src.Root; src.Root.pPrev = pfirst->pPrev; // Add the rest of the items to list. plast->pNext = Root.pNext; pfirst->pPrev = (ValueType*)&Root; Root.pNext->pPrev = plast; Root.pNext = pfirst; } } // Removes all source list items up to but NOT including the 'pend' node from the // source list and adds them to out list. void PushPrecedingListItemsToFront(List& src, ValueType *ptail) { if (src.GetFirst() != ptail) { ValueType *pfirst = src.Root.pNext; ValueType *plast = ptail->pPrev; // Remove list remainder from source. ptail->pPrev = (ValueType*)&src.Root; src.Root.pNext = ptail; // Add the rest of the items to list. plast->pNext = Root.pNext; pfirst->pPrev = (ValueType*)&Root; Root.pNext->pPrev = plast; Root.pNext = pfirst; } } // Removes a range of source list items starting at 'pfirst' and up to, but not including 'pend', // and adds them to out list. Note that source items MUST already be in the list. void PushListItemsToFront(ValueType *pfirst, ValueType *pend) { if (pfirst != pend) { ValueType *plast = pend->pPrev; // Remove list remainder from source. pfirst->pPrev->pNext = pend; pend->pPrev = pfirst->pPrev; // Add the rest of the items to list. plast->pNext = Root.pNext; pfirst->pPrev = (ValueType*)&Root; Root.pNext->pPrev = plast; Root.pNext = pfirst; } } void Alloc_MoveTo(List* pdest) { if (IsEmpty()) pdest->Clear(); else { pdest->Root.pNext = Root.pNext; pdest->Root.pPrev = Root.pPrev; Root.pNext->pPrev = (ValueType*)&pdest->Root; Root.pPrev->pNext = (ValueType*)&pdest->Root; } } private: // Copying is prohibited List(const List&); const List& operator = (const List&); ListNode Root; }; //------------------------------------------------------------------------ // ***** FreeListElements // // Remove all elements in the list and free them in the allocator template void FreeListElements(List& list, Allocator& allocator) { typename List::ValueType* self = list.GetFirst(); while(!list.IsNull(self)) { typename List::ValueType* next = list.GetNext(self); allocator.Free(self); self = next; } list.Clear(); } } // OVR #endif \ No newline at end of file +/************************************************************************************ + +PublicHeader: OVR +Filename : OVR_List.h +Content : Template implementation for doubly-connected linked List +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#ifndef OVR_List_h +#define OVR_List_h + +#include "OVR_Types.h" + +namespace OVR { + +//----------------------------------------------------------------------------------- +// ***** ListNode +// +// Base class for the elements of the intrusive linked list. +// To store elements in the List do: +// +// struct MyData : ListNode +// { +// . . . +// }; + +template +struct ListNode +{ + union { + T* pPrev; + void* pVoidPrev; + }; + union { + T* pNext; + void* pVoidNext; + }; + + void RemoveNode() + { + pPrev->pNext = pNext; + pNext->pPrev = pPrev; + } + + // Removes us from the list and inserts pnew there instead. + void ReplaceNodeWith(T* pnew) + { + pPrev->pNext = pnew; + pNext->pPrev = pnew; + pnew->pPrev = pPrev; + pnew->pNext = pNext; + } + + // Inserts the argument linked list node after us in the list. + void InsertNodeAfter(T* p) + { + p->pPrev = pNext->pPrev; // this + p->pNext = pNext; + pNext->pPrev = p; + pNext = p; + } + // Inserts the argument linked list node before us in the list. + void InsertNodeBefore(T* p) + { + p->pNext = pNext->pPrev; // this + p->pPrev = pPrev; + pPrev->pNext = p; + pPrev = p; + } + + void Alloc_MoveTo(ListNode* pdest) + { + pdest->pNext = pNext; + pdest->pPrev = pPrev; + pPrev->pNext = (T*)pdest; + pNext->pPrev = (T*)pdest; + } +}; + + +//------------------------------------------------------------------------ +// ***** List +// +// Doubly linked intrusive list. +// The data type must be derived from ListNode. +// +// Adding: PushFront(), PushBack(). +// Removing: Remove() - the element must be in the list! +// Moving: BringToFront(), SendToBack() - the element must be in the list! +// +// Iterating: +// MyData* data = MyList.GetFirst(); +// while (!MyList.IsNull(data)) +// { +// . . . +// data = MyList.GetNext(data); +// } +// +// Removing: +// MyData* data = MyList.GetFirst(); +// while (!MyList.IsNull(data)) +// { +// MyData* next = MyList.GetNext(data); +// if (ToBeRemoved(data)) +// MyList.Remove(data); +// data = next; +// } +// + +// List<> represents a doubly-linked list of T, where each T must derive +// from ListNode. B specifies the base class that was directly +// derived from ListNode, and is only necessary if there is an intermediate +// inheritance chain. + +template class List +{ +public: + typedef T ValueType; + + List() + { + Root.pNext = Root.pPrev = (ValueType*)&Root; + } + + void Clear() + { + Root.pNext = Root.pPrev = (ValueType*)&Root; + } + + const ValueType* GetFirst() const { return (const ValueType*)Root.pNext; } + const ValueType* GetLast () const { return (const ValueType*)Root.pPrev; } + ValueType* GetFirst() { return (ValueType*)Root.pNext; } + ValueType* GetLast () { return (ValueType*)Root.pPrev; } + + // Determine if list is empty (i.e.) points to itself. + // Go through void* access to avoid issues with strict-aliasing optimizing out the + // access after RemoveNode(), etc. + bool IsEmpty() const { return Root.pVoidNext == (const T*)(const B*)&Root; } + bool IsFirst(const ValueType* p) const { return p == Root.pNext; } + bool IsLast (const ValueType* p) const { return p == Root.pPrev; } + bool IsNull (const ValueType* p) const { return p == (const T*)(const B*)&Root; } + + inline static const ValueType* GetPrev(const ValueType* p) { return (const ValueType*)p->pPrev; } + inline static const ValueType* GetNext(const ValueType* p) { return (const ValueType*)p->pNext; } + inline static ValueType* GetPrev( ValueType* p) { return (ValueType*)p->pPrev; } + inline static ValueType* GetNext( ValueType* p) { return (ValueType*)p->pNext; } + + void PushFront(ValueType* p) + { + p->pNext = Root.pNext; + p->pPrev = (ValueType*)&Root; + Root.pNext->pPrev = p; + Root.pNext = p; + } + + void PushBack(ValueType* p) + { + p->pPrev = Root.pPrev; + p->pNext = (ValueType*)&Root; + Root.pPrev->pNext = p; + Root.pPrev = p; + } + + static void Remove(ValueType* p) + { + p->pPrev->pNext = p->pNext; + p->pNext->pPrev = p->pPrev; + } + + void BringToFront(ValueType* p) + { + Remove(p); + PushFront(p); + } + + void SendToBack(ValueType* p) + { + Remove(p); + PushBack(p); + } + + // Appends the contents of the argument list to the front of this list; + // items are removed from the argument list. + void PushListToFront(List& src) + { + if (!src.IsEmpty()) + { + ValueType* pfirst = src.GetFirst(); + ValueType* plast = src.GetLast(); + src.Clear(); + plast->pNext = Root.pNext; + pfirst->pPrev = (ValueType*)&Root; + Root.pNext->pPrev = plast; + Root.pNext = pfirst; + } + } + + void PushListToBack(List& src) + { + if (!src.IsEmpty()) + { + ValueType* pfirst = src.GetFirst(); + ValueType* plast = src.GetLast(); + src.Clear(); + plast->pNext = (ValueType*)&Root; + pfirst->pPrev = Root.pPrev; + Root.pPrev->pNext = pfirst; + Root.pPrev = plast; + } + } + + // Removes all source list items after (and including) the 'pfirst' node from the + // source list and adds them to out list. + void PushFollowingListItemsToFront(List& src, ValueType *pfirst) + { + if (pfirst != &src.Root) + { + ValueType *plast = src.Root.pPrev; + + // Remove list remainder from source. + pfirst->pPrev->pNext = (ValueType*)&src.Root; + src.Root.pPrev = pfirst->pPrev; + // Add the rest of the items to list. + plast->pNext = Root.pNext; + pfirst->pPrev = (ValueType*)&Root; + Root.pNext->pPrev = plast; + Root.pNext = pfirst; + } + } + + // Removes all source list items up to but NOT including the 'pend' node from the + // source list and adds them to out list. + void PushPrecedingListItemsToFront(List& src, ValueType *ptail) + { + if (src.GetFirst() != ptail) + { + ValueType *pfirst = src.Root.pNext; + ValueType *plast = ptail->pPrev; + + // Remove list remainder from source. + ptail->pPrev = (ValueType*)&src.Root; + src.Root.pNext = ptail; + + // Add the rest of the items to list. + plast->pNext = Root.pNext; + pfirst->pPrev = (ValueType*)&Root; + Root.pNext->pPrev = plast; + Root.pNext = pfirst; + } + } + + + // Removes a range of source list items starting at 'pfirst' and up to, but not including 'pend', + // and adds them to out list. Note that source items MUST already be in the list. + void PushListItemsToFront(ValueType *pfirst, ValueType *pend) + { + if (pfirst != pend) + { + ValueType *plast = pend->pPrev; + + // Remove list remainder from source. + pfirst->pPrev->pNext = pend; + pend->pPrev = pfirst->pPrev; + // Add the rest of the items to list. + plast->pNext = Root.pNext; + pfirst->pPrev = (ValueType*)&Root; + Root.pNext->pPrev = plast; + Root.pNext = pfirst; + } + } + + + void Alloc_MoveTo(List* pdest) + { + if (IsEmpty()) + pdest->Clear(); + else + { + pdest->Root.pNext = Root.pNext; + pdest->Root.pPrev = Root.pPrev; + + Root.pNext->pPrev = (ValueType*)&pdest->Root; + Root.pPrev->pNext = (ValueType*)&pdest->Root; + } + } + + +private: + // Copying is prohibited + List(const List&); + const List& operator = (const List&); + + ListNode Root; +}; + + +//------------------------------------------------------------------------ +// ***** FreeListElements +// +// Remove all elements in the list and free them in the allocator + +template +void FreeListElements(List& list, Allocator& allocator) +{ + typename List::ValueType* self = list.GetFirst(); + while(!list.IsNull(self)) + { + typename List::ValueType* next = list.GetNext(self); + allocator.Free(self); + self = next; + } + list.Clear(); +} + +} // OVR + +#endif diff -r 59fc487ba58e -r b069a5c27388 libovr/Src/Kernel/OVR_Log.cpp --- a/libovr/Src/Kernel/OVR_Log.cpp Sat Sep 14 17:51:03 2013 +0300 +++ b/libovr/Src/Kernel/OVR_Log.cpp Sun Sep 15 04:10:05 2013 +0300 @@ -1,1 +1,173 @@ -/************************************************************************************ Filename : OVR_Log.cpp Content : Logging support Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #include "OVR_Log.h" #include "OVR_Std.h" #include #include #if defined(OVR_OS_WIN32) #include #elif defined(OVR_OS_ANDROID) #include #endif namespace OVR { // Global Log pointer. Log* volatile OVR_GlobalLog = 0; //----------------------------------------------------------------------------------- // ***** Log Implementation Log::~Log() { // Clear out global log if (this == OVR_GlobalLog) { // TBD: perhaps we should ASSERT if this happens before system shutdown? OVR_GlobalLog = 0; } } void Log::LogMessageVarg(LogMessageType messageType, const char* fmt, va_list argList) { if ((messageType & LoggingMask) == 0) return; #ifndef OVR_BUILD_DEBUG if (IsDebugMessage(messageType)) return; #endif char buffer[MaxLogBufferMessageSize]; FormatLog(buffer, MaxLogBufferMessageSize, messageType, fmt, argList); DefaultLogOutput(buffer, IsDebugMessage(messageType)); } void OVR::Log::LogMessage(LogMessageType messageType, const char* pfmt, ...) { va_list argList; va_start(argList, pfmt); LogMessageVarg(messageType, pfmt, argList); va_end(argList); } void Log::FormatLog(char* buffer, unsigned bufferSize, LogMessageType messageType, const char* fmt, va_list argList) { bool addNewline = true; switch(messageType) { case Log_Error: OVR_strcpy(buffer, bufferSize, "Error: "); break; case Log_Debug: OVR_strcpy(buffer, bufferSize, "Debug: "); break; case Log_Assert: OVR_strcpy(buffer, bufferSize, "Assert: "); break; case Log_Text: buffer[0] = 0; addNewline = false; break; case Log_DebugText: buffer[0] = 0; addNewline = false; break; default: buffer[0] = 0; addNewline = false; break; } UPInt prefixLength = OVR_strlen(buffer); char *buffer2 = buffer + prefixLength; OVR_vsprintf(buffer2, bufferSize - prefixLength, fmt, argList); if (addNewline) OVR_strcat(buffer, bufferSize, "\n"); } void Log::DefaultLogOutput(const char* formattedText, bool debug) { #if defined(OVR_OS_WIN32) // Under Win32, output regular messages to console if it exists; debug window otherwise. static DWORD dummyMode; static bool hasConsole = (GetStdHandle(STD_OUTPUT_HANDLE) != INVALID_HANDLE_VALUE) && (GetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), &dummyMode)); if (!hasConsole || debug) { ::OutputDebugStringA(formattedText); } else { fputs(formattedText, stdout); } #elif defined(OVR_OS_ANDROID) __android_log_write(ANDROID_LOG_INFO, "OVR", formattedText); #else fputs(formattedText, stdout); #endif // Just in case. OVR_UNUSED2(formattedText, debug); } //static void Log::SetGlobalLog(Log *log) { OVR_GlobalLog = log; } //static Log* Log::GetGlobalLog() { // No global log by default? // if (!OVR_GlobalLog) // OVR_GlobalLog = GetDefaultLog(); return OVR_GlobalLog; } //static Log* Log::GetDefaultLog() { // Create default log pointer statically so that it can be used // even during startup. static Log defaultLog; return &defaultLog; } //----------------------------------------------------------------------------------- // ***** Global Logging functions #define OVR_LOG_FUNCTION_IMPL(Name) \ void Log##Name(const char* fmt, ...) \ { \ if (OVR_GlobalLog) \ { \ va_list argList; va_start(argList, fmt); \ OVR_GlobalLog->LogMessageVarg(Log_##Name, fmt, argList); \ va_end(argList); \ } \ } OVR_LOG_FUNCTION_IMPL(Text) OVR_LOG_FUNCTION_IMPL(Error) #ifdef OVR_BUILD_DEBUG OVR_LOG_FUNCTION_IMPL(DebugText) OVR_LOG_FUNCTION_IMPL(Debug) OVR_LOG_FUNCTION_IMPL(Assert) #endif } // OVR \ No newline at end of file +/************************************************************************************ + +Filename : OVR_Log.cpp +Content : Logging support +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#include "OVR_Log.h" +#include "OVR_Std.h" +#include +#include + +#if defined(OVR_OS_WIN32) +#include +#elif defined(OVR_OS_ANDROID) +#include +#endif + +namespace OVR { + +// Global Log pointer. +Log* volatile OVR_GlobalLog = 0; + +//----------------------------------------------------------------------------------- +// ***** Log Implementation + +Log::~Log() +{ + // Clear out global log + if (this == OVR_GlobalLog) + { + // TBD: perhaps we should ASSERT if this happens before system shutdown? + OVR_GlobalLog = 0; + } +} + +void Log::LogMessageVarg(LogMessageType messageType, const char* fmt, va_list argList) +{ + if ((messageType & LoggingMask) == 0) + return; +#ifndef OVR_BUILD_DEBUG + if (IsDebugMessage(messageType)) + return; +#endif + + char buffer[MaxLogBufferMessageSize]; + FormatLog(buffer, MaxLogBufferMessageSize, messageType, fmt, argList); + DefaultLogOutput(buffer, IsDebugMessage(messageType)); +} + +void OVR::Log::LogMessage(LogMessageType messageType, const char* pfmt, ...) +{ + va_list argList; + va_start(argList, pfmt); + LogMessageVarg(messageType, pfmt, argList); + va_end(argList); +} + + +void Log::FormatLog(char* buffer, unsigned bufferSize, LogMessageType messageType, + const char* fmt, va_list argList) +{ + bool addNewline = true; + + switch(messageType) + { + case Log_Error: OVR_strcpy(buffer, bufferSize, "Error: "); break; + case Log_Debug: OVR_strcpy(buffer, bufferSize, "Debug: "); break; + case Log_Assert: OVR_strcpy(buffer, bufferSize, "Assert: "); break; + case Log_Text: buffer[0] = 0; addNewline = false; break; + case Log_DebugText: buffer[0] = 0; addNewline = false; break; + default: + buffer[0] = 0; + addNewline = false; + break; + } + + UPInt prefixLength = OVR_strlen(buffer); + char *buffer2 = buffer + prefixLength; + OVR_vsprintf(buffer2, bufferSize - prefixLength, fmt, argList); + + if (addNewline) + OVR_strcat(buffer, bufferSize, "\n"); +} + + +void Log::DefaultLogOutput(const char* formattedText, bool debug) +{ + +#if defined(OVR_OS_WIN32) + // Under Win32, output regular messages to console if it exists; debug window otherwise. + static DWORD dummyMode; + static bool hasConsole = (GetStdHandle(STD_OUTPUT_HANDLE) != INVALID_HANDLE_VALUE) && + (GetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), &dummyMode)); + + if (!hasConsole || debug) + { + ::OutputDebugStringA(formattedText); + } + else + { + fputs(formattedText, stdout); + } + +#elif defined(OVR_OS_ANDROID) + __android_log_write(ANDROID_LOG_INFO, "OVR", formattedText); + +#else + fputs(formattedText, stdout); + +#endif + + // Just in case. + OVR_UNUSED2(formattedText, debug); +} + + +//static +void Log::SetGlobalLog(Log *log) +{ + OVR_GlobalLog = log; +} +//static +Log* Log::GetGlobalLog() +{ +// No global log by default? +// if (!OVR_GlobalLog) +// OVR_GlobalLog = GetDefaultLog(); + return OVR_GlobalLog; +} + +//static +Log* Log::GetDefaultLog() +{ + // Create default log pointer statically so that it can be used + // even during startup. + static Log defaultLog; + return &defaultLog; +} + + +//----------------------------------------------------------------------------------- +// ***** Global Logging functions + +#define OVR_LOG_FUNCTION_IMPL(Name) \ + void Log##Name(const char* fmt, ...) \ + { \ + if (OVR_GlobalLog) \ + { \ + va_list argList; va_start(argList, fmt); \ + OVR_GlobalLog->LogMessageVarg(Log_##Name, fmt, argList); \ + va_end(argList); \ + } \ + } + +OVR_LOG_FUNCTION_IMPL(Text) +OVR_LOG_FUNCTION_IMPL(Error) + +#ifdef OVR_BUILD_DEBUG +OVR_LOG_FUNCTION_IMPL(DebugText) +OVR_LOG_FUNCTION_IMPL(Debug) +OVR_LOG_FUNCTION_IMPL(Assert) +#endif + +} // OVR diff -r 59fc487ba58e -r b069a5c27388 libovr/Src/Kernel/OVR_Log.h --- a/libovr/Src/Kernel/OVR_Log.h Sat Sep 14 17:51:03 2013 +0300 +++ b/libovr/Src/Kernel/OVR_Log.h Sun Sep 15 04:10:05 2013 +0300 @@ -1,1 +1,193 @@ -/************************************************************************************ PublicHeader: OVR Filename : OVR_Log.h Content : Logging support Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #ifndef OVR_Log_h #define OVR_Log_h #include "OVR_Types.h" #include namespace OVR { //----------------------------------------------------------------------------------- // ***** Logging Constants // LogMaskConstants defined bit mask constants that describe what log messages // should be displayed. enum LogMaskConstants { LogMask_Regular = 0x100, LogMask_Debug = 0x200, LogMask_None = 0, LogMask_All = LogMask_Regular|LogMask_Debug }; // LogMessageType describes the type of the log message, controls when it is // displayed and what prefix/suffix is given to it. Messages are subdivided into // regular and debug logging types. Debug logging is only generated in debug builds. // // Log_Text - General output text displayed without prefix or new-line. // Used in OVR libraries for general log flow messages // such as "Device Initialized". // // Log_Error - Error message output with "Error: %s\n", intended for // application/sample-level use only, in cases where an expected // operation failed. OVR libraries should not use this internally, // reporting status codes instead. // // Log_DebugText - Message without prefix or new lines; output in Debug build only. // // Log_Debug - Debug-build only message, formatted with "Debug: %s\n". // Intended to comment on incorrect API usage that doesn't lead // to crashes but can be avoided with proper use. // There is no Debug Error on purpose, since real errors should // be handled by API user. // // Log_Assert - Debug-build only message, formatted with "Assert: %s\n". // Intended for severe unrecoverable conditions in library // source code. Generated though OVR_ASSERT_MSG(c, "Text"). enum LogMessageType { // General Logging Log_Text = LogMask_Regular | 0, Log_Error = LogMask_Regular | 1, // "Error: %s\n". // Debug-only messages (not generated in release build) Log_DebugText = LogMask_Debug | 0, Log_Debug = LogMask_Debug | 1, // "Debug: %s\n". Log_Assert = LogMask_Debug | 2, // "Assert: %s\n". }; // LOG_VAARG_ATTRIBUTE macro, enforces printf-style fromatting for message types #ifdef __GNUC__ # define OVR_LOG_VAARG_ATTRIBUTE(a,b) __attribute__((format (printf, a, b))) #else # define OVR_LOG_VAARG_ATTRIBUTE(a,b) #endif //----------------------------------------------------------------------------------- // ***** Log // Log defines a base class interface that can be implemented to catch both // debug and runtime messages. // Debug logging can be overridden by calling Log::SetGlobalLog. class Log { friend class System; public: Log(unsigned logMask = LogMask_Debug) : LoggingMask(logMask) { } virtual ~Log(); // Log formating buffer size used by default LogMessageVarg. Longer strings are truncated. enum { MaxLogBufferMessageSize = 2048 }; unsigned GetLoggingMask() const { return LoggingMask; } void SetLoggingMask(unsigned logMask) { LoggingMask = logMask; } // This virtual function receives all the messages, // developers should override this function in order to do custom logging virtual void LogMessageVarg(LogMessageType messageType, const char* fmt, va_list argList); // Call the logging function with specific message type, with no type filtering. void LogMessage(LogMessageType messageType, const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(3,4); // Helper used by LogMessageVarg to format the log message, writing the resulting // string into buffer. It formats text based on fmt and appends prefix/new line // based on LogMessageType. static void FormatLog(char* buffer, unsigned bufferSize, LogMessageType messageType, const char* fmt, va_list argList); // Default log output implementation used by by LogMessageVarg. // Debug flag may be used to re-direct output on some platforms, but doesn't // necessarily disable it in release builds; that is the job of the called. static void DefaultLogOutput(const char* textBuffer, bool debug); // Determines if the specified message type is for debugging only. static bool IsDebugMessage(LogMessageType messageType) { return (messageType & LogMask_Debug) != 0; } // *** Global APIs // Global Log registration APIs. // - Global log is used for OVR_DEBUG messages. Set global log to null (0) // to disable all logging. static void SetGlobalLog(Log *log); static Log* GetGlobalLog(); // Returns default log singleton instance. static Log* GetDefaultLog(); // Applies logMask to the default log and returns a pointer to it. // By default, only Debug logging is enabled, so to avoid SDK generating console // messages in user app (those are always disabled in release build, // even if the flag is set). This function is useful in System constructor. static Log* ConfigureDefaultLog(unsigned logMask = LogMask_Debug) { Log* log = GetDefaultLog(); log->SetLoggingMask(logMask); return log; } private: // Logging mask described by LogMaskConstants. unsigned LoggingMask; }; //----------------------------------------------------------------------------------- // ***** Global Logging Functions and Debug Macros // These functions will output text to global log with semantics described by // their LogMessageType. void LogText(const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(1,2); void LogError(const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(1,2); #ifdef OVR_BUILD_DEBUG // Debug build only logging. void LogDebugText(const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(1,2); void LogDebug(const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(1,2); void LogAssert(const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(1,2); // Macro to do debug logging, printf-style. // An extra set of set of parenthesis must be used around arguments, // as in: OVR_LOG_DEBUG(("Value %d", 2)). #define OVR_DEBUG_LOG(args) do { OVR::LogDebug args; } while(0) #define OVR_DEBUG_LOG_TEXT(args) do { OVR::LogDebugText args; } while(0) #define OVR_ASSERT_LOG(c, args) do { if (!(c)) { OVR::LogAssert args; OVR_DEBUG_BREAK; } } while(0) #else // If not in debug build, macros do nothing. #define OVR_DEBUG_LOG(args) ((void)0) #define OVR_DEBUG_LOG_TEXT(args) ((void)0) #define OVR_ASSERT_LOG(c, args) ((void)0) #endif } // OVR #endif \ No newline at end of file +/************************************************************************************ + +PublicHeader: OVR +Filename : OVR_Log.h +Content : Logging support +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#ifndef OVR_Log_h +#define OVR_Log_h + +#include "OVR_Types.h" +#include + +namespace OVR { + +//----------------------------------------------------------------------------------- +// ***** Logging Constants + +// LogMaskConstants defined bit mask constants that describe what log messages +// should be displayed. +enum LogMaskConstants +{ + LogMask_Regular = 0x100, + LogMask_Debug = 0x200, + LogMask_None = 0, + LogMask_All = LogMask_Regular|LogMask_Debug +}; + + +// LogMessageType describes the type of the log message, controls when it is +// displayed and what prefix/suffix is given to it. Messages are subdivided into +// regular and debug logging types. Debug logging is only generated in debug builds. +// +// Log_Text - General output text displayed without prefix or new-line. +// Used in OVR libraries for general log flow messages +// such as "Device Initialized". +// +// Log_Error - Error message output with "Error: %s\n", intended for +// application/sample-level use only, in cases where an expected +// operation failed. OVR libraries should not use this internally, +// reporting status codes instead. +// +// Log_DebugText - Message without prefix or new lines; output in Debug build only. +// +// Log_Debug - Debug-build only message, formatted with "Debug: %s\n". +// Intended to comment on incorrect API usage that doesn't lead +// to crashes but can be avoided with proper use. +// There is no Debug Error on purpose, since real errors should +// be handled by API user. +// +// Log_Assert - Debug-build only message, formatted with "Assert: %s\n". +// Intended for severe unrecoverable conditions in library +// source code. Generated though OVR_ASSERT_MSG(c, "Text"). + +enum LogMessageType +{ + // General Logging + Log_Text = LogMask_Regular | 0, + Log_Error = LogMask_Regular | 1, // "Error: %s\n". + + // Debug-only messages (not generated in release build) + Log_DebugText = LogMask_Debug | 0, + Log_Debug = LogMask_Debug | 1, // "Debug: %s\n". + Log_Assert = LogMask_Debug | 2, // "Assert: %s\n". +}; + + +// LOG_VAARG_ATTRIBUTE macro, enforces printf-style fromatting for message types +#ifdef __GNUC__ +# define OVR_LOG_VAARG_ATTRIBUTE(a,b) __attribute__((format (printf, a, b))) +#else +# define OVR_LOG_VAARG_ATTRIBUTE(a,b) +#endif + + +//----------------------------------------------------------------------------------- +// ***** Log + +// Log defines a base class interface that can be implemented to catch both +// debug and runtime messages. +// Debug logging can be overridden by calling Log::SetGlobalLog. + +class Log +{ + friend class System; +public: + Log(unsigned logMask = LogMask_Debug) : LoggingMask(logMask) { } + virtual ~Log(); + + // Log formating buffer size used by default LogMessageVarg. Longer strings are truncated. + enum { MaxLogBufferMessageSize = 2048 }; + + unsigned GetLoggingMask() const { return LoggingMask; } + void SetLoggingMask(unsigned logMask) { LoggingMask = logMask; } + + // This virtual function receives all the messages, + // developers should override this function in order to do custom logging + virtual void LogMessageVarg(LogMessageType messageType, const char* fmt, va_list argList); + + // Call the logging function with specific message type, with no type filtering. + void LogMessage(LogMessageType messageType, + const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(3,4); + + + // Helper used by LogMessageVarg to format the log message, writing the resulting + // string into buffer. It formats text based on fmt and appends prefix/new line + // based on LogMessageType. + static void FormatLog(char* buffer, unsigned bufferSize, LogMessageType messageType, + const char* fmt, va_list argList); + + // Default log output implementation used by by LogMessageVarg. + // Debug flag may be used to re-direct output on some platforms, but doesn't + // necessarily disable it in release builds; that is the job of the called. + static void DefaultLogOutput(const char* textBuffer, bool debug); + + // Determines if the specified message type is for debugging only. + static bool IsDebugMessage(LogMessageType messageType) + { + return (messageType & LogMask_Debug) != 0; + } + + // *** Global APIs + + // Global Log registration APIs. + // - Global log is used for OVR_DEBUG messages. Set global log to null (0) + // to disable all logging. + static void SetGlobalLog(Log *log); + static Log* GetGlobalLog(); + + // Returns default log singleton instance. + static Log* GetDefaultLog(); + + // Applies logMask to the default log and returns a pointer to it. + // By default, only Debug logging is enabled, so to avoid SDK generating console + // messages in user app (those are always disabled in release build, + // even if the flag is set). This function is useful in System constructor. + static Log* ConfigureDefaultLog(unsigned logMask = LogMask_Debug) + { + Log* log = GetDefaultLog(); + log->SetLoggingMask(logMask); + return log; + } + +private: + // Logging mask described by LogMaskConstants. + unsigned LoggingMask; +}; + + +//----------------------------------------------------------------------------------- +// ***** Global Logging Functions and Debug Macros + +// These functions will output text to global log with semantics described by +// their LogMessageType. +void LogText(const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(1,2); +void LogError(const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(1,2); + +#ifdef OVR_BUILD_DEBUG + + // Debug build only logging. + void LogDebugText(const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(1,2); + void LogDebug(const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(1,2); + void LogAssert(const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(1,2); + + // Macro to do debug logging, printf-style. + // An extra set of set of parenthesis must be used around arguments, + // as in: OVR_LOG_DEBUG(("Value %d", 2)). + #define OVR_DEBUG_LOG(args) do { OVR::LogDebug args; } while(0) + #define OVR_DEBUG_LOG_TEXT(args) do { OVR::LogDebugText args; } while(0) + + #define OVR_ASSERT_LOG(c, args) do { if (!(c)) { OVR::LogAssert args; OVR_DEBUG_BREAK; } } while(0) + +#else + + // If not in debug build, macros do nothing. + #define OVR_DEBUG_LOG(args) ((void)0) + #define OVR_DEBUG_LOG_TEXT(args) ((void)0) + #define OVR_ASSERT_LOG(c, args) ((void)0) + +#endif + +} // OVR + +#endif diff -r 59fc487ba58e -r b069a5c27388 libovr/Src/Kernel/OVR_Math.cpp --- a/libovr/Src/Kernel/OVR_Math.cpp Sat Sep 14 17:51:03 2013 +0300 +++ b/libovr/Src/Kernel/OVR_Math.cpp Sun Sep 15 04:10:05 2013 +0300 @@ -1,1 +1,153 @@ -/************************************************************************************ Filename : OVR_Math.h Content : Implementation of 3D primitives such as vectors, matrices. Created : September 4, 2012 Authors : Andrew Reisse, Michael Antonov Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. *************************************************************************************/ #include "OVR_Math.h" #include namespace OVR { //------------------------------------------------------------------------------------- // ***** Math // Single-precision Math constants class. const float Math::Pi = 3.1415926f; const float Math::TwoPi = 3.1415926f * 2; const float Math::PiOver2 = 3.1415926f / 2.0f; const float Math::PiOver4 = 3.1415926f / 4.0f; const float Math::E = 2.7182818f; const float Math::MaxValue = FLT_MAX; const float Math::MinPositiveValue = FLT_MIN; const float Math::RadToDegreeFactor = 360.0f / Math::TwoPi; const float Math::DegreeToRadFactor = Math::TwoPi / 360.0f; const float Math::Tolerance = 0.00001f; const float Math::SingularityRadius = 0.0000001f; // Use for Gimbal lock numerical problems // Double-precision Math constants class. const double Math::Pi = 3.14159265358979; const double Math::TwoPi = 3.14159265358979 * 2; const double Math::PiOver2 = 3.14159265358979 / 2.0; const double Math::PiOver4 = 3.14159265358979 / 4.0; const double Math::E = 2.71828182845905; const double Math::MaxValue = DBL_MAX; const double Math::MinPositiveValue = DBL_MIN; const double Math::RadToDegreeFactor = 360.0 / Math::TwoPi; const double Math::DegreeToRadFactor = Math::TwoPi / 360.0; const double Math::Tolerance = 0.00001; const double Math::SingularityRadius = 0.000000000001; // Use for Gimbal lock numerical problems //------------------------------------------------------------------------------------- // ***** Matrix4f Matrix4f Matrix4f::LookAtRH(const Vector3f& eye, const Vector3f& at, const Vector3f& up) { Vector3f z = (eye - at).Normalized(); // Forward Vector3f x = up.Cross(z).Normalized(); // Right Vector3f y = z.Cross(x); Matrix4f m(x.x, x.y, x.z, -(x * eye), y.x, y.y, y.z, -(y * eye), z.x, z.y, z.z, -(z * eye), 0, 0, 0, 1 ); return m; } Matrix4f Matrix4f::LookAtLH(const Vector3f& eye, const Vector3f& at, const Vector3f& up) { Vector3f z = (at - eye).Normalized(); // Forward Vector3f x = up.Cross(z).Normalized(); // Right Vector3f y = z.Cross(x); Matrix4f m(x.x, x.y, x.z, -(x * eye), y.x, y.y, y.z, -(y * eye), z.x, z.y, z.z, -(z * eye), 0, 0, 0, 1 ); return m; } Matrix4f Matrix4f::PerspectiveLH(float yfov, float aspect, float znear, float zfar) { Matrix4f m; float tanHalfFov = tan(yfov * 0.5f); m.M[0][0] = 1.0f / (aspect * tanHalfFov); m.M[1][1] = 1.0f / tanHalfFov; m.M[2][2] = zfar / (zfar - znear); m.M[3][2] = 1.0f; m.M[2][3] = (zfar * znear) / (znear - zfar); m.M[3][3] = 0.0f; // Note: Post-projection matrix result assumes Left-Handed coordinate system, // with Y up, X right and Z forward. This supports positive z-buffer values. return m; } Matrix4f Matrix4f::PerspectiveRH(float yfov, float aspect, float znear, float zfar) { Matrix4f m; float tanHalfFov = tan(yfov * 0.5f); m.M[0][0] = 1.0f / (aspect * tanHalfFov); m.M[1][1] = 1.0f / tanHalfFov; m.M[2][2] = zfar / (znear - zfar); // m.M[2][2] = zfar / (zfar - znear); m.M[3][2] = -1.0f; m.M[2][3] = (zfar * znear) / (znear - zfar); m.M[3][3] = 0.0f; // Note: Post-projection matrix result assumes Left-Handed coordinate system, // with Y up, X right and Z forward. This supports positive z-buffer values. // This is the case even for RHS cooridnate input. return m; } /* OffCenterLH 2*zn/(r-l) 0 0 0 0 2*zn/(t-b) 0 0 (l+r)/(l-r) (t+b)/(b-t) zf/(zf-zn) 1 0 0 zn*zf/(zn-zf) 0 */ Matrix4f Matrix4f::Ortho2D(float w, float h) { Matrix4f m; m.M[0][0] = 2.0f/w; m.M[1][1] = -2.0f/h; m.M[0][3] = -1.0; m.M[1][3] = 1.0; m.M[2][2] = 0; return m; } } \ No newline at end of file +/************************************************************************************ + +Filename : OVR_Math.h +Content : Implementation of 3D primitives such as vectors, matrices. +Created : September 4, 2012 +Authors : Andrew Reisse, Michael Antonov + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#include "OVR_Math.h" + +#include + +namespace OVR { + + +//------------------------------------------------------------------------------------- +// ***** Math + + +// Single-precision Math constants class. +const float Math::Pi = 3.1415926f; +const float Math::TwoPi = 3.1415926f * 2; +const float Math::PiOver2 = 3.1415926f / 2.0f; +const float Math::PiOver4 = 3.1415926f / 4.0f; +const float Math::E = 2.7182818f; + +const float Math::MaxValue = FLT_MAX; +const float Math::MinPositiveValue = FLT_MIN; + +const float Math::RadToDegreeFactor = 360.0f / Math::TwoPi; +const float Math::DegreeToRadFactor = Math::TwoPi / 360.0f; + +const float Math::Tolerance = 0.00001f; +const float Math::SingularityRadius = 0.0000001f; // Use for Gimbal lock numerical problems + + +// Double-precision Math constants class. +const double Math::Pi = 3.14159265358979; +const double Math::TwoPi = 3.14159265358979 * 2; +const double Math::PiOver2 = 3.14159265358979 / 2.0; +const double Math::PiOver4 = 3.14159265358979 / 4.0; +const double Math::E = 2.71828182845905; + +const double Math::MaxValue = DBL_MAX; +const double Math::MinPositiveValue = DBL_MIN; + +const double Math::RadToDegreeFactor = 360.0 / Math::TwoPi; +const double Math::DegreeToRadFactor = Math::TwoPi / 360.0; + +const double Math::Tolerance = 0.00001; +const double Math::SingularityRadius = 0.000000000001; // Use for Gimbal lock numerical problems + + + +//------------------------------------------------------------------------------------- +// ***** Matrix4f + + +Matrix4f Matrix4f::LookAtRH(const Vector3f& eye, const Vector3f& at, const Vector3f& up) +{ + Vector3f z = (eye - at).Normalized(); // Forward + Vector3f x = up.Cross(z).Normalized(); // Right + Vector3f y = z.Cross(x); + + Matrix4f m(x.x, x.y, x.z, -(x * eye), + y.x, y.y, y.z, -(y * eye), + z.x, z.y, z.z, -(z * eye), + 0, 0, 0, 1 ); + return m; +} + +Matrix4f Matrix4f::LookAtLH(const Vector3f& eye, const Vector3f& at, const Vector3f& up) +{ + Vector3f z = (at - eye).Normalized(); // Forward + Vector3f x = up.Cross(z).Normalized(); // Right + Vector3f y = z.Cross(x); + + Matrix4f m(x.x, x.y, x.z, -(x * eye), + y.x, y.y, y.z, -(y * eye), + z.x, z.y, z.z, -(z * eye), + 0, 0, 0, 1 ); + return m; +} + + +Matrix4f Matrix4f::PerspectiveLH(float yfov, float aspect, float znear, float zfar) +{ + Matrix4f m; + float tanHalfFov = tan(yfov * 0.5f); + + m.M[0][0] = 1.0f / (aspect * tanHalfFov); + m.M[1][1] = 1.0f / tanHalfFov; + m.M[2][2] = zfar / (zfar - znear); + m.M[3][2] = 1.0f; + m.M[2][3] = (zfar * znear) / (znear - zfar); + m.M[3][3] = 0.0f; + + // Note: Post-projection matrix result assumes Left-Handed coordinate system, + // with Y up, X right and Z forward. This supports positive z-buffer values. + return m; +} + + +Matrix4f Matrix4f::PerspectiveRH(float yfov, float aspect, float znear, float zfar) +{ + Matrix4f m; + float tanHalfFov = tan(yfov * 0.5f); + + m.M[0][0] = 1.0f / (aspect * tanHalfFov); + m.M[1][1] = 1.0f / tanHalfFov; + m.M[2][2] = zfar / (znear - zfar); + // m.M[2][2] = zfar / (zfar - znear); + m.M[3][2] = -1.0f; + m.M[2][3] = (zfar * znear) / (znear - zfar); + m.M[3][3] = 0.0f; + + // Note: Post-projection matrix result assumes Left-Handed coordinate system, + // with Y up, X right and Z forward. This supports positive z-buffer values. + // This is the case even for RHS cooridnate input. + return m; +} + + +/* +OffCenterLH + +2*zn/(r-l) 0 0 0 +0 2*zn/(t-b) 0 0 +(l+r)/(l-r) (t+b)/(b-t) zf/(zf-zn) 1 +0 0 zn*zf/(zn-zf) 0 + +*/ + + +Matrix4f Matrix4f::Ortho2D(float w, float h) +{ + Matrix4f m; + m.M[0][0] = 2.0f/w; + m.M[1][1] = -2.0f/h; + m.M[0][3] = -1.0; + m.M[1][3] = 1.0; + m.M[2][2] = 0; + return m; +} + +} diff -r 59fc487ba58e -r b069a5c27388 libovr/Src/Kernel/OVR_Math.h --- a/libovr/Src/Kernel/OVR_Math.h Sat Sep 14 17:51:03 2013 +0300 +++ b/libovr/Src/Kernel/OVR_Math.h Sun Sep 15 04:10:05 2013 +0300 @@ -1,1 +1,1149 @@ -/************************************************************************************ PublicHeader: OVR.h Filename : OVR_Math.h Content : Implementation of 3D primitives such as vectors, matrices. Created : September 4, 2012 Authors : Andrew Reisse, Michael Antonov, Steve LaValle, Anna Yershova Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. *************************************************************************************/ #ifndef OVR_Math_h #define OVR_Math_h #include #include #include #include "OVR_Types.h" #include "OVR_RefCount.h" namespace OVR { //------------------------------------------------------------------------------------- // Constants for 3D world/axis definitions. // Definitions of axes for coordinate and rotation conversions. enum Axis { Axis_X = 0, Axis_Y = 1, Axis_Z = 2 }; // RotateDirection describes the rotation direction around an axis, interpreted as follows: // CW - Clockwise while looking "down" from positive axis towards the origin. // CCW - Counter-clockwise while looking from the positive axis towards the origin, // which is in the negative axis direction. // CCW is the default for the RHS coordinate system. Oculus standard RHS coordinate // system defines Y up, X right, and Z back (pointing out from the screen). In this // system Rotate_CCW around Z will specifies counter-clockwise rotation in XY plane. enum RotateDirection { Rotate_CCW = 1, Rotate_CW = -1 }; enum HandedSystem { Handed_R = 1, Handed_L = -1 }; // AxisDirection describes which way the axis points. Used by WorldAxes. enum AxisDirection { Axis_Up = 2, Axis_Down = -2, Axis_Right = 1, Axis_Left = -1, Axis_In = 3, Axis_Out = -3 }; struct WorldAxes { AxisDirection XAxis, YAxis, ZAxis; WorldAxes(AxisDirection x, AxisDirection y, AxisDirection z) : XAxis(x), YAxis(y), ZAxis(z) { OVR_ASSERT(abs(x) != abs(y) && abs(y) != abs(z) && abs(z) != abs(x));} }; //------------------------------------------------------------------------------------- // ***** Math // Math class contains constants and functions. This class is a template specialized // per type, with Math and Math being distinct. template class Math { }; // Single-precision Math constants class. template<> class Math { public: static const float Pi; static const float TwoPi; static const float PiOver2; static const float PiOver4; static const float E; static const float MaxValue; // Largest positive float Value static const float MinPositiveValue; // Smallest possible positive value static const float RadToDegreeFactor; static const float DegreeToRadFactor; static const float Tolerance; // 0.00001f; static const float SingularityRadius; //0.00000000001f for Gimbal lock numerical problems }; // Double-precision Math constants class. template<> class Math { public: static const double Pi; static const double TwoPi; static const double PiOver2; static const double PiOver4; static const double E; static const double MaxValue; // Largest positive double Value static const double MinPositiveValue; // Smallest possible positive value static const double RadToDegreeFactor; static const double DegreeToRadFactor; static const double Tolerance; // 0.00001f; static const double SingularityRadius; //0.00000000001 for Gimbal lock numerical problems }; typedef Math Mathf; typedef Math Mathd; // Conversion functions between degrees and radians template FT RadToDegree(FT rads) { return rads * Math::RadToDegreeFactor; } template FT DegreeToRad(FT rads) { return rads * Math::DegreeToRadFactor; } template class Quat; //------------------------------------------------------------------------------------- // ***** Vector2f - 2D Vector2f // Vector2f represents a 2-dimensional vector or point in space, // consisting of coordinates x and y, template class Vector2 { public: T x, y; Vector2() : x(0), y(0) { } Vector2(T x_, T y_) : x(x_), y(y_) { } explicit Vector2(T s) : x(s), y(s) { } bool operator== (const Vector2& b) const { return x == b.x && y == b.y; } bool operator!= (const Vector2& b) const { return x != b.x || y != b.y; } Vector2 operator+ (const Vector2& b) const { return Vector2(x + b.x, y + b.y); } Vector2& operator+= (const Vector2& b) { x += b.x; y += b.y; return *this; } Vector2 operator- (const Vector2& b) const { return Vector2(x - b.x, y - b.y); } Vector2& operator-= (const Vector2& b) { x -= b.x; y -= b.y; return *this; } Vector2 operator- () const { return Vector2(-x, -y); } // Scalar multiplication/division scales vector. Vector2 operator* (T s) const { return Vector2(x*s, y*s); } Vector2& operator*= (T s) { x *= s; y *= s; return *this; } Vector2 operator/ (T s) const { T rcp = T(1)/s; return Vector2(x*rcp, y*rcp); } Vector2& operator/= (T s) { T rcp = T(1)/s; x *= rcp; y *= rcp; return *this; } // Compare two vectors for equality with tolerance. Returns true if vectors match withing tolerance. bool Compare(const Vector2&b, T tolerance = Mathf::Tolerance) { return (fabs(b.x-x) < tolerance) && (fabs(b.y-y) < tolerance); } // Dot product overload. // Used to calculate angle q between two vectors among other things, // as (A dot B) = |a||b|cos(q). T operator* (const Vector2& b) const { return x*b.x + y*b.y; } // Returns the angle from this vector to b, in radians. T Angle(const Vector2& b) const { return acos((*this * b)/(Length()*b.Length())); } // Return Length of the vector squared. T LengthSq() const { return (x * x + y * y); } // Return vector length. T Length() const { return sqrt(LengthSq()); } // Returns distance between two points represented by vectors. T Distance(Vector2& b) const { return (*this - b).Length(); } // Determine if this a unit vector. bool IsNormalized() const { return fabs(LengthSq() - T(1)) < Math::Tolerance; } // Normalize, convention vector length to 1. void Normalize() { *this /= Length(); } // Returns normalized (unit) version of the vector without modifying itself. Vector2 Normalized() const { return *this / Length(); } // Linearly interpolates from this vector to another. // Factor should be between 0.0 and 1.0, with 0 giving full value to this. Vector2 Lerp(const Vector2& b, T f) const { return *this*(T(1) - f) + b*f; } // Projects this vector onto the argument; in other words, // A.Project(B) returns projection of vector A onto B. Vector2 ProjectTo(const Vector2& b) const { return b * ((*this * b) / b.LengthSq()); } }; typedef Vector2 Vector2f; typedef Vector2 Vector2d; //------------------------------------------------------------------------------------- // ***** Vector3f - 3D Vector3f // Vector3f represents a 3-dimensional vector or point in space, // consisting of coordinates x, y and z. template class Vector3 { public: T x, y, z; Vector3() : x(0), y(0), z(0) { } Vector3(T x_, T y_, T z_ = 0) : x(x_), y(y_), z(z_) { } explicit Vector3(T s) : x(s), y(s), z(s) { } bool operator== (const Vector3& b) const { return x == b.x && y == b.y && z == b.z; } bool operator!= (const Vector3& b) const { return x != b.x || y != b.y || z != b.z; } Vector3 operator+ (const Vector3& b) const { return Vector3(x + b.x, y + b.y, z + b.z); } Vector3& operator+= (const Vector3& b) { x += b.x; y += b.y; z += b.z; return *this; } Vector3 operator- (const Vector3& b) const { return Vector3(x - b.x, y - b.y, z - b.z); } Vector3& operator-= (const Vector3& b) { x -= b.x; y -= b.y; z -= b.z; return *this; } Vector3 operator- () const { return Vector3(-x, -y, -z); } // Scalar multiplication/division scales vector. Vector3 operator* (T s) const { return Vector3(x*s, y*s, z*s); } Vector3& operator*= (T s) { x *= s; y *= s; z *= s; return *this; } Vector3 operator/ (T s) const { T rcp = T(1)/s; return Vector3(x*rcp, y*rcp, z*rcp); } Vector3& operator/= (T s) { T rcp = T(1)/s; x *= rcp; y *= rcp; z *= rcp; return *this; } // Compare two vectors for equality with tolerance. Returns true if vectors match withing tolerance. bool Compare(const Vector3&b, T tolerance = Mathf::Tolerance) { return (fabs(b.x-x) < tolerance) && (fabs(b.y-y) < tolerance) && (fabs(b.z-z) < tolerance); } // Dot product overload. // Used to calculate angle q between two vectors among other things, // as (A dot B) = |a||b|cos(q). T operator* (const Vector3& b) const { return x*b.x + y*b.y + z*b.z; } // Compute cross product, which generates a normal vector. // Direction vector can be determined by right-hand rule: Pointing index finder in // direction a and middle finger in direction b, thumb will point in a.Cross(b). Vector3 Cross(const Vector3& b) const { return Vector3(y*b.z - z*b.y, z*b.x - x*b.z, x*b.y - y*b.x); } // Returns the angle from this vector to b, in radians. T Angle(const Vector3& b) const { return acos((*this * b)/(Length()*b.Length())); } // Return Length of the vector squared. T LengthSq() const { return (x * x + y * y + z * z); } // Return vector length. T Length() const { return sqrt(LengthSq()); } // Returns distance between two points represented by vectors. T Distance(Vector3& b) const { return (*this - b).Length(); } // Determine if this a unit vector. bool IsNormalized() const { return fabs(LengthSq() - T(1)) < Math::Tolerance; } // Normalize, convention vector length to 1. void Normalize() { *this /= Length(); } // Returns normalized (unit) version of the vector without modifying itself. Vector3 Normalized() const { return *this / Length(); } // Linearly interpolates from this vector to another. // Factor should be between 0.0 and 1.0, with 0 giving full value to this. Vector3 Lerp(const Vector3& b, T f) const { return *this*(T(1) - f) + b*f; } // Projects this vector onto the argument; in other words, // A.Project(B) returns projection of vector A onto B. Vector3 ProjectTo(const Vector3& b) const { return b * ((*this * b) / b.LengthSq()); } }; typedef Vector3 Vector3f; typedef Vector3 Vector3d; //------------------------------------------------------------------------------------- // ***** Matrix4f // Matrix4f is a 4x4 matrix used for 3d transformations and projections. // Translation stored in the last column. // The matrix is stored in row-major order in memory, meaning that values // of the first row are stored before the next one. // // The arrangement of the matrix is chosen to be in Right-Handed // coordinate system and counterclockwise rotations when looking down // the axis // // Transformation Order: // - Transformations are applied from right to left, so the expression // M1 * M2 * M3 * V means that the vector V is transformed by M3 first, // followed by M2 and M1. // // Coordinate system: Right Handed // // Rotations: Counterclockwise when looking down the axis. All angles are in radians. // // | sx 01 02 tx | // First column (sx, 10, 20): Axis X basis vector. // | 10 sy 12 ty | // Second column (01, sy, 21): Axis Y basis vector. // | 20 21 sz tz | // Third columnt (02, 12, sz): Axis Z basis vector. // | 30 31 32 33 | // // The basis vectors are first three columns. class Matrix4f { static Matrix4f IdentityValue; public: float M[4][4]; enum NoInitType { NoInit }; // Construct with no memory initialization. Matrix4f(NoInitType) { } // By default, we construct identity matrix. Matrix4f() { SetIdentity(); } Matrix4f(float m11, float m12, float m13, float m14, float m21, float m22, float m23, float m24, float m31, float m32, float m33, float m34, float m41, float m42, float m43, float m44) { M[0][0] = m11; M[0][1] = m12; M[0][2] = m13; M[0][3] = m14; M[1][0] = m21; M[1][1] = m22; M[1][2] = m23; M[1][3] = m24; M[2][0] = m31; M[2][1] = m32; M[2][2] = m33; M[2][3] = m34; M[3][0] = m41; M[3][1] = m42; M[3][2] = m43; M[3][3] = m44; } Matrix4f(float m11, float m12, float m13, float m21, float m22, float m23, float m31, float m32, float m33) { M[0][0] = m11; M[0][1] = m12; M[0][2] = m13; M[0][3] = 0; M[1][0] = m21; M[1][1] = m22; M[1][2] = m23; M[1][3] = 0; M[2][0] = m31; M[2][1] = m32; M[2][2] = m33; M[2][3] = 0; M[3][0] = 0; M[3][1] = 0; M[3][2] = 0; M[3][3] = 1; } static const Matrix4f& Identity() { return IdentityValue; } void SetIdentity() { M[0][0] = M[1][1] = M[2][2] = M[3][3] = 1; M[0][1] = M[1][0] = M[2][3] = M[3][1] = 0; M[0][2] = M[1][2] = M[2][0] = M[3][2] = 0; M[0][3] = M[1][3] = M[2][1] = M[3][0] = 0; } // Multiplies two matrices into destination with minimum copying. static Matrix4f& Multiply(Matrix4f* d, const Matrix4f& a, const Matrix4f& b) { OVR_ASSERT((d != &a) && (d != &b)); int i = 0; do { d->M[i][0] = a.M[i][0] * b.M[0][0] + a.M[i][1] * b.M[1][0] + a.M[i][2] * b.M[2][0] + a.M[i][3] * b.M[3][0]; d->M[i][1] = a.M[i][0] * b.M[0][1] + a.M[i][1] * b.M[1][1] + a.M[i][2] * b.M[2][1] + a.M[i][3] * b.M[3][1]; d->M[i][2] = a.M[i][0] * b.M[0][2] + a.M[i][1] * b.M[1][2] + a.M[i][2] * b.M[2][2] + a.M[i][3] * b.M[3][2]; d->M[i][3] = a.M[i][0] * b.M[0][3] + a.M[i][1] * b.M[1][3] + a.M[i][2] * b.M[2][3] + a.M[i][3] * b.M[3][3]; } while((++i) < 4); return *d; } Matrix4f operator* (const Matrix4f& b) const { Matrix4f result(Matrix4f::NoInit); Multiply(&result, *this, b); return result; } Matrix4f& operator*= (const Matrix4f& b) { return Multiply(this, Matrix4f(*this), b); } Matrix4f operator* (float s) const { return Matrix4f(M[0][0] * s, M[0][1] * s, M[0][2] * s, M[0][3] * s, M[1][0] * s, M[1][1] * s, M[1][2] * s, M[1][3] * s, M[2][0] * s, M[2][1] * s, M[2][2] * s, M[2][3] * s, M[3][0] * s, M[3][1] * s, M[3][2] * s, M[3][3] * s); } Matrix4f& operator*= (float s) { M[0][0] *= s; M[0][1] *= s; M[0][2] *= s; M[0][3] *= s; M[1][0] *= s; M[1][1] *= s; M[1][2] *= s; M[1][3] *= s; M[2][0] *= s; M[2][1] *= s; M[2][2] *= s; M[2][3] *= s; M[3][0] *= s; M[3][1] *= s; M[3][2] *= s; M[3][3] *= s; return *this; } Vector3f Transform(const Vector3f& v) const { return Vector3f(M[0][0] * v.x + M[0][1] * v.y + M[0][2] * v.z + M[0][3], M[1][0] * v.x + M[1][1] * v.y + M[1][2] * v.z + M[1][3], M[2][0] * v.x + M[2][1] * v.y + M[2][2] * v.z + M[2][3]); } Matrix4f Transposed() const { return Matrix4f(M[0][0], M[1][0], M[2][0], M[3][0], M[0][1], M[1][1], M[2][1], M[3][1], M[0][2], M[1][2], M[2][2], M[3][2], M[0][3], M[1][3], M[2][3], M[3][3]); } void Transpose() { *this = Transposed(); } float SubDet (const int* rows, const int* cols) const { return M[rows[0]][cols[0]] * (M[rows[1]][cols[1]] * M[rows[2]][cols[2]] - M[rows[1]][cols[2]] * M[rows[2]][cols[1]]) - M[rows[0]][cols[1]] * (M[rows[1]][cols[0]] * M[rows[2]][cols[2]] - M[rows[1]][cols[2]] * M[rows[2]][cols[0]]) + M[rows[0]][cols[2]] * (M[rows[1]][cols[0]] * M[rows[2]][cols[1]] - M[rows[1]][cols[1]] * M[rows[2]][cols[0]]); } float Cofactor(int I, int J) const { const int indices[4][3] = {{1,2,3},{0,2,3},{0,1,3},{0,1,2}}; return ((I+J)&1) ? -SubDet(indices[I],indices[J]) : SubDet(indices[I],indices[J]); } float Determinant() const { return M[0][0] * Cofactor(0,0) + M[0][1] * Cofactor(0,1) + M[0][2] * Cofactor(0,2) + M[0][3] * Cofactor(0,3); } Matrix4f Adjugated() const { return Matrix4f(Cofactor(0,0), Cofactor(1,0), Cofactor(2,0), Cofactor(3,0), Cofactor(0,1), Cofactor(1,1), Cofactor(2,1), Cofactor(3,1), Cofactor(0,2), Cofactor(1,2), Cofactor(2,2), Cofactor(3,2), Cofactor(0,3), Cofactor(1,3), Cofactor(2,3), Cofactor(3,3)); } Matrix4f Inverted() const { float det = Determinant(); assert(det != 0); return Adjugated() * (1.0f/det); } void Invert() { *this = Inverted(); } //AnnaSteve: // a,b,c, are the YawPitchRoll angles to be returned // rotation a around axis A1 // is followed by rotation b around axis A2 // is followed by rotation c around axis A3 // rotations are CCW or CW (D) in LH or RH coordinate system (S) template void ToEulerAngles(float *a, float *b, float *c) { OVR_COMPILER_ASSERT((A1 != A2) && (A2 != A3) && (A1 != A3)); float psign = -1.0f; if (((A1 + 1) % 3 == A2) && ((A2 + 1) % 3 == A3)) // Determine whether even permutation psign = 1.0f; float pm = psign*M[A1][A3]; if (pm < -1.0f + Math::SingularityRadius) { // South pole singularity *a = 0.0f; *b = -S*D*Math::PiOver2; *c = S*D*atan2( psign*M[A2][A1], M[A2][A2] ); } else if (pm > 1.0 - Math::SingularityRadius) { // North pole singularity *a = 0.0f; *b = S*D*Math::PiOver2; *c = S*D*atan2( psign*M[A2][A1], M[A2][A2] ); } else { // Normal case (nonsingular) *a = S*D*atan2( -psign*M[A2][A3], M[A3][A3] ); *b = S*D*asin(pm); *c = S*D*atan2( -psign*M[A1][A2], M[A1][A1] ); } return; } //AnnaSteve: // a,b,c, are the YawPitchRoll angles to be returned // rotation a around axis A1 // is followed by rotation b around axis A2 // is followed by rotation c around axis A1 // rotations are CCW or CW (D) in LH or RH coordinate system (S) template void ToEulerAnglesABA(float *a, float *b, float *c) { OVR_COMPILER_ASSERT(A1 != A2); // Determine the axis that was not supplied int m = 3 - A1 - A2; float psign = -1.0f; if ((A1 + 1) % 3 == A2) // Determine whether even permutation psign = 1.0f; float c2 = M[A1][A1]; if (c2 < -1.0 + Math::SingularityRadius) { // South pole singularity *a = 0.0f; *b = S*D*Math::Pi; *c = S*D*atan2( -psign*M[A2][m],M[A2][A2]); } else if (c2 > 1.0 - Math::SingularityRadius) { // North pole singularity *a = 0.0f; *b = 0.0f; *c = S*D*atan2( -psign*M[A2][m],M[A2][A2]); } else { // Normal case (nonsingular) *a = S*D*atan2( M[A2][A1],-psign*M[m][A1]); *b = S*D*acos(c2); *c = S*D*atan2( M[A1][A2],psign*M[A1][m]); } return; } // Creates a matrix that converts the vertices from one coordinate system // to another. // static Matrix4f AxisConversion(const WorldAxes& to, const WorldAxes& from) { // Holds axis values from the 'to' structure int toArray[3] = { to.XAxis, to.YAxis, to.ZAxis }; // The inverse of the toArray int inv[4]; inv[0] = inv[abs(to.XAxis)] = 0; inv[abs(to.YAxis)] = 1; inv[abs(to.ZAxis)] = 2; Matrix4f m(0, 0, 0, 0, 0, 0, 0, 0, 0); // Only three values in the matrix need to be changed to 1 or -1. m.M[inv[abs(from.XAxis)]][0] = float(from.XAxis/toArray[inv[abs(from.XAxis)]]); m.M[inv[abs(from.YAxis)]][1] = float(from.YAxis/toArray[inv[abs(from.YAxis)]]); m.M[inv[abs(from.ZAxis)]][2] = float(from.ZAxis/toArray[inv[abs(from.ZAxis)]]); return m; } static Matrix4f Translation(const Vector3f& v) { Matrix4f t; t.M[0][3] = v.x; t.M[1][3] = v.y; t.M[2][3] = v.z; return t; } static Matrix4f Translation(float x, float y, float z = 0.0f) { Matrix4f t; t.M[0][3] = x; t.M[1][3] = y; t.M[2][3] = z; return t; } static Matrix4f Scaling(const Vector3f& v) { Matrix4f t; t.M[0][0] = v.x; t.M[1][1] = v.y; t.M[2][2] = v.z; return t; } static Matrix4f Scaling(float x, float y, float z) { Matrix4f t; t.M[0][0] = x; t.M[1][1] = y; t.M[2][2] = z; return t; } static Matrix4f Scaling(float s) { Matrix4f t; t.M[0][0] = s; t.M[1][1] = s; t.M[2][2] = s; return t; } //AnnaSteve : Just for quick testing. Not for final API. Need to remove case. static Matrix4f RotationAxis(Axis A, float angle, RotateDirection d, HandedSystem s) { float sina = s * d *sin(angle); float cosa = cos(angle); switch(A) { case Axis_X: return Matrix4f(1, 0, 0, 0, cosa, -sina, 0, sina, cosa); case Axis_Y: return Matrix4f(cosa, 0, sina, 0, 1, 0, -sina, 0, cosa); case Axis_Z: return Matrix4f(cosa, -sina, 0, sina, cosa, 0, 0, 0, 1); } } // Creates a rotation matrix rotating around the X axis by 'angle' radians. // Rotation direction is depends on the coordinate system: // RHS (Oculus default): Positive angle values rotate Counter-clockwise (CCW), // while looking in the negative axis direction. This is the // same as looking down from positive axis values towards origin. // LHS: Positive angle values rotate clock-wise (CW), while looking in the // negative axis direction. static Matrix4f RotationX(float angle) { float sina = sin(angle); float cosa = cos(angle); return Matrix4f(1, 0, 0, 0, cosa, -sina, 0, sina, cosa); } // Creates a rotation matrix rotating around the Y axis by 'angle' radians. // Rotation direction is depends on the coordinate system: // RHS (Oculus default): Positive angle values rotate Counter-clockwise (CCW), // while looking in the negative axis direction. This is the // same as looking down from positive axis values towards origin. // LHS: Positive angle values rotate clock-wise (CW), while looking in the // negative axis direction. static Matrix4f RotationY(float angle) { float sina = sin(angle); float cosa = cos(angle); return Matrix4f(cosa, 0, sina, 0, 1, 0, -sina, 0, cosa); } // Creates a rotation matrix rotating around the Z axis by 'angle' radians. // Rotation direction is depends on the coordinate system: // RHS (Oculus default): Positive angle values rotate Counter-clockwise (CCW), // while looking in the negative axis direction. This is the // same as looking down from positive axis values towards origin. // LHS: Positive angle values rotate clock-wise (CW), while looking in the // negative axis direction. static Matrix4f RotationZ(float angle) { float sina = sin(angle); float cosa = cos(angle); return Matrix4f(cosa, -sina, 0, sina, cosa, 0, 0, 0, 1); } // LookAtRH creates a View transformation matrix for right-handed coordinate system. // The resulting matrix points camera from 'eye' towards 'at' direction, with 'up' // specifying the up vector. The resulting matrix should be used with PerspectiveRH // projection. static Matrix4f LookAtRH(const Vector3f& eye, const Vector3f& at, const Vector3f& up); // LookAtLH creates a View transformation matrix for left-handed coordinate system. // The resulting matrix points camera from 'eye' towards 'at' direction, with 'up' // specifying the up vector. static Matrix4f LookAtLH(const Vector3f& eye, const Vector3f& at, const Vector3f& up); // PerspectiveRH creates a right-handed perspective projection matrix that can be // used with the Oculus sample renderer. // yfov - Specifies vertical field of view in radians. // aspect - Screen aspect ration, which is usually width/height for square pixels. // Note that xfov = yfov * aspect. // znear - Absolute value of near Z clipping clipping range. // zfar - Absolute value of far Z clipping clipping range (larger then near). // Even though RHS usually looks in the direction of negative Z, positive values // are expected for znear and zfar. static Matrix4f PerspectiveRH(float yfov, float aspect, float znear, float zfar); // PerspectiveRH creates a left-handed perspective projection matrix that can be // used with the Oculus sample renderer. // yfov - Specifies vertical field of view in radians. // aspect - Screen aspect ration, which is usually width/height for square pixels. // Note that xfov = yfov * aspect. // znear - Absolute value of near Z clipping clipping range. // zfar - Absolute value of far Z clipping clipping range (larger then near). static Matrix4f PerspectiveLH(float yfov, float aspect, float znear, float zfar); static Matrix4f Ortho2D(float w, float h); }; //------------------------------------------------------------------------------------- // ***** Quat // Quatf represents a quaternion class used for rotations. // // Quaternion multiplications are done in right-to-left order, to match the // behavior of matrices. template class Quat { public: // w + Xi + Yj + Zk T x, y, z, w; Quat() : x(0), y(0), z(0), w(1) {} Quat(T x_, T y_, T z_, T w_) : x(x_), y(y_), z(z_), w(w_) {} // Constructs rotation quaternion around the axis. Quat(const Vector3& axis, T angle) { Vector3 unitAxis = axis.Normalized(); T sinHalfAngle = sin(angle * T(0.5)); w = cos(angle * T(0.5)); x = unitAxis.x * sinHalfAngle; y = unitAxis.y * sinHalfAngle; z = unitAxis.z * sinHalfAngle; } //AnnaSteve: void AxisAngle(Axis A, T angle, RotateDirection d, HandedSystem s) { T sinHalfAngle = s * d *sin(angle * (T)0.5); T v[3]; v[0] = v[1] = v[2] = (T)0; v[A] = sinHalfAngle; //return Quat(v[0], v[1], v[2], cos(angle * (T)0.5)); w = cos(angle * (T)0.5); x = v[0]; y = v[1]; z = v[2]; } void GetAxisAngle(Vector3* axis, T* angle) const { if (LengthSq() > Math::Tolerance * Math::Tolerance) { *axis = Vector3(x, y, z).Normalized(); *angle = 2 * acos(w); } else { *axis = Vector3(1, 0, 0); *angle= 0; } } bool operator== (const Quat& b) const { return x == b.x && y == b.y && z == b.z && w == b.w; } bool operator!= (const Quat& b) const { return x != b.x || y != b.y || z != b.z || w != b.w; } Quat operator+ (const Quat& b) const { return Quat(x + b.x, y + b.y, z + b.z, w + b.w); } Quat& operator+= (const Quat& b) { w += b.w; x += b.x; y += b.y; z += b.z; return *this; } Quat operator- (const Quat& b) const { return Quat(x - b.x, y - b.y, z - b.z, w - b.w); } Quat& operator-= (const Quat& b) { w -= b.w; x -= b.x; y -= b.y; z -= b.z; return *this; } Quat operator* (T s) const { return Quat(x * s, y * s, z * s, w * s); } Quat& operator*= (T s) { w *= s; x *= s; y *= s; z *= s; return *this; } Quat operator/ (T s) const { T rcp = T(1)/s; return Quat(x * rcp, y * rcp, z * rcp, w *rcp); } Quat& operator/= (T s) { T rcp = T(1)/s; w *= rcp; x *= rcp; y *= rcp; z *= rcp; return *this; } // Get Imaginary part vector Vector3 Imag() const { return Vector3(x,y,z); } // Get quaternion length. T Length() const { return sqrt(x * x + y * y + z * z + w * w); } // Get quaternion length squared. T LengthSq() const { return (x * x + y * y + z * z + w * w); } // Simple Eulidean distance in R^4 (not SLERP distance, but at least respects Haar measure) T Distance(const Quat& q) const { T d1 = (*this - q).Length(); T d2 = (*this + q).Length(); // Antipoldal point check return (d1 < d2) ? d1 : d2; } T DistanceSq(const Quat& q) const { T d1 = (*this - q).LengthSq(); T d2 = (*this + q).LengthSq(); // Antipoldal point check return (d1 < d2) ? d1 : d2; } // Normalize bool IsNormalized() const { return fabs(LengthSq() - 1) < Math::Tolerance; } void Normalize() { *this /= Length(); } Quat Normalized() const { return *this / Length(); } // Returns conjugate of the quaternion. Produces inverse rotation if quaternion is normalized. Quat Conj() const { return Quat(-x, -y, -z, w); } // AnnaSteve fixed: order of quaternion multiplication // Quaternion multiplication. Combines quaternion rotations, performing the one on the // right hand side first. Quat operator* (const Quat& b) const { return Quat(w * b.x + x * b.w + y * b.z - z * b.y, w * b.y - x * b.z + y * b.w + z * b.x, w * b.z + x * b.y - y * b.x + z * b.w, w * b.w - x * b.x - y * b.y - z * b.z); } // // this^p normalized; same as rotating by this p times. Quat PowNormalized(T p) const { Vector3 v; T a; GetAxisAngle(&v, &a); return Quat(v, a * p); } // Rotate transforms vector in a manner that matches Matrix rotations (counter-clockwise, // assuming negative direction of the axis). Standard formula: q(t) * V * q(t)^-1. Vector3 Rotate(const Vector3& v) const { return ((*this * Quat(v.x, v.y, v.z, 0)) * Inverted()).Imag(); } // Inversed quaternion rotates in the opposite direction. Quat Inverted() const { return Quat(-x, -y, -z, w); } // Sets this quaternion to the one rotates in the opposite direction. void Invert() { *this = Quat(-x, -y, -z, w); } // Converting quaternion to matrix. operator Matrix4f() const { T ww = w*w; T xx = x*x; T yy = y*y; T zz = z*z; return Matrix4f(float(ww + xx - yy - zz), float(T(2) * (x*y - w*z)), float(T(2) * (x*z + w*y)), float(T(2) * (x*y + w*z)), float(ww - xx + yy - zz), float(T(2) * (y*z - w*x)), float(T(2) * (x*z - w*y)), float(T(2) * (y*z + w*x)), float(ww - xx - yy + zz) ); } // GetEulerAngles extracts Euler angles from the quaternion, in the specified order of // axis rotations and the specified coordinate system. Right-handed coordinate system // is the default, with CCW rotations while looking in the negative axis direction. // Here a,b,c, are the Yaw/Pitch/Roll angles to be returned. // rotation a around axis A1 // is followed by rotation b around axis A2 // is followed by rotation c around axis A3 // rotations are CCW or CW (D) in LH or RH coordinate system (S) template void GetEulerAngles(T *a, T *b, T *c) { OVR_COMPILER_ASSERT((A1 != A2) && (A2 != A3) && (A1 != A3)); T Q[3] = { x, y, z }; //Quaternion components x,y,z T ww = w*w; T Q11 = Q[A1]*Q[A1]; T Q22 = Q[A2]*Q[A2]; T Q33 = Q[A3]*Q[A3]; T psign = T(-1.0); // Determine whether even permutation if (((A1 + 1) % 3 == A2) && ((A2 + 1) % 3 == A3)) psign = T(1.0); T s2 = psign * T(2.0) * (psign*w*Q[A2] + Q[A1]*Q[A3]); if (s2 < (T)-1.0 + Math::SingularityRadius) { // South pole singularity *a = T(0.0); *b = -S*D*Math::PiOver2; *c = S*D*atan2((T)2.0*(psign*Q[A1]*Q[A2] + w*Q[A3]), ww + Q22 - Q11 - Q33 ); } else if (s2 > (T)1.0 - Math::SingularityRadius) { // North pole singularity *a = (T)0.0; *b = S*D*Math::PiOver2; *c = S*D*atan2((T)2.0*(psign*Q[A1]*Q[A2] + w*Q[A3]), ww + Q22 - Q11 - Q33); } else { *a = -S*D*atan2((T)-2.0*(w*Q[A1] - psign*Q[A2]*Q[A3]), ww + Q33 - Q11 - Q22); *b = S*D*asin(s2); *c = S*D*atan2((T)2.0*(w*Q[A3] - psign*Q[A1]*Q[A2]), ww + Q11 - Q22 - Q33); } return; } template void GetEulerAngles(T *a, T *b, T *c) { GetEulerAngles(a, b, c); } template void GetEulerAngles(T *a, T *b, T *c) { GetEulerAngles(a, b, c); } // GetEulerAnglesABA extracts Euler angles from the quaternion, in the specified order of // axis rotations and the specified coordinate system. Right-handed coordinate system // is the default, with CCW rotations while looking in the negative axis direction. // Here a,b,c, are the Yaw/Pitch/Roll angles to be returned. // rotation a around axis A1 // is followed by rotation b around axis A2 // is followed by rotation c around axis A1 // Rotations are CCW or CW (D) in LH or RH coordinate system (S) template void GetEulerAnglesABA(T *a, T *b, T *c) { OVR_COMPILER_ASSERT(A1 != A2); T Q[3] = {x, y, z}; // Quaternion components // Determine the missing axis that was not supplied int m = 3 - A1 - A2; T ww = w*w; T Q11 = Q[A1]*Q[A1]; T Q22 = Q[A2]*Q[A2]; T Qmm = Q[m]*Q[m]; T psign = T(-1.0); if ((A1 + 1) % 3 == A2) // Determine whether even permutation { psign = (T)1.0; } T c2 = ww + Q11 - Q22 - Qmm; if (c2 < (T)-1.0 + Math::SingularityRadius) { // South pole singularity *a = (T)0.0; *b = S*D*Math::Pi; *c = S*D*atan2( (T)2.0*(w*Q[A1] - psign*Q[A2]*Q[m]), ww + Q22 - Q11 - Qmm); } else if (c2 > (T)1.0 - Math::SingularityRadius) { // North pole singularity *a = (T)0.0; *b = (T)0.0; *c = S*D*atan2( (T)2.0*(w*Q[A1] - psign*Q[A2]*Q[m]), ww + Q22 - Q11 - Qmm); } else { *a = S*D*atan2( psign*w*Q[m] + Q[A1]*Q[A2], w*Q[A2] -psign*Q[A1]*Q[m]); *b = S*D*acos(c2); *c = S*D*atan2( -psign*w*Q[m] + Q[A1]*Q[A2], w*Q[A2] + psign*Q[A1]*Q[m]); } return; } }; typedef Quat Quatf; typedef Quat Quatd; //------------------------------------------------------------------------------------- // ***** Angle // Cleanly representing the algebra of 2D rotations. // The operations maintain the angle between -Pi and Pi, the same range as atan2. // template class Angle { public: enum AngularUnits { Radians = 0, Degrees = 1 }; Angle() : a(0) {} // Fix the range to be between -Pi and Pi Angle(T a_, AngularUnits u = Radians) : a((u == Radians) ? a_ : a_*Math::DegreeToRadFactor) { FixRange(); } T Get(AngularUnits u = Radians) const { return (u == Radians) ? a : a*Math::RadToDegreeFactor; } void Set(const T& x, AngularUnits u = Radians) { a = (u == Radians) ? x : x*Math::DegreeToRadFactor; FixRange(); } int Sign() const { if (a == 0) return 0; else return (a > 0) ? 1 : -1; } T Abs() const { return (a > 0) ? a : -a; } bool operator== (const Angle& b) const { return a == b.a; } bool operator!= (const Angle& b) const { return a != b.a; } // bool operator< (const Angle& b) const { return a < a.b; } // bool operator> (const Angle& b) const { return a > a.b; } // bool operator<= (const Angle& b) const { return a <= a.b; } // bool operator>= (const Angle& b) const { return a >= a.b; } // bool operator= (const T& x) { a = x; FixRange(); } // These operations assume a is already between -Pi and Pi. Angle operator+ (const Angle& b) const { return Angle(a + b.a); } Angle operator+ (const T& x) const { return Angle(a + x); } Angle& operator+= (const Angle& b) { a = a + b.a; FastFixRange(); return *this; } Angle& operator+= (const T& x) { a = a + x; FixRange(); return *this; } Angle operator- (const Angle& b) const { return Angle(a - b.a); } Angle operator- (const T& x) const { return Angle(a - x); } Angle& operator-= (const Angle& b) { a = a - b.a; FastFixRange(); return *this; } Angle& operator-= (const T& x) { a = a - x; FixRange(); return *this; } T Distance(const Angle& b) { T c = fabs(a - b.a); return (c <= Math::Pi) ? c : Math::TwoPi - c; } private: // The stored angle, which should be maintained between -Pi and Pi T a; // Fixes the angle range to [-Pi,Pi], but assumes no more than 2Pi away on either side inline void FastFixRange() { if (a < -Math::Pi) a += Math::TwoPi; else if (a > Math::Pi) a -= Math::TwoPi; } // Fixes the angle range to [-Pi,Pi] for any given range, but slower then the fast method inline void FixRange() { a = fmod(a,Math::TwoPi); if (a < -Math::Pi) a += Math::TwoPi; else if (a > Math::Pi) a -= Math::TwoPi; } }; typedef Angle Anglef; typedef Angle Angled; //------------------------------------------------------------------------------------- // ***** Plane // Consists of a normal vector and distance from the origin where the plane is located. template class Plane : public RefCountBase > { public: Vector3 N; T D; Plane() : D(0) {} // Normals must already be normalized Plane(const Vector3& n, T d) : N(n), D(d) {} Plane(T x, T y, T z, T d) : N(x,y,z), D(d) {} // construct from a point on the plane and the normal Plane(const Vector3& p, const Vector3& n) : N(n), D(-(p * n)) {} // Find the point to plane distance. The sign indicates what side of the plane the point is on (0 = point on plane). T TestSide(const Vector3& p) const { return (N * p) + D; } Plane Flipped() const { return Plane(-N, -D); } void Flip() { N = -N; D = -D; } bool operator==(const Plane& rhs) const { return (this->D == rhs.D && this->N == rhs.N); } }; typedef Plane Planef; } #endif \ No newline at end of file +/************************************************************************************ + +PublicHeader: OVR.h +Filename : OVR_Math.h +Content : Implementation of 3D primitives such as vectors, matrices. +Created : September 4, 2012 +Authors : Andrew Reisse, Michael Antonov, Steve LaValle, Anna Yershova + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_Math_h +#define OVR_Math_h + +#include +#include +#include + +#include "OVR_Types.h" +#include "OVR_RefCount.h" + +namespace OVR { + +//------------------------------------------------------------------------------------- +// Constants for 3D world/axis definitions. + +// Definitions of axes for coordinate and rotation conversions. +enum Axis +{ + Axis_X = 0, Axis_Y = 1, Axis_Z = 2 +}; + +// RotateDirection describes the rotation direction around an axis, interpreted as follows: +// CW - Clockwise while looking "down" from positive axis towards the origin. +// CCW - Counter-clockwise while looking from the positive axis towards the origin, +// which is in the negative axis direction. +// CCW is the default for the RHS coordinate system. Oculus standard RHS coordinate +// system defines Y up, X right, and Z back (pointing out from the screen). In this +// system Rotate_CCW around Z will specifies counter-clockwise rotation in XY plane. +enum RotateDirection +{ + Rotate_CCW = 1, + Rotate_CW = -1 +}; + +enum HandedSystem +{ + Handed_R = 1, Handed_L = -1 +}; + +// AxisDirection describes which way the axis points. Used by WorldAxes. +enum AxisDirection +{ + Axis_Up = 2, + Axis_Down = -2, + Axis_Right = 1, + Axis_Left = -1, + Axis_In = 3, + Axis_Out = -3 +}; + +struct WorldAxes +{ + AxisDirection XAxis, YAxis, ZAxis; + + WorldAxes(AxisDirection x, AxisDirection y, AxisDirection z) + : XAxis(x), YAxis(y), ZAxis(z) + { OVR_ASSERT(abs(x) != abs(y) && abs(y) != abs(z) && abs(z) != abs(x));} +}; + + +//------------------------------------------------------------------------------------- +// ***** Math + +// Math class contains constants and functions. This class is a template specialized +// per type, with Math and Math being distinct. +template +class Math +{ +}; + +// Single-precision Math constants class. +template<> +class Math +{ +public: + static const float Pi; + static const float TwoPi; + static const float PiOver2; + static const float PiOver4; + static const float E; + + static const float MaxValue; // Largest positive float Value + static const float MinPositiveValue; // Smallest possible positive value + + static const float RadToDegreeFactor; + static const float DegreeToRadFactor; + + static const float Tolerance; // 0.00001f; + static const float SingularityRadius; //0.00000000001f for Gimbal lock numerical problems +}; + +// Double-precision Math constants class. +template<> +class Math +{ +public: + static const double Pi; + static const double TwoPi; + static const double PiOver2; + static const double PiOver4; + static const double E; + + static const double MaxValue; // Largest positive double Value + static const double MinPositiveValue; // Smallest possible positive value + + static const double RadToDegreeFactor; + static const double DegreeToRadFactor; + + static const double Tolerance; // 0.00001f; + static const double SingularityRadius; //0.00000000001 for Gimbal lock numerical problems +}; + +typedef Math Mathf; +typedef Math Mathd; + +// Conversion functions between degrees and radians +template +FT RadToDegree(FT rads) { return rads * Math::RadToDegreeFactor; } +template +FT DegreeToRad(FT rads) { return rads * Math::DegreeToRadFactor; } + +template +class Quat; + +//------------------------------------------------------------------------------------- +// ***** Vector2f - 2D Vector2f + +// Vector2f represents a 2-dimensional vector or point in space, +// consisting of coordinates x and y, + +template +class Vector2 +{ +public: + T x, y; + + Vector2() : x(0), y(0) { } + Vector2(T x_, T y_) : x(x_), y(y_) { } + explicit Vector2(T s) : x(s), y(s) { } + + bool operator== (const Vector2& b) const { return x == b.x && y == b.y; } + bool operator!= (const Vector2& b) const { return x != b.x || y != b.y; } + + Vector2 operator+ (const Vector2& b) const { return Vector2(x + b.x, y + b.y); } + Vector2& operator+= (const Vector2& b) { x += b.x; y += b.y; return *this; } + Vector2 operator- (const Vector2& b) const { return Vector2(x - b.x, y - b.y); } + Vector2& operator-= (const Vector2& b) { x -= b.x; y -= b.y; return *this; } + Vector2 operator- () const { return Vector2(-x, -y); } + + // Scalar multiplication/division scales vector. + Vector2 operator* (T s) const { return Vector2(x*s, y*s); } + Vector2& operator*= (T s) { x *= s; y *= s; return *this; } + + Vector2 operator/ (T s) const { T rcp = T(1)/s; + return Vector2(x*rcp, y*rcp); } + Vector2& operator/= (T s) { T rcp = T(1)/s; + x *= rcp; y *= rcp; + return *this; } + + // Compare two vectors for equality with tolerance. Returns true if vectors match withing tolerance. + bool Compare(const Vector2&b, T tolerance = Mathf::Tolerance) + { + return (fabs(b.x-x) < tolerance) && (fabs(b.y-y) < tolerance); + } + + // Dot product overload. + // Used to calculate angle q between two vectors among other things, + // as (A dot B) = |a||b|cos(q). + T operator* (const Vector2& b) const { return x*b.x + y*b.y; } + + // Returns the angle from this vector to b, in radians. + T Angle(const Vector2& b) const { return acos((*this * b)/(Length()*b.Length())); } + + // Return Length of the vector squared. + T LengthSq() const { return (x * x + y * y); } + // Return vector length. + T Length() const { return sqrt(LengthSq()); } + + // Returns distance between two points represented by vectors. + T Distance(Vector2& b) const { return (*this - b).Length(); } + + // Determine if this a unit vector. + bool IsNormalized() const { return fabs(LengthSq() - T(1)) < Math::Tolerance; } + // Normalize, convention vector length to 1. + void Normalize() { *this /= Length(); } + // Returns normalized (unit) version of the vector without modifying itself. + Vector2 Normalized() const { return *this / Length(); } + + // Linearly interpolates from this vector to another. + // Factor should be between 0.0 and 1.0, with 0 giving full value to this. + Vector2 Lerp(const Vector2& b, T f) const { return *this*(T(1) - f) + b*f; } + + // Projects this vector onto the argument; in other words, + // A.Project(B) returns projection of vector A onto B. + Vector2 ProjectTo(const Vector2& b) const { return b * ((*this * b) / b.LengthSq()); } +}; + + +typedef Vector2 Vector2f; +typedef Vector2 Vector2d; + +//------------------------------------------------------------------------------------- +// ***** Vector3f - 3D Vector3f + +// Vector3f represents a 3-dimensional vector or point in space, +// consisting of coordinates x, y and z. + +template +class Vector3 +{ +public: + T x, y, z; + + Vector3() : x(0), y(0), z(0) { } + Vector3(T x_, T y_, T z_ = 0) : x(x_), y(y_), z(z_) { } + explicit Vector3(T s) : x(s), y(s), z(s) { } + + bool operator== (const Vector3& b) const { return x == b.x && y == b.y && z == b.z; } + bool operator!= (const Vector3& b) const { return x != b.x || y != b.y || z != b.z; } + + Vector3 operator+ (const Vector3& b) const { return Vector3(x + b.x, y + b.y, z + b.z); } + Vector3& operator+= (const Vector3& b) { x += b.x; y += b.y; z += b.z; return *this; } + Vector3 operator- (const Vector3& b) const { return Vector3(x - b.x, y - b.y, z - b.z); } + Vector3& operator-= (const Vector3& b) { x -= b.x; y -= b.y; z -= b.z; return *this; } + Vector3 operator- () const { return Vector3(-x, -y, -z); } + + // Scalar multiplication/division scales vector. + Vector3 operator* (T s) const { return Vector3(x*s, y*s, z*s); } + Vector3& operator*= (T s) { x *= s; y *= s; z *= s; return *this; } + + Vector3 operator/ (T s) const { T rcp = T(1)/s; + return Vector3(x*rcp, y*rcp, z*rcp); } + Vector3& operator/= (T s) { T rcp = T(1)/s; + x *= rcp; y *= rcp; z *= rcp; + return *this; } + + // Compare two vectors for equality with tolerance. Returns true if vectors match withing tolerance. + bool Compare(const Vector3&b, T tolerance = Mathf::Tolerance) + { + return (fabs(b.x-x) < tolerance) && (fabs(b.y-y) < tolerance) && (fabs(b.z-z) < tolerance); + } + + // Dot product overload. + // Used to calculate angle q between two vectors among other things, + // as (A dot B) = |a||b|cos(q). + T operator* (const Vector3& b) const { return x*b.x + y*b.y + z*b.z; } + + // Compute cross product, which generates a normal vector. + // Direction vector can be determined by right-hand rule: Pointing index finder in + // direction a and middle finger in direction b, thumb will point in a.Cross(b). + Vector3 Cross(const Vector3& b) const { return Vector3(y*b.z - z*b.y, + z*b.x - x*b.z, + x*b.y - y*b.x); } + + // Returns the angle from this vector to b, in radians. + T Angle(const Vector3& b) const { return acos((*this * b)/(Length()*b.Length())); } + + // Return Length of the vector squared. + T LengthSq() const { return (x * x + y * y + z * z); } + // Return vector length. + T Length() const { return sqrt(LengthSq()); } + + // Returns distance between two points represented by vectors. + T Distance(Vector3& b) const { return (*this - b).Length(); } + + // Determine if this a unit vector. + bool IsNormalized() const { return fabs(LengthSq() - T(1)) < Math::Tolerance; } + // Normalize, convention vector length to 1. + void Normalize() { *this /= Length(); } + // Returns normalized (unit) version of the vector without modifying itself. + Vector3 Normalized() const { return *this / Length(); } + + // Linearly interpolates from this vector to another. + // Factor should be between 0.0 and 1.0, with 0 giving full value to this. + Vector3 Lerp(const Vector3& b, T f) const { return *this*(T(1) - f) + b*f; } + + // Projects this vector onto the argument; in other words, + // A.Project(B) returns projection of vector A onto B. + Vector3 ProjectTo(const Vector3& b) const { return b * ((*this * b) / b.LengthSq()); } +}; + + +typedef Vector3 Vector3f; +typedef Vector3 Vector3d; + + +//------------------------------------------------------------------------------------- +// ***** Matrix4f + +// Matrix4f is a 4x4 matrix used for 3d transformations and projections. +// Translation stored in the last column. +// The matrix is stored in row-major order in memory, meaning that values +// of the first row are stored before the next one. +// +// The arrangement of the matrix is chosen to be in Right-Handed +// coordinate system and counterclockwise rotations when looking down +// the axis +// +// Transformation Order: +// - Transformations are applied from right to left, so the expression +// M1 * M2 * M3 * V means that the vector V is transformed by M3 first, +// followed by M2 and M1. +// +// Coordinate system: Right Handed +// +// Rotations: Counterclockwise when looking down the axis. All angles are in radians. +// +// | sx 01 02 tx | // First column (sx, 10, 20): Axis X basis vector. +// | 10 sy 12 ty | // Second column (01, sy, 21): Axis Y basis vector. +// | 20 21 sz tz | // Third columnt (02, 12, sz): Axis Z basis vector. +// | 30 31 32 33 | +// +// The basis vectors are first three columns. + +class Matrix4f +{ + static Matrix4f IdentityValue; + +public: + float M[4][4]; + + enum NoInitType { NoInit }; + + // Construct with no memory initialization. + Matrix4f(NoInitType) { } + + // By default, we construct identity matrix. + Matrix4f() + { + SetIdentity(); + } + + Matrix4f(float m11, float m12, float m13, float m14, + float m21, float m22, float m23, float m24, + float m31, float m32, float m33, float m34, + float m41, float m42, float m43, float m44) + { + M[0][0] = m11; M[0][1] = m12; M[0][2] = m13; M[0][3] = m14; + M[1][0] = m21; M[1][1] = m22; M[1][2] = m23; M[1][3] = m24; + M[2][0] = m31; M[2][1] = m32; M[2][2] = m33; M[2][3] = m34; + M[3][0] = m41; M[3][1] = m42; M[3][2] = m43; M[3][3] = m44; + } + + Matrix4f(float m11, float m12, float m13, + float m21, float m22, float m23, + float m31, float m32, float m33) + { + M[0][0] = m11; M[0][1] = m12; M[0][2] = m13; M[0][3] = 0; + M[1][0] = m21; M[1][1] = m22; M[1][2] = m23; M[1][3] = 0; + M[2][0] = m31; M[2][1] = m32; M[2][2] = m33; M[2][3] = 0; + M[3][0] = 0; M[3][1] = 0; M[3][2] = 0; M[3][3] = 1; + } + + static const Matrix4f& Identity() { return IdentityValue; } + + void SetIdentity() + { + M[0][0] = M[1][1] = M[2][2] = M[3][3] = 1; + M[0][1] = M[1][0] = M[2][3] = M[3][1] = 0; + M[0][2] = M[1][2] = M[2][0] = M[3][2] = 0; + M[0][3] = M[1][3] = M[2][1] = M[3][0] = 0; + } + + // Multiplies two matrices into destination with minimum copying. + static Matrix4f& Multiply(Matrix4f* d, const Matrix4f& a, const Matrix4f& b) + { + OVR_ASSERT((d != &a) && (d != &b)); + int i = 0; + do { + d->M[i][0] = a.M[i][0] * b.M[0][0] + a.M[i][1] * b.M[1][0] + a.M[i][2] * b.M[2][0] + a.M[i][3] * b.M[3][0]; + d->M[i][1] = a.M[i][0] * b.M[0][1] + a.M[i][1] * b.M[1][1] + a.M[i][2] * b.M[2][1] + a.M[i][3] * b.M[3][1]; + d->M[i][2] = a.M[i][0] * b.M[0][2] + a.M[i][1] * b.M[1][2] + a.M[i][2] * b.M[2][2] + a.M[i][3] * b.M[3][2]; + d->M[i][3] = a.M[i][0] * b.M[0][3] + a.M[i][1] * b.M[1][3] + a.M[i][2] * b.M[2][3] + a.M[i][3] * b.M[3][3]; + } while((++i) < 4); + + return *d; + } + + Matrix4f operator* (const Matrix4f& b) const + { + Matrix4f result(Matrix4f::NoInit); + Multiply(&result, *this, b); + return result; + } + + Matrix4f& operator*= (const Matrix4f& b) + { + return Multiply(this, Matrix4f(*this), b); + } + + Matrix4f operator* (float s) const + { + return Matrix4f(M[0][0] * s, M[0][1] * s, M[0][2] * s, M[0][3] * s, + M[1][0] * s, M[1][1] * s, M[1][2] * s, M[1][3] * s, + M[2][0] * s, M[2][1] * s, M[2][2] * s, M[2][3] * s, + M[3][0] * s, M[3][1] * s, M[3][2] * s, M[3][3] * s); + } + + Matrix4f& operator*= (float s) + { + M[0][0] *= s; M[0][1] *= s; M[0][2] *= s; M[0][3] *= s; + M[1][0] *= s; M[1][1] *= s; M[1][2] *= s; M[1][3] *= s; + M[2][0] *= s; M[2][1] *= s; M[2][2] *= s; M[2][3] *= s; + M[3][0] *= s; M[3][1] *= s; M[3][2] *= s; M[3][3] *= s; + return *this; + } + + Vector3f Transform(const Vector3f& v) const + { + return Vector3f(M[0][0] * v.x + M[0][1] * v.y + M[0][2] * v.z + M[0][3], + M[1][0] * v.x + M[1][1] * v.y + M[1][2] * v.z + M[1][3], + M[2][0] * v.x + M[2][1] * v.y + M[2][2] * v.z + M[2][3]); + } + + Matrix4f Transposed() const + { + return Matrix4f(M[0][0], M[1][0], M[2][0], M[3][0], + M[0][1], M[1][1], M[2][1], M[3][1], + M[0][2], M[1][2], M[2][2], M[3][2], + M[0][3], M[1][3], M[2][3], M[3][3]); + } + + void Transpose() + { + *this = Transposed(); + } + + + float SubDet (const int* rows, const int* cols) const + { + return M[rows[0]][cols[0]] * (M[rows[1]][cols[1]] * M[rows[2]][cols[2]] - M[rows[1]][cols[2]] * M[rows[2]][cols[1]]) + - M[rows[0]][cols[1]] * (M[rows[1]][cols[0]] * M[rows[2]][cols[2]] - M[rows[1]][cols[2]] * M[rows[2]][cols[0]]) + + M[rows[0]][cols[2]] * (M[rows[1]][cols[0]] * M[rows[2]][cols[1]] - M[rows[1]][cols[1]] * M[rows[2]][cols[0]]); + } + + float Cofactor(int I, int J) const + { + const int indices[4][3] = {{1,2,3},{0,2,3},{0,1,3},{0,1,2}}; + return ((I+J)&1) ? -SubDet(indices[I],indices[J]) : SubDet(indices[I],indices[J]); + } + + float Determinant() const + { + return M[0][0] * Cofactor(0,0) + M[0][1] * Cofactor(0,1) + M[0][2] * Cofactor(0,2) + M[0][3] * Cofactor(0,3); + } + + Matrix4f Adjugated() const + { + return Matrix4f(Cofactor(0,0), Cofactor(1,0), Cofactor(2,0), Cofactor(3,0), + Cofactor(0,1), Cofactor(1,1), Cofactor(2,1), Cofactor(3,1), + Cofactor(0,2), Cofactor(1,2), Cofactor(2,2), Cofactor(3,2), + Cofactor(0,3), Cofactor(1,3), Cofactor(2,3), Cofactor(3,3)); + } + + Matrix4f Inverted() const + { + float det = Determinant(); + assert(det != 0); + return Adjugated() * (1.0f/det); + } + + void Invert() + { + *this = Inverted(); + } + + //AnnaSteve: + // a,b,c, are the YawPitchRoll angles to be returned + // rotation a around axis A1 + // is followed by rotation b around axis A2 + // is followed by rotation c around axis A3 + // rotations are CCW or CW (D) in LH or RH coordinate system (S) + template + void ToEulerAngles(float *a, float *b, float *c) + { + OVR_COMPILER_ASSERT((A1 != A2) && (A2 != A3) && (A1 != A3)); + + float psign = -1.0f; + if (((A1 + 1) % 3 == A2) && ((A2 + 1) % 3 == A3)) // Determine whether even permutation + psign = 1.0f; + + float pm = psign*M[A1][A3]; + if (pm < -1.0f + Math::SingularityRadius) + { // South pole singularity + *a = 0.0f; + *b = -S*D*Math::PiOver2; + *c = S*D*atan2( psign*M[A2][A1], M[A2][A2] ); + } + else if (pm > 1.0 - Math::SingularityRadius) + { // North pole singularity + *a = 0.0f; + *b = S*D*Math::PiOver2; + *c = S*D*atan2( psign*M[A2][A1], M[A2][A2] ); + } + else + { // Normal case (nonsingular) + *a = S*D*atan2( -psign*M[A2][A3], M[A3][A3] ); + *b = S*D*asin(pm); + *c = S*D*atan2( -psign*M[A1][A2], M[A1][A1] ); + } + + return; + } + + //AnnaSteve: + // a,b,c, are the YawPitchRoll angles to be returned + // rotation a around axis A1 + // is followed by rotation b around axis A2 + // is followed by rotation c around axis A1 + // rotations are CCW or CW (D) in LH or RH coordinate system (S) + template + void ToEulerAnglesABA(float *a, float *b, float *c) + { + OVR_COMPILER_ASSERT(A1 != A2); + + // Determine the axis that was not supplied + int m = 3 - A1 - A2; + + float psign = -1.0f; + if ((A1 + 1) % 3 == A2) // Determine whether even permutation + psign = 1.0f; + + float c2 = M[A1][A1]; + if (c2 < -1.0 + Math::SingularityRadius) + { // South pole singularity + *a = 0.0f; + *b = S*D*Math::Pi; + *c = S*D*atan2( -psign*M[A2][m],M[A2][A2]); + } + else if (c2 > 1.0 - Math::SingularityRadius) + { // North pole singularity + *a = 0.0f; + *b = 0.0f; + *c = S*D*atan2( -psign*M[A2][m],M[A2][A2]); + } + else + { // Normal case (nonsingular) + *a = S*D*atan2( M[A2][A1],-psign*M[m][A1]); + *b = S*D*acos(c2); + *c = S*D*atan2( M[A1][A2],psign*M[A1][m]); + } + return; + } + + // Creates a matrix that converts the vertices from one coordinate system + // to another. + // + static Matrix4f AxisConversion(const WorldAxes& to, const WorldAxes& from) + { + // Holds axis values from the 'to' structure + int toArray[3] = { to.XAxis, to.YAxis, to.ZAxis }; + + // The inverse of the toArray + int inv[4]; + inv[0] = inv[abs(to.XAxis)] = 0; + inv[abs(to.YAxis)] = 1; + inv[abs(to.ZAxis)] = 2; + + Matrix4f m(0, 0, 0, + 0, 0, 0, + 0, 0, 0); + + // Only three values in the matrix need to be changed to 1 or -1. + m.M[inv[abs(from.XAxis)]][0] = float(from.XAxis/toArray[inv[abs(from.XAxis)]]); + m.M[inv[abs(from.YAxis)]][1] = float(from.YAxis/toArray[inv[abs(from.YAxis)]]); + m.M[inv[abs(from.ZAxis)]][2] = float(from.ZAxis/toArray[inv[abs(from.ZAxis)]]); + return m; + } + + + + static Matrix4f Translation(const Vector3f& v) + { + Matrix4f t; + t.M[0][3] = v.x; + t.M[1][3] = v.y; + t.M[2][3] = v.z; + return t; + } + + static Matrix4f Translation(float x, float y, float z = 0.0f) + { + Matrix4f t; + t.M[0][3] = x; + t.M[1][3] = y; + t.M[2][3] = z; + return t; + } + + static Matrix4f Scaling(const Vector3f& v) + { + Matrix4f t; + t.M[0][0] = v.x; + t.M[1][1] = v.y; + t.M[2][2] = v.z; + return t; + } + + static Matrix4f Scaling(float x, float y, float z) + { + Matrix4f t; + t.M[0][0] = x; + t.M[1][1] = y; + t.M[2][2] = z; + return t; + } + + static Matrix4f Scaling(float s) + { + Matrix4f t; + t.M[0][0] = s; + t.M[1][1] = s; + t.M[2][2] = s; + return t; + } + + + + //AnnaSteve : Just for quick testing. Not for final API. Need to remove case. + static Matrix4f RotationAxis(Axis A, float angle, RotateDirection d, HandedSystem s) + { + float sina = s * d *sin(angle); + float cosa = cos(angle); + + switch(A) + { + case Axis_X: + return Matrix4f(1, 0, 0, + 0, cosa, -sina, + 0, sina, cosa); + case Axis_Y: + return Matrix4f(cosa, 0, sina, + 0, 1, 0, + -sina, 0, cosa); + case Axis_Z: + return Matrix4f(cosa, -sina, 0, + sina, cosa, 0, + 0, 0, 1); + } + } + + + // Creates a rotation matrix rotating around the X axis by 'angle' radians. + // Rotation direction is depends on the coordinate system: + // RHS (Oculus default): Positive angle values rotate Counter-clockwise (CCW), + // while looking in the negative axis direction. This is the + // same as looking down from positive axis values towards origin. + // LHS: Positive angle values rotate clock-wise (CW), while looking in the + // negative axis direction. + static Matrix4f RotationX(float angle) + { + float sina = sin(angle); + float cosa = cos(angle); + return Matrix4f(1, 0, 0, + 0, cosa, -sina, + 0, sina, cosa); + } + + // Creates a rotation matrix rotating around the Y axis by 'angle' radians. + // Rotation direction is depends on the coordinate system: + // RHS (Oculus default): Positive angle values rotate Counter-clockwise (CCW), + // while looking in the negative axis direction. This is the + // same as looking down from positive axis values towards origin. + // LHS: Positive angle values rotate clock-wise (CW), while looking in the + // negative axis direction. + static Matrix4f RotationY(float angle) + { + float sina = sin(angle); + float cosa = cos(angle); + return Matrix4f(cosa, 0, sina, + 0, 1, 0, + -sina, 0, cosa); + } + + // Creates a rotation matrix rotating around the Z axis by 'angle' radians. + // Rotation direction is depends on the coordinate system: + // RHS (Oculus default): Positive angle values rotate Counter-clockwise (CCW), + // while looking in the negative axis direction. This is the + // same as looking down from positive axis values towards origin. + // LHS: Positive angle values rotate clock-wise (CW), while looking in the + // negative axis direction. + static Matrix4f RotationZ(float angle) + { + float sina = sin(angle); + float cosa = cos(angle); + return Matrix4f(cosa, -sina, 0, + sina, cosa, 0, + 0, 0, 1); + } + + + // LookAtRH creates a View transformation matrix for right-handed coordinate system. + // The resulting matrix points camera from 'eye' towards 'at' direction, with 'up' + // specifying the up vector. The resulting matrix should be used with PerspectiveRH + // projection. + static Matrix4f LookAtRH(const Vector3f& eye, const Vector3f& at, const Vector3f& up); + + // LookAtLH creates a View transformation matrix for left-handed coordinate system. + // The resulting matrix points camera from 'eye' towards 'at' direction, with 'up' + // specifying the up vector. + static Matrix4f LookAtLH(const Vector3f& eye, const Vector3f& at, const Vector3f& up); + + + // PerspectiveRH creates a right-handed perspective projection matrix that can be + // used with the Oculus sample renderer. + // yfov - Specifies vertical field of view in radians. + // aspect - Screen aspect ration, which is usually width/height for square pixels. + // Note that xfov = yfov * aspect. + // znear - Absolute value of near Z clipping clipping range. + // zfar - Absolute value of far Z clipping clipping range (larger then near). + // Even though RHS usually looks in the direction of negative Z, positive values + // are expected for znear and zfar. + static Matrix4f PerspectiveRH(float yfov, float aspect, float znear, float zfar); + + + // PerspectiveRH creates a left-handed perspective projection matrix that can be + // used with the Oculus sample renderer. + // yfov - Specifies vertical field of view in radians. + // aspect - Screen aspect ration, which is usually width/height for square pixels. + // Note that xfov = yfov * aspect. + // znear - Absolute value of near Z clipping clipping range. + // zfar - Absolute value of far Z clipping clipping range (larger then near). + static Matrix4f PerspectiveLH(float yfov, float aspect, float znear, float zfar); + + + static Matrix4f Ortho2D(float w, float h); +}; + + +//------------------------------------------------------------------------------------- +// ***** Quat + +// Quatf represents a quaternion class used for rotations. +// +// Quaternion multiplications are done in right-to-left order, to match the +// behavior of matrices. + + +template +class Quat +{ +public: + // w + Xi + Yj + Zk + T x, y, z, w; + + Quat() : x(0), y(0), z(0), w(1) {} + Quat(T x_, T y_, T z_, T w_) : x(x_), y(y_), z(z_), w(w_) {} + + + // Constructs rotation quaternion around the axis. + Quat(const Vector3& axis, T angle) + { + Vector3 unitAxis = axis.Normalized(); + T sinHalfAngle = sin(angle * T(0.5)); + + w = cos(angle * T(0.5)); + x = unitAxis.x * sinHalfAngle; + y = unitAxis.y * sinHalfAngle; + z = unitAxis.z * sinHalfAngle; + } + + //AnnaSteve: + void AxisAngle(Axis A, T angle, RotateDirection d, HandedSystem s) + { + T sinHalfAngle = s * d *sin(angle * (T)0.5); + T v[3]; + v[0] = v[1] = v[2] = (T)0; + v[A] = sinHalfAngle; + //return Quat(v[0], v[1], v[2], cos(angle * (T)0.5)); + w = cos(angle * (T)0.5); + x = v[0]; + y = v[1]; + z = v[2]; + } + + + void GetAxisAngle(Vector3* axis, T* angle) const + { + if (LengthSq() > Math::Tolerance * Math::Tolerance) + { + *axis = Vector3(x, y, z).Normalized(); + *angle = 2 * acos(w); + } + else + { + *axis = Vector3(1, 0, 0); + *angle= 0; + } + } + + bool operator== (const Quat& b) const { return x == b.x && y == b.y && z == b.z && w == b.w; } + bool operator!= (const Quat& b) const { return x != b.x || y != b.y || z != b.z || w != b.w; } + + Quat operator+ (const Quat& b) const { return Quat(x + b.x, y + b.y, z + b.z, w + b.w); } + Quat& operator+= (const Quat& b) { w += b.w; x += b.x; y += b.y; z += b.z; return *this; } + Quat operator- (const Quat& b) const { return Quat(x - b.x, y - b.y, z - b.z, w - b.w); } + Quat& operator-= (const Quat& b) { w -= b.w; x -= b.x; y -= b.y; z -= b.z; return *this; } + + Quat operator* (T s) const { return Quat(x * s, y * s, z * s, w * s); } + Quat& operator*= (T s) { w *= s; x *= s; y *= s; z *= s; return *this; } + Quat operator/ (T s) const { T rcp = T(1)/s; return Quat(x * rcp, y * rcp, z * rcp, w *rcp); } + Quat& operator/= (T s) { T rcp = T(1)/s; w *= rcp; x *= rcp; y *= rcp; z *= rcp; return *this; } + + // Get Imaginary part vector + Vector3 Imag() const { return Vector3(x,y,z); } + + // Get quaternion length. + T Length() const { return sqrt(x * x + y * y + z * z + w * w); } + // Get quaternion length squared. + T LengthSq() const { return (x * x + y * y + z * z + w * w); } + // Simple Eulidean distance in R^4 (not SLERP distance, but at least respects Haar measure) + T Distance(const Quat& q) const + { + T d1 = (*this - q).Length(); + T d2 = (*this + q).Length(); // Antipoldal point check + return (d1 < d2) ? d1 : d2; + } + T DistanceSq(const Quat& q) const + { + T d1 = (*this - q).LengthSq(); + T d2 = (*this + q).LengthSq(); // Antipoldal point check + return (d1 < d2) ? d1 : d2; + } + + // Normalize + bool IsNormalized() const { return fabs(LengthSq() - 1) < Math::Tolerance; } + void Normalize() { *this /= Length(); } + Quat Normalized() const { return *this / Length(); } + + // Returns conjugate of the quaternion. Produces inverse rotation if quaternion is normalized. + Quat Conj() const { return Quat(-x, -y, -z, w); } + + // AnnaSteve fixed: order of quaternion multiplication + // Quaternion multiplication. Combines quaternion rotations, performing the one on the + // right hand side first. + Quat operator* (const Quat& b) const { return Quat(w * b.x + x * b.w + y * b.z - z * b.y, + w * b.y - x * b.z + y * b.w + z * b.x, + w * b.z + x * b.y - y * b.x + z * b.w, + w * b.w - x * b.x - y * b.y - z * b.z); } + + // + // this^p normalized; same as rotating by this p times. + Quat PowNormalized(T p) const + { + Vector3 v; + T a; + GetAxisAngle(&v, &a); + return Quat(v, a * p); + } + + // Rotate transforms vector in a manner that matches Matrix rotations (counter-clockwise, + // assuming negative direction of the axis). Standard formula: q(t) * V * q(t)^-1. + Vector3 Rotate(const Vector3& v) const + { + return ((*this * Quat(v.x, v.y, v.z, 0)) * Inverted()).Imag(); + } + + + // Inversed quaternion rotates in the opposite direction. + Quat Inverted() const + { + return Quat(-x, -y, -z, w); + } + + // Sets this quaternion to the one rotates in the opposite direction. + void Invert() + { + *this = Quat(-x, -y, -z, w); + } + + // Converting quaternion to matrix. + operator Matrix4f() const + { + T ww = w*w; + T xx = x*x; + T yy = y*y; + T zz = z*z; + + return Matrix4f(float(ww + xx - yy - zz), float(T(2) * (x*y - w*z)), float(T(2) * (x*z + w*y)), + float(T(2) * (x*y + w*z)), float(ww - xx + yy - zz), float(T(2) * (y*z - w*x)), + float(T(2) * (x*z - w*y)), float(T(2) * (y*z + w*x)), float(ww - xx - yy + zz) ); + } + + + // GetEulerAngles extracts Euler angles from the quaternion, in the specified order of + // axis rotations and the specified coordinate system. Right-handed coordinate system + // is the default, with CCW rotations while looking in the negative axis direction. + // Here a,b,c, are the Yaw/Pitch/Roll angles to be returned. + // rotation a around axis A1 + // is followed by rotation b around axis A2 + // is followed by rotation c around axis A3 + // rotations are CCW or CW (D) in LH or RH coordinate system (S) + template + void GetEulerAngles(T *a, T *b, T *c) + { + OVR_COMPILER_ASSERT((A1 != A2) && (A2 != A3) && (A1 != A3)); + + T Q[3] = { x, y, z }; //Quaternion components x,y,z + + T ww = w*w; + T Q11 = Q[A1]*Q[A1]; + T Q22 = Q[A2]*Q[A2]; + T Q33 = Q[A3]*Q[A3]; + + T psign = T(-1.0); + // Determine whether even permutation + if (((A1 + 1) % 3 == A2) && ((A2 + 1) % 3 == A3)) + psign = T(1.0); + + T s2 = psign * T(2.0) * (psign*w*Q[A2] + Q[A1]*Q[A3]); + + if (s2 < (T)-1.0 + Math::SingularityRadius) + { // South pole singularity + *a = T(0.0); + *b = -S*D*Math::PiOver2; + *c = S*D*atan2((T)2.0*(psign*Q[A1]*Q[A2] + w*Q[A3]), + ww + Q22 - Q11 - Q33 ); + } + else if (s2 > (T)1.0 - Math::SingularityRadius) + { // North pole singularity + *a = (T)0.0; + *b = S*D*Math::PiOver2; + *c = S*D*atan2((T)2.0*(psign*Q[A1]*Q[A2] + w*Q[A3]), + ww + Q22 - Q11 - Q33); + } + else + { + *a = -S*D*atan2((T)-2.0*(w*Q[A1] - psign*Q[A2]*Q[A3]), + ww + Q33 - Q11 - Q22); + *b = S*D*asin(s2); + *c = S*D*atan2((T)2.0*(w*Q[A3] - psign*Q[A1]*Q[A2]), + ww + Q11 - Q22 - Q33); + } + return; + } + + template + void GetEulerAngles(T *a, T *b, T *c) + { GetEulerAngles(a, b, c); } + + template + void GetEulerAngles(T *a, T *b, T *c) + { GetEulerAngles(a, b, c); } + + + // GetEulerAnglesABA extracts Euler angles from the quaternion, in the specified order of + // axis rotations and the specified coordinate system. Right-handed coordinate system + // is the default, with CCW rotations while looking in the negative axis direction. + // Here a,b,c, are the Yaw/Pitch/Roll angles to be returned. + // rotation a around axis A1 + // is followed by rotation b around axis A2 + // is followed by rotation c around axis A1 + // Rotations are CCW or CW (D) in LH or RH coordinate system (S) + template + void GetEulerAnglesABA(T *a, T *b, T *c) + { + OVR_COMPILER_ASSERT(A1 != A2); + + T Q[3] = {x, y, z}; // Quaternion components + + // Determine the missing axis that was not supplied + int m = 3 - A1 - A2; + + T ww = w*w; + T Q11 = Q[A1]*Q[A1]; + T Q22 = Q[A2]*Q[A2]; + T Qmm = Q[m]*Q[m]; + + T psign = T(-1.0); + if ((A1 + 1) % 3 == A2) // Determine whether even permutation + { + psign = (T)1.0; + } + + T c2 = ww + Q11 - Q22 - Qmm; + if (c2 < (T)-1.0 + Math::SingularityRadius) + { // South pole singularity + *a = (T)0.0; + *b = S*D*Math::Pi; + *c = S*D*atan2( (T)2.0*(w*Q[A1] - psign*Q[A2]*Q[m]), + ww + Q22 - Q11 - Qmm); + } + else if (c2 > (T)1.0 - Math::SingularityRadius) + { // North pole singularity + *a = (T)0.0; + *b = (T)0.0; + *c = S*D*atan2( (T)2.0*(w*Q[A1] - psign*Q[A2]*Q[m]), + ww + Q22 - Q11 - Qmm); + } + else + { + *a = S*D*atan2( psign*w*Q[m] + Q[A1]*Q[A2], + w*Q[A2] -psign*Q[A1]*Q[m]); + *b = S*D*acos(c2); + *c = S*D*atan2( -psign*w*Q[m] + Q[A1]*Q[A2], + w*Q[A2] + psign*Q[A1]*Q[m]); + } + return; + } +}; + + +typedef Quat Quatf; +typedef Quat Quatd; + + + +//------------------------------------------------------------------------------------- +// ***** Angle + +// Cleanly representing the algebra of 2D rotations. +// The operations maintain the angle between -Pi and Pi, the same range as atan2. +// + +template +class Angle +{ +public: + enum AngularUnits + { + Radians = 0, + Degrees = 1 + }; + + Angle() : a(0) {} + + // Fix the range to be between -Pi and Pi + Angle(T a_, AngularUnits u = Radians) : a((u == Radians) ? a_ : a_*Math::DegreeToRadFactor) { FixRange(); } + + T Get(AngularUnits u = Radians) const { return (u == Radians) ? a : a*Math::RadToDegreeFactor; } + void Set(const T& x, AngularUnits u = Radians) { a = (u == Radians) ? x : x*Math::DegreeToRadFactor; FixRange(); } + int Sign() const { if (a == 0) return 0; else return (a > 0) ? 1 : -1; } + T Abs() const { return (a > 0) ? a : -a; } + + bool operator== (const Angle& b) const { return a == b.a; } + bool operator!= (const Angle& b) const { return a != b.a; } +// bool operator< (const Angle& b) const { return a < a.b; } +// bool operator> (const Angle& b) const { return a > a.b; } +// bool operator<= (const Angle& b) const { return a <= a.b; } +// bool operator>= (const Angle& b) const { return a >= a.b; } +// bool operator= (const T& x) { a = x; FixRange(); } + + // These operations assume a is already between -Pi and Pi. + Angle operator+ (const Angle& b) const { return Angle(a + b.a); } + Angle operator+ (const T& x) const { return Angle(a + x); } + Angle& operator+= (const Angle& b) { a = a + b.a; FastFixRange(); return *this; } + Angle& operator+= (const T& x) { a = a + x; FixRange(); return *this; } + Angle operator- (const Angle& b) const { return Angle(a - b.a); } + Angle operator- (const T& x) const { return Angle(a - x); } + Angle& operator-= (const Angle& b) { a = a - b.a; FastFixRange(); return *this; } + Angle& operator-= (const T& x) { a = a - x; FixRange(); return *this; } + + T Distance(const Angle& b) { T c = fabs(a - b.a); return (c <= Math::Pi) ? c : Math::TwoPi - c; } + +private: + + // The stored angle, which should be maintained between -Pi and Pi + T a; + + // Fixes the angle range to [-Pi,Pi], but assumes no more than 2Pi away on either side + inline void FastFixRange() + { + if (a < -Math::Pi) + a += Math::TwoPi; + else if (a > Math::Pi) + a -= Math::TwoPi; + } + + // Fixes the angle range to [-Pi,Pi] for any given range, but slower then the fast method + inline void FixRange() + { + a = fmod(a,Math::TwoPi); + if (a < -Math::Pi) + a += Math::TwoPi; + else if (a > Math::Pi) + a -= Math::TwoPi; + } +}; + + +typedef Angle Anglef; +typedef Angle Angled; + + +//------------------------------------------------------------------------------------- +// ***** Plane + +// Consists of a normal vector and distance from the origin where the plane is located. + +template +class Plane : public RefCountBase > +{ +public: + Vector3 N; + T D; + + Plane() : D(0) {} + + // Normals must already be normalized + Plane(const Vector3& n, T d) : N(n), D(d) {} + Plane(T x, T y, T z, T d) : N(x,y,z), D(d) {} + + // construct from a point on the plane and the normal + Plane(const Vector3& p, const Vector3& n) : N(n), D(-(p * n)) {} + + // Find the point to plane distance. The sign indicates what side of the plane the point is on (0 = point on plane). + T TestSide(const Vector3& p) const + { + return (N * p) + D; + } + + Plane Flipped() const + { + return Plane(-N, -D); + } + + void Flip() + { + N = -N; + D = -D; + } + + bool operator==(const Plane& rhs) const + { + return (this->D == rhs.D && this->N == rhs.N); + } +}; + +typedef Plane Planef; + +} + +#endif diff -r 59fc487ba58e -r b069a5c27388 libovr/Src/Kernel/OVR_RefCount.cpp --- a/libovr/Src/Kernel/OVR_RefCount.cpp Sat Sep 14 17:51:03 2013 +0300 +++ b/libovr/Src/Kernel/OVR_RefCount.cpp Sun Sep 15 04:10:05 2013 +0300 @@ -1,1 +1,100 @@ -/************************************************************************************ Filename : OVR_RefCount.cpp Content : Reference counting implementation Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #include "OVR_RefCount.h" #include "OVR_Atomic.h" #include "OVR_Log.h" namespace OVR { #ifdef OVR_CC_ARM void* ReturnArg0(void* p) { return p; } #endif // ***** Reference Count Base implementation RefCountImplCore::~RefCountImplCore() { // RefCount can be either 1 or 0 here. // 0 if Release() was properly called. // 1 if the object was declared on stack or as an aggregate. OVR_ASSERT(RefCount <= 1); } #ifdef OVR_BUILD_DEBUG void RefCountImplCore::reportInvalidDelete(void *pmem) { OVR_DEBUG_LOG( ("Invalid delete call on ref-counted object at %p. Please use Release()", pmem)); OVR_ASSERT(0); } #endif RefCountNTSImplCore::~RefCountNTSImplCore() { // RefCount can be either 1 or 0 here. // 0 if Release() was properly called. // 1 if the object was declared on stack or as an aggregate. OVR_ASSERT(RefCount <= 1); } #ifdef OVR_BUILD_DEBUG void RefCountNTSImplCore::reportInvalidDelete(void *pmem) { OVR_DEBUG_LOG( ("Invalid delete call on ref-counted object at %p. Please use Release()", pmem)); OVR_ASSERT(0); } #endif // *** Thread-Safe RefCountImpl void RefCountImpl::AddRef() { AtomicOps::ExchangeAdd_NoSync(&RefCount, 1); } void RefCountImpl::Release() { if ((AtomicOps::ExchangeAdd_NoSync(&RefCount, -1) - 1) == 0) delete this; } // *** Thread-Safe RefCountVImpl w/virtual AddRef/Release void RefCountVImpl::AddRef() { AtomicOps::ExchangeAdd_NoSync(&RefCount, 1); } void RefCountVImpl::Release() { if ((AtomicOps::ExchangeAdd_NoSync(&RefCount, -1) - 1) == 0) delete this; } // *** NON-Thread-Safe RefCountImpl void RefCountNTSImpl::Release() const { RefCount--; if (RefCount == 0) delete this; } } // OVR \ No newline at end of file +/************************************************************************************ + +Filename : OVR_RefCount.cpp +Content : Reference counting implementation +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#include "OVR_RefCount.h" +#include "OVR_Atomic.h" +#include "OVR_Log.h" + +namespace OVR { + +#ifdef OVR_CC_ARM +void* ReturnArg0(void* p) +{ + return p; +} +#endif + +// ***** Reference Count Base implementation + +RefCountImplCore::~RefCountImplCore() +{ + // RefCount can be either 1 or 0 here. + // 0 if Release() was properly called. + // 1 if the object was declared on stack or as an aggregate. + OVR_ASSERT(RefCount <= 1); +} + +#ifdef OVR_BUILD_DEBUG +void RefCountImplCore::reportInvalidDelete(void *pmem) +{ + OVR_DEBUG_LOG( + ("Invalid delete call on ref-counted object at %p. Please use Release()", pmem)); + OVR_ASSERT(0); +} +#endif + +RefCountNTSImplCore::~RefCountNTSImplCore() +{ + // RefCount can be either 1 or 0 here. + // 0 if Release() was properly called. + // 1 if the object was declared on stack or as an aggregate. + OVR_ASSERT(RefCount <= 1); +} + +#ifdef OVR_BUILD_DEBUG +void RefCountNTSImplCore::reportInvalidDelete(void *pmem) +{ + OVR_DEBUG_LOG( + ("Invalid delete call on ref-counted object at %p. Please use Release()", pmem)); + OVR_ASSERT(0); +} +#endif + + +// *** Thread-Safe RefCountImpl + +void RefCountImpl::AddRef() +{ + AtomicOps::ExchangeAdd_NoSync(&RefCount, 1); +} +void RefCountImpl::Release() +{ + if ((AtomicOps::ExchangeAdd_NoSync(&RefCount, -1) - 1) == 0) + delete this; +} + +// *** Thread-Safe RefCountVImpl w/virtual AddRef/Release + +void RefCountVImpl::AddRef() +{ + AtomicOps::ExchangeAdd_NoSync(&RefCount, 1); +} +void RefCountVImpl::Release() +{ + if ((AtomicOps::ExchangeAdd_NoSync(&RefCount, -1) - 1) == 0) + delete this; +} + +// *** NON-Thread-Safe RefCountImpl + +void RefCountNTSImpl::Release() const +{ + RefCount--; + if (RefCount == 0) + delete this; +} + + +} // OVR diff -r 59fc487ba58e -r b069a5c27388 libovr/Src/Kernel/OVR_RefCount.h --- a/libovr/Src/Kernel/OVR_RefCount.h Sat Sep 14 17:51:03 2013 +0300 +++ b/libovr/Src/Kernel/OVR_RefCount.h Sun Sep 15 04:10:05 2013 +0300 @@ -1,1 +1,522 @@ -/************************************************************************************ PublicHeader: Kernel Filename : OVR_RefCount.h Content : Reference counting implementation headers Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #ifndef OVR_RefCount_h #define OVR_RefCount_h #include "OVR_Types.h" #include "OVR_Allocator.h" namespace OVR { //----------------------------------------------------------------------------------- // ***** Reference Counting // There are three types of reference counting base classes: // // RefCountBase - Provides thread-safe reference counting (Default). // RefCountBaseNTS - Non Thread Safe version of reference counting. // ***** Declared classes template class RefCountBase; template class RefCountBaseNTS; class RefCountImpl; class RefCountNTSImpl; //----------------------------------------------------------------------------------- // ***** Implementation For Reference Counting // RefCountImplCore holds RefCount value and defines a few utility // functions shared by all implementations. class RefCountImplCore { protected: volatile int RefCount; public: // RefCountImpl constructor always initializes RefCount to 1 by default. OVR_FORCE_INLINE RefCountImplCore() : RefCount(1) { } // Need virtual destructor // This: 1. Makes sure the right destructor's called. // 2. Makes us have VTable, necessary if we are going to have format needed by InitNewMem() virtual ~RefCountImplCore(); // Debug method only. int GetRefCount() const { return RefCount; } // This logic is used to detect invalid 'delete' calls of reference counted // objects. Direct delete calls are not allowed on them unless they come in // internally from Release. #ifdef OVR_BUILD_DEBUG static void OVR_CDECL reportInvalidDelete(void *pmem); inline static void checkInvalidDelete(RefCountImplCore *pmem) { if (pmem->RefCount != 0) reportInvalidDelete(pmem); } #else inline static void checkInvalidDelete(RefCountImplCore *) { } #endif // Base class ref-count content should not be copied. void operator = (const RefCountImplCore &) { } }; class RefCountNTSImplCore { protected: mutable int RefCount; public: // RefCountImpl constructor always initializes RefCount to 1 by default. OVR_FORCE_INLINE RefCountNTSImplCore() : RefCount(1) { } // Need virtual destructor // This: 1. Makes sure the right destructor's called. // 2. Makes us have VTable, necessary if we are going to have format needed by InitNewMem() virtual ~RefCountNTSImplCore(); // Debug method only. int GetRefCount() const { return RefCount; } // This logic is used to detect invalid 'delete' calls of reference counted // objects. Direct delete calls are not allowed on them unless they come in // internally from Release. #ifdef OVR_BUILD_DEBUG static void OVR_CDECL reportInvalidDelete(void *pmem); OVR_FORCE_INLINE static void checkInvalidDelete(RefCountNTSImplCore *pmem) { if (pmem->RefCount != 0) reportInvalidDelete(pmem); } #else OVR_FORCE_INLINE static void checkInvalidDelete(RefCountNTSImplCore *) { } #endif // Base class ref-count content should not be copied. void operator = (const RefCountNTSImplCore &) { } }; // RefCountImpl provides Thread-Safe implementation of reference counting, so // it should be used by default in most places. class RefCountImpl : public RefCountImplCore { public: // Thread-Safe Ref-Count Implementation. void AddRef(); void Release(); }; // RefCountVImpl provides Thread-Safe implementation of reference counting, plus, // virtual AddRef and Release. class RefCountVImpl : public RefCountImplCore { public: // Thread-Safe Ref-Count Implementation. virtual void AddRef(); virtual void Release(); }; // RefCountImplNTS provides Non-Thread-Safe implementation of reference counting, // which is slightly more efficient since it doesn't use atomics. class RefCountNTSImpl : public RefCountNTSImplCore { public: OVR_FORCE_INLINE void AddRef() const { RefCount++; } void Release() const; }; // RefCountBaseStatImpl<> is a common class that adds new/delete override with Stat tracking // to the reference counting implementation. Base must be one of the RefCountImpl classes. template class RefCountBaseStatImpl : public Base { public: RefCountBaseStatImpl() { } // *** Override New and Delete // DOM-IGNORE-BEGIN // Undef new temporarily if it is being redefined #ifdef OVR_DEFINE_NEW #undef new #endif #ifdef OVR_BUILD_DEBUG // Custom check used to detect incorrect calls of 'delete' on ref-counted objects. #define OVR_REFCOUNTALLOC_CHECK_DELETE(class_name, p) \ do {if (p) Base::checkInvalidDelete((class_name*)p); } while(0) #else #define OVR_REFCOUNTALLOC_CHECK_DELETE(class_name, p) #endif // Redefine all new & delete operators. OVR_MEMORY_REDEFINE_NEW_IMPL(Base, OVR_REFCOUNTALLOC_CHECK_DELETE) #ifdef OVR_DEFINE_NEW #define new OVR_DEFINE_NEW #endif // OVR_BUILD_DEFINE_NEW // DOM-IGNORE-END }; //----------------------------------------------------------------------------------- // *** End user RefCountBase<> classes // RefCountBase is a base class for classes that require thread-safe reference // counting; it also overrides the new and delete operators to use MemoryHeap. // // Reference counted objects start out with RefCount value of 1. Further lifetime // management is done through the AddRef() and Release() methods, typically // hidden by Ptr<>. template class RefCountBase : public RefCountBaseStatImpl { public: // Constructor. OVR_FORCE_INLINE RefCountBase() : RefCountBaseStatImpl() { } }; // RefCountBaseV is the same as RefCountBase but with virtual AddRef/Release template class RefCountBaseV : public RefCountBaseStatImpl { public: // Constructor. OVR_FORCE_INLINE RefCountBaseV() : RefCountBaseStatImpl() { } }; // RefCountBaseNTS is a base class for classes that require Non-Thread-Safe reference // counting; it also overrides the new and delete operators to use MemoryHeap. // This class should only be used if all pointers to it are known to be assigned, // destroyed and manipulated within one thread. // // Reference counted objects start out with RefCount value of 1. Further lifetime // management is done through the AddRef() and Release() methods, typically // hidden by Ptr<>. template class RefCountBaseNTS : public RefCountBaseStatImpl { public: // Constructor. OVR_FORCE_INLINE RefCountBaseNTS() : RefCountBaseStatImpl() { } }; //----------------------------------------------------------------------------------- // ***** Pickable template pointer enum PickType { PickValue }; template class Pickable { public: Pickable() : pV(NULL) {} explicit Pickable(T* p) : pV(p) {} Pickable(T* p, PickType) : pV(p) { OVR_ASSERT(pV); if (pV) pV->AddRef(); } template Pickable(const Pickable& other) : pV(other.GetPtr()) {} public: Pickable& operator =(const Pickable& other) { OVR_ASSERT(pV == NULL); pV = other.pV; // Extra check. //other.pV = NULL; return *this; } public: T* GetPtr() const { return pV; } T* operator->() const { return pV; } T& operator*() const { OVR_ASSERT(pV); return *pV; } private: T* pV; }; template OVR_FORCE_INLINE Pickable MakePickable(T* p) { return Pickable(p); } //----------------------------------------------------------------------------------- // ***** Ref-Counted template pointer // Automatically AddRefs and Releases interfaces void* ReturnArg0(void* p); template class Ptr { #ifdef OVR_CC_ARM static C* ReturnArg(void* p) { return (C*)ReturnArg0(p); } #endif protected: C *pObject; public: // Constructors OVR_FORCE_INLINE Ptr() : pObject(0) { } #ifdef OVR_CC_ARM OVR_FORCE_INLINE Ptr(C &robj) : pObject(ReturnArg(&robj)) #else OVR_FORCE_INLINE Ptr(C &robj) : pObject(&robj) #endif { } OVR_FORCE_INLINE Ptr(Pickable v) : pObject(v.GetPtr()) { // No AddRef() on purpose. } OVR_FORCE_INLINE Ptr(Ptr& other, PickType) : pObject(other.pObject) { other.pObject = NULL; // No AddRef() on purpose. } OVR_FORCE_INLINE Ptr(C *pobj) { if (pobj) pobj->AddRef(); pObject = pobj; } OVR_FORCE_INLINE Ptr(const Ptr &src) { if (src.pObject) src.pObject->AddRef(); pObject = src.pObject; } template OVR_FORCE_INLINE Ptr(Ptr &src) { if (src) src->AddRef(); pObject = src; } template OVR_FORCE_INLINE Ptr(Pickable v) : pObject(v.GetPtr()) { // No AddRef() on purpose. } // Destructor OVR_FORCE_INLINE ~Ptr() { if (pObject) pObject->Release(); } // Compares OVR_FORCE_INLINE bool operator == (const Ptr &other) const { return pObject == other.pObject; } OVR_FORCE_INLINE bool operator != (const Ptr &other) const { return pObject != other.pObject; } OVR_FORCE_INLINE bool operator == (C *pother) const { return pObject == pother; } OVR_FORCE_INLINE bool operator != (C *pother) const { return pObject != pother; } OVR_FORCE_INLINE bool operator < (const Ptr &other) const { return pObject < other.pObject; } // Assignment template OVR_FORCE_INLINE const Ptr& operator = (const Ptr &src) { if (src) src->AddRef(); if (pObject) pObject->Release(); pObject = src; return *this; } // Specialization OVR_FORCE_INLINE const Ptr& operator = (const Ptr &src) { if (src) src->AddRef(); if (pObject) pObject->Release(); pObject = src; return *this; } OVR_FORCE_INLINE const Ptr& operator = (C *psrc) { if (psrc) psrc->AddRef(); if (pObject) pObject->Release(); pObject = psrc; return *this; } OVR_FORCE_INLINE const Ptr& operator = (C &src) { if (pObject) pObject->Release(); pObject = &src; return *this; } OVR_FORCE_INLINE Ptr& operator = (Pickable src) { return Pick(src); } template OVR_FORCE_INLINE Ptr& operator = (Pickable src) { return Pick(src); } // Set Assignment template OVR_FORCE_INLINE Ptr& SetPtr(const Ptr &src) { if (src) src->AddRef(); if (pObject) pObject->Release(); pObject = src; return *this; } // Specialization OVR_FORCE_INLINE Ptr& SetPtr(const Ptr &src) { if (src) src->AddRef(); if (pObject) pObject->Release(); pObject = src; return *this; } OVR_FORCE_INLINE Ptr& SetPtr(C *psrc) { if (psrc) psrc->AddRef(); if (pObject) pObject->Release(); pObject = psrc; return *this; } OVR_FORCE_INLINE Ptr& SetPtr(C &src) { if (pObject) pObject->Release(); pObject = &src; return *this; } OVR_FORCE_INLINE Ptr& SetPtr(Pickable src) { return Pick(src); } // Nulls ref-counted pointer without decrement OVR_FORCE_INLINE void NullWithoutRelease() { pObject = 0; } // Clears the pointer to the object OVR_FORCE_INLINE void Clear() { if (pObject) pObject->Release(); pObject = 0; } // Obtain pointer reference directly, for D3D interfaces OVR_FORCE_INLINE C*& GetRawRef() { return pObject; } // Access Operators OVR_FORCE_INLINE C* GetPtr() const { return pObject; } OVR_FORCE_INLINE C& operator * () const { return *pObject; } OVR_FORCE_INLINE C* operator -> () const { return pObject; } // Conversion OVR_FORCE_INLINE operator C* () const { return pObject; } // Pickers. // Pick a value. OVR_FORCE_INLINE Ptr& Pick(Ptr& other) { if (&other != this) { if (pObject) pObject->Release(); pObject = other.pObject; other.pObject = 0; } return *this; } OVR_FORCE_INLINE Ptr& Pick(Pickable v) { if (v.GetPtr() != pObject) { if (pObject) pObject->Release(); pObject = v.GetPtr(); } return *this; } template OVR_FORCE_INLINE Ptr& Pick(Pickable v) { if (v.GetPtr() != pObject) { if (pObject) pObject->Release(); pObject = v.GetPtr(); } return *this; } OVR_FORCE_INLINE Ptr& Pick(C* p) { if (p != pObject) { if (pObject) pObject->Release(); pObject = p; } return *this; } }; } // OVR #endif \ No newline at end of file +/************************************************************************************ + +PublicHeader: Kernel +Filename : OVR_RefCount.h +Content : Reference counting implementation headers +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#ifndef OVR_RefCount_h +#define OVR_RefCount_h + +#include "OVR_Types.h" +#include "OVR_Allocator.h" + +namespace OVR { + +//----------------------------------------------------------------------------------- +// ***** Reference Counting + +// There are three types of reference counting base classes: +// +// RefCountBase - Provides thread-safe reference counting (Default). +// RefCountBaseNTS - Non Thread Safe version of reference counting. + + +// ***** Declared classes + +template +class RefCountBase; +template +class RefCountBaseNTS; + +class RefCountImpl; +class RefCountNTSImpl; + + +//----------------------------------------------------------------------------------- +// ***** Implementation For Reference Counting + +// RefCountImplCore holds RefCount value and defines a few utility +// functions shared by all implementations. + +class RefCountImplCore +{ +protected: + volatile int RefCount; + +public: + // RefCountImpl constructor always initializes RefCount to 1 by default. + OVR_FORCE_INLINE RefCountImplCore() : RefCount(1) { } + + // Need virtual destructor + // This: 1. Makes sure the right destructor's called. + // 2. Makes us have VTable, necessary if we are going to have format needed by InitNewMem() + virtual ~RefCountImplCore(); + + // Debug method only. + int GetRefCount() const { return RefCount; } + + // This logic is used to detect invalid 'delete' calls of reference counted + // objects. Direct delete calls are not allowed on them unless they come in + // internally from Release. +#ifdef OVR_BUILD_DEBUG + static void OVR_CDECL reportInvalidDelete(void *pmem); + inline static void checkInvalidDelete(RefCountImplCore *pmem) + { + if (pmem->RefCount != 0) + reportInvalidDelete(pmem); + } +#else + inline static void checkInvalidDelete(RefCountImplCore *) { } +#endif + + // Base class ref-count content should not be copied. + void operator = (const RefCountImplCore &) { } +}; + +class RefCountNTSImplCore +{ +protected: + mutable int RefCount; + +public: + // RefCountImpl constructor always initializes RefCount to 1 by default. + OVR_FORCE_INLINE RefCountNTSImplCore() : RefCount(1) { } + + // Need virtual destructor + // This: 1. Makes sure the right destructor's called. + // 2. Makes us have VTable, necessary if we are going to have format needed by InitNewMem() + virtual ~RefCountNTSImplCore(); + + // Debug method only. + int GetRefCount() const { return RefCount; } + + // This logic is used to detect invalid 'delete' calls of reference counted + // objects. Direct delete calls are not allowed on them unless they come in + // internally from Release. +#ifdef OVR_BUILD_DEBUG + static void OVR_CDECL reportInvalidDelete(void *pmem); + OVR_FORCE_INLINE static void checkInvalidDelete(RefCountNTSImplCore *pmem) + { + if (pmem->RefCount != 0) + reportInvalidDelete(pmem); + } +#else + OVR_FORCE_INLINE static void checkInvalidDelete(RefCountNTSImplCore *) { } +#endif + + // Base class ref-count content should not be copied. + void operator = (const RefCountNTSImplCore &) { } +}; + + + +// RefCountImpl provides Thread-Safe implementation of reference counting, so +// it should be used by default in most places. + +class RefCountImpl : public RefCountImplCore +{ +public: + // Thread-Safe Ref-Count Implementation. + void AddRef(); + void Release(); +}; + +// RefCountVImpl provides Thread-Safe implementation of reference counting, plus, +// virtual AddRef and Release. + +class RefCountVImpl : public RefCountImplCore +{ +public: + // Thread-Safe Ref-Count Implementation. + virtual void AddRef(); + virtual void Release(); +}; + + +// RefCountImplNTS provides Non-Thread-Safe implementation of reference counting, +// which is slightly more efficient since it doesn't use atomics. + +class RefCountNTSImpl : public RefCountNTSImplCore +{ +public: + OVR_FORCE_INLINE void AddRef() const { RefCount++; } + void Release() const; +}; + + + +// RefCountBaseStatImpl<> is a common class that adds new/delete override with Stat tracking +// to the reference counting implementation. Base must be one of the RefCountImpl classes. + +template +class RefCountBaseStatImpl : public Base +{ +public: + RefCountBaseStatImpl() { } + + // *** Override New and Delete + + // DOM-IGNORE-BEGIN + // Undef new temporarily if it is being redefined +#ifdef OVR_DEFINE_NEW +#undef new +#endif + +#ifdef OVR_BUILD_DEBUG + // Custom check used to detect incorrect calls of 'delete' on ref-counted objects. + #define OVR_REFCOUNTALLOC_CHECK_DELETE(class_name, p) \ + do {if (p) Base::checkInvalidDelete((class_name*)p); } while(0) +#else + #define OVR_REFCOUNTALLOC_CHECK_DELETE(class_name, p) +#endif + + // Redefine all new & delete operators. + OVR_MEMORY_REDEFINE_NEW_IMPL(Base, OVR_REFCOUNTALLOC_CHECK_DELETE) + +#ifdef OVR_DEFINE_NEW +#define new OVR_DEFINE_NEW +#endif + // OVR_BUILD_DEFINE_NEW + // DOM-IGNORE-END +}; + + + +//----------------------------------------------------------------------------------- +// *** End user RefCountBase<> classes + + +// RefCountBase is a base class for classes that require thread-safe reference +// counting; it also overrides the new and delete operators to use MemoryHeap. +// +// Reference counted objects start out with RefCount value of 1. Further lifetime +// management is done through the AddRef() and Release() methods, typically +// hidden by Ptr<>. + +template +class RefCountBase : public RefCountBaseStatImpl +{ +public: + // Constructor. + OVR_FORCE_INLINE RefCountBase() : RefCountBaseStatImpl() { } +}; + +// RefCountBaseV is the same as RefCountBase but with virtual AddRef/Release + +template +class RefCountBaseV : public RefCountBaseStatImpl +{ +public: + // Constructor. + OVR_FORCE_INLINE RefCountBaseV() : RefCountBaseStatImpl() { } +}; + + +// RefCountBaseNTS is a base class for classes that require Non-Thread-Safe reference +// counting; it also overrides the new and delete operators to use MemoryHeap. +// This class should only be used if all pointers to it are known to be assigned, +// destroyed and manipulated within one thread. +// +// Reference counted objects start out with RefCount value of 1. Further lifetime +// management is done through the AddRef() and Release() methods, typically +// hidden by Ptr<>. + +template +class RefCountBaseNTS : public RefCountBaseStatImpl +{ +public: + // Constructor. + OVR_FORCE_INLINE RefCountBaseNTS() : RefCountBaseStatImpl() { } +}; + +//----------------------------------------------------------------------------------- +// ***** Pickable template pointer +enum PickType { PickValue }; + +template +class Pickable +{ +public: + Pickable() : pV(NULL) {} + explicit Pickable(T* p) : pV(p) {} + Pickable(T* p, PickType) : pV(p) + { + OVR_ASSERT(pV); + if (pV) + pV->AddRef(); + } + template + Pickable(const Pickable& other) : pV(other.GetPtr()) {} + +public: + Pickable& operator =(const Pickable& other) + { + OVR_ASSERT(pV == NULL); + pV = other.pV; + // Extra check. + //other.pV = NULL; + return *this; + } + +public: + T* GetPtr() const { return pV; } + T* operator->() const + { + return pV; + } + T& operator*() const + { + OVR_ASSERT(pV); + return *pV; + } + +private: + T* pV; +}; + +template +OVR_FORCE_INLINE +Pickable MakePickable(T* p) +{ + return Pickable(p); +} + +//----------------------------------------------------------------------------------- +// ***** Ref-Counted template pointer + +// Automatically AddRefs and Releases interfaces + +void* ReturnArg0(void* p); + +template +class Ptr +{ +#ifdef OVR_CC_ARM + static C* ReturnArg(void* p) { return (C*)ReturnArg0(p); } +#endif + +protected: + C *pObject; + +public: + + // Constructors + OVR_FORCE_INLINE Ptr() : pObject(0) + { } +#ifdef OVR_CC_ARM + OVR_FORCE_INLINE Ptr(C &robj) : pObject(ReturnArg(&robj)) +#else + OVR_FORCE_INLINE Ptr(C &robj) : pObject(&robj) +#endif + { } + OVR_FORCE_INLINE Ptr(Pickable v) : pObject(v.GetPtr()) + { + // No AddRef() on purpose. + } + OVR_FORCE_INLINE Ptr(Ptr& other, PickType) : pObject(other.pObject) + { + other.pObject = NULL; + // No AddRef() on purpose. + } + OVR_FORCE_INLINE Ptr(C *pobj) + { + if (pobj) pobj->AddRef(); + pObject = pobj; + } + OVR_FORCE_INLINE Ptr(const Ptr &src) + { + if (src.pObject) src.pObject->AddRef(); + pObject = src.pObject; + } + + template + OVR_FORCE_INLINE Ptr(Ptr &src) + { + if (src) src->AddRef(); + pObject = src; + } + template + OVR_FORCE_INLINE Ptr(Pickable v) : pObject(v.GetPtr()) + { + // No AddRef() on purpose. + } + + // Destructor + OVR_FORCE_INLINE ~Ptr() + { + if (pObject) pObject->Release(); + } + + // Compares + OVR_FORCE_INLINE bool operator == (const Ptr &other) const { return pObject == other.pObject; } + OVR_FORCE_INLINE bool operator != (const Ptr &other) const { return pObject != other.pObject; } + + OVR_FORCE_INLINE bool operator == (C *pother) const { return pObject == pother; } + OVR_FORCE_INLINE bool operator != (C *pother) const { return pObject != pother; } + + + OVR_FORCE_INLINE bool operator < (const Ptr &other) const { return pObject < other.pObject; } + + // Assignment + template + OVR_FORCE_INLINE const Ptr& operator = (const Ptr &src) + { + if (src) src->AddRef(); + if (pObject) pObject->Release(); + pObject = src; + return *this; + } + // Specialization + OVR_FORCE_INLINE const Ptr& operator = (const Ptr &src) + { + if (src) src->AddRef(); + if (pObject) pObject->Release(); + pObject = src; + return *this; + } + + OVR_FORCE_INLINE const Ptr& operator = (C *psrc) + { + if (psrc) psrc->AddRef(); + if (pObject) pObject->Release(); + pObject = psrc; + return *this; + } + OVR_FORCE_INLINE const Ptr& operator = (C &src) + { + if (pObject) pObject->Release(); + pObject = &src; + return *this; + } + OVR_FORCE_INLINE Ptr& operator = (Pickable src) + { + return Pick(src); + } + template + OVR_FORCE_INLINE Ptr& operator = (Pickable src) + { + return Pick(src); + } + + // Set Assignment + template + OVR_FORCE_INLINE Ptr& SetPtr(const Ptr &src) + { + if (src) src->AddRef(); + if (pObject) pObject->Release(); + pObject = src; + return *this; + } + // Specialization + OVR_FORCE_INLINE Ptr& SetPtr(const Ptr &src) + { + if (src) src->AddRef(); + if (pObject) pObject->Release(); + pObject = src; + return *this; + } + + OVR_FORCE_INLINE Ptr& SetPtr(C *psrc) + { + if (psrc) psrc->AddRef(); + if (pObject) pObject->Release(); + pObject = psrc; + return *this; + } + OVR_FORCE_INLINE Ptr& SetPtr(C &src) + { + if (pObject) pObject->Release(); + pObject = &src; + return *this; + } + OVR_FORCE_INLINE Ptr& SetPtr(Pickable src) + { + return Pick(src); + } + + // Nulls ref-counted pointer without decrement + OVR_FORCE_INLINE void NullWithoutRelease() + { + pObject = 0; + } + + // Clears the pointer to the object + OVR_FORCE_INLINE void Clear() + { + if (pObject) pObject->Release(); + pObject = 0; + } + + // Obtain pointer reference directly, for D3D interfaces + OVR_FORCE_INLINE C*& GetRawRef() { return pObject; } + + // Access Operators + OVR_FORCE_INLINE C* GetPtr() const { return pObject; } + OVR_FORCE_INLINE C& operator * () const { return *pObject; } + OVR_FORCE_INLINE C* operator -> () const { return pObject; } + // Conversion + OVR_FORCE_INLINE operator C* () const { return pObject; } + + // Pickers. + + // Pick a value. + OVR_FORCE_INLINE Ptr& Pick(Ptr& other) + { + if (&other != this) + { + if (pObject) pObject->Release(); + pObject = other.pObject; + other.pObject = 0; + } + + return *this; + } + + OVR_FORCE_INLINE Ptr& Pick(Pickable v) + { + if (v.GetPtr() != pObject) + { + if (pObject) pObject->Release(); + pObject = v.GetPtr(); + } + + return *this; + } + + template + OVR_FORCE_INLINE Ptr& Pick(Pickable v) + { + if (v.GetPtr() != pObject) + { + if (pObject) pObject->Release(); + pObject = v.GetPtr(); + } + + return *this; + } + + OVR_FORCE_INLINE Ptr& Pick(C* p) + { + if (p != pObject) + { + if (pObject) pObject->Release(); + pObject = p; + } + + return *this; + } +}; + +} // OVR + +#endif diff -r 59fc487ba58e -r b069a5c27388 libovr/Src/Kernel/OVR_Std.cpp --- a/libovr/Src/Kernel/OVR_Std.cpp Sat Sep 14 17:51:03 2013 +0300 +++ b/libovr/Src/Kernel/OVR_Std.cpp Sun Sep 15 04:10:05 2013 +0300 @@ -1,1 +1,1025 @@ -/************************************************************************************ Filename : OVR_Std.cpp Content : Standard C function implementation Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #include "OVR_Std.h" #include "OVR_Alg.h" // localeconv() call in OVR_strtod() #include namespace OVR { // Source for functions not available on all platforms is included here. // Case insensitive compare implemented in platform-specific way. int OVR_CDECL OVR_stricmp(const char* a, const char* b) { #if defined(OVR_OS_WIN32) #if defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400) return ::_stricmp(a, b); #else return ::stricmp(a, b); #endif #else return strcasecmp(a, b); #endif } int OVR_CDECL OVR_strnicmp(const char* a, const char* b, UPInt count) { #if defined(OVR_OS_WIN32) #if defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400) return ::_strnicmp(a, b, count); #else return ::strnicmp(a, b, count); #endif #else return strncasecmp(a, b, count); #endif } wchar_t* OVR_CDECL OVR_wcscpy(wchar_t* dest, UPInt destsize, const wchar_t* src) { #if defined(OVR_MSVC_SAFESTRING) wcscpy_s(dest, destsize, src); return dest; #elif defined(OVR_OS_WIN32) OVR_UNUSED(destsize); wcscpy(dest, src); return dest; #else UPInt l = OVR_wcslen(src) + 1; // incl term null l = (l < destsize) ? l : destsize; memcpy(dest, src, l * sizeof(wchar_t)); return dest; #endif } wchar_t* OVR_CDECL OVR_wcsncpy(wchar_t* dest, UPInt destsize, const wchar_t* src, UPInt count) { #if defined(OVR_MSVC_SAFESTRING) wcsncpy_s(dest, destsize, src, count); return dest; #else UPInt srclen = OVR_wcslen(src); UPInt l = Alg::Min(srclen, count); l = (l < destsize) ? l : destsize; memcpy(dest, src, l * sizeof(wchar_t)); if (count > srclen) { UPInt remLen = Alg::Min(destsize - l, (count - srclen)); memset(&dest[l], 0, sizeof(wchar_t)*remLen); } else if (l < destsize) dest[l] = 0; return dest; #endif } wchar_t* OVR_CDECL OVR_wcscat(wchar_t* dest, UPInt destsize, const wchar_t* src) { #if defined(OVR_MSVC_SAFESTRING) wcscat_s(dest, destsize, src); return dest; #elif defined(OVR_OS_WIN32) OVR_UNUSED(destsize); wcscat(dest, src); return dest; #else UPInt dstlen = OVR_wcslen(dest); // do not incl term null UPInt srclen = OVR_wcslen(src) + 1; // incl term null UPInt copylen = (dstlen + srclen < destsize) ? srclen : destsize - dstlen; memcpy(dest + dstlen, src, copylen * sizeof(wchar_t)); return dest; #endif } UPInt OVR_CDECL OVR_wcslen(const wchar_t* str) { #if defined(OVR_OS_WIN32) return wcslen(str); #else UPInt i = 0; while(str[i] != '\0') ++i; return i; #endif } int OVR_CDECL OVR_wcscmp(const wchar_t* a, const wchar_t* b) { #if defined(OVR_OS_WIN32) || defined(OVR_OS_LINUX) return wcscmp(a, b); #else // not supported, use custom implementation const wchar_t *pa = a, *pb = b; while (*pa && *pb) { wchar_t ca = *pa; wchar_t cb = *pb; if (ca < cb) return -1; else if (ca > cb) return 1; pa++; pb++; } if (*pa) return 1; else if (*pb) return -1; else return 0; #endif } int OVR_CDECL OVR_wcsicmp(const wchar_t* a, const wchar_t* b) { #if defined(OVR_OS_WIN32) #if defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400) return ::_wcsicmp(a, b); #else return ::wcsicmp(a, b); #endif #elif defined(OVR_OS_MAC) || defined(__CYGWIN__) || defined(OVR_OS_ANDROID) || defined(OVR_OS_IPHONE) // not supported, use custom implementation const wchar_t *pa = a, *pb = b; while (*pa && *pb) { wchar_t ca = OVR_towlower(*pa); wchar_t cb = OVR_towlower(*pb); if (ca < cb) return -1; else if (ca > cb) return 1; pa++; pb++; } if (*pa) return 1; else if (*pb) return -1; else return 0; #else return wcscasecmp(a, b); #endif } // This function is not inline because of dependency on double OVR_CDECL OVR_strtod(const char* string, char** tailptr) { #if !defined(OVR_OS_ANDROID) const char s = *localeconv()->decimal_point; if (s != '.') { char buffer[347 + 1]; OVR_strcpy(buffer, sizeof(buffer), string); for (char* c = buffer; *c != '\0'; ++c) { if (*c == '.') { *c = s; break; } } return strtod(buffer, tailptr); } #endif return strtod(string, tailptr); } #ifndef OVR_NO_WCTYPE //// Use this class to generate Unicode bitsets. For example: //// //// UnicodeBitSet bitSet; //// for(unsigned i = 0; i < 65536; ++i) //// { //// if (iswalpha(i)) //// bitSet.Set(i); //// } //// bitSet.Dump(); //// ////--------------------------------------------------------------- //class UnicodeBitSet //{ //public: // UnicodeBitSet() // { // memset(Offsets, 0, sizeof(Offsets)); // memset(Bits, 0, sizeof(Bits)); // } // // void Set(unsigned bit) { Bits[bit >> 8][(bit >> 4) & 15] |= 1 << (bit & 15); } // // void Dump() // { // unsigned i, j; // unsigned offsetCount = 0; // for(i = 0; i < 256; ++i) // { // if (isNull(i)) Offsets[i] = 0; // else // if (isFull(i)) Offsets[i] = 1; // else Offsets[i] = UInt16(offsetCount++ * 16 + 256); // } // for(i = 0; i < 16; ++i) // { // for(j = 0; j < 16; ++j) // { // printf("%5u,", Offsets[i*16+j]); // } // printf("\n"); // } // for(i = 0; i < 256; ++i) // { // if (Offsets[i] > 255) // { // for(j = 0; j < 16; j++) // { // printf("%5u,", Bits[i][j]); // } // printf("\n"); // } // } // } // //private: // bool isNull(unsigned n) const // { // const UInt16* p = Bits[n]; // for(unsigned i = 0; i < 16; ++i) // if (p[i] != 0) return false; // return true; // } // // bool isFull(unsigned n) const // { // const UInt16* p = Bits[n]; // for(unsigned i = 0; i < 16; ++i) // if (p[i] != 0xFFFF) return false; // return true; // } // // UInt16 Offsets[256]; // UInt16 Bits[256][16]; //}; const UInt16 UnicodeAlnumBits[] = { 256, 1, 272, 288, 304, 320, 336, 352, 0, 368, 384, 400, 416, 432, 448, 464, 480, 496, 512, 528, 544, 1, 560, 576, 592, 0, 0, 0, 0, 0, 608, 624, 640, 656, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 672, 688, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 704, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 720, 1, 1, 1, 1, 736, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 752, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 768, 784, 1, 800, 816, 832, 0, 0, 0, 1023,65534, 2047,65534, 2047, 0, 0, 0, 524,65535,65407,65535,65407, 65535,65535,65532, 15, 0,65535,65535,65535,65535,65535,16383,63999, 3, 0,16415, 0, 0, 0, 0, 0, 32, 0, 0, 1024,55104,65535,65531,65535,32767,64767,65535, 15, 65535,65535,65535,65535,65535,65535,65535,65535,61443,65535,65535,65535, 6559,65535,65535, 831, 0, 0, 0,65534,65535, 639,65534,65535, 255, 0, 0, 0, 0,65535, 2047, 7, 0, 0,65534, 2047,65534, 63, 1023,65535,65535,65535,65535,65535,65535, 8175, 8702, 8191, 0,65535, 8191,65535, 0, 0, 0, 0,65535,65535,65535, 1, 0, 0, 0, 0, 65518,65535,65535,58367, 8191,65281,65487, 0,40942,65529,65023,50117, 6559,45184,65487, 3, 34788,65529,65023,50029, 6535,24064,65472, 31,45038,65531,65023,58349, 7103, 1,65473, 0, 40942,65529,65023,58317, 6543,45248,65475, 0,51180,54845,50968,50111, 7623, 128,65408, 0, 57326,65533,65023,50159, 7647, 96,65475, 0,57324,65533,65023,50159, 7647,16480,65475, 0, 57324,65533,65023,50175, 7631, 128,65475, 0,65516,64639,65535,12283,32895,65375, 0, 12, 65534,65535,65535, 2047,32767, 1023, 0, 0, 9622,65264,60590,15359, 8223,13311, 0, 0, 1, 0, 1023, 0,65279,65535, 2047,65534, 3843,65279,65535, 8191, 0, 0, 0, 0, 65535,65535,63227, 327, 1023, 1023, 0, 0, 0, 0,65535,65535, 63,65535,65535, 127, 65535,65535,65535,65535,65535,33791,65535,65535,65535,65535,65287,65535,65535,65535,65535, 1023, 65407,65535,65535,65535,15743,15743,65535,65535,15743,65535,32767,32573,32573,65407,32767,65535, 32767,32573,65535,65535,65407, 2047,65024, 3, 0, 0,65535,65535,65535,65535,65535, 31, 65534,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 65535,65535,65535,65535,65535,65535,40959, 127,65534, 2047,65535,65535,65535,65535, 2047, 0, 0, 0, 0, 0, 0, 0, 0, 0,65535,65535,65535,65535, 511, 0, 1023, 0, 0, 1023,65535,65535,65527,65535,65535, 255,65535,65535, 1023, 0, 0, 0, 0, 0, 65535,65535,65535,65535,65535,65535,65535,65535,65535, 4095,65535,65535,65535,65535,65535, 1023, 65535,16191,65535,65535,16191,43775,65535,16383,65535,65535,65535,24543, 8156, 4047, 8191, 8156, 0, 0, 0, 0, 0, 0, 0,32768, 0, 0, 0, 0, 0, 0, 0, 0, 64644,15919,48464, 1019, 0, 0,65535,65535, 15, 0, 0, 0, 0, 0, 0, 0, 192, 0, 1022, 1792,65534,65535,65535,65535,65535, 31,65534,65535,65535,65535,65535, 2047, 65504,65535, 8191,65534,65535,65535,65535,65535,32767, 0,65535, 255, 0, 0, 0, 0, 65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, 65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, 0, 65535,65535,65535,65535,65535,65535,65535,65535, 8191, 0, 0, 0, 0, 0, 0, 0, 65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 15, 0, 0, 0, 0, 0, 65535,65535,16383, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127,41208,65023,24447,65499,65535,65535,65535,65535,65535,65535, 3, 0,65528,65535,65535, 65535,65535,65535,16383, 0,65535,65535,65535,65535,65532,65535,65535, 255, 0, 0, 4095, 0, 0, 0, 0, 0, 0, 0,65495,65535,65535,65535,65535,65535,65535,65535, 8191, 0, 1023,65534, 2047,65534, 2047,65472,65534,65535,16383,65535,32767,64764, 7420, 0, 0}; const UInt16 UnicodeAlphaBits[] = { 256, 1, 272, 288, 304, 320, 336, 352, 0, 368, 384, 400, 416, 432, 448, 464, 480, 496, 512, 528, 544, 1, 560, 576, 592, 0, 0, 0, 0, 0, 608, 624, 640, 656, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 672, 688, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 704, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 720, 1, 1, 1, 1, 736, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 752, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 768, 784, 1, 800, 816, 832, 0, 0, 0, 0,65534, 2047,65534, 2047, 0, 0, 0, 0,65535,65407,65535,65407, 65535,65535,65532, 15, 0,65535,65535,65535,65535,65535,16383,63999, 3, 0,16415, 0, 0, 0, 0, 0, 32, 0, 0, 1024,55104,65535,65531,65535,32767,64767,65535, 15, 65535,65535,65535,65535,65535,65535,65535,65535,61443,65535,65535,65535, 6559,65535,65535, 831, 0, 0, 0,65534,65535, 639,65534,65535, 255, 0, 0, 0, 0,65535, 2047, 7, 0, 0,65534, 2047,65534, 63, 0,65535,65535,65535,65535,65535,65535, 8175, 8702, 7168, 0,65535, 8191,65535, 0, 0, 0, 0,65535,65535,65535, 1, 0, 0, 0, 0, 65518,65535,65535,58367, 8191,65281, 15, 0,40942,65529,65023,50117, 6559,45184, 15, 3, 34788,65529,65023,50029, 6535,24064, 0, 31,45038,65531,65023,58349, 7103, 1, 1, 0, 40942,65529,65023,58317, 6543,45248, 3, 0,51180,54845,50968,50111, 7623, 128, 0, 0, 57326,65533,65023,50159, 7647, 96, 3, 0,57324,65533,65023,50159, 7647,16480, 3, 0, 57324,65533,65023,50175, 7631, 128, 3, 0,65516,64639,65535,12283,32895,65375, 0, 12, 65534,65535,65535, 2047,32767, 0, 0, 0, 9622,65264,60590,15359, 8223,12288, 0, 0, 1, 0, 0, 0,65279,65535, 2047,65534, 3843,65279,65535, 8191, 0, 0, 0, 0, 65535,65535,63227, 327, 0, 1023, 0, 0, 0, 0,65535,65535, 63,65535,65535, 127, 65535,65535,65535,65535,65535,33791,65535,65535,65535,65535,65287,65535,65535,65535,65535, 1023, 65407,65535,65535,65535,15743,15743,65535,65535,15743,65535,32767,32573,32573,65407,32767,65535, 32767,32573,65535,65535,65407, 2047, 0, 0, 0, 0,65535,65535,65535,65535,65535, 31, 65534,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 65535,65535,65535,65535,65535,65535,40959, 127,65534, 2047,65535,65535,65535,65535, 2047, 0, 0, 0, 0, 0, 0, 0, 0, 0,65535,65535,65535,65535, 511, 0, 0, 0, 0, 0,65535,65535,65527,65535,65535, 255,65535,65535, 1023, 0, 0, 0, 0, 0, 65535,65535,65535,65535,65535,65535,65535,65535,65535, 4095,65535,65535,65535,65535,65535, 1023, 65535,16191,65535,65535,16191,43775,65535,16383,65535,65535,65535,24543, 8156, 4047, 8191, 8156, 0, 0, 0, 0, 0, 0, 0,32768, 0, 0, 0, 0, 0, 0, 0, 0, 64644,15919,48464, 1019, 0, 0,65535,65535, 15, 0, 0, 0, 0, 0, 0, 0, 192, 0, 1022, 1792,65534,65535,65535,65535,65535, 31,65534,65535,65535,65535,65535, 2047, 65504,65535, 8191,65534,65535,65535,65535,65535,32767, 0,65535, 255, 0, 0, 0, 0, 65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, 65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, 0, 65535,65535,65535,65535,65535,65535,65535,65535, 8191, 0, 0, 0, 0, 0, 0, 0, 65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 15, 0, 0, 0, 0, 0, 65535,65535,16383, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127,41208,65023,24447,65499,65535,65535,65535,65535,65535,65535, 3, 0,65528,65535,65535, 65535,65535,65535,16383, 0,65535,65535,65535,65535,65532,65535,65535, 255, 0, 0, 4095, 0, 0, 0, 0, 0, 0, 0,65495,65535,65535,65535,65535,65535,65535,65535, 8191, 0, 0,65534, 2047,65534, 2047,65472,65534,65535,16383,65535,32767,64764, 7420, 0, 0}; const UInt16 UnicodeDigitBits[] = { 256, 0, 0, 0, 0, 0, 272, 0, 0, 288, 304, 320, 336, 352, 368, 384, 400, 0, 0, 416, 0, 0, 0, 432, 448, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 464, 0, 0, 0, 1023, 0, 0, 0, 0, 0, 0, 0, 524, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1023, 0, 0, 0, 0, 0, 0, 0, 0, 1023, 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0,65408, 0, 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1023, 0, 0, 0, 0, 0, 0, 0, 1023, 0, 0, 0, 0, 1023, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1023, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,65024, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1023, 0, 0, 1023, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1023, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; const UInt16 UnicodeSpaceBits[] = { 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 272, 0, 0, 0, 0, 0, 0, 0, 0, 0, 288, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 304, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15872, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 4095, 0,33536, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; const UInt16 UnicodeXDigitBits[] = { 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 272, 0, 0, 0, 1023, 126, 0, 126, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1023, 126, 0, 126, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // Uncomment if necessary //const UInt16 UnicodeCntrlBits[] = { // 256, 0, 0, 0, 0, 0, 0, 272, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 288, 0, 0, 0, 0, 0, 0, 0, // 304, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 320, 336, //65535,65535, 0, 0, 0, 0, 0,32768,65535,65535, 0, 0, 0, 0, 0, 0, //32768, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //30720, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //61440, 0,31744, 0, 0, 0,64512, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,32768, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3584}; // //const UInt16 UnicodeGraphBits[] = { // 256, 1, 272, 288, 304, 320, 336, 352, 0, 368, 384, 400, 416, 432, 448, 464, // 480, 496, 512, 528, 544, 1, 560, 576, 592, 0, 0, 0, 0, 0, 608, 624, // 640, 656, 0, 672, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 688, 704, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 720, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 736, // 1, 1, 1, 1, 752, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 768, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 784, 800, 1, 816, 832, 848, // 0, 0,65534,65535,65535,65535,65535,32767, 0, 0,65534,65535,65535,65535,65535,65535, //65535,65535,65532, 15, 0,65535,65535,65535,65535,65535,16383,63999, 3, 0,16415, 0, // 0, 0, 0, 0, 32, 0, 0,17408,55232,65535,65531,65535,32767,64767,65535, 15, //65535,65535,65535,65535,65535,65535,65535,65535,61443,65535,65535,65535, 6559,65535,65535, 831, // 0, 0, 0,65534,65535,65151,65534,65535, 1791, 0, 0,16384, 9,65535, 2047, 31, // 4096,34816,65534, 2047,65534, 63,16383,65535,65535,65535,65535,65535,65535, 8191, 8702, 8191, //16383,65535, 8191,65535, 0, 0, 0, 0,65535,65535,65535, 1, 0, 0, 0, 0, //65518,65535,65535,58367, 8191,65281,65535, 1,40942,65529,65023,50117, 6559,45184,65487, 3, //34788,65529,65023,50029, 6535,24064,65472, 31,45038,65531,65023,58349, 7103, 1,65473, 0, //40942,65529,65023,58317, 6543,45248,65475, 0,51180,54845,50968,50111, 7623, 128,65408, 0, //57326,65533,65023,50159, 7647, 96,65475, 0,57324,65533,65023,50159, 7647,16480,65475, 0, //57324,65533,65023,50175, 7631, 128,65475, 0,65516,64639,65535,12283,32895,65375, 0, 28, //65534,65535,65535, 2047,65535, 4095, 0, 0, 9622,65264,60590,15359, 8223,13311, 0, 0, //65521, 7, 1023,15360,65279,65535, 2047,65534, 3875,65279,65535, 8191, 0, 0, 0, 0, //65535,65535,63227, 327,65535, 1023, 0, 0, 0, 0,65535,65535, 63,65535,65535, 2175, //65535,65535,65535,65535,65535,33791,65535,65535,65535,65535,65287,65535,65535,65535,65535, 1023, //65407,65535,65535,65535,15743,15743,65535,65535,15743,65535,32767,32573,32573,65407,32767,65535, //32767,32573,65535,65535,65407, 2047,65534, 3, 0, 0,65535,65535,65535,65535,65535, 31, //65534,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, //65535,65535,65535,65535,65535,65535,65535, 127,65534, 8191,65535,65535,65535,65535,16383, 0, // 0, 0, 0, 0, 0, 0, 0, 0,65535,65535,65535,65535, 511, 6128, 1023, 0, // 2047, 1023,65535,65535,65527,65535,65535, 255,65535,65535, 1023, 0, 0, 0, 0, 0, //65535,65535,65535,65535,65535,65535,65535,65535,65535, 4095,65535,65535,65535,65535,65535, 1023, //65535,16191,65535,65535,16191,43775,65535,16383,65535,65535,65535,24543, 8156, 4047, 8191, 8156, // 0,65535, 255,65535,16239, 0, 0,57344,24576, 0, 0, 0, 0, 0, 0, 0, //64644,15919,48464, 1019, 0, 0,65535,65535, 15, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 1536, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //65486,65523, 1022, 1793,65534,65535,65535,65535,65535, 31,65534,65535,65535,65535,65535, 4095, //65504,65535, 8191,65534,65535,65535,65535,65535,32767, 0,65535, 255, 0, 0, 0, 0, //65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, //65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, 0, //65535,65535,65535,65535,65535,65535,65535,65535, 8191, 0, 0, 0, 0, 0, 0, 0, //65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 15, 0, 0, 0, 0, 0, //65535,65535,16383, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 127,41208,65023,24447,65499,65535,65535,65535,65535,65535,65535, 3, 0,65528,65535,65535, //65535,65535,65535,65535, 0,65535,65535,65535,65535,65532,65535,65535, 255, 0, 0, 4095, // 0, 0, 0,65535,65055,65527, 3339,65495,65535,65535,65535,65535,65535,65535,65535, 8191, //63470,36863,65535,49151,65534,12287,65534,65534,65535,16383,65535,32767,64764, 7420, 0, 0}; // //const UInt16 UnicodePrintBits[] = { // 256, 1, 272, 288, 304, 320, 336, 352, 0, 368, 384, 400, 416, 432, 448, 464, // 480, 496, 512, 528, 544, 1, 560, 576, 592, 0, 0, 0, 0, 0, 608, 624, // 640, 656, 0, 672, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 688, 704, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 720, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 736, // 1, 1, 1, 1, 752, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 768, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 784, 800, 1, 816, 832, 848, // 512, 0,65535,65535,65535,65535,65535,32767, 0, 0,65535,65535,65535,65535,65535,65535, //65535,65535,65532, 15, 0,65535,65535,65535,65535,65535,16383,63999, 3, 0,16415, 0, // 0, 0, 0, 0, 32, 0, 0,17408,55232,65535,65531,65535,32767,64767,65535, 15, //65535,65535,65535,65535,65535,65535,65535,65535,61443,65535,65535,65535, 6559,65535,65535, 831, // 0, 0, 0,65534,65535,65151,65534,65535, 1791, 0, 0,16384, 9,65535, 2047, 31, // 4096,34816,65534, 2047,65534, 63,16383,65535,65535,65535,65535,65535,65535, 8191, 8702, 8191, //16383,65535, 8191,65535, 0, 0, 0, 0,65535,65535,65535, 1, 0, 0, 0, 0, //65518,65535,65535,58367, 8191,65281,65535, 1,40942,65529,65023,50117, 6559,45184,65487, 3, //34788,65529,65023,50029, 6535,24064,65472, 31,45038,65531,65023,58349, 7103, 1,65473, 0, //40942,65529,65023,58317, 6543,45248,65475, 0,51180,54845,50968,50111, 7623, 128,65408, 0, //57326,65533,65023,50159, 7647, 96,65475, 0,57324,65533,65023,50159, 7647,16480,65475, 0, //57324,65533,65023,50175, 7631, 128,65475, 0,65516,64639,65535,12283,32895,65375, 0, 28, //65534,65535,65535, 2047,65535, 4095, 0, 0, 9622,65264,60590,15359, 8223,13311, 0, 0, //65521, 7, 1023,15360,65279,65535, 2047,65534, 3875,65279,65535, 8191, 0, 0, 0, 0, //65535,65535,63227, 327,65535, 1023, 0, 0, 0, 0,65535,65535, 63,65535,65535, 2175, //65535,65535,65535,65535,65535,33791,65535,65535,65535,65535,65287,65535,65535,65535,65535, 1023, //65407,65535,65535,65535,15743,15743,65535,65535,15743,65535,32767,32573,32573,65407,32767,65535, //32767,32573,65535,65535,65407, 2047,65534, 3, 0, 0,65535,65535,65535,65535,65535, 31, //65534,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, //65535,65535,65535,65535,65535,65535,65535, 127,65534, 8191,65535,65535,65535,65535,16383, 0, // 0, 0, 0, 0, 0, 0, 0, 0,65535,65535,65535,65535, 511, 6128, 1023, 0, // 2047, 1023,65535,65535,65527,65535,65535, 255,65535,65535, 1023, 0, 0, 0, 0, 0, //65535,65535,65535,65535,65535,65535,65535,65535,65535, 4095,65535,65535,65535,65535,65535, 1023, //65535,16191,65535,65535,16191,43775,65535,16383,65535,65535,65535,24543, 8156, 4047, 8191, 8156, // 0,65535, 255,65535,16239, 0, 0,57344,24576, 0, 0, 0, 0, 0, 0, 0, //64644,15919,48464, 1019, 0, 0,65535,65535, 15, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 1536, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //65487,65523, 1022, 1793,65534,65535,65535,65535,65535, 31,65534,65535,65535,65535,65535, 4095, //65504,65535, 8191,65534,65535,65535,65535,65535,32767, 0,65535, 255, 0, 0, 0, 0, //65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, //65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, 0, //65535,65535,65535,65535,65535,65535,65535,65535, 8191, 0, 0, 0, 0, 0, 0, 0, //65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 15, 0, 0, 0, 0, 0, //65535,65535,16383, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 127,41208,65023,24447,65499,65535,65535,65535,65535,65535,65535, 3, 0,65528,65535,65535, //65535,65535,65535,65535, 0,65535,65535,65535,65535,65532,65535,65535, 255, 0, 0, 4095, // 0, 0, 0,65535,65055,65527, 3339,65495,65535,65535,65535,65535,65535,65535,65535,40959, //63470,36863,65535,49151,65534,12287,65534,65534,65535,16383,65535,32767,64764, 7420, 0, 0}; // //const UInt16 UnicodePunctBits[] = { // 256, 0, 0, 272, 0, 288, 304, 320, 0, 336, 0, 0, 0, 352, 368, 384, // 400, 0, 0, 416, 0, 0, 432, 448, 464, 0, 0, 0, 0, 0, 0, 0, // 480, 0, 0, 496, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 512, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 528, 544, 560, // 0, 0,65534,64512, 1,63488, 1,30720, 0, 0,65534,65535, 0, 128, 0, 128, // 0, 0, 0, 0, 0, 0, 0,16384, 128, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0,64512, 0, 0, 1536, 0, 0,16384, 9, 0, 0, 24, // 4096,34816, 0, 0, 0, 0,15360, 0, 0, 0, 0, 0, 0, 16, 0, 0, //16383, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, // 0, 0, 0, 0,32768, 3072, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //65520, 7, 0,15360, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0,64512, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2048, // 0, 0, 0, 0, 0, 0, 510, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0,24576, 0, 0, 6144, 0, 0, 0, 0,14336, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6128, 0, 0, // 2047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0,65535, 255,65535,16239, 0, 0,24576,24576, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 1536, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //65294,65523, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2048, // 0, 0, 0,49152, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0,65535,65055,65527, 3339, 0, 0, 0, 0, 0, 0, 0, 0, 0, //63470,35840, 1,47104, 0,10240, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // //const UInt16 UnicodeLowerBits[] = { // 256, 272, 288, 304, 320, 336, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 352, 368, // 384, 400, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 416, 0, 0, 0, 432, // 0, 0, 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0,32768,65535,65407, //43690,43690,43690,21930,43861,43690,43690,54442,12585,20004,11562,58961,23392,46421,43690,43565, //43690,43690,43688, 10, 0,65535,65535,65535,65535,65535,16383, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,61440,65535,32767,43235,43690, 15, // 0, 0, 0,65535,65535,65535,43690,43690,40962,43690,43690,43690, 4372,43690,43690, 554, // 0, 0, 0, 0, 0, 0,65534,65535, 255, 0, 0, 0, 0, 0, 0, 0, //43690,43690,43690,43690,43690,43690,43690,43690,43690, 4074,43690,43690,43690,43690,43690, 682, // 255, 63, 255, 255, 63, 255, 255,16383,65535,65535,65535,20703, 4316, 207, 255, 4316, // 0, 0, 0, 0, 0, 0, 0,32768, 0, 0, 0, 0, 0, 0, 0, 0, //50176, 8,32768, 528, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 127, 248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // //const UInt16 UnicodeUpperBits[] = { // 256, 272, 288, 304, 320, 336, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 352, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 368, 384, // 0, 400, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 416, // 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0,65535,32639, 0, 0, //21845,21845,21845,43605,21674,21845,21845,11093,52950,45531,53973, 4526,44464,19114,21845,21974, //21845,21845,21844, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0,55104,65534, 4091, 0, 0,21532,21845, 0, //65535,65535,65535, 0, 0, 0,21845,21845,20481,21845,21845,21845, 2187,21845,21845, 277, // 0, 0, 0,65534,65535, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,65535,65535, 63, 0, 0, 0, //21845,21845,21845,21845,21845,21845,21845,21845,21845, 21,21845,21845,21845,21845,21845, 341, //65280,16128,65280,65280,16128,43520,65280, 0,65280,65280,65280, 7936, 7936, 3840, 7936, 7936, //14468,15911,15696, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // MA: March 19, 2010 // Modified ToUpper and ToLower tables to match values expected by AS3 tests. // ToLower modifications: // 304 -> 105 // 1024 -> 1104 * // 1037 -> 1117 * // UoUpper modifications: // 255 -> 376 // 305 -> 73 // 383 -> 83 // 1104 -> 1024 * // 1117 -> 1037 * // Entries marked with a '*' don't make complete sense based on Unicode manual, although // they match AS3. static const UInt16 UnicodeToUpperBits[] = { 256, 272, 288, 304, 320, 336, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 352, 368, 0, 384, 0, 0, 400, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 416, 0, 0, 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0,65535,65407, 43690,43690,43690,21674,43349,43690,43690,54442, 4392, 516, 8490, 8785,21056,46421,43690,43048, // MA: Modified for AS3. 43690, 170, 0, 0, 0, 2776,33545, 36, 3336, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,61440,65534,32767, 0,43688, 0, 0, 0, 0,65535,65535,65535,43690,43690, 2,43690,43690,43690, 4372,43690,35498, 554, // MA: Modified for AS3. 0, 0, 0, 0, 0, 0,65534,65535, 127, 0, 0, 0, 0, 0, 0, 0, 43690,43690,43690,43690,43690,43690,43690,43690,43690, 42,43690,43690,43690,43690,43690, 682, 255, 63, 255, 255, 63, 170, 255,16383, 0, 0, 0, 3, 0, 3, 35, 0, 0, 0, 0, 0, 0, 0, 0,65535, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,65535, 1023, 0, 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; static const UInt16 UnicodeToLowerBits[] = { 256, 272, 288, 304, 320, 336, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 352, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 368, 384, 0, 400, 0, 0, 416, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 432, 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0,65535,32639, 0, 0, 21845,21845,21845,43605,21674,21845,21845,11093,52950,45531,53909, 4526,42128,19114,21845,21522,// MA: Modidied for AS3. 21845, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,55104,65534, 4091, 0, 0, 0,21844, 0, 65535,65535,65535, 0, 0, 0,21845,21845, 1,21845,21845,21845, 2186,21845,17749, 277, 0, 0, 0,65534,65535, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,65535,65535, 63, 0, 0, 0, 21845,21845,21845,21845,21845,21845,21845,21845,21845, 21,21845,21845,21845,21845,21845, 341, 65280,16128,65280,65280,16128,43520,65280, 0, 0, 0, 0, 3840, 3840, 3840, 7936, 3840, 0, 0, 0, 0, 0, 0,65535, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,65472,65535, 0, 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; struct GUnicodePairType { UInt16 Key, Value; }; static inline bool CmpUnicodeKey(const GUnicodePairType& a, UInt16 key) { return a.Key < key; } static const GUnicodePairType UnicodeToUpperTable[] = { { 97, 65}, { 98, 66}, { 99, 67}, { 100, 68}, { 101, 69}, { 102, 70}, { 103, 71}, { 104, 72}, { 105, 73}, { 106, 74}, { 107, 75}, { 108, 76}, { 109, 77}, { 110, 78}, { 111, 79}, { 112, 80}, { 113, 81}, { 114, 82}, { 115, 83}, { 116, 84}, { 117, 85}, { 118, 86}, { 119, 87}, { 120, 88}, { 121, 89}, { 122, 90}, { 224, 192}, { 225, 193}, { 226, 194}, { 227, 195}, { 228, 196}, { 229, 197}, { 230, 198}, { 231, 199}, { 232, 200}, { 233, 201}, { 234, 202}, { 235, 203}, { 236, 204}, { 237, 205}, { 238, 206}, { 239, 207}, { 240, 208}, { 241, 209}, { 242, 210}, { 243, 211}, { 244, 212}, { 245, 213}, { 246, 214}, { 248, 216}, { 249, 217}, { 250, 218}, { 251, 219}, { 252, 220}, { 253, 221}, { 254, 222}, { 255, 376}, { 257, 256}, { 259, 258}, { 261, 260}, { 263, 262}, { 265, 264}, { 267, 266}, { 269, 268}, { 271, 270}, { 273, 272}, { 275, 274}, { 277, 276}, { 279, 278}, { 281, 280}, { 283, 282}, { 285, 284}, { 287, 286}, { 289, 288}, { 291, 290}, { 293, 292}, { 295, 294}, { 297, 296}, { 299, 298}, { 301, 300}, { 303, 302}, { 305, 73}, { 307, 306}, { 309, 308}, { 311, 310}, { 314, 313}, { 316, 315}, { 318, 317}, { 320, 319}, { 322, 321}, { 324, 323}, { 326, 325}, { 328, 327}, { 331, 330}, { 333, 332}, { 335, 334}, { 337, 336}, { 339, 338}, { 341, 340}, { 343, 342}, { 345, 344}, { 347, 346}, { 349, 348}, { 351, 350}, { 353, 352}, { 355, 354}, { 357, 356}, { 359, 358}, { 361, 360}, { 363, 362}, { 365, 364}, { 367, 366}, { 369, 368}, { 371, 370}, { 373, 372}, { 375, 374}, { 378, 377}, { 380, 379}, { 382, 381}, { 383, 83}, { 387, 386}, { 389, 388}, { 392, 391}, { 396, 395}, { 402, 401}, { 409, 408}, { 417, 416}, { 419, 418}, { 421, 420}, { 424, 423}, { 429, 428}, { 432, 431}, { 436, 435}, { 438, 437}, { 441, 440}, { 445, 444}, { 454, 452}, { 457, 455}, { 460, 458}, { 462, 461}, { 464, 463}, { 466, 465}, { 468, 467}, { 470, 469}, { 472, 471}, { 474, 473}, { 476, 475}, { 477, 398}, { 479, 478}, { 481, 480}, { 483, 482}, { 485, 484}, { 487, 486}, { 489, 488}, { 491, 490}, { 493, 492}, { 495, 494}, { 499, 497}, { 501, 500}, { 507, 506}, { 509, 508}, { 511, 510}, { 513, 512}, { 515, 514}, { 517, 516}, { 519, 518}, { 521, 520}, { 523, 522}, { 525, 524}, { 527, 526}, { 529, 528}, { 531, 530}, { 533, 532}, { 535, 534}, { 595, 385}, { 596, 390}, { 598, 393}, { 599, 394}, { 601, 399}, { 603, 400}, { 608, 403}, { 611, 404}, { 616, 407}, { 617, 406}, { 623, 412}, { 626, 413}, { 629, 415}, { 643, 425}, { 648, 430}, { 650, 433}, { 651, 434}, { 658, 439}, { 940, 902}, { 941, 904}, { 942, 905}, { 943, 906}, { 945, 913}, { 946, 914}, { 947, 915}, { 948, 916}, { 949, 917}, { 950, 918}, { 951, 919}, { 952, 920}, { 953, 921}, { 954, 922}, { 955, 923}, { 956, 924}, { 957, 925}, { 958, 926}, { 959, 927}, { 960, 928}, { 961, 929}, { 962, 931}, { 963, 931}, { 964, 932}, { 965, 933}, { 966, 934}, { 967, 935}, { 968, 936}, { 969, 937}, { 970, 938}, { 971, 939}, { 972, 908}, { 973, 910}, { 974, 911}, { 995, 994}, { 997, 996}, { 999, 998}, { 1001, 1000}, { 1003, 1002}, { 1005, 1004}, { 1007, 1006}, { 1072, 1040}, { 1073, 1041}, { 1074, 1042}, { 1075, 1043}, { 1076, 1044}, { 1077, 1045}, { 1078, 1046}, { 1079, 1047}, { 1080, 1048}, { 1081, 1049}, { 1082, 1050}, { 1083, 1051}, { 1084, 1052}, { 1085, 1053}, { 1086, 1054}, { 1087, 1055}, { 1088, 1056}, { 1089, 1057}, { 1090, 1058}, { 1091, 1059}, { 1092, 1060}, { 1093, 1061}, { 1094, 1062}, { 1095, 1063}, { 1096, 1064}, { 1097, 1065}, { 1098, 1066}, { 1099, 1067}, { 1100, 1068}, { 1101, 1069}, { 1102, 1070}, { 1103, 1071}, { 1104, 1024}, { 1105, 1025}, { 1106, 1026}, { 1107, 1027}, { 1108, 1028}, { 1109, 1029}, { 1110, 1030}, { 1111, 1031}, { 1112, 1032}, { 1113, 1033}, { 1114, 1034}, { 1115, 1035}, { 1116, 1036}, { 1117, 1037}, { 1118, 1038}, { 1119, 1039}, { 1121, 1120}, { 1123, 1122}, { 1125, 1124}, { 1127, 1126}, { 1129, 1128}, { 1131, 1130}, { 1133, 1132}, { 1135, 1134}, { 1137, 1136}, { 1139, 1138}, { 1141, 1140}, { 1143, 1142}, { 1145, 1144}, { 1147, 1146}, { 1149, 1148}, { 1151, 1150}, { 1153, 1152}, { 1169, 1168}, { 1171, 1170}, { 1173, 1172}, { 1175, 1174}, { 1177, 1176}, { 1179, 1178}, { 1181, 1180}, { 1183, 1182}, { 1185, 1184}, { 1187, 1186}, { 1189, 1188}, { 1191, 1190}, { 1193, 1192}, { 1195, 1194}, { 1197, 1196}, { 1199, 1198}, { 1201, 1200}, { 1203, 1202}, { 1205, 1204}, { 1207, 1206}, { 1209, 1208}, { 1211, 1210}, { 1213, 1212}, { 1215, 1214}, { 1218, 1217}, { 1220, 1219}, { 1224, 1223}, { 1228, 1227}, { 1233, 1232}, { 1235, 1234}, { 1237, 1236}, { 1239, 1238}, { 1241, 1240}, { 1243, 1242}, { 1245, 1244}, { 1247, 1246}, { 1249, 1248}, { 1251, 1250}, { 1253, 1252}, { 1255, 1254}, { 1257, 1256}, { 1259, 1258}, { 1263, 1262}, { 1265, 1264}, { 1267, 1266}, { 1269, 1268}, { 1273, 1272}, { 1377, 1329}, { 1378, 1330}, { 1379, 1331}, { 1380, 1332}, { 1381, 1333}, { 1382, 1334}, { 1383, 1335}, { 1384, 1336}, { 1385, 1337}, { 1386, 1338}, { 1387, 1339}, { 1388, 1340}, { 1389, 1341}, { 1390, 1342}, { 1391, 1343}, { 1392, 1344}, { 1393, 1345}, { 1394, 1346}, { 1395, 1347}, { 1396, 1348}, { 1397, 1349}, { 1398, 1350}, { 1399, 1351}, { 1400, 1352}, { 1401, 1353}, { 1402, 1354}, { 1403, 1355}, { 1404, 1356}, { 1405, 1357}, { 1406, 1358}, { 1407, 1359}, { 1408, 1360}, { 1409, 1361}, { 1410, 1362}, { 1411, 1363}, { 1412, 1364}, { 1413, 1365}, { 1414, 1366}, { 7681, 7680}, { 7683, 7682}, { 7685, 7684}, { 7687, 7686}, { 7689, 7688}, { 7691, 7690}, { 7693, 7692}, { 7695, 7694}, { 7697, 7696}, { 7699, 7698}, { 7701, 7700}, { 7703, 7702}, { 7705, 7704}, { 7707, 7706}, { 7709, 7708}, { 7711, 7710}, { 7713, 7712}, { 7715, 7714}, { 7717, 7716}, { 7719, 7718}, { 7721, 7720}, { 7723, 7722}, { 7725, 7724}, { 7727, 7726}, { 7729, 7728}, { 7731, 7730}, { 7733, 7732}, { 7735, 7734}, { 7737, 7736}, { 7739, 7738}, { 7741, 7740}, { 7743, 7742}, { 7745, 7744}, { 7747, 7746}, { 7749, 7748}, { 7751, 7750}, { 7753, 7752}, { 7755, 7754}, { 7757, 7756}, { 7759, 7758}, { 7761, 7760}, { 7763, 7762}, { 7765, 7764}, { 7767, 7766}, { 7769, 7768}, { 7771, 7770}, { 7773, 7772}, { 7775, 7774}, { 7777, 7776}, { 7779, 7778}, { 7781, 7780}, { 7783, 7782}, { 7785, 7784}, { 7787, 7786}, { 7789, 7788}, { 7791, 7790}, { 7793, 7792}, { 7795, 7794}, { 7797, 7796}, { 7799, 7798}, { 7801, 7800}, { 7803, 7802}, { 7805, 7804}, { 7807, 7806}, { 7809, 7808}, { 7811, 7810}, { 7813, 7812}, { 7815, 7814}, { 7817, 7816}, { 7819, 7818}, { 7821, 7820}, { 7823, 7822}, { 7825, 7824}, { 7827, 7826}, { 7829, 7828}, { 7841, 7840}, { 7843, 7842}, { 7845, 7844}, { 7847, 7846}, { 7849, 7848}, { 7851, 7850}, { 7853, 7852}, { 7855, 7854}, { 7857, 7856}, { 7859, 7858}, { 7861, 7860}, { 7863, 7862}, { 7865, 7864}, { 7867, 7866}, { 7869, 7868}, { 7871, 7870}, { 7873, 7872}, { 7875, 7874}, { 7877, 7876}, { 7879, 7878}, { 7881, 7880}, { 7883, 7882}, { 7885, 7884}, { 7887, 7886}, { 7889, 7888}, { 7891, 7890}, { 7893, 7892}, { 7895, 7894}, { 7897, 7896}, { 7899, 7898}, { 7901, 7900}, { 7903, 7902}, { 7905, 7904}, { 7907, 7906}, { 7909, 7908}, { 7911, 7910}, { 7913, 7912}, { 7915, 7914}, { 7917, 7916}, { 7919, 7918}, { 7921, 7920}, { 7923, 7922}, { 7925, 7924}, { 7927, 7926}, { 7929, 7928}, { 7936, 7944}, { 7937, 7945}, { 7938, 7946}, { 7939, 7947}, { 7940, 7948}, { 7941, 7949}, { 7942, 7950}, { 7943, 7951}, { 7952, 7960}, { 7953, 7961}, { 7954, 7962}, { 7955, 7963}, { 7956, 7964}, { 7957, 7965}, { 7968, 7976}, { 7969, 7977}, { 7970, 7978}, { 7971, 7979}, { 7972, 7980}, { 7973, 7981}, { 7974, 7982}, { 7975, 7983}, { 7984, 7992}, { 7985, 7993}, { 7986, 7994}, { 7987, 7995}, { 7988, 7996}, { 7989, 7997}, { 7990, 7998}, { 7991, 7999}, { 8000, 8008}, { 8001, 8009}, { 8002, 8010}, { 8003, 8011}, { 8004, 8012}, { 8005, 8013}, { 8017, 8025}, { 8019, 8027}, { 8021, 8029}, { 8023, 8031}, { 8032, 8040}, { 8033, 8041}, { 8034, 8042}, { 8035, 8043}, { 8036, 8044}, { 8037, 8045}, { 8038, 8046}, { 8039, 8047}, { 8048, 8122}, { 8049, 8123}, { 8050, 8136}, { 8051, 8137}, { 8052, 8138}, { 8053, 8139}, { 8054, 8154}, { 8055, 8155}, { 8056, 8184}, { 8057, 8185}, { 8058, 8170}, { 8059, 8171}, { 8060, 8186}, { 8061, 8187}, { 8112, 8120}, { 8113, 8121}, { 8144, 8152}, { 8145, 8153}, { 8160, 8168}, { 8161, 8169}, { 8165, 8172}, { 8560, 8544}, { 8561, 8545}, { 8562, 8546}, { 8563, 8547}, { 8564, 8548}, { 8565, 8549}, { 8566, 8550}, { 8567, 8551}, { 8568, 8552}, { 8569, 8553}, { 8570, 8554}, { 8571, 8555}, { 8572, 8556}, { 8573, 8557}, { 8574, 8558}, { 8575, 8559}, { 9424, 9398}, { 9425, 9399}, { 9426, 9400}, { 9427, 9401}, { 9428, 9402}, { 9429, 9403}, { 9430, 9404}, { 9431, 9405}, { 9432, 9406}, { 9433, 9407}, { 9434, 9408}, { 9435, 9409}, { 9436, 9410}, { 9437, 9411}, { 9438, 9412}, { 9439, 9413}, { 9440, 9414}, { 9441, 9415}, { 9442, 9416}, { 9443, 9417}, { 9444, 9418}, { 9445, 9419}, { 9446, 9420}, { 9447, 9421}, { 9448, 9422}, { 9449, 9423}, {65345,65313}, {65346,65314}, {65347,65315}, {65348,65316}, {65349,65317}, {65350,65318}, {65351,65319}, {65352,65320}, {65353,65321}, {65354,65322}, {65355,65323}, {65356,65324}, {65357,65325}, {65358,65326}, {65359,65327}, {65360,65328}, {65361,65329}, {65362,65330}, {65363,65331}, {65364,65332}, {65365,65333}, {65366,65334}, {65367,65335}, {65368,65336}, {65369,65337}, {65370,65338}, {65535, 0}}; static const GUnicodePairType UnicodeToLowerTable[] = { { 65, 97}, { 66, 98}, { 67, 99}, { 68, 100}, { 69, 101}, { 70, 102}, { 71, 103}, { 72, 104}, { 73, 105}, { 74, 106}, { 75, 107}, { 76, 108}, { 77, 109}, { 78, 110}, { 79, 111}, { 80, 112}, { 81, 113}, { 82, 114}, { 83, 115}, { 84, 116}, { 85, 117}, { 86, 118}, { 87, 119}, { 88, 120}, { 89, 121}, { 90, 122}, { 192, 224}, { 193, 225}, { 194, 226}, { 195, 227}, { 196, 228}, { 197, 229}, { 198, 230}, { 199, 231}, { 200, 232}, { 201, 233}, { 202, 234}, { 203, 235}, { 204, 236}, { 205, 237}, { 206, 238}, { 207, 239}, { 208, 240}, { 209, 241}, { 210, 242}, { 211, 243}, { 212, 244}, { 213, 245}, { 214, 246}, { 216, 248}, { 217, 249}, { 218, 250}, { 219, 251}, { 220, 252}, { 221, 253}, { 222, 254}, { 256, 257}, { 258, 259}, { 260, 261}, { 262, 263}, { 264, 265}, { 266, 267}, { 268, 269}, { 270, 271}, { 272, 273}, { 274, 275}, { 276, 277}, { 278, 279}, { 280, 281}, { 282, 283}, { 284, 285}, { 286, 287}, { 288, 289}, { 290, 291}, { 292, 293}, { 294, 295}, { 296, 297}, { 298, 299}, { 300, 301}, { 302, 303}, { 304, 105}, { 306, 307}, { 308, 309}, { 310, 311}, { 313, 314}, { 315, 316}, { 317, 318}, { 319, 320}, { 321, 322}, { 323, 324}, { 325, 326}, { 327, 328}, { 330, 331}, { 332, 333}, { 334, 335}, { 336, 337}, { 338, 339}, { 340, 341}, { 342, 343}, { 344, 345}, { 346, 347}, { 348, 349}, { 350, 351}, { 352, 353}, { 354, 355}, { 356, 357}, { 358, 359}, { 360, 361}, { 362, 363}, { 364, 365}, { 366, 367}, { 368, 369}, { 370, 371}, { 372, 373}, { 374, 375}, { 376, 255}, { 377, 378}, { 379, 380}, { 381, 382}, { 385, 595}, { 386, 387}, { 388, 389}, { 390, 596}, { 391, 392}, { 393, 598}, { 394, 599}, { 395, 396}, { 398, 477}, { 399, 601}, { 400, 603}, { 401, 402}, { 403, 608}, { 404, 611}, { 406, 617}, { 407, 616}, { 408, 409}, { 412, 623}, { 413, 626}, { 415, 629}, { 416, 417}, { 418, 419}, { 420, 421}, { 423, 424}, { 425, 643}, { 428, 429}, { 430, 648}, { 431, 432}, { 433, 650}, { 434, 651}, { 435, 436}, { 437, 438}, { 439, 658}, { 440, 441}, { 444, 445}, { 452, 454}, { 455, 457}, { 458, 460}, { 461, 462}, { 463, 464}, { 465, 466}, { 467, 468}, { 469, 470}, { 471, 472}, { 473, 474}, { 475, 476}, { 478, 479}, { 480, 481}, { 482, 483}, { 484, 485}, { 486, 487}, { 488, 489}, { 490, 491}, { 492, 493}, { 494, 495}, { 497, 499}, { 500, 501}, { 506, 507}, { 508, 509}, { 510, 511}, { 512, 513}, { 514, 515}, { 516, 517}, { 518, 519}, { 520, 521}, { 522, 523}, { 524, 525}, { 526, 527}, { 528, 529}, { 530, 531}, { 532, 533}, { 534, 535}, { 902, 940}, { 904, 941}, { 905, 942}, { 906, 943}, { 908, 972}, { 910, 973}, { 911, 974}, { 913, 945}, { 914, 946}, { 915, 947}, { 916, 948}, { 917, 949}, { 918, 950}, { 919, 951}, { 920, 952}, { 921, 953}, { 922, 954}, { 923, 955}, { 924, 956}, { 925, 957}, { 926, 958}, { 927, 959}, { 928, 960}, { 929, 961}, { 931, 963}, { 932, 964}, { 933, 965}, { 934, 966}, { 935, 967}, { 936, 968}, { 937, 969}, { 938, 970}, { 939, 971}, { 994, 995}, { 996, 997}, { 998, 999}, { 1000, 1001}, { 1002, 1003}, { 1004, 1005}, { 1006, 1007}, { 1024, 1104}, { 1025, 1105}, { 1026, 1106}, { 1027, 1107}, { 1028, 1108}, { 1029, 1109}, { 1030, 1110}, { 1031, 1111}, { 1032, 1112}, { 1033, 1113}, { 1034, 1114}, { 1035, 1115}, { 1036, 1116}, { 1037, 1117}, { 1038, 1118}, { 1039, 1119}, { 1040, 1072}, { 1041, 1073}, { 1042, 1074}, { 1043, 1075}, { 1044, 1076}, { 1045, 1077}, { 1046, 1078}, { 1047, 1079}, { 1048, 1080}, { 1049, 1081}, { 1050, 1082}, { 1051, 1083}, { 1052, 1084}, { 1053, 1085}, { 1054, 1086}, { 1055, 1087}, { 1056, 1088}, { 1057, 1089}, { 1058, 1090}, { 1059, 1091}, { 1060, 1092}, { 1061, 1093}, { 1062, 1094}, { 1063, 1095}, { 1064, 1096}, { 1065, 1097}, { 1066, 1098}, { 1067, 1099}, { 1068, 1100}, { 1069, 1101}, { 1070, 1102}, { 1071, 1103}, { 1120, 1121}, { 1122, 1123}, { 1124, 1125}, { 1126, 1127}, { 1128, 1129}, { 1130, 1131}, { 1132, 1133}, { 1134, 1135}, { 1136, 1137}, { 1138, 1139}, { 1140, 1141}, { 1142, 1143}, { 1144, 1145}, { 1146, 1147}, { 1148, 1149}, { 1150, 1151}, { 1152, 1153}, { 1168, 1169}, { 1170, 1171}, { 1172, 1173}, { 1174, 1175}, { 1176, 1177}, { 1178, 1179}, { 1180, 1181}, { 1182, 1183}, { 1184, 1185}, { 1186, 1187}, { 1188, 1189}, { 1190, 1191}, { 1192, 1193}, { 1194, 1195}, { 1196, 1197}, { 1198, 1199}, { 1200, 1201}, { 1202, 1203}, { 1204, 1205}, { 1206, 1207}, { 1208, 1209}, { 1210, 1211}, { 1212, 1213}, { 1214, 1215}, { 1217, 1218}, { 1219, 1220}, { 1223, 1224}, { 1227, 1228}, { 1232, 1233}, { 1234, 1235}, { 1236, 1237}, { 1238, 1239}, { 1240, 1241}, { 1242, 1243}, { 1244, 1245}, { 1246, 1247}, { 1248, 1249}, { 1250, 1251}, { 1252, 1253}, { 1254, 1255}, { 1256, 1257}, { 1258, 1259}, { 1262, 1263}, { 1264, 1265}, { 1266, 1267}, { 1268, 1269}, { 1272, 1273}, { 1329, 1377}, { 1330, 1378}, { 1331, 1379}, { 1332, 1380}, { 1333, 1381}, { 1334, 1382}, { 1335, 1383}, { 1336, 1384}, { 1337, 1385}, { 1338, 1386}, { 1339, 1387}, { 1340, 1388}, { 1341, 1389}, { 1342, 1390}, { 1343, 1391}, { 1344, 1392}, { 1345, 1393}, { 1346, 1394}, { 1347, 1395}, { 1348, 1396}, { 1349, 1397}, { 1350, 1398}, { 1351, 1399}, { 1352, 1400}, { 1353, 1401}, { 1354, 1402}, { 1355, 1403}, { 1356, 1404}, { 1357, 1405}, { 1358, 1406}, { 1359, 1407}, { 1360, 1408}, { 1361, 1409}, { 1362, 1410}, { 1363, 1411}, { 1364, 1412}, { 1365, 1413}, { 1366, 1414}, { 4256, 4304}, { 4257, 4305}, { 4258, 4306}, { 4259, 4307}, { 4260, 4308}, { 4261, 4309}, { 4262, 4310}, { 4263, 4311}, { 4264, 4312}, { 4265, 4313}, { 4266, 4314}, { 4267, 4315}, { 4268, 4316}, { 4269, 4317}, { 4270, 4318}, { 4271, 4319}, { 4272, 4320}, { 4273, 4321}, { 4274, 4322}, { 4275, 4323}, { 4276, 4324}, { 4277, 4325}, { 4278, 4326}, { 4279, 4327}, { 4280, 4328}, { 4281, 4329}, { 4282, 4330}, { 4283, 4331}, { 4284, 4332}, { 4285, 4333}, { 4286, 4334}, { 4287, 4335}, { 4288, 4336}, { 4289, 4337}, { 4290, 4338}, { 4291, 4339}, { 4292, 4340}, { 4293, 4341}, { 7680, 7681}, { 7682, 7683}, { 7684, 7685}, { 7686, 7687}, { 7688, 7689}, { 7690, 7691}, { 7692, 7693}, { 7694, 7695}, { 7696, 7697}, { 7698, 7699}, { 7700, 7701}, { 7702, 7703}, { 7704, 7705}, { 7706, 7707}, { 7708, 7709}, { 7710, 7711}, { 7712, 7713}, { 7714, 7715}, { 7716, 7717}, { 7718, 7719}, { 7720, 7721}, { 7722, 7723}, { 7724, 7725}, { 7726, 7727}, { 7728, 7729}, { 7730, 7731}, { 7732, 7733}, { 7734, 7735}, { 7736, 7737}, { 7738, 7739}, { 7740, 7741}, { 7742, 7743}, { 7744, 7745}, { 7746, 7747}, { 7748, 7749}, { 7750, 7751}, { 7752, 7753}, { 7754, 7755}, { 7756, 7757}, { 7758, 7759}, { 7760, 7761}, { 7762, 7763}, { 7764, 7765}, { 7766, 7767}, { 7768, 7769}, { 7770, 7771}, { 7772, 7773}, { 7774, 7775}, { 7776, 7777}, { 7778, 7779}, { 7780, 7781}, { 7782, 7783}, { 7784, 7785}, { 7786, 7787}, { 7788, 7789}, { 7790, 7791}, { 7792, 7793}, { 7794, 7795}, { 7796, 7797}, { 7798, 7799}, { 7800, 7801}, { 7802, 7803}, { 7804, 7805}, { 7806, 7807}, { 7808, 7809}, { 7810, 7811}, { 7812, 7813}, { 7814, 7815}, { 7816, 7817}, { 7818, 7819}, { 7820, 7821}, { 7822, 7823}, { 7824, 7825}, { 7826, 7827}, { 7828, 7829}, { 7840, 7841}, { 7842, 7843}, { 7844, 7845}, { 7846, 7847}, { 7848, 7849}, { 7850, 7851}, { 7852, 7853}, { 7854, 7855}, { 7856, 7857}, { 7858, 7859}, { 7860, 7861}, { 7862, 7863}, { 7864, 7865}, { 7866, 7867}, { 7868, 7869}, { 7870, 7871}, { 7872, 7873}, { 7874, 7875}, { 7876, 7877}, { 7878, 7879}, { 7880, 7881}, { 7882, 7883}, { 7884, 7885}, { 7886, 7887}, { 7888, 7889}, { 7890, 7891}, { 7892, 7893}, { 7894, 7895}, { 7896, 7897}, { 7898, 7899}, { 7900, 7901}, { 7902, 7903}, { 7904, 7905}, { 7906, 7907}, { 7908, 7909}, { 7910, 7911}, { 7912, 7913}, { 7914, 7915}, { 7916, 7917}, { 7918, 7919}, { 7920, 7921}, { 7922, 7923}, { 7924, 7925}, { 7926, 7927}, { 7928, 7929}, { 7944, 7936}, { 7945, 7937}, { 7946, 7938}, { 7947, 7939}, { 7948, 7940}, { 7949, 7941}, { 7950, 7942}, { 7951, 7943}, { 7960, 7952}, { 7961, 7953}, { 7962, 7954}, { 7963, 7955}, { 7964, 7956}, { 7965, 7957}, { 7976, 7968}, { 7977, 7969}, { 7978, 7970}, { 7979, 7971}, { 7980, 7972}, { 7981, 7973}, { 7982, 7974}, { 7983, 7975}, { 7992, 7984}, { 7993, 7985}, { 7994, 7986}, { 7995, 7987}, { 7996, 7988}, { 7997, 7989}, { 7998, 7990}, { 7999, 7991}, { 8008, 8000}, { 8009, 8001}, { 8010, 8002}, { 8011, 8003}, { 8012, 8004}, { 8013, 8005}, { 8025, 8017}, { 8027, 8019}, { 8029, 8021}, { 8031, 8023}, { 8040, 8032}, { 8041, 8033}, { 8042, 8034}, { 8043, 8035}, { 8044, 8036}, { 8045, 8037}, { 8046, 8038}, { 8047, 8039}, { 8120, 8112}, { 8121, 8113}, { 8122, 8048}, { 8123, 8049}, { 8136, 8050}, { 8137, 8051}, { 8138, 8052}, { 8139, 8053}, { 8152, 8144}, { 8153, 8145}, { 8154, 8054}, { 8155, 8055}, { 8168, 8160}, { 8169, 8161}, { 8170, 8058}, { 8171, 8059}, { 8172, 8165}, { 8184, 8056}, { 8185, 8057}, { 8186, 8060}, { 8187, 8061}, { 8544, 8560}, { 8545, 8561}, { 8546, 8562}, { 8547, 8563}, { 8548, 8564}, { 8549, 8565}, { 8550, 8566}, { 8551, 8567}, { 8552, 8568}, { 8553, 8569}, { 8554, 8570}, { 8555, 8571}, { 8556, 8572}, { 8557, 8573}, { 8558, 8574}, { 8559, 8575}, { 9398, 9424}, { 9399, 9425}, { 9400, 9426}, { 9401, 9427}, { 9402, 9428}, { 9403, 9429}, { 9404, 9430}, { 9405, 9431}, { 9406, 9432}, { 9407, 9433}, { 9408, 9434}, { 9409, 9435}, { 9410, 9436}, { 9411, 9437}, { 9412, 9438}, { 9413, 9439}, { 9414, 9440}, { 9415, 9441}, { 9416, 9442}, { 9417, 9443}, { 9418, 9444}, { 9419, 9445}, { 9420, 9446}, { 9421, 9447}, { 9422, 9448}, { 9423, 9449}, {65313,65345}, {65314,65346}, {65315,65347}, {65316,65348}, {65317,65349}, {65318,65350}, {65319,65351}, {65320,65352}, {65321,65353}, {65322,65354}, {65323,65355}, {65324,65356}, {65325,65357}, {65326,65358}, {65327,65359}, {65328,65360}, {65329,65361}, {65330,65362}, {65331,65363}, {65332,65364}, {65333,65365}, {65334,65366}, {65335,65367}, {65336,65368}, {65337,65369}, {65338,65370}, {65535, 0}}; int OVR_CDECL OVR_towupper(wchar_t charCode) { // Don't use UnicodeUpperBits! It differs from UnicodeToUpperBits. if (UnicodeCharIs(UnicodeToUpperBits, charCode)) { // To protect from memory overrun in case the character is not found // we use one extra fake element in the table {65536, 0}. UPInt idx = Alg::LowerBoundSliced( UnicodeToUpperTable, 0, sizeof(UnicodeToUpperTable) / sizeof(UnicodeToUpperTable[0]) - 1, (UInt16)charCode, CmpUnicodeKey); return UnicodeToUpperTable[idx].Value; } return charCode; } int OVR_CDECL OVR_towlower(wchar_t charCode) { // Don't use UnicodeLowerBits! It differs from UnicodeToLowerBits. if (UnicodeCharIs(UnicodeToLowerBits, charCode)) { // To protect from memory overrun in case the character is not found // we use one extra fake element in the table {65536, 0}. UPInt idx = Alg::LowerBoundSliced( UnicodeToLowerTable, 0, sizeof(UnicodeToLowerTable) / sizeof(UnicodeToLowerTable[0]) - 1, (UInt16)charCode, CmpUnicodeKey); return UnicodeToLowerTable[idx].Value; } return charCode; } #endif //OVR_NO_WCTYPE } // OVR \ No newline at end of file +/************************************************************************************ + +Filename : OVR_Std.cpp +Content : Standard C function implementation +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#include "OVR_Std.h" +#include "OVR_Alg.h" + +// localeconv() call in OVR_strtod() +#include + +namespace OVR { + +// Source for functions not available on all platforms is included here. + +// Case insensitive compare implemented in platform-specific way. +int OVR_CDECL OVR_stricmp(const char* a, const char* b) +{ +#if defined(OVR_OS_WIN32) + #if defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400) + return ::_stricmp(a, b); + #else + return ::stricmp(a, b); + #endif + +#else + return strcasecmp(a, b); +#endif +} + +int OVR_CDECL OVR_strnicmp(const char* a, const char* b, UPInt count) +{ +#if defined(OVR_OS_WIN32) +#if defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400) + return ::_strnicmp(a, b, count); +#else + return ::strnicmp(a, b, count); +#endif + +#else + return strncasecmp(a, b, count); +#endif +} + +wchar_t* OVR_CDECL OVR_wcscpy(wchar_t* dest, UPInt destsize, const wchar_t* src) +{ +#if defined(OVR_MSVC_SAFESTRING) + wcscpy_s(dest, destsize, src); + return dest; +#elif defined(OVR_OS_WIN32) + OVR_UNUSED(destsize); + wcscpy(dest, src); + return dest; +#else + UPInt l = OVR_wcslen(src) + 1; // incl term null + l = (l < destsize) ? l : destsize; + memcpy(dest, src, l * sizeof(wchar_t)); + return dest; +#endif +} + +wchar_t* OVR_CDECL OVR_wcsncpy(wchar_t* dest, UPInt destsize, const wchar_t* src, UPInt count) +{ +#if defined(OVR_MSVC_SAFESTRING) + wcsncpy_s(dest, destsize, src, count); + return dest; +#else + UPInt srclen = OVR_wcslen(src); + UPInt l = Alg::Min(srclen, count); + l = (l < destsize) ? l : destsize; + memcpy(dest, src, l * sizeof(wchar_t)); + if (count > srclen) + { + UPInt remLen = Alg::Min(destsize - l, (count - srclen)); + memset(&dest[l], 0, sizeof(wchar_t)*remLen); + } + else if (l < destsize) + dest[l] = 0; + return dest; +#endif +} + + +wchar_t* OVR_CDECL OVR_wcscat(wchar_t* dest, UPInt destsize, const wchar_t* src) +{ +#if defined(OVR_MSVC_SAFESTRING) + wcscat_s(dest, destsize, src); + return dest; +#elif defined(OVR_OS_WIN32) + OVR_UNUSED(destsize); + wcscat(dest, src); + return dest; +#else + UPInt dstlen = OVR_wcslen(dest); // do not incl term null + UPInt srclen = OVR_wcslen(src) + 1; // incl term null + UPInt copylen = (dstlen + srclen < destsize) ? srclen : destsize - dstlen; + memcpy(dest + dstlen, src, copylen * sizeof(wchar_t)); + return dest; +#endif +} + +UPInt OVR_CDECL OVR_wcslen(const wchar_t* str) +{ +#if defined(OVR_OS_WIN32) + return wcslen(str); +#else + UPInt i = 0; + while(str[i] != '\0') + ++i; + return i; +#endif +} + +int OVR_CDECL OVR_wcscmp(const wchar_t* a, const wchar_t* b) +{ +#if defined(OVR_OS_WIN32) || defined(OVR_OS_LINUX) + return wcscmp(a, b); +#else + // not supported, use custom implementation + const wchar_t *pa = a, *pb = b; + while (*pa && *pb) + { + wchar_t ca = *pa; + wchar_t cb = *pb; + if (ca < cb) + return -1; + else if (ca > cb) + return 1; + pa++; + pb++; + } + if (*pa) + return 1; + else if (*pb) + return -1; + else + return 0; +#endif +} + +int OVR_CDECL OVR_wcsicmp(const wchar_t* a, const wchar_t* b) +{ +#if defined(OVR_OS_WIN32) +#if defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400) + return ::_wcsicmp(a, b); +#else + return ::wcsicmp(a, b); +#endif +#elif defined(OVR_OS_MAC) || defined(__CYGWIN__) || defined(OVR_OS_ANDROID) || defined(OVR_OS_IPHONE) + // not supported, use custom implementation + const wchar_t *pa = a, *pb = b; + while (*pa && *pb) + { + wchar_t ca = OVR_towlower(*pa); + wchar_t cb = OVR_towlower(*pb); + if (ca < cb) + return -1; + else if (ca > cb) + return 1; + pa++; + pb++; + } + if (*pa) + return 1; + else if (*pb) + return -1; + else + return 0; +#else + return wcscasecmp(a, b); +#endif +} + +// This function is not inline because of dependency on +double OVR_CDECL OVR_strtod(const char* string, char** tailptr) +{ +#if !defined(OVR_OS_ANDROID) + const char s = *localeconv()->decimal_point; + + if (s != '.') + { + char buffer[347 + 1]; + + OVR_strcpy(buffer, sizeof(buffer), string); + + for (char* c = buffer; *c != '\0'; ++c) + { + if (*c == '.') + { + *c = s; + break; + } + } + + return strtod(buffer, tailptr); + } +#endif + + return strtod(string, tailptr); +} + + +#ifndef OVR_NO_WCTYPE + +//// Use this class to generate Unicode bitsets. For example: +//// +//// UnicodeBitSet bitSet; +//// for(unsigned i = 0; i < 65536; ++i) +//// { +//// if (iswalpha(i)) +//// bitSet.Set(i); +//// } +//// bitSet.Dump(); +//// +////--------------------------------------------------------------- +//class UnicodeBitSet +//{ +//public: +// UnicodeBitSet() +// { +// memset(Offsets, 0, sizeof(Offsets)); +// memset(Bits, 0, sizeof(Bits)); +// } +// +// void Set(unsigned bit) { Bits[bit >> 8][(bit >> 4) & 15] |= 1 << (bit & 15); } +// +// void Dump() +// { +// unsigned i, j; +// unsigned offsetCount = 0; +// for(i = 0; i < 256; ++i) +// { +// if (isNull(i)) Offsets[i] = 0; +// else +// if (isFull(i)) Offsets[i] = 1; +// else Offsets[i] = UInt16(offsetCount++ * 16 + 256); +// } +// for(i = 0; i < 16; ++i) +// { +// for(j = 0; j < 16; ++j) +// { +// printf("%5u,", Offsets[i*16+j]); +// } +// printf("\n"); +// } +// for(i = 0; i < 256; ++i) +// { +// if (Offsets[i] > 255) +// { +// for(j = 0; j < 16; j++) +// { +// printf("%5u,", Bits[i][j]); +// } +// printf("\n"); +// } +// } +// } +// +//private: +// bool isNull(unsigned n) const +// { +// const UInt16* p = Bits[n]; +// for(unsigned i = 0; i < 16; ++i) +// if (p[i] != 0) return false; +// return true; +// } +// +// bool isFull(unsigned n) const +// { +// const UInt16* p = Bits[n]; +// for(unsigned i = 0; i < 16; ++i) +// if (p[i] != 0xFFFF) return false; +// return true; +// } +// +// UInt16 Offsets[256]; +// UInt16 Bits[256][16]; +//}; + + +const UInt16 UnicodeAlnumBits[] = { + 256, 1, 272, 288, 304, 320, 336, 352, 0, 368, 384, 400, 416, 432, 448, 464, + 480, 496, 512, 528, 544, 1, 560, 576, 592, 0, 0, 0, 0, 0, 608, 624, + 640, 656, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 672, 688, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 704, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 720, + 1, 1, 1, 1, 736, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 752, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 768, 784, 1, 800, 816, 832, + 0, 0, 0, 1023,65534, 2047,65534, 2047, 0, 0, 0, 524,65535,65407,65535,65407, +65535,65535,65532, 15, 0,65535,65535,65535,65535,65535,16383,63999, 3, 0,16415, 0, + 0, 0, 0, 0, 32, 0, 0, 1024,55104,65535,65531,65535,32767,64767,65535, 15, +65535,65535,65535,65535,65535,65535,65535,65535,61443,65535,65535,65535, 6559,65535,65535, 831, + 0, 0, 0,65534,65535, 639,65534,65535, 255, 0, 0, 0, 0,65535, 2047, 7, + 0, 0,65534, 2047,65534, 63, 1023,65535,65535,65535,65535,65535,65535, 8175, 8702, 8191, + 0,65535, 8191,65535, 0, 0, 0, 0,65535,65535,65535, 1, 0, 0, 0, 0, +65518,65535,65535,58367, 8191,65281,65487, 0,40942,65529,65023,50117, 6559,45184,65487, 3, +34788,65529,65023,50029, 6535,24064,65472, 31,45038,65531,65023,58349, 7103, 1,65473, 0, +40942,65529,65023,58317, 6543,45248,65475, 0,51180,54845,50968,50111, 7623, 128,65408, 0, +57326,65533,65023,50159, 7647, 96,65475, 0,57324,65533,65023,50159, 7647,16480,65475, 0, +57324,65533,65023,50175, 7631, 128,65475, 0,65516,64639,65535,12283,32895,65375, 0, 12, +65534,65535,65535, 2047,32767, 1023, 0, 0, 9622,65264,60590,15359, 8223,13311, 0, 0, + 1, 0, 1023, 0,65279,65535, 2047,65534, 3843,65279,65535, 8191, 0, 0, 0, 0, +65535,65535,63227, 327, 1023, 1023, 0, 0, 0, 0,65535,65535, 63,65535,65535, 127, +65535,65535,65535,65535,65535,33791,65535,65535,65535,65535,65287,65535,65535,65535,65535, 1023, +65407,65535,65535,65535,15743,15743,65535,65535,15743,65535,32767,32573,32573,65407,32767,65535, +32767,32573,65535,65535,65407, 2047,65024, 3, 0, 0,65535,65535,65535,65535,65535, 31, +65534,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, +65535,65535,65535,65535,65535,65535,40959, 127,65534, 2047,65535,65535,65535,65535, 2047, 0, + 0, 0, 0, 0, 0, 0, 0, 0,65535,65535,65535,65535, 511, 0, 1023, 0, + 0, 1023,65535,65535,65527,65535,65535, 255,65535,65535, 1023, 0, 0, 0, 0, 0, +65535,65535,65535,65535,65535,65535,65535,65535,65535, 4095,65535,65535,65535,65535,65535, 1023, +65535,16191,65535,65535,16191,43775,65535,16383,65535,65535,65535,24543, 8156, 4047, 8191, 8156, + 0, 0, 0, 0, 0, 0, 0,32768, 0, 0, 0, 0, 0, 0, 0, 0, +64644,15919,48464, 1019, 0, 0,65535,65535, 15, 0, 0, 0, 0, 0, 0, 0, + 192, 0, 1022, 1792,65534,65535,65535,65535,65535, 31,65534,65535,65535,65535,65535, 2047, +65504,65535, 8191,65534,65535,65535,65535,65535,32767, 0,65535, 255, 0, 0, 0, 0, +65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, +65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, 0, +65535,65535,65535,65535,65535,65535,65535,65535, 8191, 0, 0, 0, 0, 0, 0, 0, +65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 15, 0, 0, 0, 0, 0, +65535,65535,16383, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 127,41208,65023,24447,65499,65535,65535,65535,65535,65535,65535, 3, 0,65528,65535,65535, +65535,65535,65535,16383, 0,65535,65535,65535,65535,65532,65535,65535, 255, 0, 0, 4095, + 0, 0, 0, 0, 0, 0, 0,65495,65535,65535,65535,65535,65535,65535,65535, 8191, + 0, 1023,65534, 2047,65534, 2047,65472,65534,65535,16383,65535,32767,64764, 7420, 0, 0}; + +const UInt16 UnicodeAlphaBits[] = { + 256, 1, 272, 288, 304, 320, 336, 352, 0, 368, 384, 400, 416, 432, 448, 464, + 480, 496, 512, 528, 544, 1, 560, 576, 592, 0, 0, 0, 0, 0, 608, 624, + 640, 656, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 672, 688, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 704, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 720, + 1, 1, 1, 1, 736, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 752, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 768, 784, 1, 800, 816, 832, + 0, 0, 0, 0,65534, 2047,65534, 2047, 0, 0, 0, 0,65535,65407,65535,65407, +65535,65535,65532, 15, 0,65535,65535,65535,65535,65535,16383,63999, 3, 0,16415, 0, + 0, 0, 0, 0, 32, 0, 0, 1024,55104,65535,65531,65535,32767,64767,65535, 15, +65535,65535,65535,65535,65535,65535,65535,65535,61443,65535,65535,65535, 6559,65535,65535, 831, + 0, 0, 0,65534,65535, 639,65534,65535, 255, 0, 0, 0, 0,65535, 2047, 7, + 0, 0,65534, 2047,65534, 63, 0,65535,65535,65535,65535,65535,65535, 8175, 8702, 7168, + 0,65535, 8191,65535, 0, 0, 0, 0,65535,65535,65535, 1, 0, 0, 0, 0, +65518,65535,65535,58367, 8191,65281, 15, 0,40942,65529,65023,50117, 6559,45184, 15, 3, +34788,65529,65023,50029, 6535,24064, 0, 31,45038,65531,65023,58349, 7103, 1, 1, 0, +40942,65529,65023,58317, 6543,45248, 3, 0,51180,54845,50968,50111, 7623, 128, 0, 0, +57326,65533,65023,50159, 7647, 96, 3, 0,57324,65533,65023,50159, 7647,16480, 3, 0, +57324,65533,65023,50175, 7631, 128, 3, 0,65516,64639,65535,12283,32895,65375, 0, 12, +65534,65535,65535, 2047,32767, 0, 0, 0, 9622,65264,60590,15359, 8223,12288, 0, 0, + 1, 0, 0, 0,65279,65535, 2047,65534, 3843,65279,65535, 8191, 0, 0, 0, 0, +65535,65535,63227, 327, 0, 1023, 0, 0, 0, 0,65535,65535, 63,65535,65535, 127, +65535,65535,65535,65535,65535,33791,65535,65535,65535,65535,65287,65535,65535,65535,65535, 1023, +65407,65535,65535,65535,15743,15743,65535,65535,15743,65535,32767,32573,32573,65407,32767,65535, +32767,32573,65535,65535,65407, 2047, 0, 0, 0, 0,65535,65535,65535,65535,65535, 31, +65534,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, +65535,65535,65535,65535,65535,65535,40959, 127,65534, 2047,65535,65535,65535,65535, 2047, 0, + 0, 0, 0, 0, 0, 0, 0, 0,65535,65535,65535,65535, 511, 0, 0, 0, + 0, 0,65535,65535,65527,65535,65535, 255,65535,65535, 1023, 0, 0, 0, 0, 0, +65535,65535,65535,65535,65535,65535,65535,65535,65535, 4095,65535,65535,65535,65535,65535, 1023, +65535,16191,65535,65535,16191,43775,65535,16383,65535,65535,65535,24543, 8156, 4047, 8191, 8156, + 0, 0, 0, 0, 0, 0, 0,32768, 0, 0, 0, 0, 0, 0, 0, 0, +64644,15919,48464, 1019, 0, 0,65535,65535, 15, 0, 0, 0, 0, 0, 0, 0, + 192, 0, 1022, 1792,65534,65535,65535,65535,65535, 31,65534,65535,65535,65535,65535, 2047, +65504,65535, 8191,65534,65535,65535,65535,65535,32767, 0,65535, 255, 0, 0, 0, 0, +65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, +65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, 0, +65535,65535,65535,65535,65535,65535,65535,65535, 8191, 0, 0, 0, 0, 0, 0, 0, +65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 15, 0, 0, 0, 0, 0, +65535,65535,16383, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 127,41208,65023,24447,65499,65535,65535,65535,65535,65535,65535, 3, 0,65528,65535,65535, +65535,65535,65535,16383, 0,65535,65535,65535,65535,65532,65535,65535, 255, 0, 0, 4095, + 0, 0, 0, 0, 0, 0, 0,65495,65535,65535,65535,65535,65535,65535,65535, 8191, + 0, 0,65534, 2047,65534, 2047,65472,65534,65535,16383,65535,32767,64764, 7420, 0, 0}; + +const UInt16 UnicodeDigitBits[] = { + 256, 0, 0, 0, 0, 0, 272, 0, 0, 288, 304, 320, 336, 352, 368, 384, + 400, 0, 0, 416, 0, 0, 0, 432, 448, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 464, + 0, 0, 0, 1023, 0, 0, 0, 0, 0, 0, 0, 524, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1023, 0, 0, 0, 0, 0, 0, 0, 0, 1023, + 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0,65472, 0, + 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0,65472, 0, + 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0,65408, 0, + 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0,65472, 0, + 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1023, 0, 0, 0, 0, 0, 0, 0, 1023, 0, 0, + 0, 0, 1023, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1023, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,65024, 3, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1023, 0, + 0, 1023, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1023, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +const UInt16 UnicodeSpaceBits[] = { + 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 272, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 288, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 304, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +15872, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 4095, 0,33536, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +const UInt16 UnicodeXDigitBits[] = { + 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 272, + 0, 0, 0, 1023, 126, 0, 126, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1023, 126, 0, 126, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +// Uncomment if necessary +//const UInt16 UnicodeCntrlBits[] = { +// 256, 0, 0, 0, 0, 0, 0, 272, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 288, 0, 0, 0, 0, 0, 0, 0, +// 304, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 320, 336, +//65535,65535, 0, 0, 0, 0, 0,32768,65535,65535, 0, 0, 0, 0, 0, 0, +//32768, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +//30720, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +//61440, 0,31744, 0, 0, 0,64512, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,32768, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3584}; +// +//const UInt16 UnicodeGraphBits[] = { +// 256, 1, 272, 288, 304, 320, 336, 352, 0, 368, 384, 400, 416, 432, 448, 464, +// 480, 496, 512, 528, 544, 1, 560, 576, 592, 0, 0, 0, 0, 0, 608, 624, +// 640, 656, 0, 672, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 688, 704, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 720, 1, 1, +// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 736, +// 1, 1, 1, 1, 752, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, +// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +// 1, 1, 1, 1, 1, 1, 1, 768, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 784, 800, 1, 816, 832, 848, +// 0, 0,65534,65535,65535,65535,65535,32767, 0, 0,65534,65535,65535,65535,65535,65535, +//65535,65535,65532, 15, 0,65535,65535,65535,65535,65535,16383,63999, 3, 0,16415, 0, +// 0, 0, 0, 0, 32, 0, 0,17408,55232,65535,65531,65535,32767,64767,65535, 15, +//65535,65535,65535,65535,65535,65535,65535,65535,61443,65535,65535,65535, 6559,65535,65535, 831, +// 0, 0, 0,65534,65535,65151,65534,65535, 1791, 0, 0,16384, 9,65535, 2047, 31, +// 4096,34816,65534, 2047,65534, 63,16383,65535,65535,65535,65535,65535,65535, 8191, 8702, 8191, +//16383,65535, 8191,65535, 0, 0, 0, 0,65535,65535,65535, 1, 0, 0, 0, 0, +//65518,65535,65535,58367, 8191,65281,65535, 1,40942,65529,65023,50117, 6559,45184,65487, 3, +//34788,65529,65023,50029, 6535,24064,65472, 31,45038,65531,65023,58349, 7103, 1,65473, 0, +//40942,65529,65023,58317, 6543,45248,65475, 0,51180,54845,50968,50111, 7623, 128,65408, 0, +//57326,65533,65023,50159, 7647, 96,65475, 0,57324,65533,65023,50159, 7647,16480,65475, 0, +//57324,65533,65023,50175, 7631, 128,65475, 0,65516,64639,65535,12283,32895,65375, 0, 28, +//65534,65535,65535, 2047,65535, 4095, 0, 0, 9622,65264,60590,15359, 8223,13311, 0, 0, +//65521, 7, 1023,15360,65279,65535, 2047,65534, 3875,65279,65535, 8191, 0, 0, 0, 0, +//65535,65535,63227, 327,65535, 1023, 0, 0, 0, 0,65535,65535, 63,65535,65535, 2175, +//65535,65535,65535,65535,65535,33791,65535,65535,65535,65535,65287,65535,65535,65535,65535, 1023, +//65407,65535,65535,65535,15743,15743,65535,65535,15743,65535,32767,32573,32573,65407,32767,65535, +//32767,32573,65535,65535,65407, 2047,65534, 3, 0, 0,65535,65535,65535,65535,65535, 31, +//65534,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, +//65535,65535,65535,65535,65535,65535,65535, 127,65534, 8191,65535,65535,65535,65535,16383, 0, +// 0, 0, 0, 0, 0, 0, 0, 0,65535,65535,65535,65535, 511, 6128, 1023, 0, +// 2047, 1023,65535,65535,65527,65535,65535, 255,65535,65535, 1023, 0, 0, 0, 0, 0, +//65535,65535,65535,65535,65535,65535,65535,65535,65535, 4095,65535,65535,65535,65535,65535, 1023, +//65535,16191,65535,65535,16191,43775,65535,16383,65535,65535,65535,24543, 8156, 4047, 8191, 8156, +// 0,65535, 255,65535,16239, 0, 0,57344,24576, 0, 0, 0, 0, 0, 0, 0, +//64644,15919,48464, 1019, 0, 0,65535,65535, 15, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 1536, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +//65486,65523, 1022, 1793,65534,65535,65535,65535,65535, 31,65534,65535,65535,65535,65535, 4095, +//65504,65535, 8191,65534,65535,65535,65535,65535,32767, 0,65535, 255, 0, 0, 0, 0, +//65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, +//65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, 0, +//65535,65535,65535,65535,65535,65535,65535,65535, 8191, 0, 0, 0, 0, 0, 0, 0, +//65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 15, 0, 0, 0, 0, 0, +//65535,65535,16383, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 127,41208,65023,24447,65499,65535,65535,65535,65535,65535,65535, 3, 0,65528,65535,65535, +//65535,65535,65535,65535, 0,65535,65535,65535,65535,65532,65535,65535, 255, 0, 0, 4095, +// 0, 0, 0,65535,65055,65527, 3339,65495,65535,65535,65535,65535,65535,65535,65535, 8191, +//63470,36863,65535,49151,65534,12287,65534,65534,65535,16383,65535,32767,64764, 7420, 0, 0}; +// +//const UInt16 UnicodePrintBits[] = { +// 256, 1, 272, 288, 304, 320, 336, 352, 0, 368, 384, 400, 416, 432, 448, 464, +// 480, 496, 512, 528, 544, 1, 560, 576, 592, 0, 0, 0, 0, 0, 608, 624, +// 640, 656, 0, 672, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 688, 704, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 720, 1, 1, +// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 736, +// 1, 1, 1, 1, 752, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, +// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +// 1, 1, 1, 1, 1, 1, 1, 768, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 784, 800, 1, 816, 832, 848, +// 512, 0,65535,65535,65535,65535,65535,32767, 0, 0,65535,65535,65535,65535,65535,65535, +//65535,65535,65532, 15, 0,65535,65535,65535,65535,65535,16383,63999, 3, 0,16415, 0, +// 0, 0, 0, 0, 32, 0, 0,17408,55232,65535,65531,65535,32767,64767,65535, 15, +//65535,65535,65535,65535,65535,65535,65535,65535,61443,65535,65535,65535, 6559,65535,65535, 831, +// 0, 0, 0,65534,65535,65151,65534,65535, 1791, 0, 0,16384, 9,65535, 2047, 31, +// 4096,34816,65534, 2047,65534, 63,16383,65535,65535,65535,65535,65535,65535, 8191, 8702, 8191, +//16383,65535, 8191,65535, 0, 0, 0, 0,65535,65535,65535, 1, 0, 0, 0, 0, +//65518,65535,65535,58367, 8191,65281,65535, 1,40942,65529,65023,50117, 6559,45184,65487, 3, +//34788,65529,65023,50029, 6535,24064,65472, 31,45038,65531,65023,58349, 7103, 1,65473, 0, +//40942,65529,65023,58317, 6543,45248,65475, 0,51180,54845,50968,50111, 7623, 128,65408, 0, +//57326,65533,65023,50159, 7647, 96,65475, 0,57324,65533,65023,50159, 7647,16480,65475, 0, +//57324,65533,65023,50175, 7631, 128,65475, 0,65516,64639,65535,12283,32895,65375, 0, 28, +//65534,65535,65535, 2047,65535, 4095, 0, 0, 9622,65264,60590,15359, 8223,13311, 0, 0, +//65521, 7, 1023,15360,65279,65535, 2047,65534, 3875,65279,65535, 8191, 0, 0, 0, 0, +//65535,65535,63227, 327,65535, 1023, 0, 0, 0, 0,65535,65535, 63,65535,65535, 2175, +//65535,65535,65535,65535,65535,33791,65535,65535,65535,65535,65287,65535,65535,65535,65535, 1023, +//65407,65535,65535,65535,15743,15743,65535,65535,15743,65535,32767,32573,32573,65407,32767,65535, +//32767,32573,65535,65535,65407, 2047,65534, 3, 0, 0,65535,65535,65535,65535,65535, 31, +//65534,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, +//65535,65535,65535,65535,65535,65535,65535, 127,65534, 8191,65535,65535,65535,65535,16383, 0, +// 0, 0, 0, 0, 0, 0, 0, 0,65535,65535,65535,65535, 511, 6128, 1023, 0, +// 2047, 1023,65535,65535,65527,65535,65535, 255,65535,65535, 1023, 0, 0, 0, 0, 0, +//65535,65535,65535,65535,65535,65535,65535,65535,65535, 4095,65535,65535,65535,65535,65535, 1023, +//65535,16191,65535,65535,16191,43775,65535,16383,65535,65535,65535,24543, 8156, 4047, 8191, 8156, +// 0,65535, 255,65535,16239, 0, 0,57344,24576, 0, 0, 0, 0, 0, 0, 0, +//64644,15919,48464, 1019, 0, 0,65535,65535, 15, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 1536, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +//65487,65523, 1022, 1793,65534,65535,65535,65535,65535, 31,65534,65535,65535,65535,65535, 4095, +//65504,65535, 8191,65534,65535,65535,65535,65535,32767, 0,65535, 255, 0, 0, 0, 0, +//65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, +//65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, 0, +//65535,65535,65535,65535,65535,65535,65535,65535, 8191, 0, 0, 0, 0, 0, 0, 0, +//65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 15, 0, 0, 0, 0, 0, +//65535,65535,16383, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 127,41208,65023,24447,65499,65535,65535,65535,65535,65535,65535, 3, 0,65528,65535,65535, +//65535,65535,65535,65535, 0,65535,65535,65535,65535,65532,65535,65535, 255, 0, 0, 4095, +// 0, 0, 0,65535,65055,65527, 3339,65495,65535,65535,65535,65535,65535,65535,65535,40959, +//63470,36863,65535,49151,65534,12287,65534,65534,65535,16383,65535,32767,64764, 7420, 0, 0}; +// +//const UInt16 UnicodePunctBits[] = { +// 256, 0, 0, 272, 0, 288, 304, 320, 0, 336, 0, 0, 0, 352, 368, 384, +// 400, 0, 0, 416, 0, 0, 432, 448, 464, 0, 0, 0, 0, 0, 0, 0, +// 480, 0, 0, 496, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 512, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 528, 544, 560, +// 0, 0,65534,64512, 1,63488, 1,30720, 0, 0,65534,65535, 0, 128, 0, 128, +// 0, 0, 0, 0, 0, 0, 0,16384, 128, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0,64512, 0, 0, 1536, 0, 0,16384, 9, 0, 0, 24, +// 4096,34816, 0, 0, 0, 0,15360, 0, 0, 0, 0, 0, 0, 16, 0, 0, +//16383, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, +// 0, 0, 0, 0,32768, 3072, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +//65520, 7, 0,15360, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0,64512, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2048, +// 0, 0, 0, 0, 0, 0, 510, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0,24576, 0, 0, 6144, 0, 0, 0, 0,14336, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6128, 0, 0, +// 2047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0,65535, 255,65535,16239, 0, 0,24576,24576, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 1536, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +//65294,65523, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2048, +// 0, 0, 0,49152, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0,65535,65055,65527, 3339, 0, 0, 0, 0, 0, 0, 0, 0, 0, +//63470,35840, 1,47104, 0,10240, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +// +//const UInt16 UnicodeLowerBits[] = { +// 256, 272, 288, 304, 320, 336, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 352, 368, +// 384, 400, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 416, 0, 0, 0, 432, +// 0, 0, 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0,32768,65535,65407, +//43690,43690,43690,21930,43861,43690,43690,54442,12585,20004,11562,58961,23392,46421,43690,43565, +//43690,43690,43688, 10, 0,65535,65535,65535,65535,65535,16383, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,61440,65535,32767,43235,43690, 15, +// 0, 0, 0,65535,65535,65535,43690,43690,40962,43690,43690,43690, 4372,43690,43690, 554, +// 0, 0, 0, 0, 0, 0,65534,65535, 255, 0, 0, 0, 0, 0, 0, 0, +//43690,43690,43690,43690,43690,43690,43690,43690,43690, 4074,43690,43690,43690,43690,43690, 682, +// 255, 63, 255, 255, 63, 255, 255,16383,65535,65535,65535,20703, 4316, 207, 255, 4316, +// 0, 0, 0, 0, 0, 0, 0,32768, 0, 0, 0, 0, 0, 0, 0, 0, +//50176, 8,32768, 528, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 127, 248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +// +//const UInt16 UnicodeUpperBits[] = { +// 256, 272, 288, 304, 320, 336, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 352, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 368, 384, +// 0, 400, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 416, +// 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0,65535,32639, 0, 0, +//21845,21845,21845,43605,21674,21845,21845,11093,52950,45531,53973, 4526,44464,19114,21845,21974, +//21845,21845,21844, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0,55104,65534, 4091, 0, 0,21532,21845, 0, +//65535,65535,65535, 0, 0, 0,21845,21845,20481,21845,21845,21845, 2187,21845,21845, 277, +// 0, 0, 0,65534,65535, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,65535,65535, 63, 0, 0, 0, +//21845,21845,21845,21845,21845,21845,21845,21845,21845, 21,21845,21845,21845,21845,21845, 341, +//65280,16128,65280,65280,16128,43520,65280, 0,65280,65280,65280, 7936, 7936, 3840, 7936, 7936, +//14468,15911,15696, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + +// MA: March 19, 2010 +// Modified ToUpper and ToLower tables to match values expected by AS3 tests. +// ToLower modifications: +// 304 -> 105 +// 1024 -> 1104 * +// 1037 -> 1117 * +// UoUpper modifications: +// 255 -> 376 +// 305 -> 73 +// 383 -> 83 +// 1104 -> 1024 * +// 1117 -> 1037 * +// Entries marked with a '*' don't make complete sense based on Unicode manual, although +// they match AS3. + + +static const UInt16 UnicodeToUpperBits[] = { + 256, 272, 288, 304, 320, 336, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 352, 368, + 0, 384, 0, 0, 400, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 416, + 0, 0, 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0,65535,65407, +43690,43690,43690,21674,43349,43690,43690,54442, 4392, 516, 8490, 8785,21056,46421,43690,43048, // MA: Modified for AS3. +43690, 170, 0, 0, 0, 2776,33545, 36, 3336, 4, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,61440,65534,32767, 0,43688, 0, + 0, 0, 0,65535,65535,65535,43690,43690, 2,43690,43690,43690, 4372,43690,35498, 554, // MA: Modified for AS3. + 0, 0, 0, 0, 0, 0,65534,65535, 127, 0, 0, 0, 0, 0, 0, 0, +43690,43690,43690,43690,43690,43690,43690,43690,43690, 42,43690,43690,43690,43690,43690, 682, + 255, 63, 255, 255, 63, 170, 255,16383, 0, 0, 0, 3, 0, 3, 35, 0, + 0, 0, 0, 0, 0, 0, 0,65535, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,65535, 1023, 0, + 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +static const UInt16 UnicodeToLowerBits[] = { + 256, 272, 288, 304, 320, 336, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 352, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 368, 384, + 0, 400, 0, 0, 416, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 432, + 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0,65535,32639, 0, 0, +21845,21845,21845,43605,21674,21845,21845,11093,52950,45531,53909, 4526,42128,19114,21845,21522,// MA: Modidied for AS3. +21845, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,55104,65534, 4091, 0, 0, 0,21844, 0, +65535,65535,65535, 0, 0, 0,21845,21845, 1,21845,21845,21845, 2186,21845,17749, 277, + 0, 0, 0,65534,65535, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,65535,65535, 63, 0, 0, 0, +21845,21845,21845,21845,21845,21845,21845,21845,21845, 21,21845,21845,21845,21845,21845, 341, +65280,16128,65280,65280,16128,43520,65280, 0, 0, 0, 0, 3840, 3840, 3840, 7936, 3840, + 0, 0, 0, 0, 0, 0,65535, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,65472,65535, 0, 0, 0, + 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +struct GUnicodePairType +{ + UInt16 Key, Value; +}; + +static inline bool CmpUnicodeKey(const GUnicodePairType& a, UInt16 key) +{ + return a.Key < key; +} + +static const GUnicodePairType UnicodeToUpperTable[] = { +{ 97, 65}, { 98, 66}, { 99, 67}, { 100, 68}, { 101, 69}, { 102, 70}, { 103, 71}, +{ 104, 72}, { 105, 73}, { 106, 74}, { 107, 75}, { 108, 76}, { 109, 77}, { 110, 78}, +{ 111, 79}, { 112, 80}, { 113, 81}, { 114, 82}, { 115, 83}, { 116, 84}, { 117, 85}, +{ 118, 86}, { 119, 87}, { 120, 88}, { 121, 89}, { 122, 90}, { 224, 192}, { 225, 193}, +{ 226, 194}, { 227, 195}, { 228, 196}, { 229, 197}, { 230, 198}, { 231, 199}, { 232, 200}, +{ 233, 201}, { 234, 202}, { 235, 203}, { 236, 204}, { 237, 205}, { 238, 206}, { 239, 207}, +{ 240, 208}, { 241, 209}, { 242, 210}, { 243, 211}, { 244, 212}, { 245, 213}, { 246, 214}, +{ 248, 216}, { 249, 217}, { 250, 218}, { 251, 219}, { 252, 220}, { 253, 221}, { 254, 222}, +{ 255, 376}, { 257, 256}, { 259, 258}, { 261, 260}, { 263, 262}, { 265, 264}, { 267, 266}, +{ 269, 268}, { 271, 270}, { 273, 272}, { 275, 274}, { 277, 276}, { 279, 278}, { 281, 280}, +{ 283, 282}, { 285, 284}, { 287, 286}, { 289, 288}, { 291, 290}, { 293, 292}, { 295, 294}, +{ 297, 296}, { 299, 298}, { 301, 300}, { 303, 302}, { 305, 73}, { 307, 306}, { 309, 308}, { 311, 310}, +{ 314, 313}, { 316, 315}, { 318, 317}, { 320, 319}, { 322, 321}, { 324, 323}, { 326, 325}, +{ 328, 327}, { 331, 330}, { 333, 332}, { 335, 334}, { 337, 336}, { 339, 338}, { 341, 340}, +{ 343, 342}, { 345, 344}, { 347, 346}, { 349, 348}, { 351, 350}, { 353, 352}, { 355, 354}, +{ 357, 356}, { 359, 358}, { 361, 360}, { 363, 362}, { 365, 364}, { 367, 366}, { 369, 368}, +{ 371, 370}, { 373, 372}, { 375, 374}, { 378, 377}, { 380, 379}, { 382, 381}, { 383, 83}, { 387, 386}, +{ 389, 388}, { 392, 391}, { 396, 395}, { 402, 401}, { 409, 408}, { 417, 416}, { 419, 418}, +{ 421, 420}, { 424, 423}, { 429, 428}, { 432, 431}, { 436, 435}, { 438, 437}, { 441, 440}, +{ 445, 444}, { 454, 452}, { 457, 455}, { 460, 458}, { 462, 461}, { 464, 463}, { 466, 465}, +{ 468, 467}, { 470, 469}, { 472, 471}, { 474, 473}, { 476, 475}, { 477, 398}, { 479, 478}, +{ 481, 480}, { 483, 482}, { 485, 484}, { 487, 486}, { 489, 488}, { 491, 490}, { 493, 492}, +{ 495, 494}, { 499, 497}, { 501, 500}, { 507, 506}, { 509, 508}, { 511, 510}, { 513, 512}, +{ 515, 514}, { 517, 516}, { 519, 518}, { 521, 520}, { 523, 522}, { 525, 524}, { 527, 526}, +{ 529, 528}, { 531, 530}, { 533, 532}, { 535, 534}, { 595, 385}, { 596, 390}, { 598, 393}, +{ 599, 394}, { 601, 399}, { 603, 400}, { 608, 403}, { 611, 404}, { 616, 407}, { 617, 406}, +{ 623, 412}, { 626, 413}, { 629, 415}, { 643, 425}, { 648, 430}, { 650, 433}, { 651, 434}, +{ 658, 439}, { 940, 902}, { 941, 904}, { 942, 905}, { 943, 906}, { 945, 913}, { 946, 914}, +{ 947, 915}, { 948, 916}, { 949, 917}, { 950, 918}, { 951, 919}, { 952, 920}, { 953, 921}, +{ 954, 922}, { 955, 923}, { 956, 924}, { 957, 925}, { 958, 926}, { 959, 927}, { 960, 928}, +{ 961, 929}, { 962, 931}, { 963, 931}, { 964, 932}, { 965, 933}, { 966, 934}, { 967, 935}, +{ 968, 936}, { 969, 937}, { 970, 938}, { 971, 939}, { 972, 908}, { 973, 910}, { 974, 911}, +{ 995, 994}, { 997, 996}, { 999, 998}, { 1001, 1000}, { 1003, 1002}, { 1005, 1004}, { 1007, 1006}, +{ 1072, 1040}, { 1073, 1041}, { 1074, 1042}, { 1075, 1043}, { 1076, 1044}, { 1077, 1045}, { 1078, 1046}, +{ 1079, 1047}, { 1080, 1048}, { 1081, 1049}, { 1082, 1050}, { 1083, 1051}, { 1084, 1052}, { 1085, 1053}, +{ 1086, 1054}, { 1087, 1055}, { 1088, 1056}, { 1089, 1057}, { 1090, 1058}, { 1091, 1059}, { 1092, 1060}, +{ 1093, 1061}, { 1094, 1062}, { 1095, 1063}, { 1096, 1064}, { 1097, 1065}, { 1098, 1066}, { 1099, 1067}, +{ 1100, 1068}, { 1101, 1069}, { 1102, 1070}, { 1103, 1071}, { 1104, 1024}, { 1105, 1025}, { 1106, 1026}, { 1107, 1027}, +{ 1108, 1028}, { 1109, 1029}, { 1110, 1030}, { 1111, 1031}, { 1112, 1032}, { 1113, 1033}, { 1114, 1034}, +{ 1115, 1035}, { 1116, 1036}, { 1117, 1037}, { 1118, 1038}, { 1119, 1039}, { 1121, 1120}, { 1123, 1122}, { 1125, 1124}, +{ 1127, 1126}, { 1129, 1128}, { 1131, 1130}, { 1133, 1132}, { 1135, 1134}, { 1137, 1136}, { 1139, 1138}, +{ 1141, 1140}, { 1143, 1142}, { 1145, 1144}, { 1147, 1146}, { 1149, 1148}, { 1151, 1150}, { 1153, 1152}, +{ 1169, 1168}, { 1171, 1170}, { 1173, 1172}, { 1175, 1174}, { 1177, 1176}, { 1179, 1178}, { 1181, 1180}, +{ 1183, 1182}, { 1185, 1184}, { 1187, 1186}, { 1189, 1188}, { 1191, 1190}, { 1193, 1192}, { 1195, 1194}, +{ 1197, 1196}, { 1199, 1198}, { 1201, 1200}, { 1203, 1202}, { 1205, 1204}, { 1207, 1206}, { 1209, 1208}, +{ 1211, 1210}, { 1213, 1212}, { 1215, 1214}, { 1218, 1217}, { 1220, 1219}, { 1224, 1223}, { 1228, 1227}, +{ 1233, 1232}, { 1235, 1234}, { 1237, 1236}, { 1239, 1238}, { 1241, 1240}, { 1243, 1242}, { 1245, 1244}, +{ 1247, 1246}, { 1249, 1248}, { 1251, 1250}, { 1253, 1252}, { 1255, 1254}, { 1257, 1256}, { 1259, 1258}, +{ 1263, 1262}, { 1265, 1264}, { 1267, 1266}, { 1269, 1268}, { 1273, 1272}, { 1377, 1329}, { 1378, 1330}, +{ 1379, 1331}, { 1380, 1332}, { 1381, 1333}, { 1382, 1334}, { 1383, 1335}, { 1384, 1336}, { 1385, 1337}, +{ 1386, 1338}, { 1387, 1339}, { 1388, 1340}, { 1389, 1341}, { 1390, 1342}, { 1391, 1343}, { 1392, 1344}, +{ 1393, 1345}, { 1394, 1346}, { 1395, 1347}, { 1396, 1348}, { 1397, 1349}, { 1398, 1350}, { 1399, 1351}, +{ 1400, 1352}, { 1401, 1353}, { 1402, 1354}, { 1403, 1355}, { 1404, 1356}, { 1405, 1357}, { 1406, 1358}, +{ 1407, 1359}, { 1408, 1360}, { 1409, 1361}, { 1410, 1362}, { 1411, 1363}, { 1412, 1364}, { 1413, 1365}, +{ 1414, 1366}, { 7681, 7680}, { 7683, 7682}, { 7685, 7684}, { 7687, 7686}, { 7689, 7688}, { 7691, 7690}, +{ 7693, 7692}, { 7695, 7694}, { 7697, 7696}, { 7699, 7698}, { 7701, 7700}, { 7703, 7702}, { 7705, 7704}, +{ 7707, 7706}, { 7709, 7708}, { 7711, 7710}, { 7713, 7712}, { 7715, 7714}, { 7717, 7716}, { 7719, 7718}, +{ 7721, 7720}, { 7723, 7722}, { 7725, 7724}, { 7727, 7726}, { 7729, 7728}, { 7731, 7730}, { 7733, 7732}, +{ 7735, 7734}, { 7737, 7736}, { 7739, 7738}, { 7741, 7740}, { 7743, 7742}, { 7745, 7744}, { 7747, 7746}, +{ 7749, 7748}, { 7751, 7750}, { 7753, 7752}, { 7755, 7754}, { 7757, 7756}, { 7759, 7758}, { 7761, 7760}, +{ 7763, 7762}, { 7765, 7764}, { 7767, 7766}, { 7769, 7768}, { 7771, 7770}, { 7773, 7772}, { 7775, 7774}, +{ 7777, 7776}, { 7779, 7778}, { 7781, 7780}, { 7783, 7782}, { 7785, 7784}, { 7787, 7786}, { 7789, 7788}, +{ 7791, 7790}, { 7793, 7792}, { 7795, 7794}, { 7797, 7796}, { 7799, 7798}, { 7801, 7800}, { 7803, 7802}, +{ 7805, 7804}, { 7807, 7806}, { 7809, 7808}, { 7811, 7810}, { 7813, 7812}, { 7815, 7814}, { 7817, 7816}, +{ 7819, 7818}, { 7821, 7820}, { 7823, 7822}, { 7825, 7824}, { 7827, 7826}, { 7829, 7828}, { 7841, 7840}, +{ 7843, 7842}, { 7845, 7844}, { 7847, 7846}, { 7849, 7848}, { 7851, 7850}, { 7853, 7852}, { 7855, 7854}, +{ 7857, 7856}, { 7859, 7858}, { 7861, 7860}, { 7863, 7862}, { 7865, 7864}, { 7867, 7866}, { 7869, 7868}, +{ 7871, 7870}, { 7873, 7872}, { 7875, 7874}, { 7877, 7876}, { 7879, 7878}, { 7881, 7880}, { 7883, 7882}, +{ 7885, 7884}, { 7887, 7886}, { 7889, 7888}, { 7891, 7890}, { 7893, 7892}, { 7895, 7894}, { 7897, 7896}, +{ 7899, 7898}, { 7901, 7900}, { 7903, 7902}, { 7905, 7904}, { 7907, 7906}, { 7909, 7908}, { 7911, 7910}, +{ 7913, 7912}, { 7915, 7914}, { 7917, 7916}, { 7919, 7918}, { 7921, 7920}, { 7923, 7922}, { 7925, 7924}, +{ 7927, 7926}, { 7929, 7928}, { 7936, 7944}, { 7937, 7945}, { 7938, 7946}, { 7939, 7947}, { 7940, 7948}, +{ 7941, 7949}, { 7942, 7950}, { 7943, 7951}, { 7952, 7960}, { 7953, 7961}, { 7954, 7962}, { 7955, 7963}, +{ 7956, 7964}, { 7957, 7965}, { 7968, 7976}, { 7969, 7977}, { 7970, 7978}, { 7971, 7979}, { 7972, 7980}, +{ 7973, 7981}, { 7974, 7982}, { 7975, 7983}, { 7984, 7992}, { 7985, 7993}, { 7986, 7994}, { 7987, 7995}, +{ 7988, 7996}, { 7989, 7997}, { 7990, 7998}, { 7991, 7999}, { 8000, 8008}, { 8001, 8009}, { 8002, 8010}, +{ 8003, 8011}, { 8004, 8012}, { 8005, 8013}, { 8017, 8025}, { 8019, 8027}, { 8021, 8029}, { 8023, 8031}, +{ 8032, 8040}, { 8033, 8041}, { 8034, 8042}, { 8035, 8043}, { 8036, 8044}, { 8037, 8045}, { 8038, 8046}, +{ 8039, 8047}, { 8048, 8122}, { 8049, 8123}, { 8050, 8136}, { 8051, 8137}, { 8052, 8138}, { 8053, 8139}, +{ 8054, 8154}, { 8055, 8155}, { 8056, 8184}, { 8057, 8185}, { 8058, 8170}, { 8059, 8171}, { 8060, 8186}, +{ 8061, 8187}, { 8112, 8120}, { 8113, 8121}, { 8144, 8152}, { 8145, 8153}, { 8160, 8168}, { 8161, 8169}, +{ 8165, 8172}, { 8560, 8544}, { 8561, 8545}, { 8562, 8546}, { 8563, 8547}, { 8564, 8548}, { 8565, 8549}, +{ 8566, 8550}, { 8567, 8551}, { 8568, 8552}, { 8569, 8553}, { 8570, 8554}, { 8571, 8555}, { 8572, 8556}, +{ 8573, 8557}, { 8574, 8558}, { 8575, 8559}, { 9424, 9398}, { 9425, 9399}, { 9426, 9400}, { 9427, 9401}, +{ 9428, 9402}, { 9429, 9403}, { 9430, 9404}, { 9431, 9405}, { 9432, 9406}, { 9433, 9407}, { 9434, 9408}, +{ 9435, 9409}, { 9436, 9410}, { 9437, 9411}, { 9438, 9412}, { 9439, 9413}, { 9440, 9414}, { 9441, 9415}, +{ 9442, 9416}, { 9443, 9417}, { 9444, 9418}, { 9445, 9419}, { 9446, 9420}, { 9447, 9421}, { 9448, 9422}, +{ 9449, 9423}, {65345,65313}, {65346,65314}, {65347,65315}, {65348,65316}, {65349,65317}, {65350,65318}, +{65351,65319}, {65352,65320}, {65353,65321}, {65354,65322}, {65355,65323}, {65356,65324}, {65357,65325}, +{65358,65326}, {65359,65327}, {65360,65328}, {65361,65329}, {65362,65330}, {65363,65331}, {65364,65332}, +{65365,65333}, {65366,65334}, {65367,65335}, {65368,65336}, {65369,65337}, {65370,65338}, {65535, 0}}; + +static const GUnicodePairType UnicodeToLowerTable[] = { +{ 65, 97}, { 66, 98}, { 67, 99}, { 68, 100}, { 69, 101}, { 70, 102}, { 71, 103}, +{ 72, 104}, { 73, 105}, { 74, 106}, { 75, 107}, { 76, 108}, { 77, 109}, { 78, 110}, +{ 79, 111}, { 80, 112}, { 81, 113}, { 82, 114}, { 83, 115}, { 84, 116}, { 85, 117}, +{ 86, 118}, { 87, 119}, { 88, 120}, { 89, 121}, { 90, 122}, { 192, 224}, { 193, 225}, +{ 194, 226}, { 195, 227}, { 196, 228}, { 197, 229}, { 198, 230}, { 199, 231}, { 200, 232}, +{ 201, 233}, { 202, 234}, { 203, 235}, { 204, 236}, { 205, 237}, { 206, 238}, { 207, 239}, +{ 208, 240}, { 209, 241}, { 210, 242}, { 211, 243}, { 212, 244}, { 213, 245}, { 214, 246}, +{ 216, 248}, { 217, 249}, { 218, 250}, { 219, 251}, { 220, 252}, { 221, 253}, { 222, 254}, +{ 256, 257}, { 258, 259}, { 260, 261}, { 262, 263}, { 264, 265}, { 266, 267}, { 268, 269}, +{ 270, 271}, { 272, 273}, { 274, 275}, { 276, 277}, { 278, 279}, { 280, 281}, { 282, 283}, +{ 284, 285}, { 286, 287}, { 288, 289}, { 290, 291}, { 292, 293}, { 294, 295}, { 296, 297}, +{ 298, 299}, { 300, 301}, { 302, 303}, { 304, 105}, { 306, 307}, { 308, 309}, { 310, 311}, { 313, 314}, +{ 315, 316}, { 317, 318}, { 319, 320}, { 321, 322}, { 323, 324}, { 325, 326}, { 327, 328}, +{ 330, 331}, { 332, 333}, { 334, 335}, { 336, 337}, { 338, 339}, { 340, 341}, { 342, 343}, +{ 344, 345}, { 346, 347}, { 348, 349}, { 350, 351}, { 352, 353}, { 354, 355}, { 356, 357}, +{ 358, 359}, { 360, 361}, { 362, 363}, { 364, 365}, { 366, 367}, { 368, 369}, { 370, 371}, +{ 372, 373}, { 374, 375}, { 376, 255}, { 377, 378}, { 379, 380}, { 381, 382}, { 385, 595}, +{ 386, 387}, { 388, 389}, { 390, 596}, { 391, 392}, { 393, 598}, { 394, 599}, { 395, 396}, +{ 398, 477}, { 399, 601}, { 400, 603}, { 401, 402}, { 403, 608}, { 404, 611}, { 406, 617}, +{ 407, 616}, { 408, 409}, { 412, 623}, { 413, 626}, { 415, 629}, { 416, 417}, { 418, 419}, +{ 420, 421}, { 423, 424}, { 425, 643}, { 428, 429}, { 430, 648}, { 431, 432}, { 433, 650}, +{ 434, 651}, { 435, 436}, { 437, 438}, { 439, 658}, { 440, 441}, { 444, 445}, { 452, 454}, +{ 455, 457}, { 458, 460}, { 461, 462}, { 463, 464}, { 465, 466}, { 467, 468}, { 469, 470}, +{ 471, 472}, { 473, 474}, { 475, 476}, { 478, 479}, { 480, 481}, { 482, 483}, { 484, 485}, +{ 486, 487}, { 488, 489}, { 490, 491}, { 492, 493}, { 494, 495}, { 497, 499}, { 500, 501}, +{ 506, 507}, { 508, 509}, { 510, 511}, { 512, 513}, { 514, 515}, { 516, 517}, { 518, 519}, +{ 520, 521}, { 522, 523}, { 524, 525}, { 526, 527}, { 528, 529}, { 530, 531}, { 532, 533}, +{ 534, 535}, { 902, 940}, { 904, 941}, { 905, 942}, { 906, 943}, { 908, 972}, { 910, 973}, +{ 911, 974}, { 913, 945}, { 914, 946}, { 915, 947}, { 916, 948}, { 917, 949}, { 918, 950}, +{ 919, 951}, { 920, 952}, { 921, 953}, { 922, 954}, { 923, 955}, { 924, 956}, { 925, 957}, +{ 926, 958}, { 927, 959}, { 928, 960}, { 929, 961}, { 931, 963}, { 932, 964}, { 933, 965}, +{ 934, 966}, { 935, 967}, { 936, 968}, { 937, 969}, { 938, 970}, { 939, 971}, { 994, 995}, +{ 996, 997}, { 998, 999}, { 1000, 1001}, { 1002, 1003}, { 1004, 1005}, { 1006, 1007}, { 1024, 1104}, { 1025, 1105}, +{ 1026, 1106}, { 1027, 1107}, { 1028, 1108}, { 1029, 1109}, { 1030, 1110}, { 1031, 1111}, { 1032, 1112}, +{ 1033, 1113}, { 1034, 1114}, { 1035, 1115}, { 1036, 1116}, { 1037, 1117}, { 1038, 1118}, { 1039, 1119}, { 1040, 1072}, +{ 1041, 1073}, { 1042, 1074}, { 1043, 1075}, { 1044, 1076}, { 1045, 1077}, { 1046, 1078}, { 1047, 1079}, +{ 1048, 1080}, { 1049, 1081}, { 1050, 1082}, { 1051, 1083}, { 1052, 1084}, { 1053, 1085}, { 1054, 1086}, +{ 1055, 1087}, { 1056, 1088}, { 1057, 1089}, { 1058, 1090}, { 1059, 1091}, { 1060, 1092}, { 1061, 1093}, +{ 1062, 1094}, { 1063, 1095}, { 1064, 1096}, { 1065, 1097}, { 1066, 1098}, { 1067, 1099}, { 1068, 1100}, +{ 1069, 1101}, { 1070, 1102}, { 1071, 1103}, { 1120, 1121}, { 1122, 1123}, { 1124, 1125}, { 1126, 1127}, +{ 1128, 1129}, { 1130, 1131}, { 1132, 1133}, { 1134, 1135}, { 1136, 1137}, { 1138, 1139}, { 1140, 1141}, +{ 1142, 1143}, { 1144, 1145}, { 1146, 1147}, { 1148, 1149}, { 1150, 1151}, { 1152, 1153}, { 1168, 1169}, +{ 1170, 1171}, { 1172, 1173}, { 1174, 1175}, { 1176, 1177}, { 1178, 1179}, { 1180, 1181}, { 1182, 1183}, +{ 1184, 1185}, { 1186, 1187}, { 1188, 1189}, { 1190, 1191}, { 1192, 1193}, { 1194, 1195}, { 1196, 1197}, +{ 1198, 1199}, { 1200, 1201}, { 1202, 1203}, { 1204, 1205}, { 1206, 1207}, { 1208, 1209}, { 1210, 1211}, +{ 1212, 1213}, { 1214, 1215}, { 1217, 1218}, { 1219, 1220}, { 1223, 1224}, { 1227, 1228}, { 1232, 1233}, +{ 1234, 1235}, { 1236, 1237}, { 1238, 1239}, { 1240, 1241}, { 1242, 1243}, { 1244, 1245}, { 1246, 1247}, +{ 1248, 1249}, { 1250, 1251}, { 1252, 1253}, { 1254, 1255}, { 1256, 1257}, { 1258, 1259}, { 1262, 1263}, +{ 1264, 1265}, { 1266, 1267}, { 1268, 1269}, { 1272, 1273}, { 1329, 1377}, { 1330, 1378}, { 1331, 1379}, +{ 1332, 1380}, { 1333, 1381}, { 1334, 1382}, { 1335, 1383}, { 1336, 1384}, { 1337, 1385}, { 1338, 1386}, +{ 1339, 1387}, { 1340, 1388}, { 1341, 1389}, { 1342, 1390}, { 1343, 1391}, { 1344, 1392}, { 1345, 1393}, +{ 1346, 1394}, { 1347, 1395}, { 1348, 1396}, { 1349, 1397}, { 1350, 1398}, { 1351, 1399}, { 1352, 1400}, +{ 1353, 1401}, { 1354, 1402}, { 1355, 1403}, { 1356, 1404}, { 1357, 1405}, { 1358, 1406}, { 1359, 1407}, +{ 1360, 1408}, { 1361, 1409}, { 1362, 1410}, { 1363, 1411}, { 1364, 1412}, { 1365, 1413}, { 1366, 1414}, +{ 4256, 4304}, { 4257, 4305}, { 4258, 4306}, { 4259, 4307}, { 4260, 4308}, { 4261, 4309}, { 4262, 4310}, +{ 4263, 4311}, { 4264, 4312}, { 4265, 4313}, { 4266, 4314}, { 4267, 4315}, { 4268, 4316}, { 4269, 4317}, +{ 4270, 4318}, { 4271, 4319}, { 4272, 4320}, { 4273, 4321}, { 4274, 4322}, { 4275, 4323}, { 4276, 4324}, +{ 4277, 4325}, { 4278, 4326}, { 4279, 4327}, { 4280, 4328}, { 4281, 4329}, { 4282, 4330}, { 4283, 4331}, +{ 4284, 4332}, { 4285, 4333}, { 4286, 4334}, { 4287, 4335}, { 4288, 4336}, { 4289, 4337}, { 4290, 4338}, +{ 4291, 4339}, { 4292, 4340}, { 4293, 4341}, { 7680, 7681}, { 7682, 7683}, { 7684, 7685}, { 7686, 7687}, +{ 7688, 7689}, { 7690, 7691}, { 7692, 7693}, { 7694, 7695}, { 7696, 7697}, { 7698, 7699}, { 7700, 7701}, +{ 7702, 7703}, { 7704, 7705}, { 7706, 7707}, { 7708, 7709}, { 7710, 7711}, { 7712, 7713}, { 7714, 7715}, +{ 7716, 7717}, { 7718, 7719}, { 7720, 7721}, { 7722, 7723}, { 7724, 7725}, { 7726, 7727}, { 7728, 7729}, +{ 7730, 7731}, { 7732, 7733}, { 7734, 7735}, { 7736, 7737}, { 7738, 7739}, { 7740, 7741}, { 7742, 7743}, +{ 7744, 7745}, { 7746, 7747}, { 7748, 7749}, { 7750, 7751}, { 7752, 7753}, { 7754, 7755}, { 7756, 7757}, +{ 7758, 7759}, { 7760, 7761}, { 7762, 7763}, { 7764, 7765}, { 7766, 7767}, { 7768, 7769}, { 7770, 7771}, +{ 7772, 7773}, { 7774, 7775}, { 7776, 7777}, { 7778, 7779}, { 7780, 7781}, { 7782, 7783}, { 7784, 7785}, +{ 7786, 7787}, { 7788, 7789}, { 7790, 7791}, { 7792, 7793}, { 7794, 7795}, { 7796, 7797}, { 7798, 7799}, +{ 7800, 7801}, { 7802, 7803}, { 7804, 7805}, { 7806, 7807}, { 7808, 7809}, { 7810, 7811}, { 7812, 7813}, +{ 7814, 7815}, { 7816, 7817}, { 7818, 7819}, { 7820, 7821}, { 7822, 7823}, { 7824, 7825}, { 7826, 7827}, +{ 7828, 7829}, { 7840, 7841}, { 7842, 7843}, { 7844, 7845}, { 7846, 7847}, { 7848, 7849}, { 7850, 7851}, +{ 7852, 7853}, { 7854, 7855}, { 7856, 7857}, { 7858, 7859}, { 7860, 7861}, { 7862, 7863}, { 7864, 7865}, +{ 7866, 7867}, { 7868, 7869}, { 7870, 7871}, { 7872, 7873}, { 7874, 7875}, { 7876, 7877}, { 7878, 7879}, +{ 7880, 7881}, { 7882, 7883}, { 7884, 7885}, { 7886, 7887}, { 7888, 7889}, { 7890, 7891}, { 7892, 7893}, +{ 7894, 7895}, { 7896, 7897}, { 7898, 7899}, { 7900, 7901}, { 7902, 7903}, { 7904, 7905}, { 7906, 7907}, +{ 7908, 7909}, { 7910, 7911}, { 7912, 7913}, { 7914, 7915}, { 7916, 7917}, { 7918, 7919}, { 7920, 7921}, +{ 7922, 7923}, { 7924, 7925}, { 7926, 7927}, { 7928, 7929}, { 7944, 7936}, { 7945, 7937}, { 7946, 7938}, +{ 7947, 7939}, { 7948, 7940}, { 7949, 7941}, { 7950, 7942}, { 7951, 7943}, { 7960, 7952}, { 7961, 7953}, +{ 7962, 7954}, { 7963, 7955}, { 7964, 7956}, { 7965, 7957}, { 7976, 7968}, { 7977, 7969}, { 7978, 7970}, +{ 7979, 7971}, { 7980, 7972}, { 7981, 7973}, { 7982, 7974}, { 7983, 7975}, { 7992, 7984}, { 7993, 7985}, +{ 7994, 7986}, { 7995, 7987}, { 7996, 7988}, { 7997, 7989}, { 7998, 7990}, { 7999, 7991}, { 8008, 8000}, +{ 8009, 8001}, { 8010, 8002}, { 8011, 8003}, { 8012, 8004}, { 8013, 8005}, { 8025, 8017}, { 8027, 8019}, +{ 8029, 8021}, { 8031, 8023}, { 8040, 8032}, { 8041, 8033}, { 8042, 8034}, { 8043, 8035}, { 8044, 8036}, +{ 8045, 8037}, { 8046, 8038}, { 8047, 8039}, { 8120, 8112}, { 8121, 8113}, { 8122, 8048}, { 8123, 8049}, +{ 8136, 8050}, { 8137, 8051}, { 8138, 8052}, { 8139, 8053}, { 8152, 8144}, { 8153, 8145}, { 8154, 8054}, +{ 8155, 8055}, { 8168, 8160}, { 8169, 8161}, { 8170, 8058}, { 8171, 8059}, { 8172, 8165}, { 8184, 8056}, +{ 8185, 8057}, { 8186, 8060}, { 8187, 8061}, { 8544, 8560}, { 8545, 8561}, { 8546, 8562}, { 8547, 8563}, +{ 8548, 8564}, { 8549, 8565}, { 8550, 8566}, { 8551, 8567}, { 8552, 8568}, { 8553, 8569}, { 8554, 8570}, +{ 8555, 8571}, { 8556, 8572}, { 8557, 8573}, { 8558, 8574}, { 8559, 8575}, { 9398, 9424}, { 9399, 9425}, +{ 9400, 9426}, { 9401, 9427}, { 9402, 9428}, { 9403, 9429}, { 9404, 9430}, { 9405, 9431}, { 9406, 9432}, +{ 9407, 9433}, { 9408, 9434}, { 9409, 9435}, { 9410, 9436}, { 9411, 9437}, { 9412, 9438}, { 9413, 9439}, +{ 9414, 9440}, { 9415, 9441}, { 9416, 9442}, { 9417, 9443}, { 9418, 9444}, { 9419, 9445}, { 9420, 9446}, +{ 9421, 9447}, { 9422, 9448}, { 9423, 9449}, {65313,65345}, {65314,65346}, {65315,65347}, {65316,65348}, +{65317,65349}, {65318,65350}, {65319,65351}, {65320,65352}, {65321,65353}, {65322,65354}, {65323,65355}, +{65324,65356}, {65325,65357}, {65326,65358}, {65327,65359}, {65328,65360}, {65329,65361}, {65330,65362}, +{65331,65363}, {65332,65364}, {65333,65365}, {65334,65366}, {65335,65367}, {65336,65368}, {65337,65369}, +{65338,65370}, {65535, 0}}; + +int OVR_CDECL OVR_towupper(wchar_t charCode) +{ + // Don't use UnicodeUpperBits! It differs from UnicodeToUpperBits. + if (UnicodeCharIs(UnicodeToUpperBits, charCode)) + { + // To protect from memory overrun in case the character is not found + // we use one extra fake element in the table {65536, 0}. + UPInt idx = Alg::LowerBoundSliced( + UnicodeToUpperTable, + 0, + sizeof(UnicodeToUpperTable) / sizeof(UnicodeToUpperTable[0]) - 1, + (UInt16)charCode, + CmpUnicodeKey); + return UnicodeToUpperTable[idx].Value; + } + return charCode; +} + +int OVR_CDECL OVR_towlower(wchar_t charCode) +{ + // Don't use UnicodeLowerBits! It differs from UnicodeToLowerBits. + if (UnicodeCharIs(UnicodeToLowerBits, charCode)) + { + // To protect from memory overrun in case the character is not found + // we use one extra fake element in the table {65536, 0}. + UPInt idx = Alg::LowerBoundSliced( + UnicodeToLowerTable, + 0, + sizeof(UnicodeToLowerTable) / sizeof(UnicodeToLowerTable[0]) - 1, + (UInt16)charCode, + CmpUnicodeKey); + return UnicodeToLowerTable[idx].Value; + } + return charCode; +} + +#endif //OVR_NO_WCTYPE + +} // OVR diff -r 59fc487ba58e -r b069a5c27388 libovr/Src/Kernel/OVR_Std.h --- a/libovr/Src/Kernel/OVR_Std.h Sat Sep 14 17:51:03 2013 +0300 +++ b/libovr/Src/Kernel/OVR_Std.h Sun Sep 15 04:10:05 2013 +0300 @@ -1,1 +1,503 @@ -/************************************************************************************ PublicHeader: OVR.h Filename : OVR_Std.h Content : Standard C function interface Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #ifndef OVR_Std_h #define OVR_Std_h #include "OVR_Types.h" #include // for va_list args #include #include #include #include #if !defined(OVR_OS_WINCE) && defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400) #define OVR_MSVC_SAFESTRING #include #endif // Wide-char funcs #include #include namespace OVR { #if defined(OVR_OS_WIN32) inline char* OVR_CDECL OVR_itoa(int val, char *dest, UPInt destsize, int radix) { #if defined(OVR_MSVC_SAFESTRING) _itoa_s(val, dest, destsize, radix); return dest; #else OVR_UNUSED(destsize); return itoa(val, dest, radix); #endif } #else // OVR_OS_WIN32 inline char* OVR_itoa(int val, char* dest, unsigned int len, int radix) { if (val == 0) { if (len > 1) { dest[0] = '0'; dest[1] = '\0'; } return dest; } int cur = val; unsigned int i = 0; unsigned int sign = 0; if (val < 0) { val = -val; sign = 1; } while ((val != 0) && (i < (len - 1 - sign))) { cur = val % radix; val /= radix; if (radix == 16) { switch(cur) { case 10: dest[i] = 'a'; break; case 11: dest[i] = 'b'; break; case 12: dest[i] = 'c'; break; case 13: dest[i] = 'd'; break; case 14: dest[i] = 'e'; break; case 15: dest[i] = 'f'; break; default: dest[i] = (char)('0' + cur); break; } } else { dest[i] = (char)('0' + cur); } ++i; } if (sign) { dest[i++] = '-'; } for (unsigned int j = 0; j < i / 2; ++j) { char tmp = dest[j]; dest[j] = dest[i - 1 - j]; dest[i - 1 - j] = tmp; } dest[i] = '\0'; return dest; } #endif // String functions inline UPInt OVR_CDECL OVR_strlen(const char* str) { return strlen(str); } inline char* OVR_CDECL OVR_strcpy(char* dest, UPInt destsize, const char* src) { #if defined(OVR_MSVC_SAFESTRING) strcpy_s(dest, destsize, src); return dest; #else OVR_UNUSED(destsize); return strcpy(dest, src); #endif } inline char* OVR_CDECL OVR_strncpy(char* dest, UPInt destsize, const char* src, UPInt count) { #if defined(OVR_MSVC_SAFESTRING) strncpy_s(dest, destsize, src, count); return dest; #else OVR_UNUSED(destsize); return strncpy(dest, src, count); #endif } inline char * OVR_CDECL OVR_strcat(char* dest, UPInt destsize, const char* src) { #if defined(OVR_MSVC_SAFESTRING) strcat_s(dest, destsize, src); return dest; #else OVR_UNUSED(destsize); return strcat(dest, src); #endif } inline int OVR_CDECL OVR_strcmp(const char* dest, const char* src) { return strcmp(dest, src); } inline const char* OVR_CDECL OVR_strchr(const char* str, char c) { return strchr(str, c); } inline char* OVR_CDECL OVR_strchr(char* str, char c) { return strchr(str, c); } inline const char* OVR_strrchr(const char* str, char c) { UPInt len = OVR_strlen(str); for (UPInt i=len; i>0; i--) if (str[i]==c) return str+i; return 0; } inline const UByte* OVR_CDECL OVR_memrchr(const UByte* str, UPInt size, UByte c) { for (SPInt i = (SPInt)size - 1; i >= 0; i--) { if (str[i] == c) return str + i; } return 0; } inline char* OVR_CDECL OVR_strrchr(char* str, char c) { UPInt len = OVR_strlen(str); for (UPInt i=len; i>0; i--) if (str[i]==c) return str+i; return 0; } double OVR_CDECL OVR_strtod(const char* string, char** tailptr); inline long OVR_CDECL OVR_strtol(const char* string, char** tailptr, int radix) { return strtol(string, tailptr, radix); } inline long OVR_CDECL OVR_strtoul(const char* string, char** tailptr, int radix) { return strtoul(string, tailptr, radix); } inline int OVR_CDECL OVR_strncmp(const char* ws1, const char* ws2, UPInt size) { return strncmp(ws1, ws2, size); } inline UInt64 OVR_CDECL OVR_strtouq(const char *nptr, char **endptr, int base) { #if defined(OVR_CC_MSVC) && !defined(OVR_OS_WINCE) return _strtoui64(nptr, endptr, base); #else return strtoull(nptr, endptr, base); #endif } inline SInt64 OVR_CDECL OVR_strtoq(const char *nptr, char **endptr, int base) { #if defined(OVR_CC_MSVC) && !defined(OVR_OS_WINCE) return _strtoi64(nptr, endptr, base); #else return strtoll(nptr, endptr, base); #endif } inline SInt64 OVR_CDECL OVR_atoq(const char* string) { #if defined(OVR_CC_MSVC) && !defined(OVR_OS_WINCE) return _atoi64(string); #else return atoll(string); #endif } inline UInt64 OVR_CDECL OVR_atouq(const char* string) { return OVR_strtouq(string, NULL, 10); } // Implemented in GStd.cpp in platform-specific manner. int OVR_CDECL OVR_stricmp(const char* dest, const char* src); int OVR_CDECL OVR_strnicmp(const char* dest, const char* src, UPInt count); inline UPInt OVR_CDECL OVR_sprintf(char *dest, UPInt destsize, const char* format, ...) { va_list argList; va_start(argList,format); UPInt ret; #if defined(OVR_CC_MSVC) #if defined(OVR_MSVC_SAFESTRING) ret = _vsnprintf_s(dest, destsize, _TRUNCATE, format, argList); OVR_ASSERT(ret != -1); #else OVR_UNUSED(destsize); ret = _vsnprintf(dest, destsize - 1, format, argList); // -1 for space for the null character OVR_ASSERT(ret != -1); dest[destsize-1] = 0; #endif #else OVR_UNUSED(destsize); ret = vsprintf(dest, format, argList); OVR_ASSERT(ret < destsize); #endif va_end(argList); return ret; } inline UPInt OVR_CDECL OVR_vsprintf(char *dest, UPInt destsize, const char * format, va_list argList) { UPInt ret; #if defined(OVR_CC_MSVC) #if defined(OVR_MSVC_SAFESTRING) dest[0] = '\0'; int rv = vsnprintf_s(dest, destsize, _TRUNCATE, format, argList); if (rv == -1) { dest[destsize - 1] = '\0'; ret = destsize - 1; } else ret = (UPInt)rv; #else OVR_UNUSED(destsize); int rv = _vsnprintf(dest, destsize - 1, format, argList); OVR_ASSERT(rv != -1); ret = (UPInt)rv; dest[destsize-1] = 0; #endif #else OVR_UNUSED(destsize); ret = (UPInt)vsprintf(dest, format, argList); OVR_ASSERT(ret < destsize); #endif return ret; } // Returns the number of characters in the formatted string. inline UPInt OVR_CDECL OVR_vscprintf(const char * format, va_list argList) { UPInt ret; #if defined(OVR_CC_MSVC) ret = (UPInt) _vscprintf(format, argList); #else ret = (UPInt) vsnprintf(NULL, 0, format, argList); #endif return ret; } wchar_t* OVR_CDECL OVR_wcscpy(wchar_t* dest, UPInt destsize, const wchar_t* src); wchar_t* OVR_CDECL OVR_wcsncpy(wchar_t* dest, UPInt destsize, const wchar_t* src, UPInt count); wchar_t* OVR_CDECL OVR_wcscat(wchar_t* dest, UPInt destsize, const wchar_t* src); UPInt OVR_CDECL OVR_wcslen(const wchar_t* str); int OVR_CDECL OVR_wcscmp(const wchar_t* a, const wchar_t* b); int OVR_CDECL OVR_wcsicmp(const wchar_t* a, const wchar_t* b); inline int OVR_CDECL OVR_wcsicoll(const wchar_t* a, const wchar_t* b) { #if defined(OVR_OS_WIN32) #if defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400) return ::_wcsicoll(a, b); #else return ::wcsicoll(a, b); #endif #else // not supported, use regular wcsicmp return OVR_wcsicmp(a, b); #endif } inline int OVR_CDECL OVR_wcscoll(const wchar_t* a, const wchar_t* b) { #if defined(OVR_OS_WIN32) || defined(OVR_OS_LINUX) return wcscoll(a, b); #else // not supported, use regular wcscmp return OVR_wcscmp(a, b); #endif } #ifndef OVR_NO_WCTYPE inline int OVR_CDECL UnicodeCharIs(const UInt16* table, wchar_t charCode) { unsigned offset = table[charCode >> 8]; if (offset == 0) return 0; if (offset == 1) return 1; return (table[offset + ((charCode >> 4) & 15)] & (1 << (charCode & 15))) != 0; } extern const UInt16 UnicodeAlnumBits[]; extern const UInt16 UnicodeAlphaBits[]; extern const UInt16 UnicodeDigitBits[]; extern const UInt16 UnicodeSpaceBits[]; extern const UInt16 UnicodeXDigitBits[]; // Uncomment if necessary //extern const UInt16 UnicodeCntrlBits[]; //extern const UInt16 UnicodeGraphBits[]; //extern const UInt16 UnicodeLowerBits[]; //extern const UInt16 UnicodePrintBits[]; //extern const UInt16 UnicodePunctBits[]; //extern const UInt16 UnicodeUpperBits[]; inline int OVR_CDECL OVR_iswalnum (wchar_t charCode) { return UnicodeCharIs(UnicodeAlnumBits, charCode); } inline int OVR_CDECL OVR_iswalpha (wchar_t charCode) { return UnicodeCharIs(UnicodeAlphaBits, charCode); } inline int OVR_CDECL OVR_iswdigit (wchar_t charCode) { return UnicodeCharIs(UnicodeDigitBits, charCode); } inline int OVR_CDECL OVR_iswspace (wchar_t charCode) { return UnicodeCharIs(UnicodeSpaceBits, charCode); } inline int OVR_CDECL OVR_iswxdigit(wchar_t charCode) { return UnicodeCharIs(UnicodeXDigitBits, charCode); } // Uncomment if necessary //inline int OVR_CDECL OVR_iswcntrl (wchar_t charCode) { return UnicodeCharIs(UnicodeCntrlBits, charCode); } //inline int OVR_CDECL OVR_iswgraph (wchar_t charCode) { return UnicodeCharIs(UnicodeGraphBits, charCode); } //inline int OVR_CDECL OVR_iswlower (wchar_t charCode) { return UnicodeCharIs(UnicodeLowerBits, charCode); } //inline int OVR_CDECL OVR_iswprint (wchar_t charCode) { return UnicodeCharIs(UnicodePrintBits, charCode); } //inline int OVR_CDECL OVR_iswpunct (wchar_t charCode) { return UnicodeCharIs(UnicodePunctBits, charCode); } //inline int OVR_CDECL OVR_iswupper (wchar_t charCode) { return UnicodeCharIs(UnicodeUpperBits, charCode); } int OVR_CDECL OVR_towupper(wchar_t charCode); int OVR_CDECL OVR_towlower(wchar_t charCode); #else // OVR_NO_WCTYPE inline int OVR_CDECL OVR_iswspace(wchar_t c) { return iswspace(c); } inline int OVR_CDECL OVR_iswdigit(wchar_t c) { return iswdigit(c); } inline int OVR_CDECL OVR_iswxdigit(wchar_t c) { return iswxdigit(c); } inline int OVR_CDECL OVR_iswalpha(wchar_t c) { return iswalpha(c); } inline int OVR_CDECL OVR_iswalnum(wchar_t c) { return iswalnum(c); } inline wchar_t OVR_CDECL OVR_towlower(wchar_t c) { return (wchar_t)towlower(c); } inline wchar_t OVR_towupper(wchar_t c) { return (wchar_t)towupper(c); } #endif // OVR_NO_WCTYPE // ASCII versions of tolower and toupper. Don't use "char" inline int OVR_CDECL OVR_tolower(int c) { return (c >= 'A' && c <= 'Z') ? c - 'A' + 'a' : c; } inline int OVR_CDECL OVR_toupper(int c) { return (c >= 'a' && c <= 'z') ? c - 'a' + 'A' : c; } inline double OVR_CDECL OVR_wcstod(const wchar_t* string, wchar_t** tailptr) { #if defined(OVR_OS_OTHER) OVR_UNUSED(tailptr); char buffer[64]; char* tp = NULL; UPInt max = OVR_wcslen(string); if (max > 63) max = 63; unsigned char c = 0; for (UPInt i=0; i < max; i++) { c = (unsigned char)string[i]; buffer[i] = ((c) < 128 ? (char)c : '!'); } buffer[max] = 0; return OVR_strtod(buffer, &tp); #else return wcstod(string, tailptr); #endif } inline long OVR_CDECL OVR_wcstol(const wchar_t* string, wchar_t** tailptr, int radix) { #if defined(OVR_OS_OTHER) OVR_UNUSED(tailptr); char buffer[64]; char* tp = NULL; UPInt max = OVR_wcslen(string); if (max > 63) max = 63; unsigned char c = 0; for (UPInt i=0; i < max; i++) { c = (unsigned char)string[i]; buffer[i] = ((c) < 128 ? (char)c : '!'); } buffer[max] = 0; return strtol(buffer, &tp, radix); #else return wcstol(string, tailptr, radix); #endif } } // OVR #endif // OVR_Std_h \ No newline at end of file +/************************************************************************************ + +PublicHeader: OVR.h +Filename : OVR_Std.h +Content : Standard C function interface +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#ifndef OVR_Std_h +#define OVR_Std_h + +#include "OVR_Types.h" +#include // for va_list args +#include +#include +#include +#include + +#if !defined(OVR_OS_WINCE) && defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400) +#define OVR_MSVC_SAFESTRING +#include +#endif + +// Wide-char funcs +#include +#include + +namespace OVR { + +#if defined(OVR_OS_WIN32) +inline char* OVR_CDECL OVR_itoa(int val, char *dest, UPInt destsize, int radix) +{ +#if defined(OVR_MSVC_SAFESTRING) + _itoa_s(val, dest, destsize, radix); + return dest; +#else + OVR_UNUSED(destsize); + return itoa(val, dest, radix); +#endif +} +#else // OVR_OS_WIN32 +inline char* OVR_itoa(int val, char* dest, unsigned int len, int radix) +{ + if (val == 0) + { + if (len > 1) + { + dest[0] = '0'; + dest[1] = '\0'; + } + return dest; + } + + int cur = val; + unsigned int i = 0; + unsigned int sign = 0; + + if (val < 0) + { + val = -val; + sign = 1; + } + + while ((val != 0) && (i < (len - 1 - sign))) + { + cur = val % radix; + val /= radix; + + if (radix == 16) + { + switch(cur) + { + case 10: + dest[i] = 'a'; + break; + case 11: + dest[i] = 'b'; + break; + case 12: + dest[i] = 'c'; + break; + case 13: + dest[i] = 'd'; + break; + case 14: + dest[i] = 'e'; + break; + case 15: + dest[i] = 'f'; + break; + default: + dest[i] = (char)('0' + cur); + break; + } + } + else + { + dest[i] = (char)('0' + cur); + } + ++i; + } + + if (sign) + { + dest[i++] = '-'; + } + + for (unsigned int j = 0; j < i / 2; ++j) + { + char tmp = dest[j]; + dest[j] = dest[i - 1 - j]; + dest[i - 1 - j] = tmp; + } + dest[i] = '\0'; + + return dest; +} + +#endif + + +// String functions + +inline UPInt OVR_CDECL OVR_strlen(const char* str) +{ + return strlen(str); +} + +inline char* OVR_CDECL OVR_strcpy(char* dest, UPInt destsize, const char* src) +{ +#if defined(OVR_MSVC_SAFESTRING) + strcpy_s(dest, destsize, src); + return dest; +#else + OVR_UNUSED(destsize); + return strcpy(dest, src); +#endif +} + +inline char* OVR_CDECL OVR_strncpy(char* dest, UPInt destsize, const char* src, UPInt count) +{ +#if defined(OVR_MSVC_SAFESTRING) + strncpy_s(dest, destsize, src, count); + return dest; +#else + OVR_UNUSED(destsize); + return strncpy(dest, src, count); +#endif +} + +inline char * OVR_CDECL OVR_strcat(char* dest, UPInt destsize, const char* src) +{ +#if defined(OVR_MSVC_SAFESTRING) + strcat_s(dest, destsize, src); + return dest; +#else + OVR_UNUSED(destsize); + return strcat(dest, src); +#endif +} + +inline int OVR_CDECL OVR_strcmp(const char* dest, const char* src) +{ + return strcmp(dest, src); +} + +inline const char* OVR_CDECL OVR_strchr(const char* str, char c) +{ + return strchr(str, c); +} + +inline char* OVR_CDECL OVR_strchr(char* str, char c) +{ + return strchr(str, c); +} + +inline const char* OVR_strrchr(const char* str, char c) +{ + UPInt len = OVR_strlen(str); + for (UPInt i=len; i>0; i--) + if (str[i]==c) + return str+i; + return 0; +} + +inline const UByte* OVR_CDECL OVR_memrchr(const UByte* str, UPInt size, UByte c) +{ + for (SPInt i = (SPInt)size - 1; i >= 0; i--) + { + if (str[i] == c) + return str + i; + } + return 0; +} + +inline char* OVR_CDECL OVR_strrchr(char* str, char c) +{ + UPInt len = OVR_strlen(str); + for (UPInt i=len; i>0; i--) + if (str[i]==c) + return str+i; + return 0; +} + + +double OVR_CDECL OVR_strtod(const char* string, char** tailptr); + +inline long OVR_CDECL OVR_strtol(const char* string, char** tailptr, int radix) +{ + return strtol(string, tailptr, radix); +} + +inline long OVR_CDECL OVR_strtoul(const char* string, char** tailptr, int radix) +{ + return strtoul(string, tailptr, radix); +} + +inline int OVR_CDECL OVR_strncmp(const char* ws1, const char* ws2, UPInt size) +{ + return strncmp(ws1, ws2, size); +} + +inline UInt64 OVR_CDECL OVR_strtouq(const char *nptr, char **endptr, int base) +{ +#if defined(OVR_CC_MSVC) && !defined(OVR_OS_WINCE) + return _strtoui64(nptr, endptr, base); +#else + return strtoull(nptr, endptr, base); +#endif +} + +inline SInt64 OVR_CDECL OVR_strtoq(const char *nptr, char **endptr, int base) +{ +#if defined(OVR_CC_MSVC) && !defined(OVR_OS_WINCE) + return _strtoi64(nptr, endptr, base); +#else + return strtoll(nptr, endptr, base); +#endif +} + + +inline SInt64 OVR_CDECL OVR_atoq(const char* string) +{ +#if defined(OVR_CC_MSVC) && !defined(OVR_OS_WINCE) + return _atoi64(string); +#else + return atoll(string); +#endif +} + +inline UInt64 OVR_CDECL OVR_atouq(const char* string) +{ + return OVR_strtouq(string, NULL, 10); +} + + +// Implemented in GStd.cpp in platform-specific manner. +int OVR_CDECL OVR_stricmp(const char* dest, const char* src); +int OVR_CDECL OVR_strnicmp(const char* dest, const char* src, UPInt count); + +inline UPInt OVR_CDECL OVR_sprintf(char *dest, UPInt destsize, const char* format, ...) +{ + va_list argList; + va_start(argList,format); + UPInt ret; +#if defined(OVR_CC_MSVC) + #if defined(OVR_MSVC_SAFESTRING) + ret = _vsnprintf_s(dest, destsize, _TRUNCATE, format, argList); + OVR_ASSERT(ret != -1); + #else + OVR_UNUSED(destsize); + ret = _vsnprintf(dest, destsize - 1, format, argList); // -1 for space for the null character + OVR_ASSERT(ret != -1); + dest[destsize-1] = 0; + #endif +#else + OVR_UNUSED(destsize); + ret = vsprintf(dest, format, argList); + OVR_ASSERT(ret < destsize); +#endif + va_end(argList); + return ret; +} + +inline UPInt OVR_CDECL OVR_vsprintf(char *dest, UPInt destsize, const char * format, va_list argList) +{ + UPInt ret; +#if defined(OVR_CC_MSVC) + #if defined(OVR_MSVC_SAFESTRING) + dest[0] = '\0'; + int rv = vsnprintf_s(dest, destsize, _TRUNCATE, format, argList); + if (rv == -1) + { + dest[destsize - 1] = '\0'; + ret = destsize - 1; + } + else + ret = (UPInt)rv; + #else + OVR_UNUSED(destsize); + int rv = _vsnprintf(dest, destsize - 1, format, argList); + OVR_ASSERT(rv != -1); + ret = (UPInt)rv; + dest[destsize-1] = 0; + #endif +#else + OVR_UNUSED(destsize); + ret = (UPInt)vsprintf(dest, format, argList); + OVR_ASSERT(ret < destsize); +#endif + return ret; +} + +// Returns the number of characters in the formatted string. +inline UPInt OVR_CDECL OVR_vscprintf(const char * format, va_list argList) +{ + UPInt ret; +#if defined(OVR_CC_MSVC) + ret = (UPInt) _vscprintf(format, argList); +#else + ret = (UPInt) vsnprintf(NULL, 0, format, argList); +#endif + return ret; +} + + +wchar_t* OVR_CDECL OVR_wcscpy(wchar_t* dest, UPInt destsize, const wchar_t* src); +wchar_t* OVR_CDECL OVR_wcsncpy(wchar_t* dest, UPInt destsize, const wchar_t* src, UPInt count); +wchar_t* OVR_CDECL OVR_wcscat(wchar_t* dest, UPInt destsize, const wchar_t* src); +UPInt OVR_CDECL OVR_wcslen(const wchar_t* str); +int OVR_CDECL OVR_wcscmp(const wchar_t* a, const wchar_t* b); +int OVR_CDECL OVR_wcsicmp(const wchar_t* a, const wchar_t* b); + +inline int OVR_CDECL OVR_wcsicoll(const wchar_t* a, const wchar_t* b) +{ +#if defined(OVR_OS_WIN32) +#if defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400) + return ::_wcsicoll(a, b); +#else + return ::wcsicoll(a, b); +#endif +#else + // not supported, use regular wcsicmp + return OVR_wcsicmp(a, b); +#endif +} + +inline int OVR_CDECL OVR_wcscoll(const wchar_t* a, const wchar_t* b) +{ +#if defined(OVR_OS_WIN32) || defined(OVR_OS_LINUX) + return wcscoll(a, b); +#else + // not supported, use regular wcscmp + return OVR_wcscmp(a, b); +#endif +} + +#ifndef OVR_NO_WCTYPE + +inline int OVR_CDECL UnicodeCharIs(const UInt16* table, wchar_t charCode) +{ + unsigned offset = table[charCode >> 8]; + if (offset == 0) return 0; + if (offset == 1) return 1; + return (table[offset + ((charCode >> 4) & 15)] & (1 << (charCode & 15))) != 0; +} + +extern const UInt16 UnicodeAlnumBits[]; +extern const UInt16 UnicodeAlphaBits[]; +extern const UInt16 UnicodeDigitBits[]; +extern const UInt16 UnicodeSpaceBits[]; +extern const UInt16 UnicodeXDigitBits[]; + +// Uncomment if necessary +//extern const UInt16 UnicodeCntrlBits[]; +//extern const UInt16 UnicodeGraphBits[]; +//extern const UInt16 UnicodeLowerBits[]; +//extern const UInt16 UnicodePrintBits[]; +//extern const UInt16 UnicodePunctBits[]; +//extern const UInt16 UnicodeUpperBits[]; + +inline int OVR_CDECL OVR_iswalnum (wchar_t charCode) { return UnicodeCharIs(UnicodeAlnumBits, charCode); } +inline int OVR_CDECL OVR_iswalpha (wchar_t charCode) { return UnicodeCharIs(UnicodeAlphaBits, charCode); } +inline int OVR_CDECL OVR_iswdigit (wchar_t charCode) { return UnicodeCharIs(UnicodeDigitBits, charCode); } +inline int OVR_CDECL OVR_iswspace (wchar_t charCode) { return UnicodeCharIs(UnicodeSpaceBits, charCode); } +inline int OVR_CDECL OVR_iswxdigit(wchar_t charCode) { return UnicodeCharIs(UnicodeXDigitBits, charCode); } + +// Uncomment if necessary +//inline int OVR_CDECL OVR_iswcntrl (wchar_t charCode) { return UnicodeCharIs(UnicodeCntrlBits, charCode); } +//inline int OVR_CDECL OVR_iswgraph (wchar_t charCode) { return UnicodeCharIs(UnicodeGraphBits, charCode); } +//inline int OVR_CDECL OVR_iswlower (wchar_t charCode) { return UnicodeCharIs(UnicodeLowerBits, charCode); } +//inline int OVR_CDECL OVR_iswprint (wchar_t charCode) { return UnicodeCharIs(UnicodePrintBits, charCode); } +//inline int OVR_CDECL OVR_iswpunct (wchar_t charCode) { return UnicodeCharIs(UnicodePunctBits, charCode); } +//inline int OVR_CDECL OVR_iswupper (wchar_t charCode) { return UnicodeCharIs(UnicodeUpperBits, charCode); } + +int OVR_CDECL OVR_towupper(wchar_t charCode); +int OVR_CDECL OVR_towlower(wchar_t charCode); + +#else // OVR_NO_WCTYPE + +inline int OVR_CDECL OVR_iswspace(wchar_t c) +{ + return iswspace(c); +} + +inline int OVR_CDECL OVR_iswdigit(wchar_t c) +{ + return iswdigit(c); +} + +inline int OVR_CDECL OVR_iswxdigit(wchar_t c) +{ + return iswxdigit(c); +} + +inline int OVR_CDECL OVR_iswalpha(wchar_t c) +{ + return iswalpha(c); +} + +inline int OVR_CDECL OVR_iswalnum(wchar_t c) +{ + return iswalnum(c); +} + +inline wchar_t OVR_CDECL OVR_towlower(wchar_t c) +{ + return (wchar_t)towlower(c); +} + +inline wchar_t OVR_towupper(wchar_t c) +{ + return (wchar_t)towupper(c); +} + +#endif // OVR_NO_WCTYPE + +// ASCII versions of tolower and toupper. Don't use "char" +inline int OVR_CDECL OVR_tolower(int c) +{ + return (c >= 'A' && c <= 'Z') ? c - 'A' + 'a' : c; +} + +inline int OVR_CDECL OVR_toupper(int c) +{ + return (c >= 'a' && c <= 'z') ? c - 'a' + 'A' : c; +} + + + +inline double OVR_CDECL OVR_wcstod(const wchar_t* string, wchar_t** tailptr) +{ +#if defined(OVR_OS_OTHER) + OVR_UNUSED(tailptr); + char buffer[64]; + char* tp = NULL; + UPInt max = OVR_wcslen(string); + if (max > 63) max = 63; + unsigned char c = 0; + for (UPInt i=0; i < max; i++) + { + c = (unsigned char)string[i]; + buffer[i] = ((c) < 128 ? (char)c : '!'); + } + buffer[max] = 0; + return OVR_strtod(buffer, &tp); +#else + return wcstod(string, tailptr); +#endif +} + +inline long OVR_CDECL OVR_wcstol(const wchar_t* string, wchar_t** tailptr, int radix) +{ +#if defined(OVR_OS_OTHER) + OVR_UNUSED(tailptr); + char buffer[64]; + char* tp = NULL; + UPInt max = OVR_wcslen(string); + if (max > 63) max = 63; + unsigned char c = 0; + for (UPInt i=0; i < max; i++) + { + c = (unsigned char)string[i]; + buffer[i] = ((c) < 128 ? (char)c : '!'); + } + buffer[max] = 0; + return strtol(buffer, &tp, radix); +#else + return wcstol(string, tailptr, radix); +#endif +} + +} // OVR + +#endif // OVR_Std_h diff -r 59fc487ba58e -r b069a5c27388 libovr/Src/Kernel/OVR_String.cpp --- a/libovr/Src/Kernel/OVR_String.cpp Sat Sep 14 17:51:03 2013 +0300 +++ b/libovr/Src/Kernel/OVR_String.cpp Sun Sep 15 04:10:05 2013 +0300 @@ -1,1 +1,752 @@ -/************************************************************************************ Filename : OVR_String.cpp Content : String UTF8 string implementation with copy-on-write semantics (thread-safe for assignment but not modification). Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #include "OVR_String.h" #include #include #ifdef OVR_OS_QNX # include #endif namespace OVR { #define String_LengthIsSize (UPInt(1) << String::Flag_LengthIsSizeShift) String::DataDesc String::NullData = {String_LengthIsSize, 1, {0} }; String::String() { pData = &NullData; pData->AddRef(); }; String::String(const char* pdata) { // Obtain length in bytes; it doesn't matter if _data is UTF8. UPInt size = pdata ? OVR_strlen(pdata) : 0; pData = AllocDataCopy1(size, 0, pdata, size); }; String::String(const char* pdata1, const char* pdata2, const char* pdata3) { // Obtain length in bytes; it doesn't matter if _data is UTF8. UPInt size1 = pdata1 ? OVR_strlen(pdata1) : 0; UPInt size2 = pdata2 ? OVR_strlen(pdata2) : 0; UPInt size3 = pdata3 ? OVR_strlen(pdata3) : 0; DataDesc *pdataDesc = AllocDataCopy2(size1 + size2 + size3, 0, pdata1, size1, pdata2, size2); memcpy(pdataDesc->Data + size1 + size2, pdata3, size3); pData = pdataDesc; } String::String(const char* pdata, UPInt size) { OVR_ASSERT((size == 0) || (pdata != 0)); pData = AllocDataCopy1(size, 0, pdata, size); }; String::String(const InitStruct& src, UPInt size) { pData = AllocData(size, 0); src.InitString(GetData()->Data, size); } String::String(const String& src) { pData = src.GetData(); pData->AddRef(); } String::String(const StringBuffer& src) { pData = AllocDataCopy1(src.GetSize(), 0, src.ToCStr(), src.GetSize()); } String::String(const wchar_t* data) { pData = &NullData; pData->AddRef(); // Simplified logic for wchar_t constructor. if (data) *this = data; } String::DataDesc* String::AllocData(UPInt size, UPInt lengthIsSize) { String::DataDesc* pdesc; if (size == 0) { pdesc = &NullData; pdesc->AddRef(); return pdesc; } pdesc = (DataDesc*)OVR_ALLOC(sizeof(DataDesc)+ size); pdesc->Data[size] = 0; pdesc->RefCount = 1; pdesc->Size = size | lengthIsSize; return pdesc; } String::DataDesc* String::AllocDataCopy1(UPInt size, UPInt lengthIsSize, const char* pdata, UPInt copySize) { String::DataDesc* pdesc = AllocData(size, lengthIsSize); memcpy(pdesc->Data, pdata, copySize); return pdesc; } String::DataDesc* String::AllocDataCopy2(UPInt size, UPInt lengthIsSize, const char* pdata1, UPInt copySize1, const char* pdata2, UPInt copySize2) { String::DataDesc* pdesc = AllocData(size, lengthIsSize); memcpy(pdesc->Data, pdata1, copySize1); memcpy(pdesc->Data + copySize1, pdata2, copySize2); return pdesc; } UPInt String::GetLength() const { // Optimize length accesses for non-UTF8 character strings. DataDesc* pdata = GetData(); UPInt length, size = pdata->GetSize(); if (pdata->LengthIsSize()) return size; length = (UPInt)UTF8Util::GetLength(pdata->Data, (UPInt)size); if (length == size) pdata->Size |= String_LengthIsSize; return length; } //static UInt32 String_CharSearch(const char* buf, ) UInt32 String::GetCharAt(UPInt index) const { SPInt i = (SPInt) index; DataDesc* pdata = GetData(); const char* buf = pdata->Data; UInt32 c; if (pdata->LengthIsSize()) { OVR_ASSERT(index < pdata->GetSize()); buf += i; return UTF8Util::DecodeNextChar_Advance0(&buf); } c = UTF8Util::GetCharAt(index, buf, pdata->GetSize()); return c; } UInt32 String::GetFirstCharAt(UPInt index, const char** offset) const { DataDesc* pdata = GetData(); SPInt i = (SPInt) index; const char* buf = pdata->Data; const char* end = buf + pdata->GetSize(); UInt32 c; do { c = UTF8Util::DecodeNextChar_Advance0(&buf); i--; if (buf >= end) { // We've hit the end of the string; don't go further. OVR_ASSERT(i == 0); return c; } } while (i >= 0); *offset = buf; return c; } UInt32 String::GetNextChar(const char** offset) const { return UTF8Util::DecodeNextChar(offset); } void String::AppendChar(UInt32 ch) { DataDesc* pdata = GetData(); UPInt size = pdata->GetSize(); char buff[8]; SPInt encodeSize = 0; // Converts ch into UTF8 string and fills it into buff. UTF8Util::EncodeChar(buff, &encodeSize, ch); OVR_ASSERT(encodeSize >= 0); SetData(AllocDataCopy2(size + (UPInt)encodeSize, 0, pdata->Data, size, buff, (UPInt)encodeSize)); pdata->Release(); } void String::AppendString(const wchar_t* pstr, SPInt len) { if (!pstr) return; DataDesc* pdata = GetData(); UPInt oldSize = pdata->GetSize(); UPInt encodeSize = (UPInt)UTF8Util::GetEncodeStringSize(pstr, len); DataDesc* pnewData = AllocDataCopy1(oldSize + (UPInt)encodeSize, 0, pdata->Data, oldSize); UTF8Util::EncodeString(pnewData->Data + oldSize, pstr, len); SetData(pnewData); pdata->Release(); } void String::AppendString(const char* putf8str, SPInt utf8StrSz) { if (!putf8str || !utf8StrSz) return; if (utf8StrSz == -1) utf8StrSz = (SPInt)OVR_strlen(putf8str); DataDesc* pdata = GetData(); UPInt oldSize = pdata->GetSize(); SetData(AllocDataCopy2(oldSize + (UPInt)utf8StrSz, 0, pdata->Data, oldSize, putf8str, (UPInt)utf8StrSz)); pdata->Release(); } void String::AssignString(const InitStruct& src, UPInt size) { DataDesc* poldData = GetData(); DataDesc* pnewData = AllocData(size, 0); src.InitString(pnewData->Data, size); SetData(pnewData); poldData->Release(); } void String::AssignString(const char* putf8str, UPInt size) { DataDesc* poldData = GetData(); SetData(AllocDataCopy1(size, 0, putf8str, size)); poldData->Release(); } void String::operator = (const char* pstr) { AssignString(pstr, pstr ? OVR_strlen(pstr) : 0); } void String::operator = (const wchar_t* pwstr) { DataDesc* poldData = GetData(); UPInt size = pwstr ? (UPInt)UTF8Util::GetEncodeStringSize(pwstr) : 0; DataDesc* pnewData = AllocData(size, 0); UTF8Util::EncodeString(pnewData->Data, pwstr); SetData(pnewData); poldData->Release(); } void String::operator = (const String& src) { DataDesc* psdata = src.GetData(); DataDesc* pdata = GetData(); SetData(psdata); psdata->AddRef(); pdata->Release(); } void String::operator = (const StringBuffer& src) { DataDesc* polddata = GetData(); SetData(AllocDataCopy1(src.GetSize(), 0, src.ToCStr(), src.GetSize())); polddata->Release(); } void String::operator += (const String& src) { DataDesc *pourData = GetData(), *psrcData = src.GetData(); UPInt ourSize = pourData->GetSize(), srcSize = psrcData->GetSize(); UPInt lflag = pourData->GetLengthFlag() & psrcData->GetLengthFlag(); SetData(AllocDataCopy2(ourSize + srcSize, lflag, pourData->Data, ourSize, psrcData->Data, srcSize)); pourData->Release(); } String String::operator + (const char* str) const { String tmp1(*this); tmp1 += (str ? str : ""); return tmp1; } String String::operator + (const String& src) const { String tmp1(*this); tmp1 += src; return tmp1; } void String::Remove(UPInt posAt, SPInt removeLength) { DataDesc* pdata = GetData(); UPInt oldSize = pdata->GetSize(); // Length indicates the number of characters to remove. UPInt length = GetLength(); // If index is past the string, nothing to remove. if (posAt >= length) return; // Otherwise, cap removeLength to the length of the string. if ((posAt + removeLength) > length) removeLength = length - posAt; // Get the byte position of the UTF8 char at position posAt. SPInt bytePos = UTF8Util::GetByteIndex(posAt, pdata->Data, oldSize); SPInt removeSize = UTF8Util::GetByteIndex(removeLength, pdata->Data + bytePos, oldSize-bytePos); SetData(AllocDataCopy2(oldSize - removeSize, pdata->GetLengthFlag(), pdata->Data, bytePos, pData->Data + bytePos + removeSize, (oldSize - bytePos - removeSize))); pdata->Release(); } String String::Substring(UPInt start, UPInt end) const { UPInt length = GetLength(); if ((start >= length) || (start >= end)) return String(); DataDesc* pdata = GetData(); // If size matches, we know the exact index range. if (pdata->LengthIsSize()) return String(pdata->Data + start, end - start); // Get position of starting character. SPInt byteStart = UTF8Util::GetByteIndex(start, pdata->Data, pdata->GetSize()); SPInt byteSize = UTF8Util::GetByteIndex(end - start, pdata->Data + byteStart, pdata->GetSize()-byteStart); return String(pdata->Data + byteStart, (UPInt)byteSize); } void String::Clear() { NullData.AddRef(); GetData()->Release(); SetData(&NullData); } String String::ToUpper() const { UInt32 c; const char* psource = GetData()->Data; const char* pend = psource + GetData()->GetSize(); String str; SPInt bufferOffset = 0; char buffer[512]; while(psource < pend) { do { c = UTF8Util::DecodeNextChar_Advance0(&psource); UTF8Util::EncodeChar(buffer, &bufferOffset, OVR_towupper(wchar_t(c))); } while ((psource < pend) && (bufferOffset < SPInt(sizeof(buffer)-8))); // Append string a piece at a time. str.AppendString(buffer, bufferOffset); bufferOffset = 0; } return str; } String String::ToLower() const { UInt32 c; const char* psource = GetData()->Data; const char* pend = psource + GetData()->GetSize(); String str; SPInt bufferOffset = 0; char buffer[512]; while(psource < pend) { do { c = UTF8Util::DecodeNextChar_Advance0(&psource); UTF8Util::EncodeChar(buffer, &bufferOffset, OVR_towlower(wchar_t(c))); } while ((psource < pend) && (bufferOffset < SPInt(sizeof(buffer)-8))); // Append string a piece at a time. str.AppendString(buffer, bufferOffset); bufferOffset = 0; } return str; } String& String::Insert(const char* substr, UPInt posAt, SPInt strSize) { DataDesc* poldData = GetData(); UPInt oldSize = poldData->GetSize(); UPInt insertSize = (strSize < 0) ? OVR_strlen(substr) : (UPInt)strSize; UPInt byteIndex = (poldData->LengthIsSize()) ? posAt : (UPInt)UTF8Util::GetByteIndex(posAt, poldData->Data, oldSize); OVR_ASSERT(byteIndex <= oldSize); DataDesc* pnewData = AllocDataCopy2(oldSize + insertSize, 0, poldData->Data, byteIndex, substr, insertSize); memcpy(pnewData->Data + byteIndex + insertSize, poldData->Data + byteIndex, oldSize - byteIndex); SetData(pnewData); poldData->Release(); return *this; } /* String& String::Insert(const UInt32* substr, UPInt posAt, SPInt len) { for (SPInt i = 0; i < len; ++i) { UPInt charw = InsertCharAt(substr[i], posAt); posAt += charw; } return *this; } */ UPInt String::InsertCharAt(UInt32 c, UPInt posAt) { char buf[8]; SPInt index = 0; UTF8Util::EncodeChar(buf, &index, c); OVR_ASSERT(index >= 0); buf[(UPInt)index] = 0; Insert(buf, posAt, index); return (UPInt)index; } int String::CompareNoCase(const char* a, const char* b) { return OVR_stricmp(a, b); } int String::CompareNoCase(const char* a, const char* b, SPInt len) { if (len) { SPInt f,l; SPInt slen = len; const char *s = b; do { f = (SPInt)OVR_tolower((int)(*(a++))); l = (SPInt)OVR_tolower((int)(*(b++))); } while (--len && f && (f == l) && *b != 0); if (f == l && (len != 0 || *b != 0)) { f = (SPInt)slen; l = (SPInt)OVR_strlen(s); return int(f - l); } return int(f - l); } else return (0-(int)OVR_strlen(b)); } // ***** Implement hash static functions // Hash function UPInt String::BernsteinHashFunction(const void* pdataIn, UPInt size, UPInt seed) { const UByte* pdata = (const UByte*) pdataIn; UPInt h = seed; while (size > 0) { size--; h = ((h << 5) + h) ^ (unsigned) pdata[size]; } return h; } // Hash function, case-insensitive UPInt String::BernsteinHashFunctionCIS(const void* pdataIn, UPInt size, UPInt seed) { const UByte* pdata = (const UByte*) pdataIn; UPInt h = seed; while (size > 0) { size--; h = ((h << 5) + h) ^ OVR_tolower(pdata[size]); } // Alternative: "sdbm" hash function, suggested at same web page above. // h = 0; // for bytes { h = (h << 16) + (h << 6) - hash + *p; } return h; } // ***** String Buffer used for Building Strings #define OVR_SBUFF_DEFAULT_GROW_SIZE 512 // Constructors / Destructor. StringBuffer::StringBuffer() : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false) { } StringBuffer::StringBuffer(UPInt growSize) : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false) { SetGrowSize(growSize); } StringBuffer::StringBuffer(const char* data) : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false) { *this = data; } StringBuffer::StringBuffer(const char* data, UPInt dataSize) : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false) { AppendString(data, dataSize); } StringBuffer::StringBuffer(const String& src) : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false) { AppendString(src.ToCStr(), src.GetSize()); } StringBuffer::StringBuffer(const StringBuffer& src) : pData(NULL), Size(0), BufferSize(src.GetGrowSize()), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false) { AppendString(src.ToCStr(), src.GetSize()); LengthIsSize = src.LengthIsSize; } StringBuffer::StringBuffer(const wchar_t* data) : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false) { *this = data; } StringBuffer::~StringBuffer() { if (pData) OVR_FREE(pData); } void StringBuffer::SetGrowSize(UPInt growSize) { if (growSize <= 16) GrowSize = 16; else { UByte bits = Alg::UpperBit(UInt32(growSize-1)); UPInt size = 1<= BufferSize) // >= because of trailing zero! (!AB) { BufferSize = (_size + 1 + GrowSize - 1)& ~(GrowSize-1); if (!pData) pData = (char*)OVR_ALLOC(BufferSize); else pData = (char*)OVR_REALLOC(pData, BufferSize); } } void StringBuffer::Resize(UPInt _size) { Reserve(_size); LengthIsSize = false; Size = _size; if (pData) pData[Size] = 0; } void StringBuffer::Clear() { Resize(0); /* if (pData != pEmptyNullData) { OVR_FREE(pHeap, pData); pData = pEmptyNullData; Size = BufferSize = 0; LengthIsSize = false; } */ } // Appends a character void StringBuffer::AppendChar(UInt32 ch) { char buff[8]; UPInt origSize = GetSize(); // Converts ch into UTF8 string and fills it into buff. Also increments index according to the number of bytes // in the UTF8 string. SPInt srcSize = 0; UTF8Util::EncodeChar(buff, &srcSize, ch); OVR_ASSERT(srcSize >= 0); UPInt size = origSize + srcSize; Resize(size); memcpy(pData + origSize, buff, srcSize); } // Append a string void StringBuffer::AppendString(const wchar_t* pstr, SPInt len) { if (!pstr) return; SPInt srcSize = UTF8Util::GetEncodeStringSize(pstr, len); UPInt origSize = GetSize(); UPInt size = srcSize + origSize; Resize(size); UTF8Util::EncodeString(pData + origSize, pstr, len); } void StringBuffer::AppendString(const char* putf8str, SPInt utf8StrSz) { if (!putf8str || !utf8StrSz) return; if (utf8StrSz == -1) utf8StrSz = (SPInt)OVR_strlen(putf8str); UPInt origSize = GetSize(); UPInt size = utf8StrSz + origSize; Resize(size); memcpy(pData + origSize, putf8str, utf8StrSz); } void StringBuffer::operator = (const char* pstr) { pstr = pstr ? pstr : ""; UPInt size = OVR_strlen(pstr); Resize(size); memcpy(pData, pstr, size); } void StringBuffer::operator = (const wchar_t* pstr) { pstr = pstr ? pstr : L""; UPInt size = (UPInt)UTF8Util::GetEncodeStringSize(pstr); Resize(size); UTF8Util::EncodeString(pData, pstr); } void StringBuffer::operator = (const String& src) { Resize(src.GetSize()); memcpy(pData, src.ToCStr(), src.GetSize()); } // Inserts substr at posAt void StringBuffer::Insert(const char* substr, UPInt posAt, SPInt len) { UPInt oldSize = Size; UPInt insertSize = (len < 0) ? OVR_strlen(substr) : (UPInt)len; UPInt byteIndex = LengthIsSize ? posAt : (UPInt)UTF8Util::GetByteIndex(posAt, pData, (SPInt)Size); OVR_ASSERT(byteIndex <= oldSize); Reserve(oldSize + insertSize); memmove(pData + byteIndex + insertSize, pData + byteIndex, oldSize - byteIndex + 1); memcpy (pData + byteIndex, substr, insertSize); LengthIsSize = false; Size = oldSize + insertSize; pData[Size] = 0; } // Inserts character at posAt UPInt StringBuffer::InsertCharAt(UInt32 c, UPInt posAt) { char buf[8]; SPInt len = 0; UTF8Util::EncodeChar(buf, &len, c); OVR_ASSERT(len >= 0); buf[(UPInt)len] = 0; Insert(buf, posAt, len); return (UPInt)len; } } // OVR \ No newline at end of file +/************************************************************************************ + +Filename : OVR_String.cpp +Content : String UTF8 string implementation with copy-on-write semantics + (thread-safe for assignment but not modification). +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#include "OVR_String.h" + +#include +#include + +#ifdef OVR_OS_QNX +# include +#endif + +namespace OVR { + +#define String_LengthIsSize (UPInt(1) << String::Flag_LengthIsSizeShift) + +String::DataDesc String::NullData = {String_LengthIsSize, 1, {0} }; + + +String::String() +{ + pData = &NullData; + pData->AddRef(); +}; + +String::String(const char* pdata) +{ + // Obtain length in bytes; it doesn't matter if _data is UTF8. + UPInt size = pdata ? OVR_strlen(pdata) : 0; + pData = AllocDataCopy1(size, 0, pdata, size); +}; + +String::String(const char* pdata1, const char* pdata2, const char* pdata3) +{ + // Obtain length in bytes; it doesn't matter if _data is UTF8. + UPInt size1 = pdata1 ? OVR_strlen(pdata1) : 0; + UPInt size2 = pdata2 ? OVR_strlen(pdata2) : 0; + UPInt size3 = pdata3 ? OVR_strlen(pdata3) : 0; + + DataDesc *pdataDesc = AllocDataCopy2(size1 + size2 + size3, 0, + pdata1, size1, pdata2, size2); + memcpy(pdataDesc->Data + size1 + size2, pdata3, size3); + pData = pdataDesc; +} + +String::String(const char* pdata, UPInt size) +{ + OVR_ASSERT((size == 0) || (pdata != 0)); + pData = AllocDataCopy1(size, 0, pdata, size); +}; + + +String::String(const InitStruct& src, UPInt size) +{ + pData = AllocData(size, 0); + src.InitString(GetData()->Data, size); +} + +String::String(const String& src) +{ + pData = src.GetData(); + pData->AddRef(); +} + +String::String(const StringBuffer& src) +{ + pData = AllocDataCopy1(src.GetSize(), 0, src.ToCStr(), src.GetSize()); +} + +String::String(const wchar_t* data) +{ + pData = &NullData; + pData->AddRef(); + // Simplified logic for wchar_t constructor. + if (data) + *this = data; +} + + +String::DataDesc* String::AllocData(UPInt size, UPInt lengthIsSize) +{ + String::DataDesc* pdesc; + + if (size == 0) + { + pdesc = &NullData; + pdesc->AddRef(); + return pdesc; + } + + pdesc = (DataDesc*)OVR_ALLOC(sizeof(DataDesc)+ size); + pdesc->Data[size] = 0; + pdesc->RefCount = 1; + pdesc->Size = size | lengthIsSize; + return pdesc; +} + + +String::DataDesc* String::AllocDataCopy1(UPInt size, UPInt lengthIsSize, + const char* pdata, UPInt copySize) +{ + String::DataDesc* pdesc = AllocData(size, lengthIsSize); + memcpy(pdesc->Data, pdata, copySize); + return pdesc; +} + +String::DataDesc* String::AllocDataCopy2(UPInt size, UPInt lengthIsSize, + const char* pdata1, UPInt copySize1, + const char* pdata2, UPInt copySize2) +{ + String::DataDesc* pdesc = AllocData(size, lengthIsSize); + memcpy(pdesc->Data, pdata1, copySize1); + memcpy(pdesc->Data + copySize1, pdata2, copySize2); + return pdesc; +} + + +UPInt String::GetLength() const +{ + // Optimize length accesses for non-UTF8 character strings. + DataDesc* pdata = GetData(); + UPInt length, size = pdata->GetSize(); + + if (pdata->LengthIsSize()) + return size; + + length = (UPInt)UTF8Util::GetLength(pdata->Data, (UPInt)size); + + if (length == size) + pdata->Size |= String_LengthIsSize; + + return length; +} + + +//static UInt32 String_CharSearch(const char* buf, ) + + +UInt32 String::GetCharAt(UPInt index) const +{ + SPInt i = (SPInt) index; + DataDesc* pdata = GetData(); + const char* buf = pdata->Data; + UInt32 c; + + if (pdata->LengthIsSize()) + { + OVR_ASSERT(index < pdata->GetSize()); + buf += i; + return UTF8Util::DecodeNextChar_Advance0(&buf); + } + + c = UTF8Util::GetCharAt(index, buf, pdata->GetSize()); + return c; +} + +UInt32 String::GetFirstCharAt(UPInt index, const char** offset) const +{ + DataDesc* pdata = GetData(); + SPInt i = (SPInt) index; + const char* buf = pdata->Data; + const char* end = buf + pdata->GetSize(); + UInt32 c; + + do + { + c = UTF8Util::DecodeNextChar_Advance0(&buf); + i--; + + if (buf >= end) + { + // We've hit the end of the string; don't go further. + OVR_ASSERT(i == 0); + return c; + } + } while (i >= 0); + + *offset = buf; + + return c; +} + +UInt32 String::GetNextChar(const char** offset) const +{ + return UTF8Util::DecodeNextChar(offset); +} + + + +void String::AppendChar(UInt32 ch) +{ + DataDesc* pdata = GetData(); + UPInt size = pdata->GetSize(); + char buff[8]; + SPInt encodeSize = 0; + + // Converts ch into UTF8 string and fills it into buff. + UTF8Util::EncodeChar(buff, &encodeSize, ch); + OVR_ASSERT(encodeSize >= 0); + + SetData(AllocDataCopy2(size + (UPInt)encodeSize, 0, + pdata->Data, size, buff, (UPInt)encodeSize)); + pdata->Release(); +} + + +void String::AppendString(const wchar_t* pstr, SPInt len) +{ + if (!pstr) + return; + + DataDesc* pdata = GetData(); + UPInt oldSize = pdata->GetSize(); + UPInt encodeSize = (UPInt)UTF8Util::GetEncodeStringSize(pstr, len); + + DataDesc* pnewData = AllocDataCopy1(oldSize + (UPInt)encodeSize, 0, + pdata->Data, oldSize); + UTF8Util::EncodeString(pnewData->Data + oldSize, pstr, len); + + SetData(pnewData); + pdata->Release(); +} + + +void String::AppendString(const char* putf8str, SPInt utf8StrSz) +{ + if (!putf8str || !utf8StrSz) + return; + if (utf8StrSz == -1) + utf8StrSz = (SPInt)OVR_strlen(putf8str); + + DataDesc* pdata = GetData(); + UPInt oldSize = pdata->GetSize(); + + SetData(AllocDataCopy2(oldSize + (UPInt)utf8StrSz, 0, + pdata->Data, oldSize, putf8str, (UPInt)utf8StrSz)); + pdata->Release(); +} + +void String::AssignString(const InitStruct& src, UPInt size) +{ + DataDesc* poldData = GetData(); + DataDesc* pnewData = AllocData(size, 0); + src.InitString(pnewData->Data, size); + SetData(pnewData); + poldData->Release(); +} + +void String::AssignString(const char* putf8str, UPInt size) +{ + DataDesc* poldData = GetData(); + SetData(AllocDataCopy1(size, 0, putf8str, size)); + poldData->Release(); +} + +void String::operator = (const char* pstr) +{ + AssignString(pstr, pstr ? OVR_strlen(pstr) : 0); +} + +void String::operator = (const wchar_t* pwstr) +{ + DataDesc* poldData = GetData(); + UPInt size = pwstr ? (UPInt)UTF8Util::GetEncodeStringSize(pwstr) : 0; + + DataDesc* pnewData = AllocData(size, 0); + UTF8Util::EncodeString(pnewData->Data, pwstr); + SetData(pnewData); + poldData->Release(); +} + + +void String::operator = (const String& src) +{ + DataDesc* psdata = src.GetData(); + DataDesc* pdata = GetData(); + + SetData(psdata); + psdata->AddRef(); + pdata->Release(); +} + + +void String::operator = (const StringBuffer& src) +{ + DataDesc* polddata = GetData(); + SetData(AllocDataCopy1(src.GetSize(), 0, src.ToCStr(), src.GetSize())); + polddata->Release(); +} + +void String::operator += (const String& src) +{ + DataDesc *pourData = GetData(), + *psrcData = src.GetData(); + UPInt ourSize = pourData->GetSize(), + srcSize = psrcData->GetSize(); + UPInt lflag = pourData->GetLengthFlag() & psrcData->GetLengthFlag(); + + SetData(AllocDataCopy2(ourSize + srcSize, lflag, + pourData->Data, ourSize, psrcData->Data, srcSize)); + pourData->Release(); +} + + +String String::operator + (const char* str) const +{ + String tmp1(*this); + tmp1 += (str ? str : ""); + return tmp1; +} + +String String::operator + (const String& src) const +{ + String tmp1(*this); + tmp1 += src; + return tmp1; +} + +void String::Remove(UPInt posAt, SPInt removeLength) +{ + DataDesc* pdata = GetData(); + UPInt oldSize = pdata->GetSize(); + // Length indicates the number of characters to remove. + UPInt length = GetLength(); + + // If index is past the string, nothing to remove. + if (posAt >= length) + return; + // Otherwise, cap removeLength to the length of the string. + if ((posAt + removeLength) > length) + removeLength = length - posAt; + + // Get the byte position of the UTF8 char at position posAt. + SPInt bytePos = UTF8Util::GetByteIndex(posAt, pdata->Data, oldSize); + SPInt removeSize = UTF8Util::GetByteIndex(removeLength, pdata->Data + bytePos, oldSize-bytePos); + + SetData(AllocDataCopy2(oldSize - removeSize, pdata->GetLengthFlag(), + pdata->Data, bytePos, + pData->Data + bytePos + removeSize, (oldSize - bytePos - removeSize))); + pdata->Release(); +} + + +String String::Substring(UPInt start, UPInt end) const +{ + UPInt length = GetLength(); + if ((start >= length) || (start >= end)) + return String(); + + DataDesc* pdata = GetData(); + + // If size matches, we know the exact index range. + if (pdata->LengthIsSize()) + return String(pdata->Data + start, end - start); + + // Get position of starting character. + SPInt byteStart = UTF8Util::GetByteIndex(start, pdata->Data, pdata->GetSize()); + SPInt byteSize = UTF8Util::GetByteIndex(end - start, pdata->Data + byteStart, pdata->GetSize()-byteStart); + return String(pdata->Data + byteStart, (UPInt)byteSize); +} + +void String::Clear() +{ + NullData.AddRef(); + GetData()->Release(); + SetData(&NullData); +} + + +String String::ToUpper() const +{ + UInt32 c; + const char* psource = GetData()->Data; + const char* pend = psource + GetData()->GetSize(); + String str; + SPInt bufferOffset = 0; + char buffer[512]; + + while(psource < pend) + { + do { + c = UTF8Util::DecodeNextChar_Advance0(&psource); + UTF8Util::EncodeChar(buffer, &bufferOffset, OVR_towupper(wchar_t(c))); + } while ((psource < pend) && (bufferOffset < SPInt(sizeof(buffer)-8))); + + // Append string a piece at a time. + str.AppendString(buffer, bufferOffset); + bufferOffset = 0; + } + + return str; +} + +String String::ToLower() const +{ + UInt32 c; + const char* psource = GetData()->Data; + const char* pend = psource + GetData()->GetSize(); + String str; + SPInt bufferOffset = 0; + char buffer[512]; + + while(psource < pend) + { + do { + c = UTF8Util::DecodeNextChar_Advance0(&psource); + UTF8Util::EncodeChar(buffer, &bufferOffset, OVR_towlower(wchar_t(c))); + } while ((psource < pend) && (bufferOffset < SPInt(sizeof(buffer)-8))); + + // Append string a piece at a time. + str.AppendString(buffer, bufferOffset); + bufferOffset = 0; + } + + return str; +} + + + +String& String::Insert(const char* substr, UPInt posAt, SPInt strSize) +{ + DataDesc* poldData = GetData(); + UPInt oldSize = poldData->GetSize(); + UPInt insertSize = (strSize < 0) ? OVR_strlen(substr) : (UPInt)strSize; + UPInt byteIndex = (poldData->LengthIsSize()) ? + posAt : (UPInt)UTF8Util::GetByteIndex(posAt, poldData->Data, oldSize); + + OVR_ASSERT(byteIndex <= oldSize); + + DataDesc* pnewData = AllocDataCopy2(oldSize + insertSize, 0, + poldData->Data, byteIndex, substr, insertSize); + memcpy(pnewData->Data + byteIndex + insertSize, + poldData->Data + byteIndex, oldSize - byteIndex); + SetData(pnewData); + poldData->Release(); + return *this; +} + +/* +String& String::Insert(const UInt32* substr, UPInt posAt, SPInt len) +{ + for (SPInt i = 0; i < len; ++i) + { + UPInt charw = InsertCharAt(substr[i], posAt); + posAt += charw; + } + return *this; +} +*/ + +UPInt String::InsertCharAt(UInt32 c, UPInt posAt) +{ + char buf[8]; + SPInt index = 0; + UTF8Util::EncodeChar(buf, &index, c); + OVR_ASSERT(index >= 0); + buf[(UPInt)index] = 0; + + Insert(buf, posAt, index); + return (UPInt)index; +} + + +int String::CompareNoCase(const char* a, const char* b) +{ + return OVR_stricmp(a, b); +} + +int String::CompareNoCase(const char* a, const char* b, SPInt len) +{ + if (len) + { + SPInt f,l; + SPInt slen = len; + const char *s = b; + do { + f = (SPInt)OVR_tolower((int)(*(a++))); + l = (SPInt)OVR_tolower((int)(*(b++))); + } while (--len && f && (f == l) && *b != 0); + + if (f == l && (len != 0 || *b != 0)) + { + f = (SPInt)slen; + l = (SPInt)OVR_strlen(s); + return int(f - l); + } + + return int(f - l); + } + else + return (0-(int)OVR_strlen(b)); +} + +// ***** Implement hash static functions + +// Hash function +UPInt String::BernsteinHashFunction(const void* pdataIn, UPInt size, UPInt seed) +{ + const UByte* pdata = (const UByte*) pdataIn; + UPInt h = seed; + while (size > 0) + { + size--; + h = ((h << 5) + h) ^ (unsigned) pdata[size]; + } + + return h; +} + +// Hash function, case-insensitive +UPInt String::BernsteinHashFunctionCIS(const void* pdataIn, UPInt size, UPInt seed) +{ + const UByte* pdata = (const UByte*) pdataIn; + UPInt h = seed; + while (size > 0) + { + size--; + h = ((h << 5) + h) ^ OVR_tolower(pdata[size]); + } + + // Alternative: "sdbm" hash function, suggested at same web page above. + // h = 0; + // for bytes { h = (h << 16) + (h << 6) - hash + *p; } + return h; +} + + + +// ***** String Buffer used for Building Strings + + +#define OVR_SBUFF_DEFAULT_GROW_SIZE 512 +// Constructors / Destructor. +StringBuffer::StringBuffer() + : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false) +{ +} + +StringBuffer::StringBuffer(UPInt growSize) + : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false) +{ + SetGrowSize(growSize); +} + +StringBuffer::StringBuffer(const char* data) + : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false) +{ + *this = data; +} + +StringBuffer::StringBuffer(const char* data, UPInt dataSize) + : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false) +{ + AppendString(data, dataSize); +} + +StringBuffer::StringBuffer(const String& src) + : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false) +{ + AppendString(src.ToCStr(), src.GetSize()); +} + +StringBuffer::StringBuffer(const StringBuffer& src) + : pData(NULL), Size(0), BufferSize(src.GetGrowSize()), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false) +{ + AppendString(src.ToCStr(), src.GetSize()); + LengthIsSize = src.LengthIsSize; +} + +StringBuffer::StringBuffer(const wchar_t* data) + : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false) +{ + *this = data; +} + +StringBuffer::~StringBuffer() +{ + if (pData) + OVR_FREE(pData); +} +void StringBuffer::SetGrowSize(UPInt growSize) +{ + if (growSize <= 16) + GrowSize = 16; + else + { + UByte bits = Alg::UpperBit(UInt32(growSize-1)); + UPInt size = 1<= BufferSize) // >= because of trailing zero! (!AB) + { + BufferSize = (_size + 1 + GrowSize - 1)& ~(GrowSize-1); + if (!pData) + pData = (char*)OVR_ALLOC(BufferSize); + else + pData = (char*)OVR_REALLOC(pData, BufferSize); + } +} +void StringBuffer::Resize(UPInt _size) +{ + Reserve(_size); + LengthIsSize = false; + Size = _size; + if (pData) + pData[Size] = 0; +} + +void StringBuffer::Clear() +{ + Resize(0); + /* + if (pData != pEmptyNullData) + { + OVR_FREE(pHeap, pData); + pData = pEmptyNullData; + Size = BufferSize = 0; + LengthIsSize = false; + } + */ +} +// Appends a character +void StringBuffer::AppendChar(UInt32 ch) +{ + char buff[8]; + UPInt origSize = GetSize(); + + // Converts ch into UTF8 string and fills it into buff. Also increments index according to the number of bytes + // in the UTF8 string. + SPInt srcSize = 0; + UTF8Util::EncodeChar(buff, &srcSize, ch); + OVR_ASSERT(srcSize >= 0); + + UPInt size = origSize + srcSize; + Resize(size); + memcpy(pData + origSize, buff, srcSize); +} + +// Append a string +void StringBuffer::AppendString(const wchar_t* pstr, SPInt len) +{ + if (!pstr) + return; + + SPInt srcSize = UTF8Util::GetEncodeStringSize(pstr, len); + UPInt origSize = GetSize(); + UPInt size = srcSize + origSize; + + Resize(size); + UTF8Util::EncodeString(pData + origSize, pstr, len); +} + +void StringBuffer::AppendString(const char* putf8str, SPInt utf8StrSz) +{ + if (!putf8str || !utf8StrSz) + return; + if (utf8StrSz == -1) + utf8StrSz = (SPInt)OVR_strlen(putf8str); + + UPInt origSize = GetSize(); + UPInt size = utf8StrSz + origSize; + + Resize(size); + memcpy(pData + origSize, putf8str, utf8StrSz); +} + + +void StringBuffer::operator = (const char* pstr) +{ + pstr = pstr ? pstr : ""; + UPInt size = OVR_strlen(pstr); + Resize(size); + memcpy(pData, pstr, size); +} + +void StringBuffer::operator = (const wchar_t* pstr) +{ + pstr = pstr ? pstr : L""; + UPInt size = (UPInt)UTF8Util::GetEncodeStringSize(pstr); + Resize(size); + UTF8Util::EncodeString(pData, pstr); +} + +void StringBuffer::operator = (const String& src) +{ + Resize(src.GetSize()); + memcpy(pData, src.ToCStr(), src.GetSize()); +} + + +// Inserts substr at posAt +void StringBuffer::Insert(const char* substr, UPInt posAt, SPInt len) +{ + UPInt oldSize = Size; + UPInt insertSize = (len < 0) ? OVR_strlen(substr) : (UPInt)len; + UPInt byteIndex = LengthIsSize ? posAt : + (UPInt)UTF8Util::GetByteIndex(posAt, pData, (SPInt)Size); + + OVR_ASSERT(byteIndex <= oldSize); + Reserve(oldSize + insertSize); + + memmove(pData + byteIndex + insertSize, pData + byteIndex, oldSize - byteIndex + 1); + memcpy (pData + byteIndex, substr, insertSize); + LengthIsSize = false; + Size = oldSize + insertSize; + pData[Size] = 0; +} + +// Inserts character at posAt +UPInt StringBuffer::InsertCharAt(UInt32 c, UPInt posAt) +{ + char buf[8]; + SPInt len = 0; + UTF8Util::EncodeChar(buf, &len, c); + OVR_ASSERT(len >= 0); + buf[(UPInt)len] = 0; + + Insert(buf, posAt, len); + return (UPInt)len; +} + +} // OVR diff -r 59fc487ba58e -r b069a5c27388 libovr/Src/Kernel/OVR_String.h --- a/libovr/Src/Kernel/OVR_String.h Sat Sep 14 17:51:03 2013 +0300 +++ b/libovr/Src/Kernel/OVR_String.h Sun Sep 15 04:10:05 2013 +0300 @@ -1,1 +1,645 @@ -/************************************************************************************ PublicHeader: OVR.h Filename : OVR_String.h Content : String UTF8 string implementation with copy-on-write semantics (thread-safe for assignment but not modification). Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #ifndef OVR_String_h #define OVR_String_h #include "OVR_Types.h" #include "OVR_Allocator.h" #include "OVR_UTF8Util.h" #include "OVR_Atomic.h" #include "OVR_Std.h" #include "OVR_Alg.h" namespace OVR { // ***** Classes class String; class StringBuffer; //----------------------------------------------------------------------------------- // ***** String Class // String is UTF8 based string class with copy-on-write implementation // for assignment. class String { protected: enum FlagConstants { //Flag_GetLength = 0x7FFFFFFF, // This flag is set if GetLength() == GetSize() for a string. // Avoid extra scanning is Substring and indexing logic. Flag_LengthIsSizeShift = (sizeof(UPInt)*8 - 1) }; // Internal structure to hold string data struct DataDesc { // Number of bytes. Will be the same as the number of chars if the characters // are ascii, may not be equal to number of chars in case string data is UTF8. UPInt Size; volatile SInt32 RefCount; char Data[1]; void AddRef() { AtomicOps::ExchangeAdd_NoSync(&RefCount, 1); } // Decrement ref count. This needs to be thread-safe, since // a different thread could have also decremented the ref count. // For example, if u start off with a ref count = 2. Now if u // decrement the ref count and check against 0 in different // statements, a different thread can also decrement the ref count // in between our decrement and checking against 0 and will find // the ref count = 0 and delete the object. This will lead to a crash // when context switches to our thread and we'll be trying to delete // an already deleted object. Hence decrementing the ref count and // checking against 0 needs to made an atomic operation. void Release() { if ((AtomicOps::ExchangeAdd_NoSync(&RefCount, -1) - 1) == 0) OVR_FREE(this); } static UPInt GetLengthFlagBit() { return UPInt(1) << Flag_LengthIsSizeShift; } UPInt GetSize() const { return Size & ~GetLengthFlagBit() ; } UPInt GetLengthFlag() const { return Size & GetLengthFlagBit(); } bool LengthIsSize() const { return GetLengthFlag() != 0; } }; // Heap type of the string is encoded in the lower bits. enum HeapType { HT_Global = 0, // Heap is global. HT_Local = 1, // SF::String_loc: Heap is determined based on string's address. HT_Dynamic = 2, // SF::String_temp: Heap is stored as a part of the class. HT_Mask = 3 }; union { DataDesc* pData; UPInt HeapTypeBits; }; typedef union { DataDesc* pData; UPInt HeapTypeBits; } DataDescUnion; inline HeapType GetHeapType() const { return (HeapType) (HeapTypeBits & HT_Mask); } inline DataDesc* GetData() const { DataDescUnion u; u.pData = pData; u.HeapTypeBits = (u.HeapTypeBits & ~(UPInt)HT_Mask); return u.pData; } inline void SetData(DataDesc* pdesc) { HeapType ht = GetHeapType(); pData = pdesc; OVR_ASSERT((HeapTypeBits & HT_Mask) == 0); HeapTypeBits |= ht; } DataDesc* AllocData(UPInt size, UPInt lengthIsSize); DataDesc* AllocDataCopy1(UPInt size, UPInt lengthIsSize, const char* pdata, UPInt copySize); DataDesc* AllocDataCopy2(UPInt size, UPInt lengthIsSize, const char* pdata1, UPInt copySize1, const char* pdata2, UPInt copySize2); // Special constructor to avoid data initalization when used in derived class. struct NoConstructor { }; String(const NoConstructor&) { } public: // For initializing string with dynamic buffer struct InitStruct { virtual ~InitStruct() { } virtual void InitString(char* pbuffer, UPInt size) const = 0; }; // Constructors / Destructors. String(); String(const char* data); String(const char* data1, const char* pdata2, const char* pdata3 = 0); String(const char* data, UPInt buflen); String(const String& src); String(const StringBuffer& src); String(const InitStruct& src, UPInt size); explicit String(const wchar_t* data); // Destructor (Captain Obvious guarantees!) ~String() { GetData()->Release(); } // Declaration of NullString static DataDesc NullData; // *** General Functions void Clear(); // For casting to a pointer to char. operator const char*() const { return GetData()->Data; } // Pointer to raw buffer. const char* ToCStr() const { return GetData()->Data; } // Returns number of bytes UPInt GetSize() const { return GetData()->GetSize() ; } // Tells whether or not the string is empty bool IsEmpty() const { return GetSize() == 0; } // Returns number of characters UPInt GetLength() const; // Returns character at the specified index UInt32 GetCharAt(UPInt index) const; UInt32 GetFirstCharAt(UPInt index, const char** offset) const; UInt32 GetNextChar(const char** offset) const; // Appends a character void AppendChar(UInt32 ch); // Append a string void AppendString(const wchar_t* pstr, SPInt len = -1); void AppendString(const char* putf8str, SPInt utf8StrSz = -1); // Assigned a string with dynamic data (copied through initializer). void AssignString(const InitStruct& src, UPInt size); // Assigns string with known size. void AssignString(const char* putf8str, UPInt size); // Resize the string to the new size // void Resize(UPInt _size); // Removes the character at posAt void Remove(UPInt posAt, SPInt len = 1); // Returns a String that's a substring of this. // -start is the index of the first UTF8 character you want to include. // -end is the index one past the last UTF8 character you want to include. String Substring(UPInt start, UPInt end) const; // Case-conversion String ToUpper() const; String ToLower() const; // Inserts substr at posAt String& Insert (const char* substr, UPInt posAt, SPInt len = -1); // Inserts character at posAt UPInt InsertCharAt(UInt32 c, UPInt posAt); // Inserts substr at posAt, which is an index of a character (not byte). // Of size is specified, it is in bytes. // String& Insert(const UInt32* substr, UPInt posAt, SPInt size = -1); // Get Byte index of the character at position = index UPInt GetByteIndex(UPInt index) const { return (UPInt)UTF8Util::GetByteIndex(index, GetData()->Data); } // Utility: case-insensitive string compare. stricmp() & strnicmp() are not // ANSI or POSIX, do not seem to appear in Linux. static int OVR_STDCALL CompareNoCase(const char* a, const char* b); static int OVR_STDCALL CompareNoCase(const char* a, const char* b, SPInt len); // Hash function, case-insensitive static UPInt OVR_STDCALL BernsteinHashFunctionCIS(const void* pdataIn, UPInt size, UPInt seed = 5381); // Hash function, case-sensitive static UPInt OVR_STDCALL BernsteinHashFunction(const void* pdataIn, UPInt size, UPInt seed = 5381); // ***** File path parsing helper functions. // Implemented in OVR_String_FilePath.cpp. // Absolute paths can star with: // - protocols: 'file://', 'http://' // - windows drive: 'c:\' // - UNC share name: '\\share' // - unix root '/' static bool HasAbsolutePath(const char* path); static bool HasExtension(const char* path); static bool HasProtocol(const char* path); bool HasAbsolutePath() const { return HasAbsolutePath(ToCStr()); } bool HasExtension() const { return HasExtension(ToCStr()); } bool HasProtocol() const { return HasProtocol(ToCStr()); } String GetProtocol() const; // Returns protocol, if any, with trailing '://'. String GetPath() const; // Returns path with trailing '/'. String GetFilename() const; // Returns filename, including extension. String GetExtension() const; // Returns extension with a dot. void StripProtocol(); // Strips front protocol, if any, from the string. void StripExtension(); // Strips off trailing extension. // Operators // Assignment void operator = (const char* str); void operator = (const wchar_t* str); void operator = (const String& src); void operator = (const StringBuffer& src); // Addition void operator += (const String& src); void operator += (const char* psrc) { AppendString(psrc); } void operator += (const wchar_t* psrc) { AppendString(psrc); } void operator += (char ch) { AppendChar(ch); } String operator + (const char* str) const; String operator + (const String& src) const; // Comparison bool operator == (const String& str) const { return (OVR_strcmp(GetData()->Data, str.GetData()->Data)== 0); } bool operator != (const String& str) const { return !operator == (str); } bool operator == (const char* str) const { return OVR_strcmp(GetData()->Data, str) == 0; } bool operator != (const char* str) const { return !operator == (str); } bool operator < (const char* pstr) const { return OVR_strcmp(GetData()->Data, pstr) < 0; } bool operator < (const String& str) const { return *this < str.GetData()->Data; } bool operator > (const char* pstr) const { return OVR_strcmp(GetData()->Data, pstr) > 0; } bool operator > (const String& str) const { return *this > str.GetData()->Data; } int CompareNoCase(const char* pstr) const { return CompareNoCase(GetData()->Data, pstr); } int CompareNoCase(const String& str) const { return CompareNoCase(GetData()->Data, str.ToCStr()); } // Accesses raw bytes const char& operator [] (int index) const { OVR_ASSERT(index >= 0 && (UPInt)index < GetSize()); return GetData()->Data[index]; } const char& operator [] (UPInt index) const { OVR_ASSERT(index < GetSize()); return GetData()->Data[index]; } // Case insensitive keys are used to look up insensitive string in hash tables // for SWF files with version before SWF 7. struct NoCaseKey { const String* pStr; NoCaseKey(const String &str) : pStr(&str){}; }; bool operator == (const NoCaseKey& strKey) const { return (CompareNoCase(ToCStr(), strKey.pStr->ToCStr()) == 0); } bool operator != (const NoCaseKey& strKey) const { return !(CompareNoCase(ToCStr(), strKey.pStr->ToCStr()) == 0); } // Hash functor used for strings. struct HashFunctor { UPInt operator()(const String& data) const { UPInt size = data.GetSize(); return String::BernsteinHashFunction((const char*)data, size); } }; // Case-insensitive hash functor used for strings. Supports additional // lookup based on NoCaseKey. struct NoCaseHashFunctor { UPInt operator()(const String& data) const { UPInt size = data.GetSize(); return String::BernsteinHashFunctionCIS((const char*)data, size); } UPInt operator()(const NoCaseKey& data) const { UPInt size = data.pStr->GetSize(); return String::BernsteinHashFunctionCIS((const char*)data.pStr->ToCStr(), size); } }; }; //----------------------------------------------------------------------------------- // ***** String Buffer used for Building Strings class StringBuffer { char* pData; UPInt Size; UPInt BufferSize; UPInt GrowSize; mutable bool LengthIsSize; public: // Constructors / Destructor. StringBuffer(); explicit StringBuffer(UPInt growSize); StringBuffer(const char* data); StringBuffer(const char* data, UPInt buflen); StringBuffer(const String& src); StringBuffer(const StringBuffer& src); explicit StringBuffer(const wchar_t* data); ~StringBuffer(); // Modify grow size used for growing/shrinking the buffer. UPInt GetGrowSize() const { return GrowSize; } void SetGrowSize(UPInt growSize); // *** General Functions // Does not release memory, just sets Size to 0 void Clear(); // For casting to a pointer to char. operator const char*() const { return (pData) ? pData : ""; } // Pointer to raw buffer. const char* ToCStr() const { return (pData) ? pData : ""; } // Returns number of bytes. UPInt GetSize() const { return Size ; } // Tells whether or not the string is empty. bool IsEmpty() const { return GetSize() == 0; } // Returns number of characters UPInt GetLength() const; // Returns character at the specified index UInt32 GetCharAt(UPInt index) const; UInt32 GetFirstCharAt(UPInt index, const char** offset) const; UInt32 GetNextChar(const char** offset) const; // Resize the string to the new size void Resize(UPInt _size); void Reserve(UPInt _size); // Appends a character void AppendChar(UInt32 ch); // Append a string void AppendString(const wchar_t* pstr, SPInt len = -1); void AppendString(const char* putf8str, SPInt utf8StrSz = -1); void AppendFormat(const char* format, ...); // Assigned a string with dynamic data (copied through initializer). //void AssignString(const InitStruct& src, UPInt size); // Inserts substr at posAt void Insert (const char* substr, UPInt posAt, SPInt len = -1); // Inserts character at posAt UPInt InsertCharAt(UInt32 c, UPInt posAt); // Assignment void operator = (const char* str); void operator = (const wchar_t* str); void operator = (const String& src); // Addition void operator += (const String& src) { AppendString(src.ToCStr(),src.GetSize()); } void operator += (const char* psrc) { AppendString(psrc); } void operator += (const wchar_t* psrc) { AppendString(psrc); } void operator += (char ch) { AppendChar(ch); } //String operator + (const char* str) const ; //String operator + (const String& src) const ; // Accesses raw bytes char& operator [] (int index) { OVR_ASSERT(((UPInt)index) < GetSize()); return pData[index]; } char& operator [] (UPInt index) { OVR_ASSERT(index < GetSize()); return pData[index]; } const char& operator [] (int index) const { OVR_ASSERT(((UPInt)index) < GetSize()); return pData[index]; } const char& operator [] (UPInt index) const { OVR_ASSERT(index < GetSize()); return pData[index]; } }; // // Wrapper for string data. The data must have a guaranteed // lifespan throughout the usage of the wrapper. Not intended for // cached usage. Not thread safe. // class StringDataPtr { public: StringDataPtr() : pStr(NULL), Size(0) {} StringDataPtr(const StringDataPtr& p) : pStr(p.pStr), Size(p.Size) {} StringDataPtr(const char* pstr, UPInt sz) : pStr(pstr), Size(sz) {} StringDataPtr(const char* pstr) : pStr(pstr), Size((pstr != NULL) ? OVR_strlen(pstr) : 0) {} explicit StringDataPtr(const String& str) : pStr(str.ToCStr()), Size(str.GetSize()) {} template StringDataPtr(const T (&v)[N]) : pStr(v), Size(N) {} public: const char* ToCStr() const { return pStr; } UPInt GetSize() const { return Size; } bool IsEmpty() const { return GetSize() == 0; } // value is a prefix of this string // Character's values are not compared. bool IsPrefix(const StringDataPtr& value) const { return ToCStr() == value.ToCStr() && GetSize() >= value.GetSize(); } // value is a suffix of this string // Character's values are not compared. bool IsSuffix(const StringDataPtr& value) const { return ToCStr() <= value.ToCStr() && (End()) == (value.End()); } // Find first character. // init_ind - initial index. SPInt FindChar(char c, UPInt init_ind = 0) const { for (UPInt i = init_ind; i < GetSize(); ++i) if (pStr[i] == c) return static_cast(i); return -1; } // Find last character. // init_ind - initial index. SPInt FindLastChar(char c, UPInt init_ind = ~0) const { if (init_ind == (UPInt)~0 || init_ind > GetSize()) init_ind = GetSize(); else ++init_ind; for (UPInt i = init_ind; i > 0; --i) if (pStr[i - 1] == c) return static_cast(i - 1); return -1; } // Create new object and trim size bytes from the left. StringDataPtr GetTrimLeft(UPInt size) const { // Limit trim size to the size of the string. size = Alg::PMin(GetSize(), size); return StringDataPtr(ToCStr() + size, GetSize() - size); } // Create new object and trim size bytes from the right. StringDataPtr GetTrimRight(UPInt size) const { // Limit trim to the size of the string. size = Alg::PMin(GetSize(), size); return StringDataPtr(ToCStr(), GetSize() - size); } // Create new object, which contains next token. // Useful for parsing. StringDataPtr GetNextToken(char separator = ':') const { UPInt cur_pos = 0; const char* cur_str = ToCStr(); for (; cur_pos < GetSize() && cur_str[cur_pos]; ++cur_pos) { if (cur_str[cur_pos] == separator) { break; } } return StringDataPtr(ToCStr(), cur_pos); } // Trim size bytes from the left. StringDataPtr& TrimLeft(UPInt size) { // Limit trim size to the size of the string. size = Alg::PMin(GetSize(), size); pStr += size; Size -= size; return *this; } // Trim size bytes from the right. StringDataPtr& TrimRight(UPInt size) { // Limit trim to the size of the string. size = Alg::PMin(GetSize(), size); Size -= size; return *this; } const char* Begin() const { return ToCStr(); } const char* End() const { return ToCStr() + GetSize(); } // Hash functor used string data pointers struct HashFunctor { UPInt operator()(const StringDataPtr& data) const { return String::BernsteinHashFunction(data.ToCStr(), data.GetSize()); } }; bool operator== (const StringDataPtr& data) const { return (OVR_strncmp(pStr, data.pStr, data.Size) == 0); } protected: const char* pStr; UPInt Size; }; } // OVR #endif \ No newline at end of file +/************************************************************************************ + +PublicHeader: OVR.h +Filename : OVR_String.h +Content : String UTF8 string implementation with copy-on-write semantics + (thread-safe for assignment but not modification). +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#ifndef OVR_String_h +#define OVR_String_h + +#include "OVR_Types.h" +#include "OVR_Allocator.h" +#include "OVR_UTF8Util.h" +#include "OVR_Atomic.h" +#include "OVR_Std.h" +#include "OVR_Alg.h" + +namespace OVR { + +// ***** Classes + +class String; +class StringBuffer; + + +//----------------------------------------------------------------------------------- +// ***** String Class + +// String is UTF8 based string class with copy-on-write implementation +// for assignment. + +class String +{ +protected: + + enum FlagConstants + { + //Flag_GetLength = 0x7FFFFFFF, + // This flag is set if GetLength() == GetSize() for a string. + // Avoid extra scanning is Substring and indexing logic. + Flag_LengthIsSizeShift = (sizeof(UPInt)*8 - 1) + }; + + + // Internal structure to hold string data + struct DataDesc + { + // Number of bytes. Will be the same as the number of chars if the characters + // are ascii, may not be equal to number of chars in case string data is UTF8. + UPInt Size; + volatile SInt32 RefCount; + char Data[1]; + + void AddRef() + { + AtomicOps::ExchangeAdd_NoSync(&RefCount, 1); + } + // Decrement ref count. This needs to be thread-safe, since + // a different thread could have also decremented the ref count. + // For example, if u start off with a ref count = 2. Now if u + // decrement the ref count and check against 0 in different + // statements, a different thread can also decrement the ref count + // in between our decrement and checking against 0 and will find + // the ref count = 0 and delete the object. This will lead to a crash + // when context switches to our thread and we'll be trying to delete + // an already deleted object. Hence decrementing the ref count and + // checking against 0 needs to made an atomic operation. + void Release() + { + if ((AtomicOps::ExchangeAdd_NoSync(&RefCount, -1) - 1) == 0) + OVR_FREE(this); + } + + static UPInt GetLengthFlagBit() { return UPInt(1) << Flag_LengthIsSizeShift; } + UPInt GetSize() const { return Size & ~GetLengthFlagBit() ; } + UPInt GetLengthFlag() const { return Size & GetLengthFlagBit(); } + bool LengthIsSize() const { return GetLengthFlag() != 0; } + }; + + // Heap type of the string is encoded in the lower bits. + enum HeapType + { + HT_Global = 0, // Heap is global. + HT_Local = 1, // SF::String_loc: Heap is determined based on string's address. + HT_Dynamic = 2, // SF::String_temp: Heap is stored as a part of the class. + HT_Mask = 3 + }; + + union { + DataDesc* pData; + UPInt HeapTypeBits; + }; + typedef union { + DataDesc* pData; + UPInt HeapTypeBits; + } DataDescUnion; + + inline HeapType GetHeapType() const { return (HeapType) (HeapTypeBits & HT_Mask); } + + inline DataDesc* GetData() const + { + DataDescUnion u; + u.pData = pData; + u.HeapTypeBits = (u.HeapTypeBits & ~(UPInt)HT_Mask); + return u.pData; + } + + inline void SetData(DataDesc* pdesc) + { + HeapType ht = GetHeapType(); + pData = pdesc; + OVR_ASSERT((HeapTypeBits & HT_Mask) == 0); + HeapTypeBits |= ht; + } + + + DataDesc* AllocData(UPInt size, UPInt lengthIsSize); + DataDesc* AllocDataCopy1(UPInt size, UPInt lengthIsSize, + const char* pdata, UPInt copySize); + DataDesc* AllocDataCopy2(UPInt size, UPInt lengthIsSize, + const char* pdata1, UPInt copySize1, + const char* pdata2, UPInt copySize2); + + // Special constructor to avoid data initalization when used in derived class. + struct NoConstructor { }; + String(const NoConstructor&) { } + +public: + + // For initializing string with dynamic buffer + struct InitStruct + { + virtual ~InitStruct() { } + virtual void InitString(char* pbuffer, UPInt size) const = 0; + }; + + + // Constructors / Destructors. + String(); + String(const char* data); + String(const char* data1, const char* pdata2, const char* pdata3 = 0); + String(const char* data, UPInt buflen); + String(const String& src); + String(const StringBuffer& src); + String(const InitStruct& src, UPInt size); + explicit String(const wchar_t* data); + + // Destructor (Captain Obvious guarantees!) + ~String() + { + GetData()->Release(); + } + + // Declaration of NullString + static DataDesc NullData; + + + // *** General Functions + + void Clear(); + + // For casting to a pointer to char. + operator const char*() const { return GetData()->Data; } + // Pointer to raw buffer. + const char* ToCStr() const { return GetData()->Data; } + + // Returns number of bytes + UPInt GetSize() const { return GetData()->GetSize() ; } + // Tells whether or not the string is empty + bool IsEmpty() const { return GetSize() == 0; } + + // Returns number of characters + UPInt GetLength() const; + + // Returns character at the specified index + UInt32 GetCharAt(UPInt index) const; + UInt32 GetFirstCharAt(UPInt index, const char** offset) const; + UInt32 GetNextChar(const char** offset) const; + + // Appends a character + void AppendChar(UInt32 ch); + + // Append a string + void AppendString(const wchar_t* pstr, SPInt len = -1); + void AppendString(const char* putf8str, SPInt utf8StrSz = -1); + + // Assigned a string with dynamic data (copied through initializer). + void AssignString(const InitStruct& src, UPInt size); + // Assigns string with known size. + void AssignString(const char* putf8str, UPInt size); + + // Resize the string to the new size +// void Resize(UPInt _size); + + // Removes the character at posAt + void Remove(UPInt posAt, SPInt len = 1); + + // Returns a String that's a substring of this. + // -start is the index of the first UTF8 character you want to include. + // -end is the index one past the last UTF8 character you want to include. + String Substring(UPInt start, UPInt end) const; + + // Case-conversion + String ToUpper() const; + String ToLower() const; + + // Inserts substr at posAt + String& Insert (const char* substr, UPInt posAt, SPInt len = -1); + + // Inserts character at posAt + UPInt InsertCharAt(UInt32 c, UPInt posAt); + + // Inserts substr at posAt, which is an index of a character (not byte). + // Of size is specified, it is in bytes. +// String& Insert(const UInt32* substr, UPInt posAt, SPInt size = -1); + + // Get Byte index of the character at position = index + UPInt GetByteIndex(UPInt index) const { return (UPInt)UTF8Util::GetByteIndex(index, GetData()->Data); } + + // Utility: case-insensitive string compare. stricmp() & strnicmp() are not + // ANSI or POSIX, do not seem to appear in Linux. + static int OVR_STDCALL CompareNoCase(const char* a, const char* b); + static int OVR_STDCALL CompareNoCase(const char* a, const char* b, SPInt len); + + // Hash function, case-insensitive + static UPInt OVR_STDCALL BernsteinHashFunctionCIS(const void* pdataIn, UPInt size, UPInt seed = 5381); + + // Hash function, case-sensitive + static UPInt OVR_STDCALL BernsteinHashFunction(const void* pdataIn, UPInt size, UPInt seed = 5381); + + + // ***** File path parsing helper functions. + // Implemented in OVR_String_FilePath.cpp. + + // Absolute paths can star with: + // - protocols: 'file://', 'http://' + // - windows drive: 'c:\' + // - UNC share name: '\\share' + // - unix root '/' + static bool HasAbsolutePath(const char* path); + static bool HasExtension(const char* path); + static bool HasProtocol(const char* path); + + bool HasAbsolutePath() const { return HasAbsolutePath(ToCStr()); } + bool HasExtension() const { return HasExtension(ToCStr()); } + bool HasProtocol() const { return HasProtocol(ToCStr()); } + + String GetProtocol() const; // Returns protocol, if any, with trailing '://'. + String GetPath() const; // Returns path with trailing '/'. + String GetFilename() const; // Returns filename, including extension. + String GetExtension() const; // Returns extension with a dot. + + void StripProtocol(); // Strips front protocol, if any, from the string. + void StripExtension(); // Strips off trailing extension. + + + // Operators + // Assignment + void operator = (const char* str); + void operator = (const wchar_t* str); + void operator = (const String& src); + void operator = (const StringBuffer& src); + + // Addition + void operator += (const String& src); + void operator += (const char* psrc) { AppendString(psrc); } + void operator += (const wchar_t* psrc) { AppendString(psrc); } + void operator += (char ch) { AppendChar(ch); } + String operator + (const char* str) const; + String operator + (const String& src) const; + + // Comparison + bool operator == (const String& str) const + { + return (OVR_strcmp(GetData()->Data, str.GetData()->Data)== 0); + } + + bool operator != (const String& str) const + { + return !operator == (str); + } + + bool operator == (const char* str) const + { + return OVR_strcmp(GetData()->Data, str) == 0; + } + + bool operator != (const char* str) const + { + return !operator == (str); + } + + bool operator < (const char* pstr) const + { + return OVR_strcmp(GetData()->Data, pstr) < 0; + } + + bool operator < (const String& str) const + { + return *this < str.GetData()->Data; + } + + bool operator > (const char* pstr) const + { + return OVR_strcmp(GetData()->Data, pstr) > 0; + } + + bool operator > (const String& str) const + { + return *this > str.GetData()->Data; + } + + int CompareNoCase(const char* pstr) const + { + return CompareNoCase(GetData()->Data, pstr); + } + int CompareNoCase(const String& str) const + { + return CompareNoCase(GetData()->Data, str.ToCStr()); + } + + // Accesses raw bytes + const char& operator [] (int index) const + { + OVR_ASSERT(index >= 0 && (UPInt)index < GetSize()); + return GetData()->Data[index]; + } + const char& operator [] (UPInt index) const + { + OVR_ASSERT(index < GetSize()); + return GetData()->Data[index]; + } + + + // Case insensitive keys are used to look up insensitive string in hash tables + // for SWF files with version before SWF 7. + struct NoCaseKey + { + const String* pStr; + NoCaseKey(const String &str) : pStr(&str){}; + }; + + bool operator == (const NoCaseKey& strKey) const + { + return (CompareNoCase(ToCStr(), strKey.pStr->ToCStr()) == 0); + } + bool operator != (const NoCaseKey& strKey) const + { + return !(CompareNoCase(ToCStr(), strKey.pStr->ToCStr()) == 0); + } + + // Hash functor used for strings. + struct HashFunctor + { + UPInt operator()(const String& data) const + { + UPInt size = data.GetSize(); + return String::BernsteinHashFunction((const char*)data, size); + } + }; + // Case-insensitive hash functor used for strings. Supports additional + // lookup based on NoCaseKey. + struct NoCaseHashFunctor + { + UPInt operator()(const String& data) const + { + UPInt size = data.GetSize(); + return String::BernsteinHashFunctionCIS((const char*)data, size); + } + UPInt operator()(const NoCaseKey& data) const + { + UPInt size = data.pStr->GetSize(); + return String::BernsteinHashFunctionCIS((const char*)data.pStr->ToCStr(), size); + } + }; + +}; + + +//----------------------------------------------------------------------------------- +// ***** String Buffer used for Building Strings + +class StringBuffer +{ + char* pData; + UPInt Size; + UPInt BufferSize; + UPInt GrowSize; + mutable bool LengthIsSize; + +public: + + // Constructors / Destructor. + StringBuffer(); + explicit StringBuffer(UPInt growSize); + StringBuffer(const char* data); + StringBuffer(const char* data, UPInt buflen); + StringBuffer(const String& src); + StringBuffer(const StringBuffer& src); + explicit StringBuffer(const wchar_t* data); + ~StringBuffer(); + + + // Modify grow size used for growing/shrinking the buffer. + UPInt GetGrowSize() const { return GrowSize; } + void SetGrowSize(UPInt growSize); + + + // *** General Functions + // Does not release memory, just sets Size to 0 + void Clear(); + + // For casting to a pointer to char. + operator const char*() const { return (pData) ? pData : ""; } + // Pointer to raw buffer. + const char* ToCStr() const { return (pData) ? pData : ""; } + + // Returns number of bytes. + UPInt GetSize() const { return Size ; } + // Tells whether or not the string is empty. + bool IsEmpty() const { return GetSize() == 0; } + + // Returns number of characters + UPInt GetLength() const; + + // Returns character at the specified index + UInt32 GetCharAt(UPInt index) const; + UInt32 GetFirstCharAt(UPInt index, const char** offset) const; + UInt32 GetNextChar(const char** offset) const; + + + // Resize the string to the new size + void Resize(UPInt _size); + void Reserve(UPInt _size); + + // Appends a character + void AppendChar(UInt32 ch); + + // Append a string + void AppendString(const wchar_t* pstr, SPInt len = -1); + void AppendString(const char* putf8str, SPInt utf8StrSz = -1); + void AppendFormat(const char* format, ...); + + // Assigned a string with dynamic data (copied through initializer). + //void AssignString(const InitStruct& src, UPInt size); + + // Inserts substr at posAt + void Insert (const char* substr, UPInt posAt, SPInt len = -1); + // Inserts character at posAt + UPInt InsertCharAt(UInt32 c, UPInt posAt); + + // Assignment + void operator = (const char* str); + void operator = (const wchar_t* str); + void operator = (const String& src); + + // Addition + void operator += (const String& src) { AppendString(src.ToCStr(),src.GetSize()); } + void operator += (const char* psrc) { AppendString(psrc); } + void operator += (const wchar_t* psrc) { AppendString(psrc); } + void operator += (char ch) { AppendChar(ch); } + //String operator + (const char* str) const ; + //String operator + (const String& src) const ; + + // Accesses raw bytes + char& operator [] (int index) + { + OVR_ASSERT(((UPInt)index) < GetSize()); + return pData[index]; + } + char& operator [] (UPInt index) + { + OVR_ASSERT(index < GetSize()); + return pData[index]; + } + + const char& operator [] (int index) const + { + OVR_ASSERT(((UPInt)index) < GetSize()); + return pData[index]; + } + const char& operator [] (UPInt index) const + { + OVR_ASSERT(index < GetSize()); + return pData[index]; + } +}; + + +// +// Wrapper for string data. The data must have a guaranteed +// lifespan throughout the usage of the wrapper. Not intended for +// cached usage. Not thread safe. +// +class StringDataPtr +{ +public: + StringDataPtr() : pStr(NULL), Size(0) {} + StringDataPtr(const StringDataPtr& p) + : pStr(p.pStr), Size(p.Size) {} + StringDataPtr(const char* pstr, UPInt sz) + : pStr(pstr), Size(sz) {} + StringDataPtr(const char* pstr) + : pStr(pstr), Size((pstr != NULL) ? OVR_strlen(pstr) : 0) {} + explicit StringDataPtr(const String& str) + : pStr(str.ToCStr()), Size(str.GetSize()) {} + template + StringDataPtr(const T (&v)[N]) + : pStr(v), Size(N) {} + +public: + const char* ToCStr() const { return pStr; } + UPInt GetSize() const { return Size; } + bool IsEmpty() const { return GetSize() == 0; } + + // value is a prefix of this string + // Character's values are not compared. + bool IsPrefix(const StringDataPtr& value) const + { + return ToCStr() == value.ToCStr() && GetSize() >= value.GetSize(); + } + // value is a suffix of this string + // Character's values are not compared. + bool IsSuffix(const StringDataPtr& value) const + { + return ToCStr() <= value.ToCStr() && (End()) == (value.End()); + } + + // Find first character. + // init_ind - initial index. + SPInt FindChar(char c, UPInt init_ind = 0) const + { + for (UPInt i = init_ind; i < GetSize(); ++i) + if (pStr[i] == c) + return static_cast(i); + + return -1; + } + + // Find last character. + // init_ind - initial index. + SPInt FindLastChar(char c, UPInt init_ind = ~0) const + { + if (init_ind == (UPInt)~0 || init_ind > GetSize()) + init_ind = GetSize(); + else + ++init_ind; + + for (UPInt i = init_ind; i > 0; --i) + if (pStr[i - 1] == c) + return static_cast(i - 1); + + return -1; + } + + // Create new object and trim size bytes from the left. + StringDataPtr GetTrimLeft(UPInt size) const + { + // Limit trim size to the size of the string. + size = Alg::PMin(GetSize(), size); + + return StringDataPtr(ToCStr() + size, GetSize() - size); + } + // Create new object and trim size bytes from the right. + StringDataPtr GetTrimRight(UPInt size) const + { + // Limit trim to the size of the string. + size = Alg::PMin(GetSize(), size); + + return StringDataPtr(ToCStr(), GetSize() - size); + } + + // Create new object, which contains next token. + // Useful for parsing. + StringDataPtr GetNextToken(char separator = ':') const + { + UPInt cur_pos = 0; + const char* cur_str = ToCStr(); + + for (; cur_pos < GetSize() && cur_str[cur_pos]; ++cur_pos) + { + if (cur_str[cur_pos] == separator) + { + break; + } + } + + return StringDataPtr(ToCStr(), cur_pos); + } + + // Trim size bytes from the left. + StringDataPtr& TrimLeft(UPInt size) + { + // Limit trim size to the size of the string. + size = Alg::PMin(GetSize(), size); + pStr += size; + Size -= size; + + return *this; + } + // Trim size bytes from the right. + StringDataPtr& TrimRight(UPInt size) + { + // Limit trim to the size of the string. + size = Alg::PMin(GetSize(), size); + Size -= size; + + return *this; + } + + const char* Begin() const { return ToCStr(); } + const char* End() const { return ToCStr() + GetSize(); } + + // Hash functor used string data pointers + struct HashFunctor + { + UPInt operator()(const StringDataPtr& data) const + { + return String::BernsteinHashFunction(data.ToCStr(), data.GetSize()); + } + }; + + bool operator== (const StringDataPtr& data) const + { + return (OVR_strncmp(pStr, data.pStr, data.Size) == 0); + } + +protected: + const char* pStr; + UPInt Size; +}; + +} // OVR + +#endif diff -r 59fc487ba58e -r b069a5c27388 libovr/Src/Kernel/OVR_StringHash.h --- a/libovr/Src/Kernel/OVR_StringHash.h Sat Sep 14 17:51:03 2013 +0300 +++ b/libovr/Src/Kernel/OVR_StringHash.h Sun Sep 15 04:10:05 2013 +0300 @@ -1,1 +1,89 @@ -/************************************************************************************ PublicHeader: None Filename : OVR_StringHash.h Content : String hash table used when optional case-insensitive lookup is required. Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #ifndef OVR_StringHash_h #define OVR_StringHash_h #include "OVR_String.h" #include "OVR_Hash.h" namespace OVR { //----------------------------------------------------------------------------------- // *** StringHash // This is a custom string hash table that supports case-insensitive // searches through special functions such as GetCaseInsensitive, etc. // This class is used for Flash labels, exports and other case-insensitive tables. template > class StringHash : public Hash { public: typedef U ValueType; typedef StringHash SelfType; typedef Hash BaseType; public: void operator = (const SelfType& src) { BaseType::operator = (src); } bool GetCaseInsensitive(const String& key, U* pvalue) const { String::NoCaseKey ikey(key); return BaseType::GetAlt(ikey, pvalue); } // Pointer-returning get variety. const U* GetCaseInsensitive(const String& key) const { String::NoCaseKey ikey(key); return BaseType::GetAlt(ikey); } U* GetCaseInsensitive(const String& key) { String::NoCaseKey ikey(key); return BaseType::GetAlt(ikey); } typedef typename BaseType::Iterator base_iterator; base_iterator FindCaseInsensitive(const String& key) { String::NoCaseKey ikey(key); return BaseType::FindAlt(ikey); } // Set just uses a find and assigns value if found. The key is not modified; // this behavior is identical to Flash string variable assignment. void SetCaseInsensitive(const String& key, const U& value) { base_iterator it = FindCaseInsensitive(key); if (it != BaseType::End()) { it->Second = value; } else { BaseType::Add(key, value); } } }; } // OVR #endif \ No newline at end of file +/************************************************************************************ + +PublicHeader: None +Filename : OVR_StringHash.h +Content : String hash table used when optional case-insensitive + lookup is required. +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#ifndef OVR_StringHash_h +#define OVR_StringHash_h + +#include "OVR_String.h" +#include "OVR_Hash.h" + +namespace OVR { + +//----------------------------------------------------------------------------------- +// *** StringHash + +// This is a custom string hash table that supports case-insensitive +// searches through special functions such as GetCaseInsensitive, etc. +// This class is used for Flash labels, exports and other case-insensitive tables. + +template > +class StringHash : public Hash +{ +public: + typedef U ValueType; + typedef StringHash SelfType; + typedef Hash BaseType; + +public: + + void operator = (const SelfType& src) { BaseType::operator = (src); } + + bool GetCaseInsensitive(const String& key, U* pvalue) const + { + String::NoCaseKey ikey(key); + return BaseType::GetAlt(ikey, pvalue); + } + // Pointer-returning get variety. + const U* GetCaseInsensitive(const String& key) const + { + String::NoCaseKey ikey(key); + return BaseType::GetAlt(ikey); + } + U* GetCaseInsensitive(const String& key) + { + String::NoCaseKey ikey(key); + return BaseType::GetAlt(ikey); + } + + + typedef typename BaseType::Iterator base_iterator; + + base_iterator FindCaseInsensitive(const String& key) + { + String::NoCaseKey ikey(key); + return BaseType::FindAlt(ikey); + } + + // Set just uses a find and assigns value if found. The key is not modified; + // this behavior is identical to Flash string variable assignment. + void SetCaseInsensitive(const String& key, const U& value) + { + base_iterator it = FindCaseInsensitive(key); + if (it != BaseType::End()) + { + it->Second = value; + } + else + { + BaseType::Add(key, value); + } + } +}; + +} // OVR + +#endif diff -r 59fc487ba58e -r b069a5c27388 libovr/Src/Kernel/OVR_String_FormatUtil.cpp --- a/libovr/Src/Kernel/OVR_String_FormatUtil.cpp Sat Sep 14 17:51:03 2013 +0300 +++ b/libovr/Src/Kernel/OVR_String_FormatUtil.cpp Sun Sep 15 04:10:05 2013 +0300 @@ -1,1 +1,42 @@ -/************************************************************************************ Filename : OVR_String_FormatUtil.cpp Content : String format functions. Created : February 27, 2013 Notes : Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #include "OVR_String.h" #include "OVR_Log.h" namespace OVR { void StringBuffer::AppendFormat(const char* format, ...) { va_list argList; va_start(argList, format); UPInt size = OVR_vscprintf(format, argList); va_end(argList); char* buffer = (char*) OVR_ALLOC(sizeof(char) * (size+1)); va_start(argList, format); UPInt result = OVR_vsprintf(buffer, size+1, format, argList); OVR_UNUSED1(result); va_end(argList); OVR_ASSERT_LOG(result == size, ("Error in OVR_vsprintf")); AppendString(buffer); OVR_FREE(buffer); } } // OVR \ No newline at end of file +/************************************************************************************ + +Filename : OVR_String_FormatUtil.cpp +Content : String format functions. +Created : February 27, 2013 +Notes : + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#include "OVR_String.h" +#include "OVR_Log.h" + +namespace OVR { + +void StringBuffer::AppendFormat(const char* format, ...) +{ + va_list argList; + + va_start(argList, format); + UPInt size = OVR_vscprintf(format, argList); + va_end(argList); + + char* buffer = (char*) OVR_ALLOC(sizeof(char) * (size+1)); + + va_start(argList, format); + UPInt result = OVR_vsprintf(buffer, size+1, format, argList); + OVR_UNUSED1(result); + va_end(argList); + OVR_ASSERT_LOG(result == size, ("Error in OVR_vsprintf")); + + AppendString(buffer); + + OVR_FREE(buffer); +} + +} // OVR diff -r 59fc487ba58e -r b069a5c27388 libovr/Src/Kernel/OVR_String_PathUtil.cpp --- a/libovr/Src/Kernel/OVR_String_PathUtil.cpp Sat Sep 14 17:51:03 2013 +0300 +++ b/libovr/Src/Kernel/OVR_String_PathUtil.cpp Sun Sep 15 04:10:05 2013 +0300 @@ -1,1 +1,200 @@ -/************************************************************************************ Filename : OVR_String_PathUtil.cpp Content : String filename/url helper function Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #include "OVR_String.h" #include "OVR_UTF8Util.h" namespace OVR { //-------------------------------------------------------------------- // ***** Path-Scanner helper function // Scans file path finding filename start and extension start, fills in their addess. void ScanFilePath(const char* url, const char** pfilename, const char** pext) { const char* urlStart = url; const char *filename = 0; const char *lastDot = 0; UInt32 charVal = UTF8Util::DecodeNextChar(&url); while (charVal != 0) { if ((charVal == '/') || (charVal == '\\')) { filename = url; lastDot = 0; } else if (charVal == '.') { lastDot = url - 1; } charVal = UTF8Util::DecodeNextChar(&url); } if (pfilename) { // It was a naked filename if (urlStart && (*urlStart != '.') && *urlStart) *pfilename = urlStart; else *pfilename = filename; } if (pext) { *pext = lastDot; } } // Scans till the end of protocol. Returns first character past protocol, // 0 if not found. // - protocol: 'file://', 'http://' const char* ScanPathProtocol(const char* url) { UInt32 charVal = UTF8Util::DecodeNextChar(&url); UInt32 charVal2; while (charVal != 0) { // Treat a colon followed by a slash as absolute. if (charVal == ':') { charVal2 = UTF8Util::DecodeNextChar(&url); charVal = UTF8Util::DecodeNextChar(&url); if ((charVal == '/') && (charVal2 == '\\')) return url; } charVal = UTF8Util::DecodeNextChar(&url); } return 0; } //-------------------------------------------------------------------- // ***** String Path API implementation bool String::HasAbsolutePath(const char* url) { // Absolute paths can star with: // - protocols: 'file://', 'http://' // - windows drive: 'c:\' // - UNC share name: '\\share' // - unix root '/' // On the other hand, relative paths are: // - directory: 'directory/file' // - this directory: './file' // - parent directory: '../file' // // For now, we don't parse '.' or '..' out, but instead let it be concatenated // to string and let the OS figure it out. This, however, is not good for file // name matching in library/etc, so it should be improved. if (!url || !*url) return true; // Treat empty strings as absolute. UInt32 charVal = UTF8Util::DecodeNextChar(&url); // Fist character of '/' or '\\' means absolute url. if ((charVal == '/') || (charVal == '\\')) return true; while (charVal != 0) { // Treat a colon followed by a slash as absolute. if (charVal == ':') { charVal = UTF8Util::DecodeNextChar(&url); // Protocol or windows drive. Absolute. if ((charVal == '/') || (charVal == '\\')) return true; } else if ((charVal == '/') || (charVal == '\\')) { // Not a first character (else 'if' above the loop would have caught it). // Must be a relative url. break; } charVal = UTF8Util::DecodeNextChar(&url); } // We get here for relative paths. return false; } bool String::HasExtension(const char* path) { const char* ext = 0; ScanFilePath(path, 0, &ext); return ext != 0; } bool String::HasProtocol(const char* path) { return ScanPathProtocol(path) != 0; } String String::GetPath() const { const char* filename = 0; ScanFilePath(ToCStr(), &filename, 0); // Technically we can have extra logic somewhere for paths, // such as enforcing protocol and '/' only based on flags, // but we keep it simple for now. return String(ToCStr(), filename ? (filename-ToCStr()) : GetSize()); } String String::GetProtocol() const { const char* protocolEnd = ScanPathProtocol(ToCStr()); return String(ToCStr(), protocolEnd ? (protocolEnd-ToCStr()) : 0); } String String::GetFilename() const { const char* filename = 0; ScanFilePath(ToCStr(), &filename, 0); return String(filename); } String String::GetExtension() const { const char* ext = 0; ScanFilePath(ToCStr(), 0, &ext); return String(ext); } void String::StripExtension() { const char* ext = 0; ScanFilePath(ToCStr(), 0, &ext); if (ext) { *this = String(ToCStr(), ext-ToCStr()); } } void String::StripProtocol() { const char* protocol = ScanPathProtocol(ToCStr()); if (protocol) AssignString(protocol, OVR_strlen(protocol)); } } // OVR \ No newline at end of file +/************************************************************************************ + +Filename : OVR_String_PathUtil.cpp +Content : String filename/url helper function +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#include "OVR_String.h" +#include "OVR_UTF8Util.h" + +namespace OVR { + +//-------------------------------------------------------------------- +// ***** Path-Scanner helper function + +// Scans file path finding filename start and extension start, fills in their addess. +void ScanFilePath(const char* url, const char** pfilename, const char** pext) +{ + const char* urlStart = url; + const char *filename = 0; + const char *lastDot = 0; + + UInt32 charVal = UTF8Util::DecodeNextChar(&url); + + while (charVal != 0) + { + if ((charVal == '/') || (charVal == '\\')) + { + filename = url; + lastDot = 0; + } + else if (charVal == '.') + { + lastDot = url - 1; + } + + charVal = UTF8Util::DecodeNextChar(&url); + } + + if (pfilename) + { + // It was a naked filename + if (urlStart && (*urlStart != '.') && *urlStart) + *pfilename = urlStart; + else + *pfilename = filename; + } + + if (pext) + { + *pext = lastDot; + } +} + +// Scans till the end of protocol. Returns first character past protocol, +// 0 if not found. +// - protocol: 'file://', 'http://' +const char* ScanPathProtocol(const char* url) +{ + UInt32 charVal = UTF8Util::DecodeNextChar(&url); + UInt32 charVal2; + + while (charVal != 0) + { + // Treat a colon followed by a slash as absolute. + if (charVal == ':') + { + charVal2 = UTF8Util::DecodeNextChar(&url); + charVal = UTF8Util::DecodeNextChar(&url); + if ((charVal == '/') && (charVal2 == '\\')) + return url; + } + charVal = UTF8Util::DecodeNextChar(&url); + } + return 0; +} + + +//-------------------------------------------------------------------- +// ***** String Path API implementation + +bool String::HasAbsolutePath(const char* url) +{ + // Absolute paths can star with: + // - protocols: 'file://', 'http://' + // - windows drive: 'c:\' + // - UNC share name: '\\share' + // - unix root '/' + + // On the other hand, relative paths are: + // - directory: 'directory/file' + // - this directory: './file' + // - parent directory: '../file' + // + // For now, we don't parse '.' or '..' out, but instead let it be concatenated + // to string and let the OS figure it out. This, however, is not good for file + // name matching in library/etc, so it should be improved. + + if (!url || !*url) + return true; // Treat empty strings as absolute. + + UInt32 charVal = UTF8Util::DecodeNextChar(&url); + + // Fist character of '/' or '\\' means absolute url. + if ((charVal == '/') || (charVal == '\\')) + return true; + + while (charVal != 0) + { + // Treat a colon followed by a slash as absolute. + if (charVal == ':') + { + charVal = UTF8Util::DecodeNextChar(&url); + // Protocol or windows drive. Absolute. + if ((charVal == '/') || (charVal == '\\')) + return true; + } + else if ((charVal == '/') || (charVal == '\\')) + { + // Not a first character (else 'if' above the loop would have caught it). + // Must be a relative url. + break; + } + + charVal = UTF8Util::DecodeNextChar(&url); + } + + // We get here for relative paths. + return false; +} + + +bool String::HasExtension(const char* path) +{ + const char* ext = 0; + ScanFilePath(path, 0, &ext); + return ext != 0; +} +bool String::HasProtocol(const char* path) +{ + return ScanPathProtocol(path) != 0; +} + + +String String::GetPath() const +{ + const char* filename = 0; + ScanFilePath(ToCStr(), &filename, 0); + + // Technically we can have extra logic somewhere for paths, + // such as enforcing protocol and '/' only based on flags, + // but we keep it simple for now. + return String(ToCStr(), filename ? (filename-ToCStr()) : GetSize()); +} + +String String::GetProtocol() const +{ + const char* protocolEnd = ScanPathProtocol(ToCStr()); + return String(ToCStr(), protocolEnd ? (protocolEnd-ToCStr()) : 0); +} + +String String::GetFilename() const +{ + const char* filename = 0; + ScanFilePath(ToCStr(), &filename, 0); + return String(filename); +} +String String::GetExtension() const +{ + const char* ext = 0; + ScanFilePath(ToCStr(), 0, &ext); + return String(ext); +} + +void String::StripExtension() +{ + const char* ext = 0; + ScanFilePath(ToCStr(), 0, &ext); + if (ext) + { + *this = String(ToCStr(), ext-ToCStr()); + } +} + +void String::StripProtocol() +{ + const char* protocol = ScanPathProtocol(ToCStr()); + if (protocol) + AssignString(protocol, OVR_strlen(protocol)); +} + +} // OVR diff -r 59fc487ba58e -r b069a5c27388 libovr/Src/Kernel/OVR_SysFile.cpp --- a/libovr/Src/Kernel/OVR_SysFile.cpp Sat Sep 14 17:51:03 2013 +0300 +++ b/libovr/Src/Kernel/OVR_SysFile.cpp Sun Sep 15 04:10:05 2013 +0300 @@ -1,1 +1,125 @@ -/************************************************************************** Filename : OVR_SysFile.cpp Content : File wrapper class implementation (Win32) Created : April 5, 1999 Authors : Michael Antonov Copyright : Copyright 2011 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. **************************************************************************/ #define GFILE_CXX // Standard C library (Captain Obvious guarantees!) #include #include "OVR_SysFile.h" namespace OVR { // This is - a dummy file that fails on all calls. class UnopenedFile : public File { public: UnopenedFile() { } ~UnopenedFile() { } virtual const char* GetFilePath() { return 0; } // ** File Information virtual bool IsValid() { return 0; } virtual bool IsWritable() { return 0; } // Return position / file size virtual int Tell() { return 0; } virtual SInt64 LTell() { return 0; } virtual int GetLength() { return 0; } virtual SInt64 LGetLength() { return 0; } // virtual bool Stat(FileStats *pfs) { return 0; } virtual int GetErrorCode() { return Error_FileNotFound; } // ** Stream implementation & I/O virtual int Write(const UByte *pbuffer, int numBytes) { return -1; OVR_UNUSED2(pbuffer, numBytes); } virtual int Read(UByte *pbuffer, int numBytes) { return -1; OVR_UNUSED2(pbuffer, numBytes); } virtual int SkipBytes(int numBytes) { return 0; OVR_UNUSED(numBytes); } virtual int BytesAvailable() { return 0; } virtual bool Flush() { return 0; } virtual int Seek(int offset, int origin) { return -1; OVR_UNUSED2(offset, origin); } virtual SInt64 LSeek(SInt64 offset, int origin) { return -1; OVR_UNUSED2(offset, origin); } virtual int CopyFromStream(File *pstream, int byteSize) { return -1; OVR_UNUSED2(pstream, byteSize); } virtual bool Close() { return 0; } }; // ***** System File // System file is created to access objects on file system directly // This file can refer directly to path // ** Constructor SysFile::SysFile() : DelegatedFile(0) { pFile = *new UnopenedFile; } File* FileFILEOpen(const String& path, int flags, int mode); // Opens a file SysFile::SysFile(const String& path, int flags, int mode) : DelegatedFile(0) { Open(path, flags, mode); } // ** Open & management // Will fail if file's already open bool SysFile::Open(const String& path, int flags, int mode) { pFile = *FileFILEOpen(path, flags, mode); if ((!pFile) || (!pFile->IsValid())) { pFile = *new UnopenedFile; return 0; } //pFile = *OVR_NEW DelegatedFile(pFile); // MA Testing if (flags & Open_Buffered) pFile = *new BufferedFile(pFile); return 1; } // ** Overrides int SysFile::GetErrorCode() { return pFile ? pFile->GetErrorCode() : Error_FileNotFound; } // Overrides to provide re-open support bool SysFile::IsValid() { return pFile && pFile->IsValid(); } bool SysFile::Close() { if (IsValid()) { DelegatedFile::Close(); pFile = *new UnopenedFile; return 1; } return 0; } } // OVR \ No newline at end of file +/************************************************************************** + +Filename : OVR_SysFile.cpp +Content : File wrapper class implementation (Win32) + +Created : April 5, 1999 +Authors : Michael Antonov + +Copyright : Copyright 2011 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +**************************************************************************/ + +#define GFILE_CXX + +// Standard C library (Captain Obvious guarantees!) +#include + +#include "OVR_SysFile.h" + +namespace OVR { + +// This is - a dummy file that fails on all calls. + +class UnopenedFile : public File +{ +public: + UnopenedFile() { } + ~UnopenedFile() { } + + virtual const char* GetFilePath() { return 0; } + + // ** File Information + virtual bool IsValid() { return 0; } + virtual bool IsWritable() { return 0; } + + // Return position / file size + virtual int Tell() { return 0; } + virtual SInt64 LTell() { return 0; } + virtual int GetLength() { return 0; } + virtual SInt64 LGetLength() { return 0; } + +// virtual bool Stat(FileStats *pfs) { return 0; } + virtual int GetErrorCode() { return Error_FileNotFound; } + + // ** Stream implementation & I/O + virtual int Write(const UByte *pbuffer, int numBytes) { return -1; OVR_UNUSED2(pbuffer, numBytes); } + virtual int Read(UByte *pbuffer, int numBytes) { return -1; OVR_UNUSED2(pbuffer, numBytes); } + virtual int SkipBytes(int numBytes) { return 0; OVR_UNUSED(numBytes); } + virtual int BytesAvailable() { return 0; } + virtual bool Flush() { return 0; } + virtual int Seek(int offset, int origin) { return -1; OVR_UNUSED2(offset, origin); } + virtual SInt64 LSeek(SInt64 offset, int origin) { return -1; OVR_UNUSED2(offset, origin); } + + virtual int CopyFromStream(File *pstream, int byteSize) { return -1; OVR_UNUSED2(pstream, byteSize); } + virtual bool Close() { return 0; } +}; + + + +// ***** System File + +// System file is created to access objects on file system directly +// This file can refer directly to path + +// ** Constructor +SysFile::SysFile() : DelegatedFile(0) +{ + pFile = *new UnopenedFile; +} + +File* FileFILEOpen(const String& path, int flags, int mode); + +// Opens a file +SysFile::SysFile(const String& path, int flags, int mode) : DelegatedFile(0) +{ + Open(path, flags, mode); +} + + +// ** Open & management +// Will fail if file's already open +bool SysFile::Open(const String& path, int flags, int mode) +{ + pFile = *FileFILEOpen(path, flags, mode); + if ((!pFile) || (!pFile->IsValid())) + { + pFile = *new UnopenedFile; + return 0; + } + //pFile = *OVR_NEW DelegatedFile(pFile); // MA Testing + if (flags & Open_Buffered) + pFile = *new BufferedFile(pFile); + return 1; +} + + +// ** Overrides + +int SysFile::GetErrorCode() +{ + return pFile ? pFile->GetErrorCode() : Error_FileNotFound; +} + + +// Overrides to provide re-open support +bool SysFile::IsValid() +{ + return pFile && pFile->IsValid(); +} +bool SysFile::Close() +{ + if (IsValid()) + { + DelegatedFile::Close(); + pFile = *new UnopenedFile; + return 1; + } + return 0; +} + +} // OVR diff -r 59fc487ba58e -r b069a5c27388 libovr/Src/Kernel/OVR_SysFile.h --- a/libovr/Src/Kernel/OVR_SysFile.h Sat Sep 14 17:51:03 2013 +0300 +++ b/libovr/Src/Kernel/OVR_SysFile.h Sun Sep 15 04:10:05 2013 +0300 @@ -1,1 +1,93 @@ -/************************************************************************************ PublicHeader: Kernel Filename : OVR_SysFile.h Content : Header for all internal file management - functions and structures to be inherited by OS specific subclasses. Created : September 19, 2012 Notes : Notes : errno may not be preserved across use of GBaseFile member functions : Directories cannot be deleted while files opened from them are in use (For the GetFullName function) Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #ifndef OVR_SysFile_h #define OVR_SysFile_h #include "OVR_File.h" namespace OVR { // ***** Declared classes class SysFile; //----------------------------------------------------------------------------------- // *** File Statistics // This class contents are similar to _stat, providing // creation, modify and other information about the file. struct FileStat { // No change or create time because they are not available on most systems SInt64 ModifyTime; SInt64 AccessTime; SInt64 FileSize; bool operator== (const FileStat& stat) const { return ( (ModifyTime == stat.ModifyTime) && (AccessTime == stat.AccessTime) && (FileSize == stat.FileSize) ); } }; //----------------------------------------------------------------------------------- // *** System File // System file is created to access objects on file system directly // This file can refer directly to path. // System file can be open & closed several times; however, such use is not recommended // This class is realy a wrapper around an implementation of File interface for a // particular platform. class SysFile : public DelegatedFile { protected: SysFile(const SysFile &source) : DelegatedFile () { OVR_UNUSED(source); } public: // ** Constructor SysFile(); // Opens a file SysFile(const String& path, int flags = Open_Read|Open_Buffered, int mode = Mode_ReadWrite); // ** Open & management bool Open(const String& path, int flags = Open_Read|Open_Buffered, int mode = Mode_ReadWrite); OVR_FORCE_INLINE bool Create(const String& path, int mode = Mode_ReadWrite) { return Open(path, Open_ReadWrite|Open_Create, mode); } // Helper function: obtain file statistics information. In GFx, this is used to detect file changes. // Return 0 if function failed, most likely because the file doesn't exist. static bool OVR_CDECL GetFileStat(FileStat* pfileStats, const String& path); // ** Overrides // Overridden to provide re-open support virtual int GetErrorCode(); virtual bool IsValid(); virtual bool Close(); }; } // Scaleform #endif \ No newline at end of file +/************************************************************************************ + +PublicHeader: Kernel +Filename : OVR_SysFile.h +Content : Header for all internal file management - functions and structures + to be inherited by OS specific subclasses. +Created : September 19, 2012 +Notes : + +Notes : errno may not be preserved across use of GBaseFile member functions + : Directories cannot be deleted while files opened from them are in use + (For the GetFullName function) + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#ifndef OVR_SysFile_h +#define OVR_SysFile_h + +#include "OVR_File.h" + +namespace OVR { + +// ***** Declared classes +class SysFile; + +//----------------------------------------------------------------------------------- +// *** File Statistics + +// This class contents are similar to _stat, providing +// creation, modify and other information about the file. +struct FileStat +{ + // No change or create time because they are not available on most systems + SInt64 ModifyTime; + SInt64 AccessTime; + SInt64 FileSize; + + bool operator== (const FileStat& stat) const + { + return ( (ModifyTime == stat.ModifyTime) && + (AccessTime == stat.AccessTime) && + (FileSize == stat.FileSize) ); + } +}; + +//----------------------------------------------------------------------------------- +// *** System File + +// System file is created to access objects on file system directly +// This file can refer directly to path. +// System file can be open & closed several times; however, such use is not recommended +// This class is realy a wrapper around an implementation of File interface for a +// particular platform. + +class SysFile : public DelegatedFile +{ +protected: + SysFile(const SysFile &source) : DelegatedFile () { OVR_UNUSED(source); } +public: + + // ** Constructor + SysFile(); + // Opens a file + SysFile(const String& path, int flags = Open_Read|Open_Buffered, int mode = Mode_ReadWrite); + + // ** Open & management + bool Open(const String& path, int flags = Open_Read|Open_Buffered, int mode = Mode_ReadWrite); + + OVR_FORCE_INLINE bool Create(const String& path, int mode = Mode_ReadWrite) + { return Open(path, Open_ReadWrite|Open_Create, mode); } + + // Helper function: obtain file statistics information. In GFx, this is used to detect file changes. + // Return 0 if function failed, most likely because the file doesn't exist. + static bool OVR_CDECL GetFileStat(FileStat* pfileStats, const String& path); + + // ** Overrides + // Overridden to provide re-open support + virtual int GetErrorCode(); + + virtual bool IsValid(); + + virtual bool Close(); +}; + +} // Scaleform + +#endif diff -r 59fc487ba58e -r b069a5c27388 libovr/Src/Kernel/OVR_System.cpp --- a/libovr/Src/Kernel/OVR_System.cpp Sat Sep 14 17:51:03 2013 +0300 +++ b/libovr/Src/Kernel/OVR_System.cpp Sun Sep 15 04:10:05 2013 +0300 @@ -1,1 +1,70 @@ -/************************************************************************************ Filename : OVR_System.cpp Content : General kernel initialization/cleanup, including that of the memory allocator. Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #include "OVR_System.h" #include "OVR_Threads.h" #include "OVR_Timer.h" namespace OVR { // ***** OVR::System Implementation // Initializes System core, installing allocator. void System::Init(Log* log, Allocator *palloc) { if (!Allocator::GetInstance()) { Log::SetGlobalLog(log); Timer::initializeTimerSystem(); Allocator::setInstance(palloc); } else { OVR_DEBUG_LOG(("System::Init failed - duplicate call.")); } } void System::Destroy() { if (Allocator::GetInstance()) { // Wait for all threads to finish; this must be done so that memory // allocator and all destructors finalize correctly. #ifdef OVR_ENABLE_THREADS Thread::FinishAllThreads(); #endif // Shutdown heap and destroy SysAlloc singleton, if any. Allocator::GetInstance()->onSystemShutdown(); Allocator::setInstance(0); Timer::shutdownTimerSystem(); Log::SetGlobalLog(Log::GetDefaultLog()); } else { OVR_DEBUG_LOG(("System::Destroy failed - System not initialized.")); } } // Returns 'true' if system was properly initialized. bool System::IsInitialized() { return Allocator::GetInstance() != 0; } } // OVR \ No newline at end of file +/************************************************************************************ + +Filename : OVR_System.cpp +Content : General kernel initialization/cleanup, including that + of the memory allocator. +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#include "OVR_System.h" +#include "OVR_Threads.h" +#include "OVR_Timer.h" + +namespace OVR { + +// ***** OVR::System Implementation + +// Initializes System core, installing allocator. +void System::Init(Log* log, Allocator *palloc) +{ + if (!Allocator::GetInstance()) + { + Log::SetGlobalLog(log); + Timer::initializeTimerSystem(); + Allocator::setInstance(palloc); + } + else + { + OVR_DEBUG_LOG(("System::Init failed - duplicate call.")); + } +} + +void System::Destroy() +{ + if (Allocator::GetInstance()) + { + // Wait for all threads to finish; this must be done so that memory + // allocator and all destructors finalize correctly. +#ifdef OVR_ENABLE_THREADS + Thread::FinishAllThreads(); +#endif + + // Shutdown heap and destroy SysAlloc singleton, if any. + Allocator::GetInstance()->onSystemShutdown(); + Allocator::setInstance(0); + + Timer::shutdownTimerSystem(); + Log::SetGlobalLog(Log::GetDefaultLog()); + } + else + { + OVR_DEBUG_LOG(("System::Destroy failed - System not initialized.")); + } +} + +// Returns 'true' if system was properly initialized. +bool System::IsInitialized() +{ + return Allocator::GetInstance() != 0; +} + +} // OVR + diff -r 59fc487ba58e -r b069a5c27388 libovr/Src/Kernel/OVR_System.h --- a/libovr/Src/Kernel/OVR_System.h Sat Sep 14 17:51:03 2013 +0300 +++ b/libovr/Src/Kernel/OVR_System.h Sun Sep 15 04:10:05 2013 +0300 @@ -1,1 +1,67 @@ -/************************************************************************************ PublicHeader: OVR Filename : OVR_System.h Content : General kernel initialization/cleanup, including that of the memory allocator. Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #ifndef OVR_System_h #define OVR_System_h #include "OVR_Allocator.h" #include "OVR_Log.h" namespace OVR { // ***** System Core Initialization class // System initialization must take place before any other OVR_Kernel objects are used; // this is done my calling System::Init(). Among other things, this is necessary to // initialize the memory allocator. Similarly, System::Destroy must be // called before program exist for proper cleanup. Both of these tasks can be achieved by // simply creating System object first, allowing its constructor/destructor do the work. // TBD: Require additional System class for Oculus Rift API? class System { public: // System constructor expects allocator to be specified, if it is being substituted. System(Log* log = Log::ConfigureDefaultLog(LogMask_Debug), Allocator* palloc = DefaultAllocator::InitSystemSingleton()) { Init(log, palloc); } ~System() { Destroy(); } // Returns 'true' if system was properly initialized. static bool OVR_CDECL IsInitialized(); // Initializes System core. Users can override memory implementation by passing // a different Allocator here. static void OVR_CDECL Init(Log* log = Log::ConfigureDefaultLog(LogMask_Debug), Allocator *palloc = DefaultAllocator::InitSystemSingleton()); // De-initializes System more, finalizing the threading system and destroying // the global memory allocator. static void OVR_CDECL Destroy(); }; } // OVR #endif \ No newline at end of file +/************************************************************************************ + +PublicHeader: OVR +Filename : OVR_System.h +Content : General kernel initialization/cleanup, including that + of the memory allocator. +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#ifndef OVR_System_h +#define OVR_System_h + +#include "OVR_Allocator.h" +#include "OVR_Log.h" + +namespace OVR { + +// ***** System Core Initialization class + +// System initialization must take place before any other OVR_Kernel objects are used; +// this is done my calling System::Init(). Among other things, this is necessary to +// initialize the memory allocator. Similarly, System::Destroy must be +// called before program exist for proper cleanup. Both of these tasks can be achieved by +// simply creating System object first, allowing its constructor/destructor do the work. + +// TBD: Require additional System class for Oculus Rift API? + +class System +{ +public: + + // System constructor expects allocator to be specified, if it is being substituted. + System(Log* log = Log::ConfigureDefaultLog(LogMask_Debug), + Allocator* palloc = DefaultAllocator::InitSystemSingleton()) + { + Init(log, palloc); + } + + ~System() + { + Destroy(); + } + + // Returns 'true' if system was properly initialized. + static bool OVR_CDECL IsInitialized(); + + // Initializes System core. Users can override memory implementation by passing + // a different Allocator here. + static void OVR_CDECL Init(Log* log = Log::ConfigureDefaultLog(LogMask_Debug), + Allocator *palloc = DefaultAllocator::InitSystemSingleton()); + + // De-initializes System more, finalizing the threading system and destroying + // the global memory allocator. + static void OVR_CDECL Destroy(); +}; + +} // OVR + +#endif diff -r 59fc487ba58e -r b069a5c27388 libovr/Src/Kernel/OVR_Threads.h --- a/libovr/Src/Kernel/OVR_Threads.h Sat Sep 14 17:51:03 2013 +0300 +++ b/libovr/Src/Kernel/OVR_Threads.h Sun Sep 15 04:10:05 2013 +0300 @@ -1,1 +1,396 @@ -/************************************************************************************ PublicHeader: None Filename : OVR_Threads.h Content : Contains thread-related (safe) functionality Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #ifndef OVR_Threads_h #define OVR_Threads_h #include "OVR_Types.h" #include "OVR_Atomic.h" #include "OVR_RefCount.h" #include "OVR_Array.h" // Defines the infinite wait delay timeout #define OVR_WAIT_INFINITE 0xFFFFFFFF // To be defined in the project configuration options #ifdef OVR_ENABLE_THREADS namespace OVR { //----------------------------------------------------------------------------------- // ****** Declared classes // Declared with thread support only class Mutex; class WaitCondition; class Event; // Implementation forward declarations class MutexImpl; class WaitConditionImpl; //----------------------------------------------------------------------------------- // ***** Mutex // Mutex class represents a system Mutex synchronization object that provides access // serialization between different threads, allowing one thread mutually exclusive access // to a resource. Mutex is more heavy-weight then Lock, but supports WaitCondition. class Mutex { friend class WaitConditionImpl; friend class MutexImpl; MutexImpl *pImpl; public: // Constructor/destructor Mutex(bool recursive = 1); ~Mutex(); // Locking functions void DoLock(); bool TryLock(); void Unlock(); // Returns 1 if the mutes is currently locked by another thread // Returns 0 if the mutex is not locked by another thread, and can therefore be acquired. bool IsLockedByAnotherThread(); // Locker class; Used for automatic locking of a mutex withing scope class Locker { public: Mutex *pMutex; Locker(Mutex *pmutex) { pMutex = pmutex; pMutex->DoLock(); } ~Locker() { pMutex->Unlock(); } }; }; //----------------------------------------------------------------------------------- // ***** WaitCondition /* WaitCondition is a synchronization primitive that can be used to implement what is known as a monitor. Dependent threads wait on a wait condition by calling Wait(), and get woken up by other threads that call Notify() or NotifyAll(). The unique feature of this class is that it provides an atomic way of first releasing a Mutex, and then starting a wait on a wait condition. If both the mutex and the wait condition are associated with the same resource, this ensures that any condition checked for while the mutex was locked does not change before the wait on the condition is actually initiated. */ class WaitCondition { friend class WaitConditionImpl; // Internal implementation structure WaitConditionImpl *pImpl; public: // Constructor/destructor WaitCondition(); ~WaitCondition(); // Release mutex and wait for condition. The mutex is re-aquired after the wait. // Delay is specified in milliseconds (1/1000 of a second). bool Wait(Mutex *pmutex, unsigned delay = OVR_WAIT_INFINITE); // Notify a condition, releasing at one object waiting void Notify(); // Notify a condition, releasing all objects waiting void NotifyAll(); }; //----------------------------------------------------------------------------------- // ***** Event // Event is a wait-able synchronization object similar to Windows event. // Event can be waited on until it's signaled by another thread calling // either SetEvent or PulseEvent. class Event { // Event state, its mutex and the wait condition volatile bool State; volatile bool Temporary; mutable Mutex StateMutex; WaitCondition StateWaitCondition; void updateState(bool newState, bool newTemp, bool mustNotify); public: Event(bool setInitially = 0) : State(setInitially), Temporary(false) { } ~Event() { } // Wait on an event condition until it is set // Delay is specified in milliseconds (1/1000 of a second). bool Wait(unsigned delay = OVR_WAIT_INFINITE); // Set an event, releasing objects waiting on it void SetEvent() { updateState(true, false, true); } // Reset an event, un-signaling it void ResetEvent() { updateState(false, false, false); } // Set and then reset an event once a waiter is released. // If threads are already waiting, they will be notified and released // If threads are not waiting, the event is set until the first thread comes in void PulseEvent() { updateState(true, true, true); } }; //----------------------------------------------------------------------------------- // ***** Thread class // ThreadId uniquely identifies a thread; returned by GetCurrentThreadId() and // Thread::GetThreadId. typedef void* ThreadId; // *** Thread flags // Indicates that the thread is has been started, i.e. Start method has been called, and threads // OnExit() method has not yet been called/returned. #define OVR_THREAD_STARTED 0x01 // This flag is set once the thread has ran, and finished. #define OVR_THREAD_FINISHED 0x02 // This flag is set temporarily if this thread was started suspended. It is used internally. #define OVR_THREAD_START_SUSPENDED 0x08 // This flag is used to ask a thread to exit. Message driven threads will usually check this flag // and finish once it is set. #define OVR_THREAD_EXIT 0x10 class Thread : public RefCountBase { // NOTE: Waitable must be the first base since it implements RefCountImpl. public: // *** Callback functions, can be used instead of overriding Run // Run function prototypes. // Thread function and user handle passed to it, executed by the default // Thread::Run implementation if not null. typedef int (*ThreadFn)(Thread *pthread, void* h); // Thread ThreadFunction1 is executed if not 0, otherwise ThreadFunction2 is tried ThreadFn ThreadFunction; // User handle passes to a thread void* UserHandle; // Thread state to start a thread with enum ThreadState { NotRunning = 0, Running = 1, Suspended = 2 }; // Thread priority enum ThreadPriority { CriticalPriority, HighestPriority, AboveNormalPriority, NormalPriority, BelowNormalPriority, LowestPriority, IdlePriority, }; // Thread constructor parameters struct CreateParams { CreateParams(ThreadFn func = 0, void* hand = 0, UPInt ssize = 128 * 1024, int proc = -1, ThreadState state = NotRunning, ThreadPriority prior = NormalPriority) : threadFunction(func), userHandle(hand), stackSize(ssize), processor(proc), initialState(state), priority(prior) {} ThreadFn threadFunction; // Thread function void* userHandle; // User handle passes to a thread UPInt stackSize; // Thread stack size int processor; // Thread hardware processor ThreadState initialState; // ThreadPriority priority; // Thread priority }; // *** Constructors // A default constructor always creates a thread in NotRunning state, because // the derived class has not yet been initialized. The derived class can call Start explicitly. // "processor" parameter specifies which hardware processor this thread will be run on. // -1 means OS decides this. Implemented only on Win32 Thread(UPInt stackSize = 128 * 1024, int processor = -1); // Constructors that initialize the thread with a pointer to function. // An option to start a thread is available, but it should not be used if classes are derived from Thread. // "processor" parameter specifies which hardware processor this thread will be run on. // -1 means OS decides this. Implemented only on Win32 Thread(ThreadFn threadFunction, void* userHandle = 0, UPInt stackSize = 128 * 1024, int processor = -1, ThreadState initialState = NotRunning); // Constructors that initialize the thread with a create parameters structure. explicit Thread(const CreateParams& params); // Destructor. virtual ~Thread(); // Waits for all Threads to finish; should be called only from the root // application thread. Once this function returns, we know that all other // thread's references to Thread object have been released. static void OVR_CDECL FinishAllThreads(); // *** Overridable Run function for thread processing // - returning from this method will end the execution of the thread // - return value is usually 0 for success virtual int Run(); // Called after return/exit function virtual void OnExit(); // *** Thread management // Starts the thread if its not already running // - internally sets up the threading and calls Run() // - initial state can either be Running or Suspended, NotRunning will just fail and do nothing // - returns the exit code virtual bool Start(ThreadState initialState = Running); // Quits with an exit code virtual void Exit(int exitCode=0); // Suspend the thread until resumed // Returns 1 for success, 0 for failure. bool Suspend(); // Resumes currently suspended thread // Returns 1 for success, 0 for failure. bool Resume(); // Static function to return a pointer to the current thread //static Thread* GetThread(); // *** Thread status query functions bool GetExitFlag() const; void SetExitFlag(bool exitFlag); // Determines whether the thread was running and is now finished bool IsFinished() const; // Determines if the thread is currently suspended bool IsSuspended() const; // Returns current thread state ThreadState GetThreadState() const; // Returns the number of available CPUs on the system static int GetCPUCount(); // Returns the thread exit code. Exit code is initialized to 0, // and set to the return value if Run function after the thread is finished. inline int GetExitCode() const { return ExitCode; } // Returns an OS handle #if defined(OVR_OS_WIN32) void* GetOSHandle() const { return ThreadHandle; } #else pthread_t GetOSHandle() const { return ThreadHandle; } #endif #if defined(OVR_OS_WIN32) ThreadId GetThreadId() const { return IdValue; } #else ThreadId GetThreadId() const { return (ThreadId)GetOSHandle(); } #endif static int GetOSPriority(ThreadPriority); // *** Sleep // Sleep secs seconds static bool Sleep(unsigned secs); // Sleep msecs milliseconds static bool MSleep(unsigned msecs); // *** Debugging functionality #if defined(OVR_OS_WIN32) virtual void SetThreadName( const char* name ); #else virtual void SetThreadName( const char* name ) { OVR_UNUSED(name); } #endif private: #if defined(OVR_OS_WIN32) friend unsigned WINAPI Thread_Win32StartFn(void *pthread); #else friend void *Thread_PthreadStartFn(void * phandle); static int InitAttr; static pthread_attr_t Attr; #endif protected: // Thread state flags AtomicInt ThreadFlags; AtomicInt SuspendCount; UPInt StackSize; // Hardware processor which this thread is running on. int Processor; ThreadPriority Priority; #if defined(OVR_OS_WIN32) void* ThreadHandle; volatile ThreadId IdValue; // System-specific cleanup function called from destructor void CleanupSystemThread(); #else pthread_t ThreadHandle; #endif // Exit code of the thread, as returned by Run. int ExitCode; // Internal run function. int PRun(); // Finishes the thread and releases internal reference to it. void FinishAndRelease(); void Init(const CreateParams& params); // Protected copy constructor Thread(const Thread &source) { OVR_UNUSED(source); } }; // Returns the unique Id of a thread it is called on, intended for // comparison purposes. ThreadId GetCurrentThreadId(); } // OVR #endif // OVR_ENABLE_THREADS #endif // OVR_Threads_h \ No newline at end of file +/************************************************************************************ + +PublicHeader: None +Filename : OVR_Threads.h +Content : Contains thread-related (safe) functionality +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ +#ifndef OVR_Threads_h +#define OVR_Threads_h + +#include "OVR_Types.h" +#include "OVR_Atomic.h" +#include "OVR_RefCount.h" +#include "OVR_Array.h" + +// Defines the infinite wait delay timeout +#define OVR_WAIT_INFINITE 0xFFFFFFFF + +// To be defined in the project configuration options +#ifdef OVR_ENABLE_THREADS + + +namespace OVR { + +//----------------------------------------------------------------------------------- +// ****** Declared classes + +// Declared with thread support only +class Mutex; +class WaitCondition; +class Event; +// Implementation forward declarations +class MutexImpl; +class WaitConditionImpl; + + + +//----------------------------------------------------------------------------------- +// ***** Mutex + +// Mutex class represents a system Mutex synchronization object that provides access +// serialization between different threads, allowing one thread mutually exclusive access +// to a resource. Mutex is more heavy-weight then Lock, but supports WaitCondition. + +class Mutex +{ + friend class WaitConditionImpl; + friend class MutexImpl; + + MutexImpl *pImpl; + +public: + // Constructor/destructor + Mutex(bool recursive = 1); + ~Mutex(); + + // Locking functions + void DoLock(); + bool TryLock(); + void Unlock(); + + // Returns 1 if the mutes is currently locked by another thread + // Returns 0 if the mutex is not locked by another thread, and can therefore be acquired. + bool IsLockedByAnotherThread(); + + // Locker class; Used for automatic locking of a mutex withing scope + class Locker + { + public: + Mutex *pMutex; + Locker(Mutex *pmutex) + { pMutex = pmutex; pMutex->DoLock(); } + ~Locker() + { pMutex->Unlock(); } + }; +}; + + +//----------------------------------------------------------------------------------- +// ***** WaitCondition + +/* + WaitCondition is a synchronization primitive that can be used to implement what is known as a monitor. + Dependent threads wait on a wait condition by calling Wait(), and get woken up by other threads that + call Notify() or NotifyAll(). + + The unique feature of this class is that it provides an atomic way of first releasing a Mutex, and then + starting a wait on a wait condition. If both the mutex and the wait condition are associated with the same + resource, this ensures that any condition checked for while the mutex was locked does not change before + the wait on the condition is actually initiated. +*/ + +class WaitCondition +{ + friend class WaitConditionImpl; + // Internal implementation structure + WaitConditionImpl *pImpl; + +public: + // Constructor/destructor + WaitCondition(); + ~WaitCondition(); + + // Release mutex and wait for condition. The mutex is re-aquired after the wait. + // Delay is specified in milliseconds (1/1000 of a second). + bool Wait(Mutex *pmutex, unsigned delay = OVR_WAIT_INFINITE); + + // Notify a condition, releasing at one object waiting + void Notify(); + // Notify a condition, releasing all objects waiting + void NotifyAll(); +}; + + +//----------------------------------------------------------------------------------- +// ***** Event + +// Event is a wait-able synchronization object similar to Windows event. +// Event can be waited on until it's signaled by another thread calling +// either SetEvent or PulseEvent. + +class Event +{ + // Event state, its mutex and the wait condition + volatile bool State; + volatile bool Temporary; + mutable Mutex StateMutex; + WaitCondition StateWaitCondition; + + void updateState(bool newState, bool newTemp, bool mustNotify); + +public: + Event(bool setInitially = 0) : State(setInitially), Temporary(false) { } + ~Event() { } + + // Wait on an event condition until it is set + // Delay is specified in milliseconds (1/1000 of a second). + bool Wait(unsigned delay = OVR_WAIT_INFINITE); + + // Set an event, releasing objects waiting on it + void SetEvent() + { updateState(true, false, true); } + + // Reset an event, un-signaling it + void ResetEvent() + { updateState(false, false, false); } + + // Set and then reset an event once a waiter is released. + // If threads are already waiting, they will be notified and released + // If threads are not waiting, the event is set until the first thread comes in + void PulseEvent() + { updateState(true, true, true); } +}; + + +//----------------------------------------------------------------------------------- +// ***** Thread class + +// ThreadId uniquely identifies a thread; returned by GetCurrentThreadId() and +// Thread::GetThreadId. +typedef void* ThreadId; + + +// *** Thread flags + +// Indicates that the thread is has been started, i.e. Start method has been called, and threads +// OnExit() method has not yet been called/returned. +#define OVR_THREAD_STARTED 0x01 +// This flag is set once the thread has ran, and finished. +#define OVR_THREAD_FINISHED 0x02 +// This flag is set temporarily if this thread was started suspended. It is used internally. +#define OVR_THREAD_START_SUSPENDED 0x08 +// This flag is used to ask a thread to exit. Message driven threads will usually check this flag +// and finish once it is set. +#define OVR_THREAD_EXIT 0x10 + + +class Thread : public RefCountBase +{ // NOTE: Waitable must be the first base since it implements RefCountImpl. + +public: + + // *** Callback functions, can be used instead of overriding Run + + // Run function prototypes. + // Thread function and user handle passed to it, executed by the default + // Thread::Run implementation if not null. + typedef int (*ThreadFn)(Thread *pthread, void* h); + + // Thread ThreadFunction1 is executed if not 0, otherwise ThreadFunction2 is tried + ThreadFn ThreadFunction; + // User handle passes to a thread + void* UserHandle; + + // Thread state to start a thread with + enum ThreadState + { + NotRunning = 0, + Running = 1, + Suspended = 2 + }; + + // Thread priority + enum ThreadPriority + { + CriticalPriority, + HighestPriority, + AboveNormalPriority, + NormalPriority, + BelowNormalPriority, + LowestPriority, + IdlePriority, + }; + + // Thread constructor parameters + struct CreateParams + { + CreateParams(ThreadFn func = 0, void* hand = 0, UPInt ssize = 128 * 1024, + int proc = -1, ThreadState state = NotRunning, ThreadPriority prior = NormalPriority) + : threadFunction(func), userHandle(hand), stackSize(ssize), + processor(proc), initialState(state), priority(prior) {} + ThreadFn threadFunction; // Thread function + void* userHandle; // User handle passes to a thread + UPInt stackSize; // Thread stack size + int processor; // Thread hardware processor + ThreadState initialState; // + ThreadPriority priority; // Thread priority + }; + + // *** Constructors + + // A default constructor always creates a thread in NotRunning state, because + // the derived class has not yet been initialized. The derived class can call Start explicitly. + // "processor" parameter specifies which hardware processor this thread will be run on. + // -1 means OS decides this. Implemented only on Win32 + Thread(UPInt stackSize = 128 * 1024, int processor = -1); + // Constructors that initialize the thread with a pointer to function. + // An option to start a thread is available, but it should not be used if classes are derived from Thread. + // "processor" parameter specifies which hardware processor this thread will be run on. + // -1 means OS decides this. Implemented only on Win32 + Thread(ThreadFn threadFunction, void* userHandle = 0, UPInt stackSize = 128 * 1024, + int processor = -1, ThreadState initialState = NotRunning); + // Constructors that initialize the thread with a create parameters structure. + explicit Thread(const CreateParams& params); + + // Destructor. + virtual ~Thread(); + + // Waits for all Threads to finish; should be called only from the root + // application thread. Once this function returns, we know that all other + // thread's references to Thread object have been released. + static void OVR_CDECL FinishAllThreads(); + + + // *** Overridable Run function for thread processing + + // - returning from this method will end the execution of the thread + // - return value is usually 0 for success + virtual int Run(); + // Called after return/exit function + virtual void OnExit(); + + + // *** Thread management + + // Starts the thread if its not already running + // - internally sets up the threading and calls Run() + // - initial state can either be Running or Suspended, NotRunning will just fail and do nothing + // - returns the exit code + virtual bool Start(ThreadState initialState = Running); + + // Quits with an exit code + virtual void Exit(int exitCode=0); + + // Suspend the thread until resumed + // Returns 1 for success, 0 for failure. + bool Suspend(); + // Resumes currently suspended thread + // Returns 1 for success, 0 for failure. + bool Resume(); + + // Static function to return a pointer to the current thread + //static Thread* GetThread(); + + + // *** Thread status query functions + + bool GetExitFlag() const; + void SetExitFlag(bool exitFlag); + + // Determines whether the thread was running and is now finished + bool IsFinished() const; + // Determines if the thread is currently suspended + bool IsSuspended() const; + // Returns current thread state + ThreadState GetThreadState() const; + + // Returns the number of available CPUs on the system + static int GetCPUCount(); + + // Returns the thread exit code. Exit code is initialized to 0, + // and set to the return value if Run function after the thread is finished. + inline int GetExitCode() const { return ExitCode; } + // Returns an OS handle +#if defined(OVR_OS_WIN32) + void* GetOSHandle() const { return ThreadHandle; } +#else + pthread_t GetOSHandle() const { return ThreadHandle; } +#endif + +#if defined(OVR_OS_WIN32) + ThreadId GetThreadId() const { return IdValue; } +#else + ThreadId GetThreadId() const { return (ThreadId)GetOSHandle(); } +#endif + + static int GetOSPriority(ThreadPriority); + // *** Sleep + + // Sleep secs seconds + static bool Sleep(unsigned secs); + // Sleep msecs milliseconds + static bool MSleep(unsigned msecs); + + + // *** Debugging functionality +#if defined(OVR_OS_WIN32) + virtual void SetThreadName( const char* name ); +#else + virtual void SetThreadName( const char* name ) { OVR_UNUSED(name); } +#endif + +private: +#if defined(OVR_OS_WIN32) + friend unsigned WINAPI Thread_Win32StartFn(void *pthread); + +#else + friend void *Thread_PthreadStartFn(void * phandle); + + static int InitAttr; + static pthread_attr_t Attr; +#endif + +protected: + // Thread state flags + AtomicInt ThreadFlags; + AtomicInt SuspendCount; + UPInt StackSize; + + // Hardware processor which this thread is running on. + int Processor; + ThreadPriority Priority; + +#if defined(OVR_OS_WIN32) + void* ThreadHandle; + volatile ThreadId IdValue; + + // System-specific cleanup function called from destructor + void CleanupSystemThread(); + +#else + pthread_t ThreadHandle; +#endif + + // Exit code of the thread, as returned by Run. + int ExitCode; + + // Internal run function. + int PRun(); + // Finishes the thread and releases internal reference to it. + void FinishAndRelease(); + + void Init(const CreateParams& params); + + // Protected copy constructor + Thread(const Thread &source) { OVR_UNUSED(source); } + +}; + +// Returns the unique Id of a thread it is called on, intended for +// comparison purposes. +ThreadId GetCurrentThreadId(); + + +} // OVR + +#endif // OVR_ENABLE_THREADS +#endif // OVR_Threads_h diff -r 59fc487ba58e -r b069a5c27388 libovr/Src/Kernel/OVR_Timer.cpp --- a/libovr/Src/Kernel/OVR_Timer.cpp Sat Sep 14 17:51:03 2013 +0300 +++ b/libovr/Src/Kernel/OVR_Timer.cpp Sun Sep 15 04:10:05 2013 +0300 @@ -1,1 +1,156 @@ -/************************************************************************************ Filename : OVR_Timer.cpp Content : Provides static functions for precise timing Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #include "OVR_Timer.h" #if defined (OVR_OS_WIN32) #include #else #include #endif namespace OVR { //----------------------------------------------------------------------------------- // ***** Timer Class UInt64 Timer::GetProfileTicks() { return (GetRawTicks() * MksPerSecond) / GetRawFrequency(); } double Timer::GetProfileSeconds() { static UInt64 StartTime = GetProfileTicks(); return TicksToSeconds(GetProfileTicks()-StartTime); } //------------------------------------------------------------------------ // *** Win32 Specific Timer #if (defined (OVR_OS_WIN32)) CRITICAL_SECTION WinAPI_GetTimeCS; volatile UInt32 WinAPI_OldTime = 0; volatile UInt32 WinAPI_WrapCounter = 0; UInt32 Timer::GetTicksMs() { return timeGetTime(); } UInt64 Timer::GetTicks() { DWORD ticks = timeGetTime(); UInt64 result; // On Win32 QueryPerformanceFrequency is unreliable due to SMP and // performance levels, so use this logic to detect wrapping and track // high bits. ::EnterCriticalSection(&WinAPI_GetTimeCS); if (WinAPI_OldTime > ticks) WinAPI_WrapCounter++; WinAPI_OldTime = ticks; result = (UInt64(WinAPI_WrapCounter) << 32) | ticks; ::LeaveCriticalSection(&WinAPI_GetTimeCS); return result * MksPerMs; } UInt64 Timer::GetRawTicks() { LARGE_INTEGER li; QueryPerformanceCounter(&li); return li.QuadPart; } UInt64 Timer::GetRawFrequency() { static UInt64 perfFreq = 0; if (perfFreq == 0) { LARGE_INTEGER freq; QueryPerformanceFrequency(&freq); perfFreq = freq.QuadPart; } return perfFreq; } void Timer::initializeTimerSystem() { timeBeginPeriod(1); InitializeCriticalSection(&WinAPI_GetTimeCS); } void Timer::shutdownTimerSystem() { DeleteCriticalSection(&WinAPI_GetTimeCS); timeEndPeriod(1); } #else // !OVR_OS_WIN32 //------------------------------------------------------------------------ // *** Standard OS Timer UInt32 Timer::GetTicksMs() { return (UInt32)(GetProfileTicks() / 1000); } // The profile ticks implementation is just fine for a normal timer. UInt64 Timer::GetTicks() { return GetProfileTicks(); } void Timer::initializeTimerSystem() { } void Timer::shutdownTimerSystem() { } UInt64 Timer::GetRawTicks() { // TODO: prefer rdtsc when available? // Return microseconds. struct timeval tv; UInt64 result; gettimeofday(&tv, 0); result = (UInt64)tv.tv_sec * 1000000; result += tv.tv_usec; return result; } UInt64 Timer::GetRawFrequency() { return MksPerSecond; } #endif // !OVR_OS_WIN32 } // OVR \ No newline at end of file +/************************************************************************************ + +Filename : OVR_Timer.cpp +Content : Provides static functions for precise timing +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#include "OVR_Timer.h" + +#if defined (OVR_OS_WIN32) +#include + +#else +#include +#endif + +namespace OVR { + +//----------------------------------------------------------------------------------- +// ***** Timer Class + +UInt64 Timer::GetProfileTicks() +{ + return (GetRawTicks() * MksPerSecond) / GetRawFrequency(); +} +double Timer::GetProfileSeconds() +{ + static UInt64 StartTime = GetProfileTicks(); + return TicksToSeconds(GetProfileTicks()-StartTime); +} + + +//------------------------------------------------------------------------ +// *** Win32 Specific Timer + +#if (defined (OVR_OS_WIN32)) + +CRITICAL_SECTION WinAPI_GetTimeCS; +volatile UInt32 WinAPI_OldTime = 0; +volatile UInt32 WinAPI_WrapCounter = 0; + + +UInt32 Timer::GetTicksMs() +{ + return timeGetTime(); +} + +UInt64 Timer::GetTicks() +{ + DWORD ticks = timeGetTime(); + UInt64 result; + + // On Win32 QueryPerformanceFrequency is unreliable due to SMP and + // performance levels, so use this logic to detect wrapping and track + // high bits. + ::EnterCriticalSection(&WinAPI_GetTimeCS); + + if (WinAPI_OldTime > ticks) + WinAPI_WrapCounter++; + WinAPI_OldTime = ticks; + + result = (UInt64(WinAPI_WrapCounter) << 32) | ticks; + ::LeaveCriticalSection(&WinAPI_GetTimeCS); + + return result * MksPerMs; +} + +UInt64 Timer::GetRawTicks() +{ + LARGE_INTEGER li; + QueryPerformanceCounter(&li); + return li.QuadPart; +} + +UInt64 Timer::GetRawFrequency() +{ + static UInt64 perfFreq = 0; + if (perfFreq == 0) + { + LARGE_INTEGER freq; + QueryPerformanceFrequency(&freq); + perfFreq = freq.QuadPart; + } + return perfFreq; +} + +void Timer::initializeTimerSystem() +{ + timeBeginPeriod(1); + InitializeCriticalSection(&WinAPI_GetTimeCS); + +} +void Timer::shutdownTimerSystem() +{ + DeleteCriticalSection(&WinAPI_GetTimeCS); + timeEndPeriod(1); +} + +#else // !OVR_OS_WIN32 + + +//------------------------------------------------------------------------ +// *** Standard OS Timer + +UInt32 Timer::GetTicksMs() +{ + return (UInt32)(GetProfileTicks() / 1000); +} +// The profile ticks implementation is just fine for a normal timer. +UInt64 Timer::GetTicks() +{ + return GetProfileTicks(); +} + +void Timer::initializeTimerSystem() +{ +} +void Timer::shutdownTimerSystem() +{ +} + +UInt64 Timer::GetRawTicks() +{ + // TODO: prefer rdtsc when available? + + // Return microseconds. + struct timeval tv; + UInt64 result; + + gettimeofday(&tv, 0); + + result = (UInt64)tv.tv_sec * 1000000; + result += tv.tv_usec; + + return result; +} + +UInt64 Timer::GetRawFrequency() +{ + return MksPerSecond; +} + +#endif // !OVR_OS_WIN32 + + + +} // OVR + diff -r 59fc487ba58e -r b069a5c27388 libovr/Src/Kernel/OVR_Timer.h --- a/libovr/Src/Kernel/OVR_Timer.h Sat Sep 14 17:51:03 2013 +0300 +++ b/libovr/Src/Kernel/OVR_Timer.h Sun Sep 15 04:10:05 2013 +0300 @@ -1,1 +1,100 @@ -/************************************************************************************ PublicHeader: OVR Filename : OVR_Timer.h Content : Provides static functions for precise timing Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #ifndef OVR_Timer_h #define OVR_Timer_h #include "OVR_Types.h" namespace OVR { //----------------------------------------------------------------------------------- // ***** Timer // Timer class defines a family of static functions used for application // timing and profiling. class Timer { public: enum { MsPerSecond = 1000, // Milliseconds in one second. MksPerMs = 1000, // Microseconds in one millisecond. MksPerSecond = MsPerSecond * MksPerMs }; // ***** Timing APIs for Application // These APIs should be used to guide animation and other program functions // that require precision. // Returns ticks in milliseconds, as a 32-bit number. May wrap around every // 49.2 days. Use either time difference of two values of GetTicks to avoid // wrap-around. GetTicksMs may perform better then GetTicks. static UInt32 OVR_STDCALL GetTicksMs(); // GetTicks returns general-purpose high resolution application timer value, // measured in microseconds (mks, or 1/1000000 of a second). The actual precision // is system-specific and may be much lower, such as 1 ms. static UInt64 OVR_STDCALL GetTicks(); // ***** Profiling APIs. // These functions should be used for profiling, but may have system specific // artifacts that make them less appropriate for general system use. // On Win32, for example these rely on QueryPerformanceConter may have // problems with thread-core switching and power modes. // Return a hi-res timer value in mks (1/1000000 of a sec). // Generally you want to call this at the start and end of an // operation, and pass the difference to // TicksToSeconds() to find out how long the operation took. static UInt64 OVR_STDCALL GetProfileTicks(); // More convenient zero-based profile timer in seconds. First call initializes // the "zero" value; future calls return the difference. Not thread safe for first call. // Due to low precision of Double, may malfunction after long runtime. static double OVR_STDCALL GetProfileSeconds(); // Get the raw cycle counter value, providing the maximum possible timer resolution. static UInt64 OVR_STDCALL GetRawTicks(); static UInt64 OVR_STDCALL GetRawFrequency(); // ***** Tick and time unit conversion. // Convert micro-second ticks value into seconds value. static inline double TicksToSeconds(UInt64 ticks) { return static_cast(ticks) * (1.0 / (double)MksPerSecond); } // Convert Raw or frequency-unit ticks to seconds based on specified frequency. static inline double RawTicksToSeconds(UInt64 rawTicks, UInt64 rawFrequency) { return static_cast(rawTicks) * rawFrequency; } private: friend class System; // System called during program startup/shutdown. static void initializeTimerSystem(); static void shutdownTimerSystem(); }; } // Scaleform::Timer #endif \ No newline at end of file +/************************************************************************************ + +PublicHeader: OVR +Filename : OVR_Timer.h +Content : Provides static functions for precise timing +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#ifndef OVR_Timer_h +#define OVR_Timer_h + +#include "OVR_Types.h" + +namespace OVR { + +//----------------------------------------------------------------------------------- +// ***** Timer + +// Timer class defines a family of static functions used for application +// timing and profiling. + +class Timer +{ +public: + enum { + MsPerSecond = 1000, // Milliseconds in one second. + MksPerMs = 1000, // Microseconds in one millisecond. + MksPerSecond = MsPerSecond * MksPerMs + }; + + + // ***** Timing APIs for Application + // These APIs should be used to guide animation and other program functions + // that require precision. + + // Returns ticks in milliseconds, as a 32-bit number. May wrap around every + // 49.2 days. Use either time difference of two values of GetTicks to avoid + // wrap-around. GetTicksMs may perform better then GetTicks. + static UInt32 OVR_STDCALL GetTicksMs(); + + // GetTicks returns general-purpose high resolution application timer value, + // measured in microseconds (mks, or 1/1000000 of a second). The actual precision + // is system-specific and may be much lower, such as 1 ms. + static UInt64 OVR_STDCALL GetTicks(); + + + // ***** Profiling APIs. + // These functions should be used for profiling, but may have system specific + // artifacts that make them less appropriate for general system use. + // On Win32, for example these rely on QueryPerformanceConter may have + // problems with thread-core switching and power modes. + + // Return a hi-res timer value in mks (1/1000000 of a sec). + // Generally you want to call this at the start and end of an + // operation, and pass the difference to + // TicksToSeconds() to find out how long the operation took. + static UInt64 OVR_STDCALL GetProfileTicks(); + + // More convenient zero-based profile timer in seconds. First call initializes + // the "zero" value; future calls return the difference. Not thread safe for first call. + // Due to low precision of Double, may malfunction after long runtime. + static double OVR_STDCALL GetProfileSeconds(); + + // Get the raw cycle counter value, providing the maximum possible timer resolution. + static UInt64 OVR_STDCALL GetRawTicks(); + static UInt64 OVR_STDCALL GetRawFrequency(); + + + // ***** Tick and time unit conversion. + + // Convert micro-second ticks value into seconds value. + static inline double TicksToSeconds(UInt64 ticks) + { + return static_cast(ticks) * (1.0 / (double)MksPerSecond); + } + // Convert Raw or frequency-unit ticks to seconds based on specified frequency. + static inline double RawTicksToSeconds(UInt64 rawTicks, UInt64 rawFrequency) + { + return static_cast(rawTicks) * rawFrequency; + } + +private: + friend class System; + // System called during program startup/shutdown. + static void initializeTimerSystem(); + static void shutdownTimerSystem(); +}; + + +} // Scaleform::Timer + +#endif diff -r 59fc487ba58e -r b069a5c27388 libovr/Src/Kernel/OVR_Types.h --- a/libovr/Src/Kernel/OVR_Types.h Sat Sep 14 17:51:03 2013 +0300 +++ b/libovr/Src/Kernel/OVR_Types.h Sun Sep 15 04:10:05 2013 +0300 @@ -1,1 +1,456 @@ -/************************************************************************************ PublicHeader: OVR.h Filename : OVR_Types.h Content : Standard library defines and simple types Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #ifndef OVR_Types_H #define OVR_Types_H //----------------------------------------------------------------------------------- // ****** Operating System // // Type definitions exist for the following operating systems: (OVR_OS_x) // // WIN32 - Win32 (Windows 95/98/ME and Windows NT/2000/XP) // DARWIN - Darwin OS (Mac OS X) // LINUX - Linux // ANDROID - Android // IPHONE - iPhone #if (defined(__APPLE__) && (defined(__GNUC__) ||\ defined(__xlC__) || defined(__xlc__))) || defined(__MACOS__) # if (defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) || defined(__IPHONE_OS_VERSION_MIN_REQUIRED)) # define OVR_OS_IPHONE # else # define OVR_OS_DARWIN # define OVR_OS_MAC # endif #elif (defined(WIN64) || defined(_WIN64) || defined(__WIN64__)) # define OVR_OS_WIN32 #elif (defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)) # define OVR_OS_WIN32 #elif defined(__linux__) || defined(__linux) # define OVR_OS_LINUX #else # define OVR_OS_OTHER #endif #if defined(ANDROID) # define OVR_OS_ANDROID #endif //----------------------------------------------------------------------------------- // ***** CPU Architecture // // The following CPUs are defined: (OVR_CPU_x) // // X86 - x86 (IA-32) // X86_64 - x86_64 (amd64) // PPC - PowerPC // PPC64 - PowerPC64 // MIPS - MIPS // OTHER - CPU for which no special support is present or needed #if defined(__x86_64__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64__) # define OVR_CPU_X86_64 # define OVR_64BIT_POINTERS #elif defined(__i386__) || defined(OVR_OS_WIN32) # define OVR_CPU_X86 #elif defined(__powerpc64__) # define OVR_CPU_PPC64 #elif defined(__ppc__) # define OVR_CPU_PPC #elif defined(__mips__) || defined(__MIPSEL__) # define OVR_CPU_MIPS #elif defined(__arm__) # define OVR_CPU_ARM #else # define OVR_CPU_OTHER #endif //----------------------------------------------------------------------------------- // ***** Co-Processor Architecture // // The following co-processors are defined: (OVR_CPU_x) // // SSE - Available on all modern x86 processors. // Altivec - Available on all modern ppc processors. // Neon - Available on some armv7+ processors. #if defined(__SSE__) || defined(OVR_OS_WIN32) # define OVR_CPU_SSE #endif // __SSE__ #if defined( __ALTIVEC__ ) # define OVR_CPU_ALTIVEC #endif // __ALTIVEC__ #if defined(__ARM_NEON__) # define OVR_CPU_ARM_NEON #endif // __ARM_NEON__ //----------------------------------------------------------------------------------- // ***** Compiler // // The following compilers are defined: (OVR_CC_x) // // MSVC - Microsoft Visual C/C++ // INTEL - Intel C++ for Linux / Windows // GNU - GNU C++ // ARM - ARM C/C++ #if defined(__INTEL_COMPILER) // Intel 4.0 = 400 // Intel 5.0 = 500 // Intel 6.0 = 600 // Intel 8.0 = 800 // Intel 9.0 = 900 # define OVR_CC_INTEL __INTEL_COMPILER #elif defined(_MSC_VER) // MSVC 5.0 = 1100 // MSVC 6.0 = 1200 // MSVC 7.0 (VC2002) = 1300 // MSVC 7.1 (VC2003) = 1310 // MSVC 8.0 (VC2005) = 1400 // MSVC 9.0 (VC2008) = 1500 // MSVC 10.0 (VC2010) = 1600 # define OVR_CC_MSVC _MSC_VER #elif defined(__GNUC__) # define OVR_CC_GNU #elif defined(__CC_ARM) # define OVR_CC_ARM #else # error "Oculus does not support this Compiler" #endif //----------------------------------------------------------------------------------- // ***** Compiler Warnings // Disable MSVC warnings #if defined(OVR_CC_MSVC) # pragma warning(disable : 4127) // Inconsistent dll linkage # pragma warning(disable : 4530) // Exception handling # if (OVR_CC_MSVC<1300) # pragma warning(disable : 4514) // Unreferenced inline function has been removed # pragma warning(disable : 4710) // Function not inlined # pragma warning(disable : 4714) // _force_inline not inlined # pragma warning(disable : 4786) // Debug variable name longer than 255 chars # endif // (OVR_CC_MSVC<1300) #endif // (OVR_CC_MSVC) // *** Linux Unicode - must come before Standard Includes #ifdef OVR_OS_LINUX // Use glibc unicode functions on linux. # ifndef _GNU_SOURCE # define _GNU_SOURCE # endif #endif //----------------------------------------------------------------------------------- // ***** Standard Includes // #include #include #include // MSVC Based Memory Leak checking - for now #if defined(OVR_CC_MSVC) && defined(OVR_BUILD_DEBUG) # define _CRTDBG_MAP_ALLOC # include # include // Uncomment this to help debug memory leaks under Visual Studio in OVR apps only. // This shouldn't be defined in customer releases. # ifndef OVR_DEFINE_NEW # define OVR_DEFINE_NEW new(__FILE__, __LINE__) # define new OVR_DEFINE_NEW # endif #endif //----------------------------------------------------------------------------------- // ***** Type definitions for Common Systems namespace OVR { typedef char Char; // Pointer-sized integer typedef size_t UPInt; typedef ptrdiff_t SPInt; #if defined(OVR_OS_WIN32) typedef char SByte; // 8 bit Integer (Byte) typedef unsigned char UByte; typedef short SInt16; // 16 bit Integer (Word) typedef unsigned short UInt16; typedef long SInt32; // 32 bit Integer typedef unsigned long UInt32; typedef __int64 SInt64; // 64 bit Integer (QWord) typedef unsigned __int64 UInt64; #elif defined(OVR_OS_MAC) || defined(OVR_OS_IPHONE) || defined(OVR_CC_GNU) typedef int SByte __attribute__((__mode__ (__QI__))); typedef unsigned int UByte __attribute__((__mode__ (__QI__))); typedef int SInt16 __attribute__((__mode__ (__HI__))); typedef unsigned int UInt16 __attribute__((__mode__ (__HI__))); typedef int SInt32 __attribute__((__mode__ (__SI__))); typedef unsigned int UInt32 __attribute__((__mode__ (__SI__))); typedef int SInt64 __attribute__((__mode__ (__DI__))); typedef unsigned int UInt64 __attribute__((__mode__ (__DI__))); #else #include typedef int8_t SByte; typedef uint8_t UByte; typedef int16_t SInt16; typedef uint16_t UInt16; typedef int32_t SInt32; typedef uint32_t UInt32; typedef int64_t SInt64; typedef uint64_t UInt64; #endif // ***** BaseTypes Namespace // BaseTypes namespace is explicitly declared to allow base types to be used // by customers directly without other contents of OVR namespace. // // Its is expected that GFx samples will declare 'using namespace OVR::BaseTypes' // to allow using these directly without polluting the target scope with other // OVR declarations, such as Ptr<>, String or Mutex. namespace BaseTypes { using OVR::UPInt; using OVR::SPInt; using OVR::UByte; using OVR::SByte; using OVR::UInt16; using OVR::SInt16; using OVR::UInt32; using OVR::SInt32; using OVR::UInt64; using OVR::SInt64; } // OVR::BaseTypes } // OVR //----------------------------------------------------------------------------------- // ***** Macro Definitions // // We define the following: // // OVR_BYTE_ORDER - Defined to either OVR_LITTLE_ENDIAN or OVR_BIG_ENDIAN // OVR_FORCE_INLINE - Forces inline expansion of function // OVR_ASM - Assembly language prefix // OVR_STR - Prefixes string with L"" if building unicode // // OVR_STDCALL - Use stdcall calling convention (Pascal arg order) // OVR_CDECL - Use cdecl calling convention (C argument order) // OVR_FASTCALL - Use fastcall calling convention (registers) // // Byte order constants, OVR_BYTE_ORDER is defined to be one of these. #define OVR_LITTLE_ENDIAN 1 #define OVR_BIG_ENDIAN 2 // Force inline substitute - goes before function declaration #if defined(OVR_CC_MSVC) # define OVR_FORCE_INLINE __forceinline #elif defined(OVR_CC_GNU) # define OVR_FORCE_INLINE __attribute__((always_inline)) inline #else # define OVR_FORCE_INLINE inline #endif // OVR_CC_MSVC #if defined(OVR_OS_WIN32) // ***** Win32 // Byte order #define OVR_BYTE_ORDER OVR_LITTLE_ENDIAN // Calling convention - goes after function return type but before function name #ifdef __cplusplus_cli # define OVR_FASTCALL __stdcall #else # define OVR_FASTCALL __fastcall #endif #define OVR_STDCALL __stdcall #define OVR_CDECL __cdecl // Assembly macros #if defined(OVR_CC_MSVC) # define OVR_ASM _asm #else # define OVR_ASM asm #endif // (OVR_CC_MSVC) #ifdef UNICODE # define OVR_STR(str) L##str #else # define OVR_STR(str) str #endif // UNICODE #else // **** Standard systems #if (defined(BYTE_ORDER) && (BYTE_ORDER == BIG_ENDIAN))|| \ (defined(_BYTE_ORDER) && (_BYTE_ORDER == _BIG_ENDIAN)) # define OVR_BYTE_ORDER OVR_BIG_ENDIAN #elif (defined(__ARMEB__) || defined(OVR_CPU_PPC) || defined(OVR_CPU_PPC64)) # define OVR_BYTE_ORDER OVR_BIG_ENDIAN #else # define OVR_BYTE_ORDER OVR_LITTLE_ENDIAN #endif // Assembly macros #define OVR_ASM __asm__ #define OVR_ASM_PROC(procname) OVR_ASM #define OVR_ASM_END OVR_ASM // Calling convention - goes after function return type but before function name #define OVR_FASTCALL #define OVR_STDCALL #define OVR_CDECL #endif // defined(OVR_OS_WIN32) //----------------------------------------------------------------------------------- // ***** OVR_DEBUG_BREAK, OVR_ASSERT // // If not in debug build, macros do nothing #ifndef OVR_BUILD_DEBUG # define OVR_DEBUG_BREAK ((void)0) # define OVR_ASSERT(p) ((void)0) #else // Microsoft Win32 specific debugging support #if defined(OVR_OS_WIN32) # ifdef OVR_CPU_X86 # if defined(__cplusplus_cli) # define OVR_DEBUG_BREAK do { __debugbreak(); } while(0) # elif defined(OVR_CC_GNU) # define OVR_DEBUG_BREAK do { OVR_ASM("int $3\n\t"); } while(0) # else # define OVR_DEBUG_BREAK do { OVR_ASM int 3 } while (0) # endif # else # define OVR_DEBUG_BREAK do { __debugbreak(); } while(0) # endif // Unix specific debugging support #elif defined(OVR_CPU_X86) || defined(OVR_CPU_X86_64) # define OVR_DEBUG_BREAK do { OVR_ASM("int $3\n\t"); } while(0) #else # define OVR_DEBUG_BREAK do { *((int *) 0) = 1; } while(0) #endif // This will cause compiler breakpoint #define OVR_ASSERT(p) do { if (!(p)) { OVR_DEBUG_BREAK; } } while(0) #endif // OVR_BUILD_DEBUG // Compile-time assert; produces compiler error if condition is false #define OVR_COMPILER_ASSERT(x) { int zero = 0; switch(zero) {case 0: case x:;} } //----------------------------------------------------------------------------------- // ***** OVR_UNUSED - Unused Argument handling // Macro to quiet compiler warnings about unused parameters/variables. #if defined(OVR_CC_GNU) # define OVR_UNUSED(a) do {__typeof__ (&a) __attribute__ ((unused)) __tmp = &a; } while(0) #else # define OVR_UNUSED(a) (a) #endif #define OVR_UNUSED1(a1) OVR_UNUSED(a1) #define OVR_UNUSED2(a1,a2) OVR_UNUSED(a1); OVR_UNUSED(a2) #define OVR_UNUSED3(a1,a2,a3) OVR_UNUSED2(a1,a2); OVR_UNUSED(a3) #define OVR_UNUSED4(a1,a2,a3,a4) OVR_UNUSED3(a1,a2,a3); OVR_UNUSED(a4) #define OVR_UNUSED5(a1,a2,a3,a4,a5) OVR_UNUSED4(a1,a2,a3,a4); OVR_UNUSED(a5) #define OVR_UNUSED6(a1,a2,a3,a4,a5,a6) OVR_UNUSED4(a1,a2,a3,a4); OVR_UNUSED2(a5,a6) #define OVR_UNUSED7(a1,a2,a3,a4,a5,a6,a7) OVR_UNUSED4(a1,a2,a3,a4); OVR_UNUSED3(a5,a6,a7) #define OVR_UNUSED8(a1,a2,a3,a4,a5,a6,a7,a8) OVR_UNUSED4(a1,a2,a3,a4); OVR_UNUSED4(a5,a6,a7,a8) #define OVR_UNUSED9(a1,a2,a3,a4,a5,a6,a7,a8,a9) OVR_UNUSED4(a1,a2,a3,a4); OVR_UNUSED5(a5,a6,a7,a8,a9) //----------------------------------------------------------------------------------- // ***** Configuration Macros // SF Build type #ifdef OVR_BUILD_DEBUG # define OVR_BUILD_STRING "Debug" #else # define OVR_BUILD_STRING "Release" #endif //// Enables SF Debugging information //# define OVR_BUILD_DEBUG // OVR_DEBUG_STATEMENT injects a statement only in debug builds. // OVR_DEBUG_SELECT injects first argument in debug builds, second argument otherwise. #ifdef OVR_BUILD_DEBUG #define OVR_DEBUG_STATEMENT(s) s #define OVR_DEBUG_SELECT(d, nd) d #else #define OVR_DEBUG_STATEMENT(s) #define OVR_DEBUG_SELECT(d, nd) nd #endif #define OVR_ENABLE_THREADS // // Prevents OVR from defining new within // type macros, so developers can override // new using the #define new new(...) trick // - used with OVR_DEFINE_NEW macro //# define OVR_BUILD_DEFINE_NEW // #endif // OVR_Types_h \ No newline at end of file +/************************************************************************************ + +PublicHeader: OVR.h +Filename : OVR_Types.h +Content : Standard library defines and simple types +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#ifndef OVR_Types_H +#define OVR_Types_H + +//----------------------------------------------------------------------------------- +// ****** Operating System +// +// Type definitions exist for the following operating systems: (OVR_OS_x) +// +// WIN32 - Win32 (Windows 95/98/ME and Windows NT/2000/XP) +// DARWIN - Darwin OS (Mac OS X) +// LINUX - Linux +// ANDROID - Android +// IPHONE - iPhone + +#if (defined(__APPLE__) && (defined(__GNUC__) ||\ + defined(__xlC__) || defined(__xlc__))) || defined(__MACOS__) +# if (defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) || defined(__IPHONE_OS_VERSION_MIN_REQUIRED)) +# define OVR_OS_IPHONE +# else +# define OVR_OS_DARWIN +# define OVR_OS_MAC +# endif +#elif (defined(WIN64) || defined(_WIN64) || defined(__WIN64__)) +# define OVR_OS_WIN32 +#elif (defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)) +# define OVR_OS_WIN32 +#elif defined(__linux__) || defined(__linux) +# define OVR_OS_LINUX +#else +# define OVR_OS_OTHER +#endif + +#if defined(ANDROID) +# define OVR_OS_ANDROID +#endif + + +//----------------------------------------------------------------------------------- +// ***** CPU Architecture +// +// The following CPUs are defined: (OVR_CPU_x) +// +// X86 - x86 (IA-32) +// X86_64 - x86_64 (amd64) +// PPC - PowerPC +// PPC64 - PowerPC64 +// MIPS - MIPS +// OTHER - CPU for which no special support is present or needed + + +#if defined(__x86_64__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64__) +# define OVR_CPU_X86_64 +# define OVR_64BIT_POINTERS +#elif defined(__i386__) || defined(OVR_OS_WIN32) +# define OVR_CPU_X86 +#elif defined(__powerpc64__) +# define OVR_CPU_PPC64 +#elif defined(__ppc__) +# define OVR_CPU_PPC +#elif defined(__mips__) || defined(__MIPSEL__) +# define OVR_CPU_MIPS +#elif defined(__arm__) +# define OVR_CPU_ARM +#else +# define OVR_CPU_OTHER +#endif + +//----------------------------------------------------------------------------------- +// ***** Co-Processor Architecture +// +// The following co-processors are defined: (OVR_CPU_x) +// +// SSE - Available on all modern x86 processors. +// Altivec - Available on all modern ppc processors. +// Neon - Available on some armv7+ processors. + +#if defined(__SSE__) || defined(OVR_OS_WIN32) +# define OVR_CPU_SSE +#endif // __SSE__ + +#if defined( __ALTIVEC__ ) +# define OVR_CPU_ALTIVEC +#endif // __ALTIVEC__ + +#if defined(__ARM_NEON__) +# define OVR_CPU_ARM_NEON +#endif // __ARM_NEON__ + + +//----------------------------------------------------------------------------------- +// ***** Compiler +// +// The following compilers are defined: (OVR_CC_x) +// +// MSVC - Microsoft Visual C/C++ +// INTEL - Intel C++ for Linux / Windows +// GNU - GNU C++ +// ARM - ARM C/C++ + +#if defined(__INTEL_COMPILER) +// Intel 4.0 = 400 +// Intel 5.0 = 500 +// Intel 6.0 = 600 +// Intel 8.0 = 800 +// Intel 9.0 = 900 +# define OVR_CC_INTEL __INTEL_COMPILER + +#elif defined(_MSC_VER) +// MSVC 5.0 = 1100 +// MSVC 6.0 = 1200 +// MSVC 7.0 (VC2002) = 1300 +// MSVC 7.1 (VC2003) = 1310 +// MSVC 8.0 (VC2005) = 1400 +// MSVC 9.0 (VC2008) = 1500 +// MSVC 10.0 (VC2010) = 1600 +# define OVR_CC_MSVC _MSC_VER + +#elif defined(__GNUC__) +# define OVR_CC_GNU + +#elif defined(__CC_ARM) +# define OVR_CC_ARM + +#else +# error "Oculus does not support this Compiler" +#endif + + +//----------------------------------------------------------------------------------- +// ***** Compiler Warnings + +// Disable MSVC warnings +#if defined(OVR_CC_MSVC) +# pragma warning(disable : 4127) // Inconsistent dll linkage +# pragma warning(disable : 4530) // Exception handling +# if (OVR_CC_MSVC<1300) +# pragma warning(disable : 4514) // Unreferenced inline function has been removed +# pragma warning(disable : 4710) // Function not inlined +# pragma warning(disable : 4714) // _force_inline not inlined +# pragma warning(disable : 4786) // Debug variable name longer than 255 chars +# endif // (OVR_CC_MSVC<1300) +#endif // (OVR_CC_MSVC) + + + +// *** Linux Unicode - must come before Standard Includes + +#ifdef OVR_OS_LINUX +// Use glibc unicode functions on linux. +# ifndef _GNU_SOURCE +# define _GNU_SOURCE +# endif +#endif + +//----------------------------------------------------------------------------------- +// ***** Standard Includes +// +#include +#include +#include + + +// MSVC Based Memory Leak checking - for now +#if defined(OVR_CC_MSVC) && defined(OVR_BUILD_DEBUG) +# define _CRTDBG_MAP_ALLOC +# include +# include + +// Uncomment this to help debug memory leaks under Visual Studio in OVR apps only. +// This shouldn't be defined in customer releases. +# ifndef OVR_DEFINE_NEW +# define OVR_DEFINE_NEW new(__FILE__, __LINE__) +# define new OVR_DEFINE_NEW +# endif + +#endif + + +//----------------------------------------------------------------------------------- +// ***** Type definitions for Common Systems + +namespace OVR { + +typedef char Char; + +// Pointer-sized integer +typedef size_t UPInt; +typedef ptrdiff_t SPInt; + + +#if defined(OVR_OS_WIN32) + +typedef char SByte; // 8 bit Integer (Byte) +typedef unsigned char UByte; +typedef short SInt16; // 16 bit Integer (Word) +typedef unsigned short UInt16; +typedef long SInt32; // 32 bit Integer +typedef unsigned long UInt32; +typedef __int64 SInt64; // 64 bit Integer (QWord) +typedef unsigned __int64 UInt64; + + +#elif defined(OVR_OS_MAC) || defined(OVR_OS_IPHONE) || defined(OVR_CC_GNU) + +typedef int SByte __attribute__((__mode__ (__QI__))); +typedef unsigned int UByte __attribute__((__mode__ (__QI__))); +typedef int SInt16 __attribute__((__mode__ (__HI__))); +typedef unsigned int UInt16 __attribute__((__mode__ (__HI__))); +typedef int SInt32 __attribute__((__mode__ (__SI__))); +typedef unsigned int UInt32 __attribute__((__mode__ (__SI__))); +typedef int SInt64 __attribute__((__mode__ (__DI__))); +typedef unsigned int UInt64 __attribute__((__mode__ (__DI__))); + +#else + +#include +typedef int8_t SByte; +typedef uint8_t UByte; +typedef int16_t SInt16; +typedef uint16_t UInt16; +typedef int32_t SInt32; +typedef uint32_t UInt32; +typedef int64_t SInt64; +typedef uint64_t UInt64; + +#endif + + +// ***** BaseTypes Namespace + +// BaseTypes namespace is explicitly declared to allow base types to be used +// by customers directly without other contents of OVR namespace. +// +// Its is expected that GFx samples will declare 'using namespace OVR::BaseTypes' +// to allow using these directly without polluting the target scope with other +// OVR declarations, such as Ptr<>, String or Mutex. +namespace BaseTypes +{ + using OVR::UPInt; + using OVR::SPInt; + using OVR::UByte; + using OVR::SByte; + using OVR::UInt16; + using OVR::SInt16; + using OVR::UInt32; + using OVR::SInt32; + using OVR::UInt64; + using OVR::SInt64; +} // OVR::BaseTypes + +} // OVR + + +//----------------------------------------------------------------------------------- +// ***** Macro Definitions +// +// We define the following: +// +// OVR_BYTE_ORDER - Defined to either OVR_LITTLE_ENDIAN or OVR_BIG_ENDIAN +// OVR_FORCE_INLINE - Forces inline expansion of function +// OVR_ASM - Assembly language prefix +// OVR_STR - Prefixes string with L"" if building unicode +// +// OVR_STDCALL - Use stdcall calling convention (Pascal arg order) +// OVR_CDECL - Use cdecl calling convention (C argument order) +// OVR_FASTCALL - Use fastcall calling convention (registers) +// + +// Byte order constants, OVR_BYTE_ORDER is defined to be one of these. +#define OVR_LITTLE_ENDIAN 1 +#define OVR_BIG_ENDIAN 2 + + +// Force inline substitute - goes before function declaration +#if defined(OVR_CC_MSVC) +# define OVR_FORCE_INLINE __forceinline +#elif defined(OVR_CC_GNU) +# define OVR_FORCE_INLINE __attribute__((always_inline)) inline +#else +# define OVR_FORCE_INLINE inline +#endif // OVR_CC_MSVC + + +#if defined(OVR_OS_WIN32) + + // ***** Win32 + + // Byte order + #define OVR_BYTE_ORDER OVR_LITTLE_ENDIAN + + // Calling convention - goes after function return type but before function name + #ifdef __cplusplus_cli + # define OVR_FASTCALL __stdcall + #else + # define OVR_FASTCALL __fastcall + #endif + + #define OVR_STDCALL __stdcall + #define OVR_CDECL __cdecl + + + // Assembly macros + #if defined(OVR_CC_MSVC) + # define OVR_ASM _asm + #else + # define OVR_ASM asm + #endif // (OVR_CC_MSVC) + + #ifdef UNICODE + # define OVR_STR(str) L##str + #else + # define OVR_STR(str) str + #endif // UNICODE + +#else + + // **** Standard systems + + #if (defined(BYTE_ORDER) && (BYTE_ORDER == BIG_ENDIAN))|| \ + (defined(_BYTE_ORDER) && (_BYTE_ORDER == _BIG_ENDIAN)) + # define OVR_BYTE_ORDER OVR_BIG_ENDIAN + #elif (defined(__ARMEB__) || defined(OVR_CPU_PPC) || defined(OVR_CPU_PPC64)) + # define OVR_BYTE_ORDER OVR_BIG_ENDIAN + #else + # define OVR_BYTE_ORDER OVR_LITTLE_ENDIAN + #endif + + // Assembly macros + #define OVR_ASM __asm__ + #define OVR_ASM_PROC(procname) OVR_ASM + #define OVR_ASM_END OVR_ASM + + // Calling convention - goes after function return type but before function name + #define OVR_FASTCALL + #define OVR_STDCALL + #define OVR_CDECL + +#endif // defined(OVR_OS_WIN32) + + + +//----------------------------------------------------------------------------------- +// ***** OVR_DEBUG_BREAK, OVR_ASSERT +// +// If not in debug build, macros do nothing +#ifndef OVR_BUILD_DEBUG + +# define OVR_DEBUG_BREAK ((void)0) +# define OVR_ASSERT(p) ((void)0) + +#else + +// Microsoft Win32 specific debugging support +#if defined(OVR_OS_WIN32) +# ifdef OVR_CPU_X86 +# if defined(__cplusplus_cli) +# define OVR_DEBUG_BREAK do { __debugbreak(); } while(0) +# elif defined(OVR_CC_GNU) +# define OVR_DEBUG_BREAK do { OVR_ASM("int $3\n\t"); } while(0) +# else +# define OVR_DEBUG_BREAK do { OVR_ASM int 3 } while (0) +# endif +# else +# define OVR_DEBUG_BREAK do { __debugbreak(); } while(0) +# endif +// Unix specific debugging support +#elif defined(OVR_CPU_X86) || defined(OVR_CPU_X86_64) +# define OVR_DEBUG_BREAK do { OVR_ASM("int $3\n\t"); } while(0) +#else +# define OVR_DEBUG_BREAK do { *((int *) 0) = 1; } while(0) +#endif + +// This will cause compiler breakpoint +#define OVR_ASSERT(p) do { if (!(p)) { OVR_DEBUG_BREAK; } } while(0) + +#endif // OVR_BUILD_DEBUG + + +// Compile-time assert; produces compiler error if condition is false +#define OVR_COMPILER_ASSERT(x) { int zero = 0; switch(zero) {case 0: case x:;} } + + + +//----------------------------------------------------------------------------------- +// ***** OVR_UNUSED - Unused Argument handling + +// Macro to quiet compiler warnings about unused parameters/variables. +#if defined(OVR_CC_GNU) +# define OVR_UNUSED(a) do {__typeof__ (&a) __attribute__ ((unused)) __tmp = &a; } while(0) +#else +# define OVR_UNUSED(a) (a) +#endif + +#define OVR_UNUSED1(a1) OVR_UNUSED(a1) +#define OVR_UNUSED2(a1,a2) OVR_UNUSED(a1); OVR_UNUSED(a2) +#define OVR_UNUSED3(a1,a2,a3) OVR_UNUSED2(a1,a2); OVR_UNUSED(a3) +#define OVR_UNUSED4(a1,a2,a3,a4) OVR_UNUSED3(a1,a2,a3); OVR_UNUSED(a4) +#define OVR_UNUSED5(a1,a2,a3,a4,a5) OVR_UNUSED4(a1,a2,a3,a4); OVR_UNUSED(a5) +#define OVR_UNUSED6(a1,a2,a3,a4,a5,a6) OVR_UNUSED4(a1,a2,a3,a4); OVR_UNUSED2(a5,a6) +#define OVR_UNUSED7(a1,a2,a3,a4,a5,a6,a7) OVR_UNUSED4(a1,a2,a3,a4); OVR_UNUSED3(a5,a6,a7) +#define OVR_UNUSED8(a1,a2,a3,a4,a5,a6,a7,a8) OVR_UNUSED4(a1,a2,a3,a4); OVR_UNUSED4(a5,a6,a7,a8) +#define OVR_UNUSED9(a1,a2,a3,a4,a5,a6,a7,a8,a9) OVR_UNUSED4(a1,a2,a3,a4); OVR_UNUSED5(a5,a6,a7,a8,a9) + + +//----------------------------------------------------------------------------------- +// ***** Configuration Macros + +// SF Build type +#ifdef OVR_BUILD_DEBUG +# define OVR_BUILD_STRING "Debug" +#else +# define OVR_BUILD_STRING "Release" +#endif + + +//// Enables SF Debugging information +//# define OVR_BUILD_DEBUG + +// OVR_DEBUG_STATEMENT injects a statement only in debug builds. +// OVR_DEBUG_SELECT injects first argument in debug builds, second argument otherwise. +#ifdef OVR_BUILD_DEBUG +#define OVR_DEBUG_STATEMENT(s) s +#define OVR_DEBUG_SELECT(d, nd) d +#else +#define OVR_DEBUG_STATEMENT(s) +#define OVR_DEBUG_SELECT(d, nd) nd +#endif + + +#define OVR_ENABLE_THREADS +// +// Prevents OVR from defining new within +// type macros, so developers can override +// new using the #define new new(...) trick +// - used with OVR_DEFINE_NEW macro +//# define OVR_BUILD_DEFINE_NEW +// + + +#endif // OVR_Types_h diff -r 59fc487ba58e -r b069a5c27388 libovr/Src/Kernel/OVR_UTF8Util.cpp --- a/libovr/Src/Kernel/OVR_UTF8Util.cpp Sat Sep 14 17:51:03 2013 +0300 +++ b/libovr/Src/Kernel/OVR_UTF8Util.cpp Sun Sep 15 04:10:05 2013 +0300 @@ -1,1 +1,545 @@ -/************************************************************************** Filename : OVR_UTF8Util.cpp Content : UTF8 Unicode character encoding/decoding support Created : September 19, 2012 Notes : Notes : Much useful info at "UTF-8 and Unicode FAQ" http://www.cl.cam.ac.uk/~mgk25/unicode.html Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #include "OVR_UTF8Util.h" namespace OVR { namespace UTF8Util { SPInt OVR_STDCALL GetLength(const char* buf, SPInt buflen) { const char* p = buf; SPInt length = 0; if (buflen != -1) { while (p - buf < buflen) { // We should be able to have ASStrings with 0 in the middle. UTF8Util::DecodeNextChar_Advance0(&p); length++; } } else { while (UTF8Util::DecodeNextChar_Advance0(&p)) length++; } return length; } UInt32 OVR_STDCALL GetCharAt(SPInt index, const char* putf8str, SPInt length) { const char* buf = putf8str; UInt32 c = 0; if (length != -1) { while (buf - putf8str < length) { c = UTF8Util::DecodeNextChar_Advance0(&buf); if (index == 0) return c; index--; } return c; } do { c = UTF8Util::DecodeNextChar_Advance0(&buf); index--; if (c == 0) { // We've hit the end of the string; don't go further. OVR_ASSERT(index == 0); return c; } } while (index >= 0); return c; } SPInt OVR_STDCALL GetByteIndex(SPInt index, const char *putf8str, SPInt length) { const char* buf = putf8str; if (length != -1) { while ((buf - putf8str) < length && index > 0) { UTF8Util::DecodeNextChar_Advance0(&buf); index--; } return buf-putf8str; } while (index > 0) { UInt32 c = UTF8Util::DecodeNextChar_Advance0(&buf); index--; if (c == 0) return buf-putf8str; }; return buf-putf8str; } int OVR_STDCALL GetEncodeCharSize(UInt32 ucs_character) { if (ucs_character <= 0x7F) return 1; else if (ucs_character <= 0x7FF) return 2; else if (ucs_character <= 0xFFFF) return 3; else if (ucs_character <= 0x1FFFFF) return 4; else if (ucs_character <= 0x3FFFFFF) return 5; else if (ucs_character <= 0x7FFFFFFF) return 6; else return 0; } UInt32 OVR_STDCALL DecodeNextChar_Advance0(const char** putf8Buffer) { UInt32 uc; char c; // Security considerations: // // Changed, this is now only the case for DecodeNextChar: // - If we hit a zero byte, we want to return 0 without stepping // the buffer pointer past the 0. th // // If we hit an "overlong sequence"; i.e. a character encoded // in a longer multibyte string than is necessary, then we // need to discard the character. This is so attackers can't // disguise dangerous characters or character sequences -- // there is only one valid encoding for each character. // // If we decode characters { 0xD800 .. 0xDFFF } or { 0xFFFE, // 0xFFFF } then we ignore them; they are not valid in UTF-8. // This isn't actually an invalid character; it's a valid char that // looks like an inverted question mark. #define INVALID_CHAR 0x0FFFD #define FIRST_BYTE(mask, shift) \ uc = (c & (mask)) << (shift); #define NEXT_BYTE(shift) \ c = **putf8Buffer; \ if (c == 0) return 0; /* end of buffer, do not advance */ \ if ((c & 0xC0) != 0x80) return INVALID_CHAR; /* standard check */ \ (*putf8Buffer)++; \ uc |= (c & 0x3F) << shift; c = **putf8Buffer; (*putf8Buffer)++; if (c == 0) return 0; // End of buffer. if ((c & 0x80) == 0) return (UInt32) c; // Conventional 7-bit ASCII. // Multi-byte sequences. if ((c & 0xE0) == 0xC0) { // Two-byte sequence. FIRST_BYTE(0x1F, 6); NEXT_BYTE(0); if (uc < 0x80) return INVALID_CHAR; // overlong return uc; } else if ((c & 0xF0) == 0xE0) { // Three-byte sequence. FIRST_BYTE(0x0F, 12); NEXT_BYTE(6); NEXT_BYTE(0); if (uc < 0x800) return INVALID_CHAR; // overlong // Not valid ISO 10646, but Flash requires these to work // see AS3 test e15_5_3_2_3 for String.fromCharCode().charCodeAt(0) // if (uc >= 0x0D800 && uc <= 0x0DFFF) return INVALID_CHAR; // if (uc == 0x0FFFE || uc == 0x0FFFF) return INVALID_CHAR; // not valid ISO 10646 return uc; } else if ((c & 0xF8) == 0xF0) { // Four-byte sequence. FIRST_BYTE(0x07, 18); NEXT_BYTE(12); NEXT_BYTE(6); NEXT_BYTE(0); if (uc < 0x010000) return INVALID_CHAR; // overlong return uc; } else if ((c & 0xFC) == 0xF8) { // Five-byte sequence. FIRST_BYTE(0x03, 24); NEXT_BYTE(18); NEXT_BYTE(12); NEXT_BYTE(6); NEXT_BYTE(0); if (uc < 0x0200000) return INVALID_CHAR; // overlong return uc; } else if ((c & 0xFE) == 0xFC) { // Six-byte sequence. FIRST_BYTE(0x01, 30); NEXT_BYTE(24); NEXT_BYTE(18); NEXT_BYTE(12); NEXT_BYTE(6); NEXT_BYTE(0); if (uc < 0x04000000) return INVALID_CHAR; // overlong return uc; } else { // Invalid. return INVALID_CHAR; } } void OVR_STDCALL EncodeChar(char* pbuffer, SPInt* pindex, UInt32 ucs_character) { if (ucs_character <= 0x7F) { // Plain single-byte ASCII. pbuffer[(*pindex)++] = (char) ucs_character; } else if (ucs_character <= 0x7FF) { // Two bytes. pbuffer[(*pindex)++] = 0xC0 | (char)(ucs_character >> 6); pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 0) & 0x3F); } else if (ucs_character <= 0xFFFF) { // Three bytes. pbuffer[(*pindex)++] = 0xE0 | (char)(ucs_character >> 12); pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 6) & 0x3F); pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 0) & 0x3F); } else if (ucs_character <= 0x1FFFFF) { // Four bytes. pbuffer[(*pindex)++] = 0xF0 | (char)(ucs_character >> 18); pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 12) & 0x3F); pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 6) & 0x3F); pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 0) & 0x3F); } else if (ucs_character <= 0x3FFFFFF) { // Five bytes. pbuffer[(*pindex)++] = 0xF8 | (char)(ucs_character >> 24); pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 18) & 0x3F); pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 12) & 0x3F); pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 6) & 0x3F); pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 0) & 0x3F); } else if (ucs_character <= 0x7FFFFFFF) { // Six bytes. pbuffer[(*pindex)++] = 0xFC | (char)(ucs_character >> 30); pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 24) & 0x3F); pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 18) & 0x3F); pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 12) & 0x3F); pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 6) & 0x3F); pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 0) & 0x3F); } else { // Invalid char; don't encode anything. } } SPInt OVR_STDCALL GetEncodeStringSize(const wchar_t* pchar, SPInt length) { SPInt len = 0; if (length != -1) for (int i = 0; i < length; i++) { len += GetEncodeCharSize(pchar[i]); } else for (int i = 0;; i++) { if (pchar[i] == 0) return len; len += GetEncodeCharSize(pchar[i]); } return len; } void OVR_STDCALL EncodeString(char *pbuff, const wchar_t* pchar, SPInt length) { SPInt ofs = 0; if (length != -1) { for (int i = 0; i < length; i++) { EncodeChar(pbuff, &ofs, pchar[i]); } } else { for (int i = 0;; i++) { if (pchar[i] == 0) break; EncodeChar(pbuff, &ofs, pchar[i]); } } pbuff[ofs] = 0; } UPInt OVR_STDCALL DecodeString(wchar_t *pbuff, const char* putf8str, SPInt bytesLen) { wchar_t *pbegin = pbuff; if (bytesLen == -1) { while (1) { UInt32 ch = DecodeNextChar_Advance0(&putf8str); if (ch == 0) break; else if (ch >= 0xFFFF) ch = 0xFFFD; *pbuff++ = wchar_t(ch); } } else { const char* p = putf8str; while ((p - putf8str) < bytesLen) { UInt32 ch = DecodeNextChar_Advance0(&p); if (ch >= 0xFFFF) ch = 0xFFFD; *pbuff++ = wchar_t(ch); } } *pbuff = 0; return pbuff - pbegin; } #ifdef UTF8_UNIT_TEST // Compile this test case with something like: // // gcc utf8.cpp -g -I.. -DUTF8_UNIT_TEST -lstdc++ -o utf8_test // // or // // cl utf8.cpp -Zi -Od -DUTF8_UNIT_TEST -I.. // // If possible, try running the test program with the first arg // pointing at the file: // // http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt // // and examine the results by eye to make sure they are acceptable to // you. #include "base/utility.h" #include bool check_equal(const char* utf8_in, const UInt32* ucs_in) { for (;;) { UInt32 next_ucs = *ucs_in++; UInt32 next_ucs_from_utf8 = utf8::decode_next_unicode_character(&utf8_in); if (next_ucs != next_ucs_from_utf8) { return false; } if (next_ucs == 0) { OVR_ASSERT(next_ucs_from_utf8 == 0); break; } } return true; } void log_ascii(const char* line) { for (;;) { unsigned char c = (unsigned char) *line++; if (c == 0) { // End of line. return; } else if (c != '\n' && (c < 32 || c > 127)) { // Non-printable as plain ASCII. printf("<0x%02X>", (int) c); } else { printf("%c", c); } } } void log_ucs(const UInt32* line) { for (;;) { UInt32 uc = *line++; if (uc == 0) { // End of line. return; } else if (uc != '\n' && (uc < 32 || uc > 127)) { // Non-printable as plain ASCII. printf("", uc); } else { printf("%c", (char) uc); } } } // Simple canned test. int main(int argc, const char* argv[]) { { const char* test8 = "Ignacio Castaño"; const UInt32 test32[] = { 0x49, 0x67, 0x6E, 0x61, 0x63, 0x69, 0x6F, 0x20, 0x43, 0x61, 0x73, 0x74, 0x61, 0xF1, 0x6F, 0x00 }; OVR_ASSERT(check_equal(test8, test32)); } // If user passed an arg, try reading the file as UTF-8 encoded text. if (argc > 1) { const char* filename = argv[1]; FILE* fp = fopen(filename, "rb"); if (fp == NULL) { printf("Can't open file '%s'\n", filename); return 1; } // Read lines from the file, encode/decode them, and highlight discrepancies. const int LINE_SIZE = 200; // max line size char line_buffer_utf8[LINE_SIZE]; char reencoded_utf8[6 * LINE_SIZE]; UInt32 line_buffer_ucs[LINE_SIZE]; int byte_counter = 0; for (;;) { int c = fgetc(fp); if (c == EOF) { // Done. break; } line_buffer_utf8[byte_counter++] = c; if (c == '\n' || byte_counter >= LINE_SIZE - 2) { // End of line. Process the line. line_buffer_utf8[byte_counter++] = 0; // terminate. // Decode into UCS. const char* p = line_buffer_utf8; UInt32* q = line_buffer_ucs; for (;;) { UInt32 uc = UTF8Util::DecodeNextChar(&p); *q++ = uc; OVR_ASSERT(q < line_buffer_ucs + LINE_SIZE); OVR_ASSERT(p < line_buffer_utf8 + LINE_SIZE); if (uc == 0) break; } // Encode back into UTF-8. q = line_buffer_ucs; int index = 0; for (;;) { UInt32 uc = *q++; OVR_ASSERT(index < LINE_SIZE * 6 - 6); int last_index = index; UTF8Util::EncodeChar(reencoded_utf8, &index, uc); OVR_ASSERT(index <= last_index + 6); if (uc == 0) break; } // This can be useful for debugging. #if 0 // Show the UCS and the re-encoded UTF-8. log_ucs(line_buffer_ucs); log_ascii(reencoded_utf8); #endif // 0 OVR_ASSERT(check_equal(line_buffer_utf8, line_buffer_ucs)); OVR_ASSERT(check_equal(reencoded_utf8, line_buffer_ucs)); // Start next line. byte_counter = 0; } } fclose(fp); } return 0; } #endif // UTF8_UNIT_TEST }} // namespace UTF8Util::OVR \ No newline at end of file +/************************************************************************** + +Filename : OVR_UTF8Util.cpp +Content : UTF8 Unicode character encoding/decoding support +Created : September 19, 2012 +Notes : +Notes : Much useful info at "UTF-8 and Unicode FAQ" + http://www.cl.cam.ac.uk/~mgk25/unicode.html + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#include "OVR_UTF8Util.h" + +namespace OVR { namespace UTF8Util { + +SPInt OVR_STDCALL GetLength(const char* buf, SPInt buflen) +{ + const char* p = buf; + SPInt length = 0; + + if (buflen != -1) + { + while (p - buf < buflen) + { + // We should be able to have ASStrings with 0 in the middle. + UTF8Util::DecodeNextChar_Advance0(&p); + length++; + } + } + else + { + while (UTF8Util::DecodeNextChar_Advance0(&p)) + length++; + } + + return length; +} + +UInt32 OVR_STDCALL GetCharAt(SPInt index, const char* putf8str, SPInt length) +{ + const char* buf = putf8str; + UInt32 c = 0; + + if (length != -1) + { + while (buf - putf8str < length) + { + c = UTF8Util::DecodeNextChar_Advance0(&buf); + if (index == 0) + return c; + index--; + } + + return c; + } + + do + { + c = UTF8Util::DecodeNextChar_Advance0(&buf); + index--; + + if (c == 0) + { + // We've hit the end of the string; don't go further. + OVR_ASSERT(index == 0); + return c; + } + } while (index >= 0); + + return c; +} + +SPInt OVR_STDCALL GetByteIndex(SPInt index, const char *putf8str, SPInt length) +{ + const char* buf = putf8str; + + if (length != -1) + { + while ((buf - putf8str) < length && index > 0) + { + UTF8Util::DecodeNextChar_Advance0(&buf); + index--; + } + + return buf-putf8str; + } + + while (index > 0) + { + UInt32 c = UTF8Util::DecodeNextChar_Advance0(&buf); + index--; + + if (c == 0) + return buf-putf8str; + }; + + return buf-putf8str; +} + +int OVR_STDCALL GetEncodeCharSize(UInt32 ucs_character) +{ + if (ucs_character <= 0x7F) + return 1; + else if (ucs_character <= 0x7FF) + return 2; + else if (ucs_character <= 0xFFFF) + return 3; + else if (ucs_character <= 0x1FFFFF) + return 4; + else if (ucs_character <= 0x3FFFFFF) + return 5; + else if (ucs_character <= 0x7FFFFFFF) + return 6; + else + return 0; +} + +UInt32 OVR_STDCALL DecodeNextChar_Advance0(const char** putf8Buffer) +{ + UInt32 uc; + char c; + + // Security considerations: + // + // Changed, this is now only the case for DecodeNextChar: + // - If we hit a zero byte, we want to return 0 without stepping + // the buffer pointer past the 0. th + // + // If we hit an "overlong sequence"; i.e. a character encoded + // in a longer multibyte string than is necessary, then we + // need to discard the character. This is so attackers can't + // disguise dangerous characters or character sequences -- + // there is only one valid encoding for each character. + // + // If we decode characters { 0xD800 .. 0xDFFF } or { 0xFFFE, + // 0xFFFF } then we ignore them; they are not valid in UTF-8. + + // This isn't actually an invalid character; it's a valid char that + // looks like an inverted question mark. +#define INVALID_CHAR 0x0FFFD + +#define FIRST_BYTE(mask, shift) \ + uc = (c & (mask)) << (shift); + +#define NEXT_BYTE(shift) \ + c = **putf8Buffer; \ + if (c == 0) return 0; /* end of buffer, do not advance */ \ + if ((c & 0xC0) != 0x80) return INVALID_CHAR; /* standard check */ \ + (*putf8Buffer)++; \ + uc |= (c & 0x3F) << shift; + + c = **putf8Buffer; + (*putf8Buffer)++; + if (c == 0) + return 0; // End of buffer. + + if ((c & 0x80) == 0) return (UInt32) c; // Conventional 7-bit ASCII. + + // Multi-byte sequences. + if ((c & 0xE0) == 0xC0) + { + // Two-byte sequence. + FIRST_BYTE(0x1F, 6); + NEXT_BYTE(0); + if (uc < 0x80) return INVALID_CHAR; // overlong + return uc; + } + else if ((c & 0xF0) == 0xE0) + { + // Three-byte sequence. + FIRST_BYTE(0x0F, 12); + NEXT_BYTE(6); + NEXT_BYTE(0); + if (uc < 0x800) return INVALID_CHAR; // overlong + // Not valid ISO 10646, but Flash requires these to work + // see AS3 test e15_5_3_2_3 for String.fromCharCode().charCodeAt(0) + // if (uc >= 0x0D800 && uc <= 0x0DFFF) return INVALID_CHAR; + // if (uc == 0x0FFFE || uc == 0x0FFFF) return INVALID_CHAR; // not valid ISO 10646 + return uc; + } + else if ((c & 0xF8) == 0xF0) + { + // Four-byte sequence. + FIRST_BYTE(0x07, 18); + NEXT_BYTE(12); + NEXT_BYTE(6); + NEXT_BYTE(0); + if (uc < 0x010000) return INVALID_CHAR; // overlong + return uc; + } + else if ((c & 0xFC) == 0xF8) + { + // Five-byte sequence. + FIRST_BYTE(0x03, 24); + NEXT_BYTE(18); + NEXT_BYTE(12); + NEXT_BYTE(6); + NEXT_BYTE(0); + if (uc < 0x0200000) return INVALID_CHAR; // overlong + return uc; + } + else if ((c & 0xFE) == 0xFC) + { + // Six-byte sequence. + FIRST_BYTE(0x01, 30); + NEXT_BYTE(24); + NEXT_BYTE(18); + NEXT_BYTE(12); + NEXT_BYTE(6); + NEXT_BYTE(0); + if (uc < 0x04000000) return INVALID_CHAR; // overlong + return uc; + } + else + { + // Invalid. + return INVALID_CHAR; + } +} + + +void OVR_STDCALL EncodeChar(char* pbuffer, SPInt* pindex, UInt32 ucs_character) +{ + if (ucs_character <= 0x7F) + { + // Plain single-byte ASCII. + pbuffer[(*pindex)++] = (char) ucs_character; + } + else if (ucs_character <= 0x7FF) + { + // Two bytes. + pbuffer[(*pindex)++] = 0xC0 | (char)(ucs_character >> 6); + pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 0) & 0x3F); + } + else if (ucs_character <= 0xFFFF) + { + // Three bytes. + pbuffer[(*pindex)++] = 0xE0 | (char)(ucs_character >> 12); + pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 6) & 0x3F); + pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 0) & 0x3F); + } + else if (ucs_character <= 0x1FFFFF) + { + // Four bytes. + pbuffer[(*pindex)++] = 0xF0 | (char)(ucs_character >> 18); + pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 12) & 0x3F); + pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 6) & 0x3F); + pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 0) & 0x3F); + } + else if (ucs_character <= 0x3FFFFFF) + { + // Five bytes. + pbuffer[(*pindex)++] = 0xF8 | (char)(ucs_character >> 24); + pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 18) & 0x3F); + pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 12) & 0x3F); + pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 6) & 0x3F); + pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 0) & 0x3F); + } + else if (ucs_character <= 0x7FFFFFFF) + { + // Six bytes. + pbuffer[(*pindex)++] = 0xFC | (char)(ucs_character >> 30); + pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 24) & 0x3F); + pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 18) & 0x3F); + pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 12) & 0x3F); + pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 6) & 0x3F); + pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 0) & 0x3F); + } + else + { + // Invalid char; don't encode anything. + } +} + +SPInt OVR_STDCALL GetEncodeStringSize(const wchar_t* pchar, SPInt length) +{ + SPInt len = 0; + if (length != -1) + for (int i = 0; i < length; i++) + { + len += GetEncodeCharSize(pchar[i]); + } + else + for (int i = 0;; i++) + { + if (pchar[i] == 0) + return len; + len += GetEncodeCharSize(pchar[i]); + } + return len; +} + +void OVR_STDCALL EncodeString(char *pbuff, const wchar_t* pchar, SPInt length) +{ + SPInt ofs = 0; + if (length != -1) + { + for (int i = 0; i < length; i++) + { + EncodeChar(pbuff, &ofs, pchar[i]); + } + } + else + { + for (int i = 0;; i++) + { + if (pchar[i] == 0) + break; + EncodeChar(pbuff, &ofs, pchar[i]); + } + } + pbuff[ofs] = 0; +} + +UPInt OVR_STDCALL DecodeString(wchar_t *pbuff, const char* putf8str, SPInt bytesLen) +{ + wchar_t *pbegin = pbuff; + if (bytesLen == -1) + { + while (1) + { + UInt32 ch = DecodeNextChar_Advance0(&putf8str); + if (ch == 0) + break; + else if (ch >= 0xFFFF) + ch = 0xFFFD; + *pbuff++ = wchar_t(ch); + } + } + else + { + const char* p = putf8str; + while ((p - putf8str) < bytesLen) + { + UInt32 ch = DecodeNextChar_Advance0(&p); + if (ch >= 0xFFFF) + ch = 0xFFFD; + *pbuff++ = wchar_t(ch); + } + } + + *pbuff = 0; + return pbuff - pbegin; +} + + +#ifdef UTF8_UNIT_TEST + +// Compile this test case with something like: +// +// gcc utf8.cpp -g -I.. -DUTF8_UNIT_TEST -lstdc++ -o utf8_test +// +// or +// +// cl utf8.cpp -Zi -Od -DUTF8_UNIT_TEST -I.. +// +// If possible, try running the test program with the first arg +// pointing at the file: +// +// http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt +// +// and examine the results by eye to make sure they are acceptable to +// you. + + +#include "base/utility.h" +#include + + +bool check_equal(const char* utf8_in, const UInt32* ucs_in) +{ + for (;;) + { + UInt32 next_ucs = *ucs_in++; + UInt32 next_ucs_from_utf8 = utf8::decode_next_unicode_character(&utf8_in); + if (next_ucs != next_ucs_from_utf8) + { + return false; + } + if (next_ucs == 0) + { + OVR_ASSERT(next_ucs_from_utf8 == 0); + break; + } + } + + return true; +} + + +void log_ascii(const char* line) +{ + for (;;) + { + unsigned char c = (unsigned char) *line++; + if (c == 0) + { + // End of line. + return; + } + else if (c != '\n' + && (c < 32 || c > 127)) + { + // Non-printable as plain ASCII. + printf("<0x%02X>", (int) c); + } + else + { + printf("%c", c); + } + } +} + + +void log_ucs(const UInt32* line) +{ + for (;;) + { + UInt32 uc = *line++; + if (uc == 0) + { + // End of line. + return; + } + else if (uc != '\n' + && (uc < 32 || uc > 127)) + { + // Non-printable as plain ASCII. + printf("", uc); + } + else + { + printf("%c", (char) uc); + } + } +} + + +// Simple canned test. +int main(int argc, const char* argv[]) +{ + { + const char* test8 = "Ignacio Castaño"; + const UInt32 test32[] = + { + 0x49, 0x67, 0x6E, 0x61, 0x63, + 0x69, 0x6F, 0x20, 0x43, 0x61, + 0x73, 0x74, 0x61, 0xF1, 0x6F, + 0x00 + }; + + OVR_ASSERT(check_equal(test8, test32)); + } + + // If user passed an arg, try reading the file as UTF-8 encoded text. + if (argc > 1) + { + const char* filename = argv[1]; + FILE* fp = fopen(filename, "rb"); + if (fp == NULL) + { + printf("Can't open file '%s'\n", filename); + return 1; + } + + // Read lines from the file, encode/decode them, and highlight discrepancies. + const int LINE_SIZE = 200; // max line size + char line_buffer_utf8[LINE_SIZE]; + char reencoded_utf8[6 * LINE_SIZE]; + UInt32 line_buffer_ucs[LINE_SIZE]; + + int byte_counter = 0; + for (;;) + { + int c = fgetc(fp); + if (c == EOF) + { + // Done. + break; + } + line_buffer_utf8[byte_counter++] = c; + if (c == '\n' || byte_counter >= LINE_SIZE - 2) + { + // End of line. Process the line. + line_buffer_utf8[byte_counter++] = 0; // terminate. + + // Decode into UCS. + const char* p = line_buffer_utf8; + UInt32* q = line_buffer_ucs; + for (;;) + { + UInt32 uc = UTF8Util::DecodeNextChar(&p); + *q++ = uc; + + OVR_ASSERT(q < line_buffer_ucs + LINE_SIZE); + OVR_ASSERT(p < line_buffer_utf8 + LINE_SIZE); + + if (uc == 0) break; + } + + // Encode back into UTF-8. + q = line_buffer_ucs; + int index = 0; + for (;;) + { + UInt32 uc = *q++; + OVR_ASSERT(index < LINE_SIZE * 6 - 6); + int last_index = index; + UTF8Util::EncodeChar(reencoded_utf8, &index, uc); + OVR_ASSERT(index <= last_index + 6); + if (uc == 0) break; + } + + // This can be useful for debugging. +#if 0 + // Show the UCS and the re-encoded UTF-8. + log_ucs(line_buffer_ucs); + log_ascii(reencoded_utf8); +#endif // 0 + + OVR_ASSERT(check_equal(line_buffer_utf8, line_buffer_ucs)); + OVR_ASSERT(check_equal(reencoded_utf8, line_buffer_ucs)); + + // Start next line. + byte_counter = 0; + } + } + + fclose(fp); + } + + return 0; +} + + +#endif // UTF8_UNIT_TEST + +}} // namespace UTF8Util::OVR + diff -r 59fc487ba58e -r b069a5c27388 libovr/Src/Kernel/OVR_UTF8Util.h --- a/libovr/Src/Kernel/OVR_UTF8Util.h Sat Sep 14 17:51:03 2013 +0300 +++ b/libovr/Src/Kernel/OVR_UTF8Util.h Sun Sep 15 04:10:05 2013 +0300 @@ -1,1 +1,88 @@ -/************************************************************************************ PublicHeader: OVR.h Filename : OVR_UTF8Util.h Content : UTF8 Unicode character encoding/decoding support Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #ifndef OVR_UTF8Util_h #define OVR_UTF8Util_h #include "OVR_Types.h" namespace OVR { namespace UTF8Util { //----------------------------------------------------------------------------------- // *** UTF8 string length and indexing. // Determines the length of UTF8 string in characters. // If source length is specified (in bytes), null 0 character is counted properly. SPInt OVR_STDCALL GetLength(const char* putf8str, SPInt length = -1); // Gets a decoded UTF8 character at index; you can access up to the index returned // by GetLength. 0 will be returned for out of bounds access. UInt32 OVR_STDCALL GetCharAt(SPInt index, const char* putf8str, SPInt length = -1); // Converts UTF8 character index into byte offset. // -1 is returned if index was out of bounds. SPInt OVR_STDCALL GetByteIndex(SPInt index, const char* putf8str, SPInt length = -1); // *** 16-bit Unicode string Encoding/Decoding routines. // Determines the number of bytes necessary to encode a string. // Does not count the terminating 0 (null) character. SPInt OVR_STDCALL GetEncodeStringSize(const wchar_t* pchar, SPInt length = -1); // Encodes a unicode (UCS-2 only) string into a buffer. The size of buffer must be at // least GetEncodeStringSize() + 1. void OVR_STDCALL EncodeString(char *pbuff, const wchar_t* pchar, SPInt length = -1); // Decode UTF8 into a wchar_t buffer. Must have GetLength()+1 characters available. // Characters over 0xFFFF are replaced with 0xFFFD. // Returns the length of resulting string (number of characters) UPInt OVR_STDCALL DecodeString(wchar_t *pbuff, const char* putf8str, SPInt bytesLen = -1); // *** Individual character Encoding/Decoding. // Determined the number of bytes necessary to encode a UCS character. int OVR_STDCALL GetEncodeCharSize(UInt32 ucsCharacter); // Encodes the given UCS character into the given UTF-8 buffer. // Writes the data starting at buffer[offset], and // increments offset by the number of bytes written. // May write up to 6 bytes, so make sure there's room in the buffer void OVR_STDCALL EncodeChar(char* pbuffer, SPInt* poffset, UInt32 ucsCharacter); // Return the next Unicode character in the UTF-8 encoded buffer. // Invalid UTF-8 sequences produce a U+FFFD character as output. // Advances *utf8_buffer past the character returned. Pointer advance // occurs even if the terminating 0 character is hit, since that allows // strings with middle '\0' characters to be supported. UInt32 OVR_STDCALL DecodeNextChar_Advance0(const char** putf8Buffer); // Safer version of DecodeNextChar, which doesn't advance pointer if // null character is hit. inline UInt32 DecodeNextChar(const char** putf8Buffer) { UInt32 ch = DecodeNextChar_Advance0(putf8Buffer); if (ch == 0) (*putf8Buffer)--; return ch; } }} // OVR::UTF8Util #endif \ No newline at end of file +/************************************************************************************ + +PublicHeader: OVR.h +Filename : OVR_UTF8Util.h +Content : UTF8 Unicode character encoding/decoding support +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#ifndef OVR_UTF8Util_h +#define OVR_UTF8Util_h + +#include "OVR_Types.h" + +namespace OVR { namespace UTF8Util { + +//----------------------------------------------------------------------------------- + +// *** UTF8 string length and indexing. + +// Determines the length of UTF8 string in characters. +// If source length is specified (in bytes), null 0 character is counted properly. +SPInt OVR_STDCALL GetLength(const char* putf8str, SPInt length = -1); + +// Gets a decoded UTF8 character at index; you can access up to the index returned +// by GetLength. 0 will be returned for out of bounds access. +UInt32 OVR_STDCALL GetCharAt(SPInt index, const char* putf8str, SPInt length = -1); + +// Converts UTF8 character index into byte offset. +// -1 is returned if index was out of bounds. +SPInt OVR_STDCALL GetByteIndex(SPInt index, const char* putf8str, SPInt length = -1); + + +// *** 16-bit Unicode string Encoding/Decoding routines. + +// Determines the number of bytes necessary to encode a string. +// Does not count the terminating 0 (null) character. +SPInt OVR_STDCALL GetEncodeStringSize(const wchar_t* pchar, SPInt length = -1); + +// Encodes a unicode (UCS-2 only) string into a buffer. The size of buffer must be at +// least GetEncodeStringSize() + 1. +void OVR_STDCALL EncodeString(char *pbuff, const wchar_t* pchar, SPInt length = -1); + +// Decode UTF8 into a wchar_t buffer. Must have GetLength()+1 characters available. +// Characters over 0xFFFF are replaced with 0xFFFD. +// Returns the length of resulting string (number of characters) +UPInt OVR_STDCALL DecodeString(wchar_t *pbuff, const char* putf8str, SPInt bytesLen = -1); + + +// *** Individual character Encoding/Decoding. + +// Determined the number of bytes necessary to encode a UCS character. +int OVR_STDCALL GetEncodeCharSize(UInt32 ucsCharacter); + +// Encodes the given UCS character into the given UTF-8 buffer. +// Writes the data starting at buffer[offset], and +// increments offset by the number of bytes written. +// May write up to 6 bytes, so make sure there's room in the buffer +void OVR_STDCALL EncodeChar(char* pbuffer, SPInt* poffset, UInt32 ucsCharacter); + +// Return the next Unicode character in the UTF-8 encoded buffer. +// Invalid UTF-8 sequences produce a U+FFFD character as output. +// Advances *utf8_buffer past the character returned. Pointer advance +// occurs even if the terminating 0 character is hit, since that allows +// strings with middle '\0' characters to be supported. +UInt32 OVR_STDCALL DecodeNextChar_Advance0(const char** putf8Buffer); + +// Safer version of DecodeNextChar, which doesn't advance pointer if +// null character is hit. +inline UInt32 DecodeNextChar(const char** putf8Buffer) +{ + UInt32 ch = DecodeNextChar_Advance0(putf8Buffer); + if (ch == 0) + (*putf8Buffer)--; + return ch; +} + + +}} // OVR::UTF8Util + +#endif diff -r 59fc487ba58e -r b069a5c27388 libovr/Src/Util/Util_LatencyTest.cpp --- a/libovr/Src/Util/Util_LatencyTest.cpp Sat Sep 14 17:51:03 2013 +0300 +++ b/libovr/Src/Util/Util_LatencyTest.cpp Sun Sep 15 04:10:05 2013 +0300 @@ -1,1 +1,571 @@ -/************************************************************************************ Filename : Util_LatencyTest.cpp Content : Wraps the lower level LatencyTester interface and adds functionality. Created : February 14, 2013 Authors : Lee Cooper Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. *************************************************************************************/ #include "Util_LatencyTest.h" #include "../Kernel/OVR_Log.h" #include "../Kernel/OVR_Timer.h" namespace OVR { namespace Util { static const UInt32 TIME_TO_WAIT_FOR_SETTLE_PRE_CALIBRATION = 16*10; static const UInt32 TIME_TO_WAIT_FOR_SETTLE_POST_CALIBRATION = 16*10; static const UInt32 TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT = 16*5; static const UInt32 TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS = 16*5; static const UInt32 DEFAULT_NUMBER_OF_SAMPLES = 10; // For both color 1->2 and color 2->1 transitions. static const UInt32 INITIAL_SAMPLES_TO_IGNORE = 4; static const UInt32 TIMEOUT_WAITING_FOR_TEST_STARTED = 1000; static const UInt32 TIMEOUT_WAITING_FOR_COLOR_DETECTED = 4000; static const Color CALIBRATE_BLACK(0, 0, 0); static const Color CALIBRATE_WHITE(255, 255, 255); static const Color COLOR1(0, 0, 0); static const Color COLOR2(255, 255, 255); static const Color SENSOR_DETECT_THRESHOLD(128, 255, 255); static const float BIG_FLOAT = 1000000.0f; static const float SMALL_FLOAT = -1000000.0f; //------------------------------------------------------------------------------------- // ***** LatencyTest LatencyTest::LatencyTest(LatencyTestDevice* device) : Handler(getThis()) { if (device != NULL) { SetDevice(device); } reset(); srand(Timer::GetTicksMs()); } LatencyTest::~LatencyTest() { clearMeasurementResults(); } bool LatencyTest::SetDevice(LatencyTestDevice* device) { if (device != Device) { if (device != NULL) { if (device->GetMessageHandler() != NULL) { OVR_DEBUG_LOG( ("LatencyTest::AttachToDevice failed - device %p already has handler", device)); return false; } } if (Device != NULL) { Device->SetMessageHandler(0); } Device = device; if (Device != NULL) { Device->SetMessageHandler(&Handler); // Set trigger threshold. LatencyTestConfiguration configuration(SENSOR_DETECT_THRESHOLD, false); // No samples streaming. Device->SetConfiguration(configuration, true); // Set display to intial (3 dashes). LatencyTestDisplay ltd(2, 0x40400040); Device->SetDisplay(ltd); } } return true; } UInt32 LatencyTest::getRandomComponent(UInt32 range) { UInt32 val = rand() % range; return val; } void LatencyTest::BeginTest() { if (State == State_WaitingForButton) { // Set color to black and wait a while. RenderColor = CALIBRATE_BLACK; State = State_WaitingForSettlePreCalibrationColorBlack; OVR_DEBUG_LOG(("State_WaitingForButton -> State_WaitingForSettlePreCalibrationColorBlack.")); setTimer(TIME_TO_WAIT_FOR_SETTLE_PRE_CALIBRATION); } } void LatencyTest::handleMessage(const Message& msg, LatencyTestMessageType latencyTestMessage) { // For debugging. /* if (msg.Type == Message_LatencyTestSamples) { MessageLatencyTestSamples* pSamples = (MessageLatencyTestSamples*) &msg; if (pSamples->Samples.GetSize() > 0) { // Just show the first one for now. Color c = pSamples->Samples[0]; OVR_DEBUG_LOG(("%d %d %d", c.R, c.G, c.B)); } return; } */ if (latencyTestMessage == LatencyTest_Timer) { if (!Device) { reset(); return; } if (State == State_WaitingForSettlePreCalibrationColorBlack) { // Send calibrate message to device and wait a while. Device->SetCalibrate(CALIBRATE_BLACK); State = State_WaitingForSettlePostCalibrationColorBlack; OVR_DEBUG_LOG(("State_WaitingForSettlePreCalibrationColorBlack -> State_WaitingForSettlePostCalibrationColorBlack.")); setTimer(TIME_TO_WAIT_FOR_SETTLE_POST_CALIBRATION); } else if (State == State_WaitingForSettlePostCalibrationColorBlack) { // Change color to white and wait a while. RenderColor = CALIBRATE_WHITE; State = State_WaitingForSettlePreCalibrationColorWhite; OVR_DEBUG_LOG(("State_WaitingForSettlePostCalibrationColorBlack -> State_WaitingForSettlePreCalibrationColorWhite.")); setTimer(TIME_TO_WAIT_FOR_SETTLE_PRE_CALIBRATION); } else if (State == State_WaitingForSettlePreCalibrationColorWhite) { // Send calibrate message to device and wait a while. Device->SetCalibrate(CALIBRATE_WHITE); State = State_WaitingForSettlePostCalibrationColorWhite; OVR_DEBUG_LOG(("State_WaitingForSettlePreCalibrationColorWhite -> State_WaitingForSettlePostCalibrationColorWhite.")); setTimer(TIME_TO_WAIT_FOR_SETTLE_POST_CALIBRATION); } else if (State == State_WaitingForSettlePostCalibrationColorWhite) { // Calibration is done. Switch to color 1 and wait for it to settle. RenderColor = COLOR1; State = State_WaitingForSettlePostMeasurement; OVR_DEBUG_LOG(("State_WaitingForSettlePostCalibrationColorWhite -> State_WaitingForSettlePostMeasurement.")); UInt32 waitTime = TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT + getRandomComponent(TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS); setTimer(waitTime); } else if (State == State_WaitingForSettlePostMeasurement) { // Prepare for next measurement. // Create a new result object. MeasurementResult* pResult = new MeasurementResult(); Results.PushBack(pResult); State = State_WaitingToTakeMeasurement; OVR_DEBUG_LOG(("State_WaitingForSettlePostMeasurement -> State_WaitingToTakeMeasurement.")); } else if (State == State_WaitingForTestStarted) { // We timed out waiting for 'TestStarted'. Abandon this measurement and setup for the next. getActiveResult()->TimedOutWaitingForTestStarted = true; State = State_WaitingForSettlePostMeasurement; OVR_DEBUG_LOG(("** Timed out waiting for 'TestStarted'.")); OVR_DEBUG_LOG(("State_WaitingForTestStarted -> State_WaitingForSettlePostMeasurement.")); UInt32 waitTime = TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT + getRandomComponent(TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS); setTimer(waitTime); } else if (State == State_WaitingForColorDetected) { // We timed out waiting for 'ColorDetected'. Abandon this measurement and setup for the next. getActiveResult()->TimedOutWaitingForColorDetected = true; State = State_WaitingForSettlePostMeasurement; OVR_DEBUG_LOG(("** Timed out waiting for 'ColorDetected'.")); OVR_DEBUG_LOG(("State_WaitingForColorDetected -> State_WaitingForSettlePostMeasurement.")); UInt32 waitTime = TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT + getRandomComponent(TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS); setTimer(waitTime); } } else if (latencyTestMessage == LatencyTest_ProcessInputs) { if (State == State_WaitingToTakeMeasurement) { if (!Device) { reset(); return; } // Send 'StartTest' feature report with opposite target color. if (RenderColor == COLOR1) { RenderColor = COLOR2; } else { RenderColor = COLOR1; } getActiveResult()->TargetColor = RenderColor; // Record time so we can determine usb roundtrip time. getActiveResult()->StartTestTicksMicroS = Timer::GetTicks(); Device->SetStartTest(RenderColor); State = State_WaitingForTestStarted; OVR_DEBUG_LOG(("State_WaitingToTakeMeasurement -> State_WaitingForTestStarted.")); setTimer(TIMEOUT_WAITING_FOR_TEST_STARTED); LatencyTestDisplay ltd(2, 0x40090040); Device->SetDisplay(ltd); } } else if (msg.Type == Message_LatencyTestButton) { BeginTest(); } else if (msg.Type == Message_LatencyTestStarted) { if (State == State_WaitingForTestStarted) { clearTimer(); // Record time so we can determine usb roundtrip time. getActiveResult()->TestStartedTicksMicroS = Timer::GetTicks(); State = State_WaitingForColorDetected; OVR_DEBUG_LOG(("State_WaitingForTestStarted -> State_WaitingForColorDetected.")); setTimer(TIMEOUT_WAITING_FOR_COLOR_DETECTED); } } else if (msg.Type == Message_LatencyTestColorDetected) { if (State == State_WaitingForColorDetected) { // Record time to detect color. MessageLatencyTestColorDetected* pDetected = (MessageLatencyTestColorDetected*) &msg; UInt16 elapsedTime = pDetected->Elapsed; OVR_DEBUG_LOG(("Time to 'ColorDetected' = %d", elapsedTime)); getActiveResult()->DeviceMeasuredElapsedMilliS = elapsedTime; if (areResultsComplete()) { // We're done. processResults(); reset(); } else { // Run another measurement. State = State_WaitingForSettlePostMeasurement; OVR_DEBUG_LOG(("State_WaitingForColorDetected -> State_WaitingForSettlePostMeasurement.")); UInt32 waitTime = TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT + getRandomComponent(TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS); setTimer(waitTime); LatencyTestDisplay ltd(2, 0x40400040); Device->SetDisplay(ltd); } } } else if (msg.Type == Message_DeviceRemoved) { reset(); } } LatencyTest::MeasurementResult* LatencyTest::getActiveResult() { OVR_ASSERT(!Results.IsEmpty()); return Results.GetLast(); } void LatencyTest::setTimer(UInt32 timeMilliS) { ActiveTimerMilliS = timeMilliS; } void LatencyTest::clearTimer() { ActiveTimerMilliS = 0; } void LatencyTest::reset() { clearMeasurementResults(); State = State_WaitingForButton; HaveOldTime = false; ActiveTimerMilliS = 0; } void LatencyTest::clearMeasurementResults() { while(!Results.IsEmpty()) { MeasurementResult* pElem = Results.GetFirst(); pElem->RemoveNode(); delete pElem; } } LatencyTest::LatencyTestHandler::~LatencyTestHandler() { RemoveHandlerFromDevices(); } void LatencyTest::LatencyTestHandler::OnMessage(const Message& msg) { pLatencyTestUtil->handleMessage(msg); } void LatencyTest::ProcessInputs() { updateForTimeouts(); handleMessage(Message(), LatencyTest_ProcessInputs); } bool LatencyTest::DisplayScreenColor(Color& colorToDisplay) { updateForTimeouts(); if (State == State_WaitingForButton) { return false; } colorToDisplay = RenderColor; return true; } const char* LatencyTest::GetResultsString() { if (!ResultsString.IsEmpty() && ReturnedResultString != ResultsString.ToCStr()) { ReturnedResultString = ResultsString; return ReturnedResultString.ToCStr(); } return NULL; } bool LatencyTest::areResultsComplete() { UInt32 initialMeasurements = 0; UInt32 measurements1to2 = 0; UInt32 measurements2to1 = 0; MeasurementResult* pCurr = Results.GetFirst(); while(true) { // Process. if (!pCurr->TimedOutWaitingForTestStarted && !pCurr->TimedOutWaitingForColorDetected) { initialMeasurements++; if (initialMeasurements > INITIAL_SAMPLES_TO_IGNORE) { if (pCurr->TargetColor == COLOR2) { measurements1to2++; } else { measurements2to1++; } } } if (Results.IsLast(pCurr)) { break; } pCurr = Results.GetNext(pCurr); } if (measurements1to2 >= DEFAULT_NUMBER_OF_SAMPLES && measurements2to1 >= DEFAULT_NUMBER_OF_SAMPLES) { return true; } return false; } void LatencyTest::processResults() { UInt32 minTime1To2 = UINT_MAX; UInt32 maxTime1To2 = 0; float averageTime1To2 = 0.0f; UInt32 minTime2To1 = UINT_MAX; UInt32 maxTime2To1 = 0; float averageTime2To1 = 0.0f; float minUSBTripMilliS = BIG_FLOAT; float maxUSBTripMilliS = SMALL_FLOAT; float averageUSBTripMilliS = 0.0f; UInt32 countUSBTripTime = 0; UInt32 measurementsCount = 0; UInt32 measurements1to2 = 0; UInt32 measurements2to1 = 0; MeasurementResult* pCurr = Results.GetFirst(); UInt32 count = 0; while(true) { count++; if (!pCurr->TimedOutWaitingForTestStarted && !pCurr->TimedOutWaitingForColorDetected) { measurementsCount++; if (measurementsCount > INITIAL_SAMPLES_TO_IGNORE) { if (pCurr->TargetColor == COLOR2) { measurements1to2++; if (measurements1to2 <= DEFAULT_NUMBER_OF_SAMPLES) { UInt32 elapsed = pCurr->DeviceMeasuredElapsedMilliS; minTime1To2 = Alg::Min(elapsed, minTime1To2); maxTime1To2 = Alg::Max(elapsed, maxTime1To2); averageTime1To2 += (float) elapsed; } } else { measurements2to1++; if (measurements2to1 <= DEFAULT_NUMBER_OF_SAMPLES) { UInt32 elapsed = pCurr->DeviceMeasuredElapsedMilliS; minTime2To1 = Alg::Min(elapsed, minTime2To1); maxTime2To1 = Alg::Max(elapsed, maxTime2To1); averageTime2To1 += (float) elapsed; } } float usbRountripElapsedMilliS = 0.001f * (float) (pCurr->TestStartedTicksMicroS - pCurr->StartTestTicksMicroS); minUSBTripMilliS = Alg::Min(usbRountripElapsedMilliS, minUSBTripMilliS); maxUSBTripMilliS = Alg::Max(usbRountripElapsedMilliS, maxUSBTripMilliS); averageUSBTripMilliS += usbRountripElapsedMilliS; countUSBTripTime++; } } if (measurements1to2 >= DEFAULT_NUMBER_OF_SAMPLES && measurements2to1 >= DEFAULT_NUMBER_OF_SAMPLES) { break; } if (Results.IsLast(pCurr)) { break; } pCurr = Results.GetNext(pCurr); } averageTime1To2 /= (float) DEFAULT_NUMBER_OF_SAMPLES; averageTime2To1 /= (float) DEFAULT_NUMBER_OF_SAMPLES; averageUSBTripMilliS /= countUSBTripTime; float finalResult = 0.5f * (averageTime1To2 + averageTime2To1); finalResult += averageUSBTripMilliS; ResultsString.Clear(); ResultsString.AppendFormat("RESULT=%.1f (add half Tracker period) [b->w %d|%.1f|%d] [w->b %d|%.1f|%d] [usb rndtrp %.1f|%.1f|%.1f] [cnt %d] [tmouts %d]", finalResult, minTime1To2, averageTime1To2, maxTime1To2, minTime2To1, averageTime2To1, maxTime2To1, minUSBTripMilliS, averageUSBTripMilliS, maxUSBTripMilliS, DEFAULT_NUMBER_OF_SAMPLES*2, count - measurementsCount); // Display result on latency tester display. LatencyTestDisplay ltd(1, (int)finalResult); Device->SetDisplay(ltd); } void LatencyTest::updateForTimeouts() { if (!HaveOldTime) { HaveOldTime = true; OldTime = Timer::GetTicksMs(); return; } UInt32 newTime = Timer::GetTicksMs(); UInt32 elapsedMilliS = newTime - OldTime; if (newTime < OldTime) { elapsedMilliS = OldTime - newTime; elapsedMilliS = UINT_MAX - elapsedMilliS; } OldTime = newTime; elapsedMilliS = Alg::Min(elapsedMilliS, (UInt32) 100); // Clamp at 100mS in case we're not being called very often. if (ActiveTimerMilliS == 0) { return; } if (elapsedMilliS >= ActiveTimerMilliS) { ActiveTimerMilliS = 0; handleMessage(Message(), LatencyTest_Timer); return; } ActiveTimerMilliS -= elapsedMilliS; } }} // namespace OVR::Util \ No newline at end of file +/************************************************************************************ + +Filename : Util_LatencyTest.cpp +Content : Wraps the lower level LatencyTester interface and adds functionality. +Created : February 14, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#include "Util_LatencyTest.h" + +#include "../Kernel/OVR_Log.h" +#include "../Kernel/OVR_Timer.h" + +namespace OVR { namespace Util { + +static const UInt32 TIME_TO_WAIT_FOR_SETTLE_PRE_CALIBRATION = 16*10; +static const UInt32 TIME_TO_WAIT_FOR_SETTLE_POST_CALIBRATION = 16*10; +static const UInt32 TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT = 16*5; +static const UInt32 TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS = 16*5; +static const UInt32 DEFAULT_NUMBER_OF_SAMPLES = 10; // For both color 1->2 and color 2->1 transitions. +static const UInt32 INITIAL_SAMPLES_TO_IGNORE = 4; +static const UInt32 TIMEOUT_WAITING_FOR_TEST_STARTED = 1000; +static const UInt32 TIMEOUT_WAITING_FOR_COLOR_DETECTED = 4000; +static const Color CALIBRATE_BLACK(0, 0, 0); +static const Color CALIBRATE_WHITE(255, 255, 255); +static const Color COLOR1(0, 0, 0); +static const Color COLOR2(255, 255, 255); +static const Color SENSOR_DETECT_THRESHOLD(128, 255, 255); +static const float BIG_FLOAT = 1000000.0f; +static const float SMALL_FLOAT = -1000000.0f; + +//------------------------------------------------------------------------------------- +// ***** LatencyTest + +LatencyTest::LatencyTest(LatencyTestDevice* device) + : Handler(getThis()) +{ + if (device != NULL) + { + SetDevice(device); + } + + reset(); + + srand(Timer::GetTicksMs()); +} + +LatencyTest::~LatencyTest() +{ + clearMeasurementResults(); +} + +bool LatencyTest::SetDevice(LatencyTestDevice* device) +{ + + if (device != Device) + { + if (device != NULL) + { + if (device->GetMessageHandler() != NULL) + { + OVR_DEBUG_LOG( + ("LatencyTest::AttachToDevice failed - device %p already has handler", device)); + return false; + } + } + + if (Device != NULL) + { + Device->SetMessageHandler(0); + } + Device = device; + + if (Device != NULL) + { + Device->SetMessageHandler(&Handler); + + // Set trigger threshold. + LatencyTestConfiguration configuration(SENSOR_DETECT_THRESHOLD, false); // No samples streaming. + Device->SetConfiguration(configuration, true); + + // Set display to intial (3 dashes). + LatencyTestDisplay ltd(2, 0x40400040); + Device->SetDisplay(ltd); + } + } + + return true; +} + +UInt32 LatencyTest::getRandomComponent(UInt32 range) +{ + UInt32 val = rand() % range; + return val; +} + +void LatencyTest::BeginTest() +{ + if (State == State_WaitingForButton) + { + // Set color to black and wait a while. + RenderColor = CALIBRATE_BLACK; + + State = State_WaitingForSettlePreCalibrationColorBlack; + OVR_DEBUG_LOG(("State_WaitingForButton -> State_WaitingForSettlePreCalibrationColorBlack.")); + + setTimer(TIME_TO_WAIT_FOR_SETTLE_PRE_CALIBRATION); + } +} + +void LatencyTest::handleMessage(const Message& msg, LatencyTestMessageType latencyTestMessage) +{ + // For debugging. +/* if (msg.Type == Message_LatencyTestSamples) + { + MessageLatencyTestSamples* pSamples = (MessageLatencyTestSamples*) &msg; + + if (pSamples->Samples.GetSize() > 0) + { + // Just show the first one for now. + Color c = pSamples->Samples[0]; + OVR_DEBUG_LOG(("%d %d %d", c.R, c.G, c.B)); + } + return; + } +*/ + + if (latencyTestMessage == LatencyTest_Timer) + { + if (!Device) + { + reset(); + return; + } + + if (State == State_WaitingForSettlePreCalibrationColorBlack) + { + // Send calibrate message to device and wait a while. + Device->SetCalibrate(CALIBRATE_BLACK); + + State = State_WaitingForSettlePostCalibrationColorBlack; + OVR_DEBUG_LOG(("State_WaitingForSettlePreCalibrationColorBlack -> State_WaitingForSettlePostCalibrationColorBlack.")); + + setTimer(TIME_TO_WAIT_FOR_SETTLE_POST_CALIBRATION); + } + else if (State == State_WaitingForSettlePostCalibrationColorBlack) + { + // Change color to white and wait a while. + RenderColor = CALIBRATE_WHITE; + + State = State_WaitingForSettlePreCalibrationColorWhite; + OVR_DEBUG_LOG(("State_WaitingForSettlePostCalibrationColorBlack -> State_WaitingForSettlePreCalibrationColorWhite.")); + + setTimer(TIME_TO_WAIT_FOR_SETTLE_PRE_CALIBRATION); + } + else if (State == State_WaitingForSettlePreCalibrationColorWhite) + { + // Send calibrate message to device and wait a while. + Device->SetCalibrate(CALIBRATE_WHITE); + + State = State_WaitingForSettlePostCalibrationColorWhite; + OVR_DEBUG_LOG(("State_WaitingForSettlePreCalibrationColorWhite -> State_WaitingForSettlePostCalibrationColorWhite.")); + + setTimer(TIME_TO_WAIT_FOR_SETTLE_POST_CALIBRATION); + } + else if (State == State_WaitingForSettlePostCalibrationColorWhite) + { + // Calibration is done. Switch to color 1 and wait for it to settle. + RenderColor = COLOR1; + + State = State_WaitingForSettlePostMeasurement; + OVR_DEBUG_LOG(("State_WaitingForSettlePostCalibrationColorWhite -> State_WaitingForSettlePostMeasurement.")); + + UInt32 waitTime = TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT + getRandomComponent(TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS); + setTimer(waitTime); + } + else if (State == State_WaitingForSettlePostMeasurement) + { + // Prepare for next measurement. + + // Create a new result object. + MeasurementResult* pResult = new MeasurementResult(); + Results.PushBack(pResult); + + State = State_WaitingToTakeMeasurement; + OVR_DEBUG_LOG(("State_WaitingForSettlePostMeasurement -> State_WaitingToTakeMeasurement.")); + } + else if (State == State_WaitingForTestStarted) + { + // We timed out waiting for 'TestStarted'. Abandon this measurement and setup for the next. + getActiveResult()->TimedOutWaitingForTestStarted = true; + + State = State_WaitingForSettlePostMeasurement; + OVR_DEBUG_LOG(("** Timed out waiting for 'TestStarted'.")); + OVR_DEBUG_LOG(("State_WaitingForTestStarted -> State_WaitingForSettlePostMeasurement.")); + + UInt32 waitTime = TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT + getRandomComponent(TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS); + setTimer(waitTime); + } + else if (State == State_WaitingForColorDetected) + { + // We timed out waiting for 'ColorDetected'. Abandon this measurement and setup for the next. + getActiveResult()->TimedOutWaitingForColorDetected = true; + + State = State_WaitingForSettlePostMeasurement; + OVR_DEBUG_LOG(("** Timed out waiting for 'ColorDetected'.")); + OVR_DEBUG_LOG(("State_WaitingForColorDetected -> State_WaitingForSettlePostMeasurement.")); + + UInt32 waitTime = TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT + getRandomComponent(TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS); + setTimer(waitTime); + } + } + else if (latencyTestMessage == LatencyTest_ProcessInputs) + { + if (State == State_WaitingToTakeMeasurement) + { + if (!Device) + { + reset(); + return; + } + + // Send 'StartTest' feature report with opposite target color. + if (RenderColor == COLOR1) + { + RenderColor = COLOR2; + } + else + { + RenderColor = COLOR1; + } + + getActiveResult()->TargetColor = RenderColor; + + // Record time so we can determine usb roundtrip time. + getActiveResult()->StartTestTicksMicroS = Timer::GetTicks(); + + Device->SetStartTest(RenderColor); + + State = State_WaitingForTestStarted; + OVR_DEBUG_LOG(("State_WaitingToTakeMeasurement -> State_WaitingForTestStarted.")); + + setTimer(TIMEOUT_WAITING_FOR_TEST_STARTED); + + LatencyTestDisplay ltd(2, 0x40090040); + Device->SetDisplay(ltd); + } + } + else if (msg.Type == Message_LatencyTestButton) + { + BeginTest(); + } + else if (msg.Type == Message_LatencyTestStarted) + { + if (State == State_WaitingForTestStarted) + { + clearTimer(); + + // Record time so we can determine usb roundtrip time. + getActiveResult()->TestStartedTicksMicroS = Timer::GetTicks(); + + State = State_WaitingForColorDetected; + OVR_DEBUG_LOG(("State_WaitingForTestStarted -> State_WaitingForColorDetected.")); + + setTimer(TIMEOUT_WAITING_FOR_COLOR_DETECTED); + } + } + else if (msg.Type == Message_LatencyTestColorDetected) + { + if (State == State_WaitingForColorDetected) + { + // Record time to detect color. + MessageLatencyTestColorDetected* pDetected = (MessageLatencyTestColorDetected*) &msg; + UInt16 elapsedTime = pDetected->Elapsed; + OVR_DEBUG_LOG(("Time to 'ColorDetected' = %d", elapsedTime)); + + getActiveResult()->DeviceMeasuredElapsedMilliS = elapsedTime; + + if (areResultsComplete()) + { + // We're done. + processResults(); + reset(); + } + else + { + // Run another measurement. + State = State_WaitingForSettlePostMeasurement; + OVR_DEBUG_LOG(("State_WaitingForColorDetected -> State_WaitingForSettlePostMeasurement.")); + + UInt32 waitTime = TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT + getRandomComponent(TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS); + setTimer(waitTime); + + LatencyTestDisplay ltd(2, 0x40400040); + Device->SetDisplay(ltd); + } + } + } + else if (msg.Type == Message_DeviceRemoved) + { + reset(); + } +} + +LatencyTest::MeasurementResult* LatencyTest::getActiveResult() +{ + OVR_ASSERT(!Results.IsEmpty()); + return Results.GetLast(); +} + +void LatencyTest::setTimer(UInt32 timeMilliS) +{ + ActiveTimerMilliS = timeMilliS; +} + +void LatencyTest::clearTimer() +{ + ActiveTimerMilliS = 0; +} + +void LatencyTest::reset() +{ + clearMeasurementResults(); + State = State_WaitingForButton; + + HaveOldTime = false; + ActiveTimerMilliS = 0; +} + +void LatencyTest::clearMeasurementResults() +{ + while(!Results.IsEmpty()) + { + MeasurementResult* pElem = Results.GetFirst(); + pElem->RemoveNode(); + delete pElem; + } +} + +LatencyTest::LatencyTestHandler::~LatencyTestHandler() +{ + RemoveHandlerFromDevices(); +} + +void LatencyTest::LatencyTestHandler::OnMessage(const Message& msg) +{ + pLatencyTestUtil->handleMessage(msg); +} + +void LatencyTest::ProcessInputs() +{ + updateForTimeouts(); + handleMessage(Message(), LatencyTest_ProcessInputs); +} + +bool LatencyTest::DisplayScreenColor(Color& colorToDisplay) +{ + updateForTimeouts(); + + if (State == State_WaitingForButton) + { + return false; + } + + colorToDisplay = RenderColor; + return true; +} + +const char* LatencyTest::GetResultsString() +{ + if (!ResultsString.IsEmpty() && ReturnedResultString != ResultsString.ToCStr()) + { + ReturnedResultString = ResultsString; + return ReturnedResultString.ToCStr(); + } + + return NULL; +} + +bool LatencyTest::areResultsComplete() +{ + UInt32 initialMeasurements = 0; + + UInt32 measurements1to2 = 0; + UInt32 measurements2to1 = 0; + + MeasurementResult* pCurr = Results.GetFirst(); + while(true) + { + // Process. + if (!pCurr->TimedOutWaitingForTestStarted && + !pCurr->TimedOutWaitingForColorDetected) + { + initialMeasurements++; + + if (initialMeasurements > INITIAL_SAMPLES_TO_IGNORE) + { + if (pCurr->TargetColor == COLOR2) + { + measurements1to2++; + } + else + { + measurements2to1++; + } + } + } + + if (Results.IsLast(pCurr)) + { + break; + } + pCurr = Results.GetNext(pCurr); + } + + if (measurements1to2 >= DEFAULT_NUMBER_OF_SAMPLES && + measurements2to1 >= DEFAULT_NUMBER_OF_SAMPLES) + { + return true; + } + + return false; +} + +void LatencyTest::processResults() +{ + + UInt32 minTime1To2 = UINT_MAX; + UInt32 maxTime1To2 = 0; + float averageTime1To2 = 0.0f; + UInt32 minTime2To1 = UINT_MAX; + UInt32 maxTime2To1 = 0; + float averageTime2To1 = 0.0f; + + float minUSBTripMilliS = BIG_FLOAT; + float maxUSBTripMilliS = SMALL_FLOAT; + float averageUSBTripMilliS = 0.0f; + UInt32 countUSBTripTime = 0; + + UInt32 measurementsCount = 0; + UInt32 measurements1to2 = 0; + UInt32 measurements2to1 = 0; + + MeasurementResult* pCurr = Results.GetFirst(); + UInt32 count = 0; + while(true) + { + count++; + + if (!pCurr->TimedOutWaitingForTestStarted && + !pCurr->TimedOutWaitingForColorDetected) + { + measurementsCount++; + + if (measurementsCount > INITIAL_SAMPLES_TO_IGNORE) + { + if (pCurr->TargetColor == COLOR2) + { + measurements1to2++; + + if (measurements1to2 <= DEFAULT_NUMBER_OF_SAMPLES) + { + UInt32 elapsed = pCurr->DeviceMeasuredElapsedMilliS; + + minTime1To2 = Alg::Min(elapsed, minTime1To2); + maxTime1To2 = Alg::Max(elapsed, maxTime1To2); + + averageTime1To2 += (float) elapsed; + } + } + else + { + measurements2to1++; + + if (measurements2to1 <= DEFAULT_NUMBER_OF_SAMPLES) + { + UInt32 elapsed = pCurr->DeviceMeasuredElapsedMilliS; + + minTime2To1 = Alg::Min(elapsed, minTime2To1); + maxTime2To1 = Alg::Max(elapsed, maxTime2To1); + + averageTime2To1 += (float) elapsed; + } + } + + float usbRountripElapsedMilliS = 0.001f * (float) (pCurr->TestStartedTicksMicroS - pCurr->StartTestTicksMicroS); + minUSBTripMilliS = Alg::Min(usbRountripElapsedMilliS, minUSBTripMilliS); + maxUSBTripMilliS = Alg::Max(usbRountripElapsedMilliS, maxUSBTripMilliS); + averageUSBTripMilliS += usbRountripElapsedMilliS; + countUSBTripTime++; + } + } + + if (measurements1to2 >= DEFAULT_NUMBER_OF_SAMPLES && + measurements2to1 >= DEFAULT_NUMBER_OF_SAMPLES) + { + break; + } + + if (Results.IsLast(pCurr)) + { + break; + } + pCurr = Results.GetNext(pCurr); + } + + averageTime1To2 /= (float) DEFAULT_NUMBER_OF_SAMPLES; + averageTime2To1 /= (float) DEFAULT_NUMBER_OF_SAMPLES; + + averageUSBTripMilliS /= countUSBTripTime; + + float finalResult = 0.5f * (averageTime1To2 + averageTime2To1); + finalResult += averageUSBTripMilliS; + + ResultsString.Clear(); + ResultsString.AppendFormat("RESULT=%.1f (add half Tracker period) [b->w %d|%.1f|%d] [w->b %d|%.1f|%d] [usb rndtrp %.1f|%.1f|%.1f] [cnt %d] [tmouts %d]", + finalResult, + minTime1To2, averageTime1To2, maxTime1To2, + minTime2To1, averageTime2To1, maxTime2To1, + minUSBTripMilliS, averageUSBTripMilliS, maxUSBTripMilliS, + DEFAULT_NUMBER_OF_SAMPLES*2, count - measurementsCount); + + // Display result on latency tester display. + LatencyTestDisplay ltd(1, (int)finalResult); + Device->SetDisplay(ltd); +} + +void LatencyTest::updateForTimeouts() +{ + if (!HaveOldTime) + { + HaveOldTime = true; + OldTime = Timer::GetTicksMs(); + return; + } + + UInt32 newTime = Timer::GetTicksMs(); + UInt32 elapsedMilliS = newTime - OldTime; + if (newTime < OldTime) + { + elapsedMilliS = OldTime - newTime; + elapsedMilliS = UINT_MAX - elapsedMilliS; + } + OldTime = newTime; + + elapsedMilliS = Alg::Min(elapsedMilliS, (UInt32) 100); // Clamp at 100mS in case we're not being called very often. + + + if (ActiveTimerMilliS == 0) + { + return; + } + + if (elapsedMilliS >= ActiveTimerMilliS) + { + ActiveTimerMilliS = 0; + handleMessage(Message(), LatencyTest_Timer); + return; + } + + ActiveTimerMilliS -= elapsedMilliS; +} + +}} // namespace OVR::Util diff -r 59fc487ba58e -r b069a5c27388 libovr/Src/Util/Util_LatencyTest.h --- a/libovr/Src/Util/Util_LatencyTest.h Sat Sep 14 17:51:03 2013 +0300 +++ b/libovr/Src/Util/Util_LatencyTest.h Sun Sep 15 04:10:05 2013 +0300 @@ -1,1 +1,160 @@ -/************************************************************************************ PublicHeader: OVR.h Filename : Util_LatencyTest.h Content : Wraps the lower level LatencyTesterDevice and adds functionality. Created : February 14, 2013 Authors : Lee Cooper Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. *************************************************************************************/ #ifndef OVR_Util_LatencyTest_h #define OVR_Util_LatencyTest_h #include "../OVR_Device.h" #include "../Kernel/OVR_String.h" #include "../Kernel/OVR_List.h" namespace OVR { namespace Util { //------------------------------------------------------------------------------------- // ***** LatencyTest // // LatencyTest utility class wraps the low level LatencyTestDevice and manages the scheduling // of a latency test. A single test is composed of a series of individual latency measurements // which are used to derive min, max, and an average latency value. // // Developers are required to call the following methods: // SetDevice - Sets the LatencyTestDevice to be used for the tests. // ProcessInputs - This should be called at the same place in the code where the game engine // reads the headset orientation from LibOVR (typically done by calling // 'GetOrientation' on the SensorFusion object). Calling this at the right time // enables us to measure the same latency that occurs for headset orientation // changes. // DisplayScreenColor - The latency tester works by sensing the color of the pixels directly // beneath it. The color of these pixels can be set by drawing a small // quad at the end of the rendering stage. The quad should be small // such that it doesn't significantly impact the rendering of the scene, // but large enough to be 'seen' by the sensor. See the SDK // documentation for more information. // GetResultsString - Call this to get a string containing the most recent results. // If the string has already been gotten then NULL will be returned. // The string pointer will remain valid until the next time this // method is called. // class LatencyTest : public NewOverrideBase { public: LatencyTest(LatencyTestDevice* device = NULL); ~LatencyTest(); // Set the Latency Tester device that we'll use to send commands to and receive // notification messages from. bool SetDevice(LatencyTestDevice* device); // Returns true if this LatencyTestUtil has a Latency Tester device. bool HasDevice() const { return Handler.IsHandlerInstalled(); } void ProcessInputs(); bool DisplayScreenColor(Color& colorToDisplay); const char* GetResultsString(); // Begin test. Equivalent to pressing the button on the latency tester. void BeginTest(); private: LatencyTest* getThis() { return this; } enum LatencyTestMessageType { LatencyTest_None, LatencyTest_Timer, LatencyTest_ProcessInputs, }; UInt32 getRandomComponent(UInt32 range); void handleMessage(const Message& msg, LatencyTestMessageType latencyTestMessage = LatencyTest_None); void reset(); void setTimer(UInt32 timeMilliS); void clearTimer(); class LatencyTestHandler : public MessageHandler { LatencyTest* pLatencyTestUtil; public: LatencyTestHandler(LatencyTest* latencyTester) : pLatencyTestUtil(latencyTester) { } ~LatencyTestHandler(); virtual void OnMessage(const Message& msg); }; bool areResultsComplete(); void processResults(); void updateForTimeouts(); Ptr Device; LatencyTestHandler Handler; enum TesterState { State_WaitingForButton, State_WaitingForSettlePreCalibrationColorBlack, State_WaitingForSettlePostCalibrationColorBlack, State_WaitingForSettlePreCalibrationColorWhite, State_WaitingForSettlePostCalibrationColorWhite, State_WaitingToTakeMeasurement, State_WaitingForTestStarted, State_WaitingForColorDetected, State_WaitingForSettlePostMeasurement }; TesterState State; bool HaveOldTime; UInt32 OldTime; UInt32 ActiveTimerMilliS; Color RenderColor; struct MeasurementResult : public ListNode, public NewOverrideBase { MeasurementResult() : DeviceMeasuredElapsedMilliS(0), TimedOutWaitingForTestStarted(false), TimedOutWaitingForColorDetected(false), StartTestTicksMicroS(0), TestStartedTicksMicroS(0) {} Color TargetColor; UInt32 DeviceMeasuredElapsedMilliS; bool TimedOutWaitingForTestStarted; bool TimedOutWaitingForColorDetected; UInt64 StartTestTicksMicroS; UInt64 TestStartedTicksMicroS; }; List Results; void clearMeasurementResults(); MeasurementResult* getActiveResult(); StringBuffer ResultsString; String ReturnedResultString; }; }} // namespace OVR::Util #endif // OVR_Util_LatencyTest_h \ No newline at end of file +/************************************************************************************ + +PublicHeader: OVR.h +Filename : Util_LatencyTest.h +Content : Wraps the lower level LatencyTesterDevice and adds functionality. +Created : February 14, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_Util_LatencyTest_h +#define OVR_Util_LatencyTest_h + +#include "../OVR_Device.h" + +#include "../Kernel/OVR_String.h" +#include "../Kernel/OVR_List.h" + +namespace OVR { namespace Util { + + +//------------------------------------------------------------------------------------- +// ***** LatencyTest +// +// LatencyTest utility class wraps the low level LatencyTestDevice and manages the scheduling +// of a latency test. A single test is composed of a series of individual latency measurements +// which are used to derive min, max, and an average latency value. +// +// Developers are required to call the following methods: +// SetDevice - Sets the LatencyTestDevice to be used for the tests. +// ProcessInputs - This should be called at the same place in the code where the game engine +// reads the headset orientation from LibOVR (typically done by calling +// 'GetOrientation' on the SensorFusion object). Calling this at the right time +// enables us to measure the same latency that occurs for headset orientation +// changes. +// DisplayScreenColor - The latency tester works by sensing the color of the pixels directly +// beneath it. The color of these pixels can be set by drawing a small +// quad at the end of the rendering stage. The quad should be small +// such that it doesn't significantly impact the rendering of the scene, +// but large enough to be 'seen' by the sensor. See the SDK +// documentation for more information. +// GetResultsString - Call this to get a string containing the most recent results. +// If the string has already been gotten then NULL will be returned. +// The string pointer will remain valid until the next time this +// method is called. +// + +class LatencyTest : public NewOverrideBase +{ +public: + LatencyTest(LatencyTestDevice* device = NULL); + ~LatencyTest(); + + // Set the Latency Tester device that we'll use to send commands to and receive + // notification messages from. + bool SetDevice(LatencyTestDevice* device); + + // Returns true if this LatencyTestUtil has a Latency Tester device. + bool HasDevice() const + { return Handler.IsHandlerInstalled(); } + + void ProcessInputs(); + bool DisplayScreenColor(Color& colorToDisplay); + const char* GetResultsString(); + + // Begin test. Equivalent to pressing the button on the latency tester. + void BeginTest(); + +private: + LatencyTest* getThis() { return this; } + + enum LatencyTestMessageType + { + LatencyTest_None, + LatencyTest_Timer, + LatencyTest_ProcessInputs, + }; + + UInt32 getRandomComponent(UInt32 range); + void handleMessage(const Message& msg, LatencyTestMessageType latencyTestMessage = LatencyTest_None); + void reset(); + void setTimer(UInt32 timeMilliS); + void clearTimer(); + + class LatencyTestHandler : public MessageHandler + { + LatencyTest* pLatencyTestUtil; + public: + LatencyTestHandler(LatencyTest* latencyTester) : pLatencyTestUtil(latencyTester) { } + ~LatencyTestHandler(); + + virtual void OnMessage(const Message& msg); + }; + + bool areResultsComplete(); + void processResults(); + void updateForTimeouts(); + + Ptr Device; + LatencyTestHandler Handler; + + enum TesterState + { + State_WaitingForButton, + State_WaitingForSettlePreCalibrationColorBlack, + State_WaitingForSettlePostCalibrationColorBlack, + State_WaitingForSettlePreCalibrationColorWhite, + State_WaitingForSettlePostCalibrationColorWhite, + State_WaitingToTakeMeasurement, + State_WaitingForTestStarted, + State_WaitingForColorDetected, + State_WaitingForSettlePostMeasurement + }; + TesterState State; + + bool HaveOldTime; + UInt32 OldTime; + UInt32 ActiveTimerMilliS; + + Color RenderColor; + + struct MeasurementResult : public ListNode, public NewOverrideBase + { + MeasurementResult() + : DeviceMeasuredElapsedMilliS(0), + TimedOutWaitingForTestStarted(false), + TimedOutWaitingForColorDetected(false), + StartTestTicksMicroS(0), + TestStartedTicksMicroS(0) + {} + + Color TargetColor; + + UInt32 DeviceMeasuredElapsedMilliS; + + bool TimedOutWaitingForTestStarted; + bool TimedOutWaitingForColorDetected; + + UInt64 StartTestTicksMicroS; + UInt64 TestStartedTicksMicroS; + }; + + List Results; + void clearMeasurementResults(); + + MeasurementResult* getActiveResult(); + + StringBuffer ResultsString; + String ReturnedResultString; +}; + +}} // namespace OVR::Util + +#endif // OVR_Util_LatencyTest_h diff -r 59fc487ba58e -r b069a5c27388 libovr/Src/Util/Util_MagCalibration.cpp --- a/libovr/Src/Util/Util_MagCalibration.cpp Sat Sep 14 17:51:03 2013 +0300 +++ b/libovr/Src/Util/Util_MagCalibration.cpp Sun Sep 15 04:10:05 2013 +0300 @@ -1,1 +1,227 @@ -/************************************************************************************ Filename : Util_MagCalibration.cpp Content : Procedures for calibrating the magnetometer Created : April 16, 2013 Authors : Steve LaValle, Andrew Reisse Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. *************************************************************************************/ #include "Util_MagCalibration.h" namespace OVR { namespace Util { void MagCalibration::BeginAutoCalibration(SensorFusion& sf) { Stat = Mag_AutoCalibrating; // This is a "hard" reset of the mag, so need to clear stored values sf.ClearMagCalibration(); SampleCount = 0; // reset the statistics MinMagValues = Vector3f(10000.0f,10000.0f,10000.0f); MaxMagValues = Vector3f(-10000.0f,-10000.0f,-10000.0f); MinQuatValues = Quatf(1.0f,1.0f,1.0f,1.0f); MaxQuatValues = Quatf(0.0f,0.0f,0.0f,0.0f); } unsigned MagCalibration::UpdateAutoCalibration(SensorFusion& sf) { if (Stat != Mag_AutoCalibrating) return Stat; Quatf q = sf.GetOrientation(); Vector3f m = sf.GetMagnetometer(); InsertIfAcceptable(q, m); if ((SampleCount == 4) && (Stat == Mag_AutoCalibrating)) { //LogText("Magnetometer Output Spread: %f %f %f\n",MagSpread.x,MagSpread.y,MagSpread.z); //LogText("Quaternion Spread: %f %f %f %f\n",QuatSpread.x,QuatSpread.y,QuatSpread.z,QuatSpread.w); SetCalibration(sf); } return Stat; } void MagCalibration::BeginManualCalibration(SensorFusion& sf) { Stat = Mag_ManuallyCalibrating; sf.ClearMagCalibration(); SampleCount = 0; } bool MagCalibration::IsAcceptableSample(const Quatf& q, const Vector3f& m) { switch (SampleCount) { // Initial sample is always acceptable case 0: return true; break; case 1: return (q.DistanceSq(QuatSamples[0]) > MinQuatDistanceSq)&& ((m - MagSamples[0]).LengthSq() > MinMagDistanceSq); break; case 2: return (q.DistanceSq(QuatSamples[0]) > MinQuatDistanceSq)&& (q.DistanceSq(QuatSamples[1]) > MinQuatDistanceSq)&& ((m - MagSamples[0]).LengthSq() > MinMagDistanceSq)&& ((m - MagSamples[1]).LengthSq() > MinMagDistanceSq); break; case 3: return (q.DistanceSq(QuatSamples[0]) > MinQuatDistanceSq)&& (q.DistanceSq(QuatSamples[1]) > MinQuatDistanceSq)&& (q.DistanceSq(QuatSamples[2]) > MinQuatDistanceSq)&& ((PointToPlaneDistance(MagSamples[0],MagSamples[1],MagSamples[2],m) > MinMagDistance)|| (PointToPlaneDistance(MagSamples[1],MagSamples[2],m,MagSamples[0]) > MinMagDistance)|| (PointToPlaneDistance(MagSamples[2],m,MagSamples[0],MagSamples[1]) > MinMagDistance)|| (PointToPlaneDistance(m,MagSamples[0],MagSamples[1],MagSamples[2]) > MinMagDistance)); } return false; } bool MagCalibration::InsertIfAcceptable(const Quatf& q, const Vector3f& m) { // Update some statistics if (m.x < MinMagValues.x) MinMagValues.x = m.x; if (m.y < MinMagValues.y) MinMagValues.y = m.y; if (m.z < MinMagValues.z) MinMagValues.z = m.z; if (m.x > MaxMagValues.x) MaxMagValues.x = m.x; if (m.y > MaxMagValues.y) MaxMagValues.y = m.y; if (m.z > MaxMagValues.z) MaxMagValues.z = m.z; if (q.x < MinQuatValues.x) MinQuatValues.x = q.x; if (q.y < MinQuatValues.y) MinQuatValues.y = q.y; if (q.z < MinQuatValues.z) MinQuatValues.z = q.z; if (q.w < MinQuatValues.w) MinQuatValues.w = q.w; if (q.x > MaxQuatValues.x) MaxQuatValues.x = q.x; if (q.y > MaxQuatValues.y) MaxQuatValues.y = q.y; if (q.z > MaxQuatValues.z) MaxQuatValues.z = q.z; if (q.w > MaxQuatValues.w) MaxQuatValues.w = q.w; MagSpread = MaxMagValues - MinMagValues; QuatSpread = MaxQuatValues - MinQuatValues; if (IsAcceptableSample(q, m)) { MagSamples[SampleCount] = m; QuatSamples[SampleCount] = q; SampleCount++; return true; } return false; } Matrix4f MagCalibration::GetMagCalibration() const { Matrix4f calMat = Matrix4f(); calMat.M[0][3] = -MagCenter.x; calMat.M[1][3] = -MagCenter.y; calMat.M[2][3] = -MagCenter.z; return calMat; } bool MagCalibration::SetCalibration(SensorFusion& sf) { if (SampleCount < 4) return false; MagCenter = CalculateSphereCenter(MagSamples[0],MagSamples[1],MagSamples[2],MagSamples[3]); Matrix4f calMat = GetMagCalibration(); sf.SetMagCalibration(calMat); Stat = Mag_Calibrated; //LogText("MagCenter: %f %f %f\n",MagCenter.x,MagCenter.y,MagCenter.z); return true; } // Calculate the center of a sphere that passes through p1, p2, p3, p4 Vector3f MagCalibration::CalculateSphereCenter(const Vector3f& p1, const Vector3f& p2, const Vector3f& p3, const Vector3f& p4) { Matrix4f A; int i; Vector3f p[4]; p[0] = p1; p[1] = p2; p[2] = p3; p[3] = p4; for (i = 0; i < 4; i++) { A.M[i][0] = p[i].x; A.M[i][1] = p[i].y; A.M[i][2] = p[i].z; A.M[i][3] = 1.0f; } float m11 = A.Determinant(); OVR_ASSERT(m11 != 0.0f); for (i = 0; i < 4; i++) { A.M[i][0] = p[i].x*p[i].x + p[i].y*p[i].y + p[i].z*p[i].z; A.M[i][1] = p[i].y; A.M[i][2] = p[i].z; A.M[i][3] = 1.0f; } float m12 = A.Determinant(); for (i = 0; i < 4; i++) { A.M[i][0] = p[i].x*p[i].x + p[i].y*p[i].y + p[i].z*p[i].z; A.M[i][1] = p[i].x; A.M[i][2] = p[i].z; A.M[i][3] = 1.0f; } float m13 = A.Determinant(); for (i = 0; i < 4; i++) { A.M[i][0] = p[i].x*p[i].x + p[i].y*p[i].y + p[i].z*p[i].z; A.M[i][1] = p[i].x; A.M[i][2] = p[i].y; A.M[i][3] = 1.0f; } float m14 = A.Determinant(); float c = 0.5f / m11; return Vector3f(c*m12, -c*m13, c*m14); } // Distance from p4 to the nearest point on a plane through p1, p2, p3 float MagCalibration::PointToPlaneDistance(const Vector3f& p1, const Vector3f& p2, const Vector3f& p3, const Vector3f& p4) { Vector3f v1 = p1 - p2; Vector3f v2 = p1 - p3; Vector3f planeNormal = v1.Cross(v2); planeNormal.Normalize(); return (fabs((planeNormal * p4) - planeNormal * p1)); } }} \ No newline at end of file +/************************************************************************************ + +Filename : Util_MagCalibration.cpp +Content : Procedures for calibrating the magnetometer +Created : April 16, 2013 +Authors : Steve LaValle, Andrew Reisse + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#include "Util_MagCalibration.h" + +namespace OVR { namespace Util { + +void MagCalibration::BeginAutoCalibration(SensorFusion& sf) +{ + Stat = Mag_AutoCalibrating; + // This is a "hard" reset of the mag, so need to clear stored values + sf.ClearMagCalibration(); + SampleCount = 0; + + // reset the statistics + MinMagValues = Vector3f(10000.0f,10000.0f,10000.0f); + MaxMagValues = Vector3f(-10000.0f,-10000.0f,-10000.0f); + MinQuatValues = Quatf(1.0f,1.0f,1.0f,1.0f); + MaxQuatValues = Quatf(0.0f,0.0f,0.0f,0.0f); +} + +unsigned MagCalibration::UpdateAutoCalibration(SensorFusion& sf) +{ + if (Stat != Mag_AutoCalibrating) + return Stat; + + Quatf q = sf.GetOrientation(); + Vector3f m = sf.GetMagnetometer(); + + InsertIfAcceptable(q, m); + + if ((SampleCount == 4) && (Stat == Mag_AutoCalibrating)) + { + //LogText("Magnetometer Output Spread: %f %f %f\n",MagSpread.x,MagSpread.y,MagSpread.z); + //LogText("Quaternion Spread: %f %f %f %f\n",QuatSpread.x,QuatSpread.y,QuatSpread.z,QuatSpread.w); + SetCalibration(sf); + } + + return Stat; + +} + +void MagCalibration::BeginManualCalibration(SensorFusion& sf) +{ + Stat = Mag_ManuallyCalibrating; + sf.ClearMagCalibration(); + SampleCount = 0; +} + +bool MagCalibration::IsAcceptableSample(const Quatf& q, const Vector3f& m) +{ + switch (SampleCount) + { + // Initial sample is always acceptable + case 0: + return true; + break; + case 1: + return (q.DistanceSq(QuatSamples[0]) > MinQuatDistanceSq)&& + ((m - MagSamples[0]).LengthSq() > MinMagDistanceSq); + break; + case 2: + return (q.DistanceSq(QuatSamples[0]) > MinQuatDistanceSq)&& + (q.DistanceSq(QuatSamples[1]) > MinQuatDistanceSq)&& + ((m - MagSamples[0]).LengthSq() > MinMagDistanceSq)&& + ((m - MagSamples[1]).LengthSq() > MinMagDistanceSq); + break; + case 3: + return (q.DistanceSq(QuatSamples[0]) > MinQuatDistanceSq)&& + (q.DistanceSq(QuatSamples[1]) > MinQuatDistanceSq)&& + (q.DistanceSq(QuatSamples[2]) > MinQuatDistanceSq)&& + ((PointToPlaneDistance(MagSamples[0],MagSamples[1],MagSamples[2],m) > MinMagDistance)|| + (PointToPlaneDistance(MagSamples[1],MagSamples[2],m,MagSamples[0]) > MinMagDistance)|| + (PointToPlaneDistance(MagSamples[2],m,MagSamples[0],MagSamples[1]) > MinMagDistance)|| + (PointToPlaneDistance(m,MagSamples[0],MagSamples[1],MagSamples[2]) > MinMagDistance)); + } + + return false; +} + + +bool MagCalibration::InsertIfAcceptable(const Quatf& q, const Vector3f& m) +{ + // Update some statistics + if (m.x < MinMagValues.x) + MinMagValues.x = m.x; + if (m.y < MinMagValues.y) + MinMagValues.y = m.y; + if (m.z < MinMagValues.z) + MinMagValues.z = m.z; + if (m.x > MaxMagValues.x) + MaxMagValues.x = m.x; + if (m.y > MaxMagValues.y) + MaxMagValues.y = m.y; + if (m.z > MaxMagValues.z) + MaxMagValues.z = m.z; + if (q.x < MinQuatValues.x) + MinQuatValues.x = q.x; + if (q.y < MinQuatValues.y) + MinQuatValues.y = q.y; + if (q.z < MinQuatValues.z) + MinQuatValues.z = q.z; + if (q.w < MinQuatValues.w) + MinQuatValues.w = q.w; + if (q.x > MaxQuatValues.x) + MaxQuatValues.x = q.x; + if (q.y > MaxQuatValues.y) + MaxQuatValues.y = q.y; + if (q.z > MaxQuatValues.z) + MaxQuatValues.z = q.z; + if (q.w > MaxQuatValues.w) + MaxQuatValues.w = q.w; + MagSpread = MaxMagValues - MinMagValues; + QuatSpread = MaxQuatValues - MinQuatValues; + + if (IsAcceptableSample(q, m)) + { + MagSamples[SampleCount] = m; + QuatSamples[SampleCount] = q; + SampleCount++; + return true; + } + + return false; +} + +Matrix4f MagCalibration::GetMagCalibration() const +{ + Matrix4f calMat = Matrix4f(); + calMat.M[0][3] = -MagCenter.x; + calMat.M[1][3] = -MagCenter.y; + calMat.M[2][3] = -MagCenter.z; + return calMat; +} + +bool MagCalibration::SetCalibration(SensorFusion& sf) +{ + if (SampleCount < 4) + return false; + + MagCenter = CalculateSphereCenter(MagSamples[0],MagSamples[1],MagSamples[2],MagSamples[3]); + Matrix4f calMat = GetMagCalibration(); + sf.SetMagCalibration(calMat); + Stat = Mag_Calibrated; + //LogText("MagCenter: %f %f %f\n",MagCenter.x,MagCenter.y,MagCenter.z); + + return true; +} + + +// Calculate the center of a sphere that passes through p1, p2, p3, p4 +Vector3f MagCalibration::CalculateSphereCenter(const Vector3f& p1, const Vector3f& p2, + const Vector3f& p3, const Vector3f& p4) +{ + Matrix4f A; + int i; + Vector3f p[4]; + p[0] = p1; + p[1] = p2; + p[2] = p3; + p[3] = p4; + + for (i = 0; i < 4; i++) + { + A.M[i][0] = p[i].x; + A.M[i][1] = p[i].y; + A.M[i][2] = p[i].z; + A.M[i][3] = 1.0f; + } + float m11 = A.Determinant(); + OVR_ASSERT(m11 != 0.0f); + + for (i = 0; i < 4; i++) + { + A.M[i][0] = p[i].x*p[i].x + p[i].y*p[i].y + p[i].z*p[i].z; + A.M[i][1] = p[i].y; + A.M[i][2] = p[i].z; + A.M[i][3] = 1.0f; + } + float m12 = A.Determinant(); + + for (i = 0; i < 4; i++) + { + A.M[i][0] = p[i].x*p[i].x + p[i].y*p[i].y + p[i].z*p[i].z; + A.M[i][1] = p[i].x; + A.M[i][2] = p[i].z; + A.M[i][3] = 1.0f; + } + float m13 = A.Determinant(); + + for (i = 0; i < 4; i++) + { + A.M[i][0] = p[i].x*p[i].x + p[i].y*p[i].y + p[i].z*p[i].z; + A.M[i][1] = p[i].x; + A.M[i][2] = p[i].y; + A.M[i][3] = 1.0f; + } + float m14 = A.Determinant(); + + float c = 0.5f / m11; + return Vector3f(c*m12, -c*m13, c*m14); +} + +// Distance from p4 to the nearest point on a plane through p1, p2, p3 +float MagCalibration::PointToPlaneDistance(const Vector3f& p1, const Vector3f& p2, + const Vector3f& p3, const Vector3f& p4) +{ + Vector3f v1 = p1 - p2; + Vector3f v2 = p1 - p3; + Vector3f planeNormal = v1.Cross(v2); + planeNormal.Normalize(); + return (fabs((planeNormal * p4) - planeNormal * p1)); +} + +}} diff -r 59fc487ba58e -r b069a5c27388 libovr/Src/Util/Util_MagCalibration.h --- a/libovr/Src/Util/Util_MagCalibration.h Sat Sep 14 17:51:03 2013 +0300 +++ b/libovr/Src/Util/Util_MagCalibration.h Sun Sep 15 04:10:05 2013 +0300 @@ -1,1 +1,138 @@ -/************************************************************************************ PublicHeader: OVR.h Filename : Util_MagCalibration.h Content : Procedures for calibrating the magnetometer Created : April 16, 2013 Authors : Steve LaValle, Andrew Reisse Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. *************************************************************************************/ #ifndef OVR_Util_MagCalibration_h #define OVR_Util_MagCalibration_h #include "../OVR_SensorFusion.h" #include "../Kernel/OVR_String.h" #include "../Kernel/OVR_Log.h" namespace OVR { namespace Util { class MagCalibration { public: enum MagStatus { Mag_Uninitialized = 0, Mag_AutoCalibrating = 1, Mag_ManuallyCalibrating = 2, Mag_Calibrated = 3 }; MagCalibration() : Stat(Mag_Uninitialized), MinMagDistance(0.2f), MinQuatDistance(0.5f), SampleCount(0) { MinMagDistanceSq = MinMagDistance * MinMagDistance; MinQuatDistanceSq = MinQuatDistance * MinQuatDistance; MinMagValues = Vector3f(10000.0f,10000.0f,10000.0f); MaxMagValues = Vector3f(-10000.0f,-10000.0f,-10000.0f); MinQuatValues = Quatf(1.0f,1.0f,1.0f,1.0f); MaxQuatValues = Quatf(0.0f,0.0f,0.0f,0.0f); } // Methods that are useful for either auto or manual calibration bool IsUnitialized() const { return Stat == Mag_Uninitialized; } bool IsCalibrated() const { return Stat == Mag_Calibrated; } int NumberOfSamples() const { return SampleCount; } int RequiredSampleCount() const { return 4; } void AbortCalibration() { Stat = Mag_Uninitialized; SampleCount = 0; } void ClearCalibration(SensorFusion& sf) { Stat = Mag_Uninitialized; SampleCount = 0; sf.ClearMagCalibration(); }; // Methods for automatic magnetometer calibration void BeginAutoCalibration(SensorFusion& sf); unsigned UpdateAutoCalibration(SensorFusion& sf); bool IsAutoCalibrating() const { return Stat == Mag_AutoCalibrating; } // Methods for building a manual (user-guided) calibraton procedure void BeginManualCalibration(SensorFusion& sf); bool IsAcceptableSample(const Quatf& q, const Vector3f& m); bool InsertIfAcceptable(const Quatf& q, const Vector3f& m); // Returns true if successful, requiring that SampleCount = 4 bool SetCalibration(SensorFusion& sf); bool IsManuallyCalibrating() const { return Stat == Mag_ManuallyCalibrating; } // This is the minimum acceptable distance (Euclidean) between raw // magnetometer values to be acceptable for usage in calibration. void SetMinMagDistance(float dist) { MinMagDistance = dist; MinMagDistanceSq = MinMagDistance * MinMagDistance; } // The minimum acceptable distance (4D Euclidean) between orientations // to be acceptable for calibration usage. void SetMinQuatDistance(float dist) { MinQuatDistance = dist; MinQuatDistanceSq = MinQuatDistance * MinQuatDistance; } // A result of the calibration, which is the center of a sphere that // roughly approximates the magnetometer data. Vector3f GetMagCenter() const { return MagCenter; } // Retrieves the full magnetometer calibration matrix Matrix4f GetMagCalibration() const; // Retrieves the range of each quaternion term during calibration Quatf GetCalibrationQuatSpread() const { return QuatSpread; } // Retrieves the range of each magnetometer term during calibration Vector3f GetCalibrationMagSpread() const { return MagSpread; } private: // Determine the unique sphere through 4 non-coplanar points Vector3f CalculateSphereCenter(const Vector3f& p1, const Vector3f& p2, const Vector3f& p3, const Vector3f& p4); // Distance from p4 to the nearest point on a plane through p1, p2, p3 float PointToPlaneDistance(const Vector3f& p1, const Vector3f& p2, const Vector3f& p3, const Vector3f& p4); Vector3f MagCenter; unsigned Stat; float MinMagDistance; float MinQuatDistance; float MinMagDistanceSq; float MinQuatDistanceSq; // For gathering statistics during calibration Vector3f MinMagValues; Vector3f MaxMagValues; Vector3f MagSpread; Quatf MinQuatValues; Quatf MaxQuatValues; Quatf QuatSpread; unsigned SampleCount; Vector3f MagSamples[4]; Quatf QuatSamples[4]; }; }} #endif \ No newline at end of file +/************************************************************************************ + +PublicHeader: OVR.h +Filename : Util_MagCalibration.h +Content : Procedures for calibrating the magnetometer +Created : April 16, 2013 +Authors : Steve LaValle, Andrew Reisse + +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_Util_MagCalibration_h +#define OVR_Util_MagCalibration_h + +#include "../OVR_SensorFusion.h" +#include "../Kernel/OVR_String.h" +#include "../Kernel/OVR_Log.h" + +namespace OVR { namespace Util { + +class MagCalibration +{ +public: + enum MagStatus + { + Mag_Uninitialized = 0, + Mag_AutoCalibrating = 1, + Mag_ManuallyCalibrating = 2, + Mag_Calibrated = 3 + }; + + MagCalibration() : + Stat(Mag_Uninitialized), + MinMagDistance(0.2f), MinQuatDistance(0.5f), + SampleCount(0) + { + MinMagDistanceSq = MinMagDistance * MinMagDistance; + MinQuatDistanceSq = MinQuatDistance * MinQuatDistance; + MinMagValues = Vector3f(10000.0f,10000.0f,10000.0f); + MaxMagValues = Vector3f(-10000.0f,-10000.0f,-10000.0f); + MinQuatValues = Quatf(1.0f,1.0f,1.0f,1.0f); + MaxQuatValues = Quatf(0.0f,0.0f,0.0f,0.0f); + } + + // Methods that are useful for either auto or manual calibration + bool IsUnitialized() const { return Stat == Mag_Uninitialized; } + bool IsCalibrated() const { return Stat == Mag_Calibrated; } + int NumberOfSamples() const { return SampleCount; } + int RequiredSampleCount() const { return 4; } + void AbortCalibration() + { + Stat = Mag_Uninitialized; + SampleCount = 0; + } + + void ClearCalibration(SensorFusion& sf) + { + Stat = Mag_Uninitialized; + SampleCount = 0; + sf.ClearMagCalibration(); + }; + + // Methods for automatic magnetometer calibration + void BeginAutoCalibration(SensorFusion& sf); + unsigned UpdateAutoCalibration(SensorFusion& sf); + bool IsAutoCalibrating() const { return Stat == Mag_AutoCalibrating; } + + // Methods for building a manual (user-guided) calibraton procedure + void BeginManualCalibration(SensorFusion& sf); + bool IsAcceptableSample(const Quatf& q, const Vector3f& m); + bool InsertIfAcceptable(const Quatf& q, const Vector3f& m); + // Returns true if successful, requiring that SampleCount = 4 + bool SetCalibration(SensorFusion& sf); + bool IsManuallyCalibrating() const { return Stat == Mag_ManuallyCalibrating; } + + // This is the minimum acceptable distance (Euclidean) between raw + // magnetometer values to be acceptable for usage in calibration. + void SetMinMagDistance(float dist) + { + MinMagDistance = dist; + MinMagDistanceSq = MinMagDistance * MinMagDistance; + } + + // The minimum acceptable distance (4D Euclidean) between orientations + // to be acceptable for calibration usage. + void SetMinQuatDistance(float dist) + { + MinQuatDistance = dist; + MinQuatDistanceSq = MinQuatDistance * MinQuatDistance; + } + + // A result of the calibration, which is the center of a sphere that + // roughly approximates the magnetometer data. + Vector3f GetMagCenter() const { return MagCenter; } + // Retrieves the full magnetometer calibration matrix + Matrix4f GetMagCalibration() const; + // Retrieves the range of each quaternion term during calibration + Quatf GetCalibrationQuatSpread() const { return QuatSpread; } + // Retrieves the range of each magnetometer term during calibration + Vector3f GetCalibrationMagSpread() const { return MagSpread; } + +private: + // Determine the unique sphere through 4 non-coplanar points + Vector3f CalculateSphereCenter(const Vector3f& p1, const Vector3f& p2, + const Vector3f& p3, const Vector3f& p4); + + // Distance from p4 to the nearest point on a plane through p1, p2, p3 + float PointToPlaneDistance(const Vector3f& p1, const Vector3f& p2, + const Vector3f& p3, const Vector3f& p4); + + Vector3f MagCenter; + unsigned Stat; + float MinMagDistance; + float MinQuatDistance; + float MinMagDistanceSq; + float MinQuatDistanceSq; + // For gathering statistics during calibration + Vector3f MinMagValues; + Vector3f MaxMagValues; + Vector3f MagSpread; + Quatf MinQuatValues; + Quatf MaxQuatValues; + Quatf QuatSpread; + + unsigned SampleCount; + Vector3f MagSamples[4]; + Quatf QuatSamples[4]; + +}; + +}} + +#endif diff -r 59fc487ba58e -r b069a5c27388 libovr/Src/Util/Util_Render_Stereo.cpp --- a/libovr/Src/Util/Util_Render_Stereo.cpp Sat Sep 14 17:51:03 2013 +0300 +++ b/libovr/Src/Util/Util_Render_Stereo.cpp Sun Sep 15 04:10:05 2013 +0300 @@ -1,1 +1,314 @@ -/************************************************************************************ Filename : Util_Render_Stereo.cpp Content : Stereo rendering configuration implementation Created : October 22, 2012 Authors : Michael Antonov, Andrew Reisse Copyright : Copyright 2012 Oculus, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus Inc license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. *************************************************************************************/ #include "Util_Render_Stereo.h" namespace OVR { namespace Util { namespace Render { //----------------------------------------------------------------------------------- // DistortionFnInverse computes the inverse of the distortion function on an argument. float DistortionConfig::DistortionFnInverse(float r) { OVR_ASSERT((r <= 10.0f)); float s, d; float delta = r * 0.25f; s = r * 0.5f; d = fabs(r - DistortionFn(s)); for (int i = 0; i < 20; i++) { float sUp = s + delta; float sDown = s - delta; float dUp = fabs(r - DistortionFn(sUp)); float dDown = fabs(r - DistortionFn(sDown)); if (dUp < d) { s = sUp; d = dUp; } else if (dDown < d) { s = sDown; d = dDown; } else { delta *= 0.5f; } } return s; } //----------------------------------------------------------------------------------- // **** StereoConfig Implementation StereoConfig::StereoConfig(StereoMode mode, const Viewport& vp) : Mode(mode), InterpupillaryDistance(0.064f), AspectMultiplier(1.0f), FullView(vp), DirtyFlag(true), IPDOverride(false), YFov(0), Aspect(vp.w / float(vp.h)), ProjectionCenterOffset(0), OrthoPixelOffset(0) { // And default distortion for it. Distortion.SetCoefficients(1.0f, 0.22f, 0.24f); Distortion.Scale = 1.0f; // Will be computed later. // Fit left of the image. DistortionFitX = -1.0f; DistortionFitY = 0.0f; // Initialize "fake" default HMD values for testing without HMD plugged in. // These default values match those returned by the HMD. HMD.HResolution = 1280; HMD.VResolution = 800; HMD.HScreenSize = 0.14976f; HMD.VScreenSize = HMD.HScreenSize / (1280.0f / 800.0f); HMD.InterpupillaryDistance = InterpupillaryDistance; HMD.LensSeparationDistance = 0.0635f; HMD.EyeToScreenDistance = 0.041f; HMD.DistortionK[0] = Distortion.K[0]; HMD.DistortionK[1] = Distortion.K[1]; HMD.DistortionK[2] = Distortion.K[2]; HMD.DistortionK[3] = 0; Set2DAreaFov(DegreeToRad(85.0f)); } void StereoConfig::SetFullViewport(const Viewport& vp) { if (vp != FullView) { FullView = vp; DirtyFlag = true; } } void StereoConfig::SetHMDInfo(const HMDInfo& hmd) { HMD = hmd; Distortion.K[0] = hmd.DistortionK[0]; Distortion.K[1] = hmd.DistortionK[1]; Distortion.K[2] = hmd.DistortionK[2]; Distortion.K[3] = hmd.DistortionK[3]; Distortion.SetChromaticAberration(hmd.ChromaAbCorrection[0], hmd.ChromaAbCorrection[1], hmd.ChromaAbCorrection[2], hmd.ChromaAbCorrection[3]); if (!IPDOverride) InterpupillaryDistance = HMD.InterpupillaryDistance; DirtyFlag = true; } void StereoConfig::SetDistortionFitPointVP(float x, float y) { DistortionFitX = x; DistortionFitY = y; DirtyFlag = true; } void StereoConfig::SetDistortionFitPointPixels(float x, float y) { DistortionFitX = (4 * x / float(FullView.w)) - 1.0f; DistortionFitY = (2 * y / float(FullView.h)) - 1.0f; DirtyFlag = true; } void StereoConfig::Set2DAreaFov(float fovRadians) { Area2DFov = fovRadians; DirtyFlag = true; } const StereoEyeParams& StereoConfig::GetEyeRenderParams(StereoEye eye) { static const UByte eyeParamIndices[3] = { 0, 0, 1 }; updateIfDirty(); OVR_ASSERT(eye < sizeof(eyeParamIndices)); return EyeRenderParams[eyeParamIndices[eye]]; } void StereoConfig::updateComputedState() { // Need to compute all of the following: // - Aspect Ratio // - FOV // - Projection offsets for 3D // - Distortion XCenterOffset // - Update 2D // - Initialize EyeRenderParams // Compute aspect ratio. Stereo mode cuts width in half. Aspect = float(FullView.w) / float(FullView.h); Aspect *= (Mode == Stereo_None) ? 1.0f : 0.5f; Aspect *= AspectMultiplier; updateDistortionOffsetAndScale(); // Compute Vertical FOV based on distance, distortion, etc. // Distance from vertical center to render vertical edge perceived through the lens. // This will be larger then normal screen size due to magnification & distortion. // // This percievedHalfRTDistance equation should hold as long as the render target // and display have the same aspect ratios. What we'd like to know is where the edge // of the render target will on the perceived screen surface. With NO LENS, // the answer would be: // // halfRTDistance = (VScreenSize / 2) * aspect * // DistortionFn_Inverse( DistortionScale / aspect ) // // To model the optical lens we eliminates DistortionFn_Inverse. Aspect ratios // cancel out, so we get: // // halfRTDistance = (VScreenSize / 2) * DistortionScale // if (Mode == Stereo_None) { YFov = DegreeToRad(80.0f); } else { float percievedHalfRTDistance = (HMD.VScreenSize / 2) * Distortion.Scale; YFov = 2.0f * atan(percievedHalfRTDistance/HMD.EyeToScreenDistance); } updateProjectionOffset(); update2D(); updateEyeParams(); DirtyFlag = false; } void StereoConfig::updateDistortionOffsetAndScale() { // Distortion center shift is stored separately, since it isn't affected // by the eye distance. float lensOffset = HMD.LensSeparationDistance * 0.5f; float lensShift = HMD.HScreenSize * 0.25f - lensOffset; float lensViewportShift = 4.0f * lensShift / HMD.HScreenSize; Distortion.XCenterOffset= lensViewportShift; // Compute distortion scale from DistortionFitX & DistortionFitY. // Fit value of 0.0 means "no fit". if ((fabs(DistortionFitX) < 0.0001f) && (fabs(DistortionFitY) < 0.0001f)) { Distortion.Scale = 1.0f; } else { // Convert fit value to distortion-centered coordinates before fit radius // calculation. float stereoAspect = 0.5f * float(FullView.w) / float(FullView.h); float dx = DistortionFitX - Distortion.XCenterOffset; float dy = DistortionFitY / stereoAspect; float fitRadius = sqrt(dx * dx + dy * dy); Distortion.Scale = Distortion.DistortionFn(fitRadius)/fitRadius; } } void StereoConfig::updateProjectionOffset() { // Post-projection viewport coordinates range from (-1.0, 1.0), with the // center of the left viewport falling at (1/4) of horizontal screen size. // We need to shift this projection center to match with the lens center; // note that we don't use the IPD here due to collimated light property of the lens. // We compute this shift in physical units (meters) to // correct for different screen sizes and then rescale to viewport coordinates. float viewCenter = HMD.HScreenSize * 0.25f; float eyeProjectionShift = viewCenter - HMD.LensSeparationDistance*0.5f; ProjectionCenterOffset = 4.0f * eyeProjectionShift / HMD.HScreenSize; } void StereoConfig::update2D() { // Orthographic projection fakes a screen at a distance of 0.8m from the // eye, where hmd screen projection surface is at 0.05m distance. // This introduces an extra off-center pixel projection shift based on eye distance. // This offCenterShift is the pixel offset of the other camera's center // in your reference camera based on surface distance. float metersToPixels = (HMD.HResolution / HMD.HScreenSize); float lensDistanceScreenPixels= metersToPixels * HMD.LensSeparationDistance; float eyeDistanceScreenPixels = metersToPixels * InterpupillaryDistance; float offCenterShiftPixels = (HMD.EyeToScreenDistance / 0.8f) * eyeDistanceScreenPixels; float leftPixelCenter = (HMD.HResolution / 2) - lensDistanceScreenPixels * 0.5f; float rightPixelCenter = lensDistanceScreenPixels * 0.5f; float pixelDifference = leftPixelCenter - rightPixelCenter; // This computes the number of pixels that fit within specified 2D FOV (assuming // distortion scaling will be done). float percievedHalfScreenDistance = tan(Area2DFov * 0.5f) * HMD.EyeToScreenDistance; float vfovSize = 2.0f * percievedHalfScreenDistance / Distortion.Scale; FovPixels = HMD.VResolution * vfovSize / HMD.VScreenSize; // Create orthographic matrix. Matrix4f& m = OrthoCenter; m.SetIdentity(); m.M[0][0] = FovPixels / (FullView.w * 0.5f); m.M[1][1] = -FovPixels / FullView.h; m.M[0][3] = 0; m.M[1][3] = 0; m.M[2][2] = 0; float orthoPixelOffset = (pixelDifference + offCenterShiftPixels/Distortion.Scale) * 0.5f; OrthoPixelOffset = orthoPixelOffset * 2.0f / FovPixels; } void StereoConfig::updateEyeParams() { // Projection matrix for the center eye, which the left/right matrices are based on. Matrix4f projCenter = Matrix4f::PerspectiveRH(YFov, Aspect, 0.01f, 2000.0f); switch(Mode) { case Stereo_None: { EyeRenderParams[0].Init(StereoEye_Center, FullView, 0, projCenter, OrthoCenter); } break; case Stereo_LeftRight_Multipass: { Matrix4f projLeft = Matrix4f::Translation(ProjectionCenterOffset, 0, 0) * projCenter, projRight = Matrix4f::Translation(-ProjectionCenterOffset, 0, 0) * projCenter; EyeRenderParams[0].Init(StereoEye_Left, Viewport(FullView.x, FullView.y, FullView.w/2, FullView.h), +InterpupillaryDistance * 0.5f, // World view shift. projLeft, OrthoCenter * Matrix4f::Translation(OrthoPixelOffset, 0, 0), &Distortion); EyeRenderParams[1].Init(StereoEye_Right, Viewport(FullView.x + FullView.w/2, FullView.y, FullView.w/2, FullView.h), -InterpupillaryDistance * 0.5f, projRight, OrthoCenter * Matrix4f::Translation(-OrthoPixelOffset, 0, 0), &Distortion); } break; } } }}} // OVR::Util::Render \ No newline at end of file +/************************************************************************************ + +Filename : Util_Render_Stereo.cpp +Content : Stereo rendering configuration implementation +Created : October 22, 2012 +Authors : Michael Antonov, Andrew Reisse + +Copyright : Copyright 2012 Oculus, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus Inc license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#include "Util_Render_Stereo.h" + +namespace OVR { namespace Util { namespace Render { + + +//----------------------------------------------------------------------------------- + +// DistortionFnInverse computes the inverse of the distortion function on an argument. +float DistortionConfig::DistortionFnInverse(float r) +{ + OVR_ASSERT((r <= 10.0f)); + + float s, d; + float delta = r * 0.25f; + + s = r * 0.5f; + d = fabs(r - DistortionFn(s)); + + for (int i = 0; i < 20; i++) + { + float sUp = s + delta; + float sDown = s - delta; + float dUp = fabs(r - DistortionFn(sUp)); + float dDown = fabs(r - DistortionFn(sDown)); + + if (dUp < d) + { + s = sUp; + d = dUp; + } + else if (dDown < d) + { + s = sDown; + d = dDown; + } + else + { + delta *= 0.5f; + } + } + + return s; +} + + +//----------------------------------------------------------------------------------- +// **** StereoConfig Implementation + +StereoConfig::StereoConfig(StereoMode mode, const Viewport& vp) + : Mode(mode), + InterpupillaryDistance(0.064f), AspectMultiplier(1.0f), + FullView(vp), DirtyFlag(true), IPDOverride(false), + YFov(0), Aspect(vp.w / float(vp.h)), ProjectionCenterOffset(0), + OrthoPixelOffset(0) +{ + // And default distortion for it. + Distortion.SetCoefficients(1.0f, 0.22f, 0.24f); + Distortion.Scale = 1.0f; // Will be computed later. + + // Fit left of the image. + DistortionFitX = -1.0f; + DistortionFitY = 0.0f; + + // Initialize "fake" default HMD values for testing without HMD plugged in. + // These default values match those returned by the HMD. + HMD.HResolution = 1280; + HMD.VResolution = 800; + HMD.HScreenSize = 0.14976f; + HMD.VScreenSize = HMD.HScreenSize / (1280.0f / 800.0f); + HMD.InterpupillaryDistance = InterpupillaryDistance; + HMD.LensSeparationDistance = 0.0635f; + HMD.EyeToScreenDistance = 0.041f; + HMD.DistortionK[0] = Distortion.K[0]; + HMD.DistortionK[1] = Distortion.K[1]; + HMD.DistortionK[2] = Distortion.K[2]; + HMD.DistortionK[3] = 0; + + Set2DAreaFov(DegreeToRad(85.0f)); +} + +void StereoConfig::SetFullViewport(const Viewport& vp) +{ + if (vp != FullView) + { + FullView = vp; + DirtyFlag = true; + } +} + +void StereoConfig::SetHMDInfo(const HMDInfo& hmd) +{ + HMD = hmd; + Distortion.K[0] = hmd.DistortionK[0]; + Distortion.K[1] = hmd.DistortionK[1]; + Distortion.K[2] = hmd.DistortionK[2]; + Distortion.K[3] = hmd.DistortionK[3]; + + Distortion.SetChromaticAberration(hmd.ChromaAbCorrection[0], hmd.ChromaAbCorrection[1], + hmd.ChromaAbCorrection[2], hmd.ChromaAbCorrection[3]); + + if (!IPDOverride) + InterpupillaryDistance = HMD.InterpupillaryDistance; + + DirtyFlag = true; +} + +void StereoConfig::SetDistortionFitPointVP(float x, float y) +{ + DistortionFitX = x; + DistortionFitY = y; + DirtyFlag = true; +} + +void StereoConfig::SetDistortionFitPointPixels(float x, float y) +{ + DistortionFitX = (4 * x / float(FullView.w)) - 1.0f; + DistortionFitY = (2 * y / float(FullView.h)) - 1.0f; + DirtyFlag = true; +} + +void StereoConfig::Set2DAreaFov(float fovRadians) +{ + Area2DFov = fovRadians; + DirtyFlag = true; +} + + +const StereoEyeParams& StereoConfig::GetEyeRenderParams(StereoEye eye) +{ + static const UByte eyeParamIndices[3] = { 0, 0, 1 }; + + updateIfDirty(); + OVR_ASSERT(eye < sizeof(eyeParamIndices)); + return EyeRenderParams[eyeParamIndices[eye]]; +} + + +void StereoConfig::updateComputedState() +{ + // Need to compute all of the following: + // - Aspect Ratio + // - FOV + // - Projection offsets for 3D + // - Distortion XCenterOffset + // - Update 2D + // - Initialize EyeRenderParams + + // Compute aspect ratio. Stereo mode cuts width in half. + Aspect = float(FullView.w) / float(FullView.h); + Aspect *= (Mode == Stereo_None) ? 1.0f : 0.5f; + Aspect *= AspectMultiplier; + + updateDistortionOffsetAndScale(); + + // Compute Vertical FOV based on distance, distortion, etc. + // Distance from vertical center to render vertical edge perceived through the lens. + // This will be larger then normal screen size due to magnification & distortion. + // + // This percievedHalfRTDistance equation should hold as long as the render target + // and display have the same aspect ratios. What we'd like to know is where the edge + // of the render target will on the perceived screen surface. With NO LENS, + // the answer would be: + // + // halfRTDistance = (VScreenSize / 2) * aspect * + // DistortionFn_Inverse( DistortionScale / aspect ) + // + // To model the optical lens we eliminates DistortionFn_Inverse. Aspect ratios + // cancel out, so we get: + // + // halfRTDistance = (VScreenSize / 2) * DistortionScale + // + if (Mode == Stereo_None) + { + YFov = DegreeToRad(80.0f); + } + else + { + float percievedHalfRTDistance = (HMD.VScreenSize / 2) * Distortion.Scale; + YFov = 2.0f * atan(percievedHalfRTDistance/HMD.EyeToScreenDistance); + } + + updateProjectionOffset(); + update2D(); + updateEyeParams(); + + DirtyFlag = false; +} + +void StereoConfig::updateDistortionOffsetAndScale() +{ + // Distortion center shift is stored separately, since it isn't affected + // by the eye distance. + float lensOffset = HMD.LensSeparationDistance * 0.5f; + float lensShift = HMD.HScreenSize * 0.25f - lensOffset; + float lensViewportShift = 4.0f * lensShift / HMD.HScreenSize; + Distortion.XCenterOffset= lensViewportShift; + + // Compute distortion scale from DistortionFitX & DistortionFitY. + // Fit value of 0.0 means "no fit". + if ((fabs(DistortionFitX) < 0.0001f) && (fabs(DistortionFitY) < 0.0001f)) + { + Distortion.Scale = 1.0f; + } + else + { + // Convert fit value to distortion-centered coordinates before fit radius + // calculation. + float stereoAspect = 0.5f * float(FullView.w) / float(FullView.h); + float dx = DistortionFitX - Distortion.XCenterOffset; + float dy = DistortionFitY / stereoAspect; + float fitRadius = sqrt(dx * dx + dy * dy); + Distortion.Scale = Distortion.DistortionFn(fitRadius)/fitRadius; + } +} + +void StereoConfig::updateProjectionOffset() +{ + // Post-projection viewport coordinates range from (-1.0, 1.0), with the + // center of the left viewport falling at (1/4) of horizontal screen size. + // We need to shift this projection center to match with the lens center; + // note that we don't use the IPD here due to collimated light property of the lens. + // We compute this shift in physical units (meters) to + // correct for different screen sizes and then rescale to viewport coordinates. + float viewCenter = HMD.HScreenSize * 0.25f; + float eyeProjectionShift = viewCenter - HMD.LensSeparationDistance*0.5f; + ProjectionCenterOffset = 4.0f * eyeProjectionShift / HMD.HScreenSize; +} + +void StereoConfig::update2D() +{ + // Orthographic projection fakes a screen at a distance of 0.8m from the + // eye, where hmd screen projection surface is at 0.05m distance. + // This introduces an extra off-center pixel projection shift based on eye distance. + // This offCenterShift is the pixel offset of the other camera's center + // in your reference camera based on surface distance. + float metersToPixels = (HMD.HResolution / HMD.HScreenSize); + float lensDistanceScreenPixels= metersToPixels * HMD.LensSeparationDistance; + float eyeDistanceScreenPixels = metersToPixels * InterpupillaryDistance; + float offCenterShiftPixels = (HMD.EyeToScreenDistance / 0.8f) * eyeDistanceScreenPixels; + float leftPixelCenter = (HMD.HResolution / 2) - lensDistanceScreenPixels * 0.5f; + float rightPixelCenter = lensDistanceScreenPixels * 0.5f; + float pixelDifference = leftPixelCenter - rightPixelCenter; + + // This computes the number of pixels that fit within specified 2D FOV (assuming + // distortion scaling will be done). + float percievedHalfScreenDistance = tan(Area2DFov * 0.5f) * HMD.EyeToScreenDistance; + float vfovSize = 2.0f * percievedHalfScreenDistance / Distortion.Scale; + FovPixels = HMD.VResolution * vfovSize / HMD.VScreenSize; + + // Create orthographic matrix. + Matrix4f& m = OrthoCenter; + m.SetIdentity(); + m.M[0][0] = FovPixels / (FullView.w * 0.5f); + m.M[1][1] = -FovPixels / FullView.h; + m.M[0][3] = 0; + m.M[1][3] = 0; + m.M[2][2] = 0; + + float orthoPixelOffset = (pixelDifference + offCenterShiftPixels/Distortion.Scale) * 0.5f; + OrthoPixelOffset = orthoPixelOffset * 2.0f / FovPixels; +} + +void StereoConfig::updateEyeParams() +{ + // Projection matrix for the center eye, which the left/right matrices are based on. + Matrix4f projCenter = Matrix4f::PerspectiveRH(YFov, Aspect, 0.01f, 2000.0f); + + switch(Mode) + { + case Stereo_None: + { + EyeRenderParams[0].Init(StereoEye_Center, FullView, 0, projCenter, OrthoCenter); + } + break; + + case Stereo_LeftRight_Multipass: + { + Matrix4f projLeft = Matrix4f::Translation(ProjectionCenterOffset, 0, 0) * projCenter, + projRight = Matrix4f::Translation(-ProjectionCenterOffset, 0, 0) * projCenter; + + EyeRenderParams[0].Init(StereoEye_Left, + Viewport(FullView.x, FullView.y, FullView.w/2, FullView.h), + +InterpupillaryDistance * 0.5f, // World view shift. + projLeft, OrthoCenter * Matrix4f::Translation(OrthoPixelOffset, 0, 0), + &Distortion); + EyeRenderParams[1].Init(StereoEye_Right, + Viewport(FullView.x + FullView.w/2, FullView.y, FullView.w/2, FullView.h), + -InterpupillaryDistance * 0.5f, + projRight, OrthoCenter * Matrix4f::Translation(-OrthoPixelOffset, 0, 0), + &Distortion); + } + break; + } + +} + + +}}} // OVR::Util::Render + diff -r 59fc487ba58e -r b069a5c27388 libovr/Src/Util/Util_Render_Stereo.h --- a/libovr/Src/Util/Util_Render_Stereo.h Sat Sep 14 17:51:03 2013 +0300 +++ b/libovr/Src/Util/Util_Render_Stereo.h Sun Sep 15 04:10:05 2013 +0300 @@ -1,1 +1,300 @@ -/************************************************************************************ PublicHeader: OVR.h Filename : Util_Render_Stereo.h Content : Sample stereo rendering configuration classes. Created : October 22, 2012 Authors : Michael Antonov Copyright : Copyright 2012 Oculus, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus Inc license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. *************************************************************************************/ #ifndef OVR_Util_Render_Stereo_h #define OVR_Util_Render_Stereo_h #include "../OVR_Device.h" namespace OVR { namespace Util { namespace Render { //----------------------------------------------------------------------------------- // ***** Stereo Enumerations // StereoMode describes rendering modes that can be used by StereoConfig. // These modes control whether stereo rendering is used or not (Stereo_None), // and how it is implemented. enum StereoMode { Stereo_None = 0, Stereo_LeftRight_Multipass = 1 }; // StereoEye specifies which eye we are rendering for; it is used to // retrieve StereoEyeParams. enum StereoEye { StereoEye_Center, StereoEye_Left, StereoEye_Right }; //----------------------------------------------------------------------------------- // ***** Viewport // Viewport describes a rectangular area used for rendering, in pixels. struct Viewport { int x, y; int w, h; Viewport() {} Viewport(int x1, int y1, int w1, int h1) : x(x1), y(y1), w(w1), h(h1) { } bool operator == (const Viewport& vp) const { return (x == vp.x) && (y == vp.y) && (w == vp.w) && (h == vp.h); } bool operator != (const Viewport& vp) const { return !operator == (vp); } }; //----------------------------------------------------------------------------------- // ***** DistortionConfig // DistortionConfig Provides controls for the distortion shader. // - K[0] - K[3] are coefficients for the distortion function. // - XCenterOffset is the offset of lens distortion center from the // center of one-eye screen half. [-1, 1] Range. // - Scale is a factor of how much larger will the input image be, // with a factor of 1.0f being no scaling. An inverse of this // value is applied to sampled UV coordinates (1/Scale). // - ChromaticAberration is an array of parameters for controlling // additional Red and Blue scaling in order to reduce chromatic aberration // caused by the Rift lenses. class DistortionConfig { public: DistortionConfig(float k0 = 1.0f, float k1 = 0.0f, float k2 = 0.0f, float k3 = 0.0f) : XCenterOffset(0), YCenterOffset(0), Scale(1.0f) { SetCoefficients(k0, k1, k2, k3); SetChromaticAberration(); } void SetCoefficients(float k0, float k1 = 0.0f, float k2 = 0.0f, float k3 = 0.0f) { K[0] = k0; K[1] = k1; K[2] = k2; K[3] = k3; } void SetChromaticAberration(float red1 = 1.0f, float red2 = 0.0f, float blue1 = 1.0f, float blue2 = 0.0f) { ChromaticAberration[0] = red1; ChromaticAberration[1] = red2; ChromaticAberration[2] = blue1; ChromaticAberration[3] = blue2; } // DistortionFn applies distortion equation to the argument. The returned // value should match distortion equation used in shader. float DistortionFn(float r) const { float rsq = r * r; float scale = r * (K[0] + K[1] * rsq + K[2] * rsq * rsq + K[3] * rsq * rsq * rsq); return scale; } // DistortionFnInverse computes the inverse of the distortion function on an argument. float DistortionFnInverse(float r); float K[4]; float XCenterOffset, YCenterOffset; float Scale; float ChromaticAberration[4]; // Additional per-channel scaling is applied after distortion: // Index [0] - Red channel constant coefficient. // Index [1] - Red channel r^2 coefficient. // Index [2] - Blue channel constant coefficient. // Index [3] - Blue channel r^2 coefficient. }; //----------------------------------------------------------------------------------- // ***** StereoEyeParams // StereoEyeParams describes RenderDevice configuration needed to render // the scene for one eye. class StereoEyeParams { public: StereoEye Eye; Viewport VP; // Viewport that we are rendering to const DistortionConfig* pDistortion; Matrix4f ViewAdjust; // Translation to be applied to view matrix. Matrix4f Projection; // Projection matrix used with this eye. Matrix4f OrthoProjection; // Orthographic projection used with this eye. void Init(StereoEye eye, const Viewport &vp, float vofs, const Matrix4f& proj, const Matrix4f& orthoProj, const DistortionConfig* distortion = 0) { Eye = eye; VP = vp; ViewAdjust = Matrix4f::Translation(Vector3f(vofs,0,0)); Projection = proj; OrthoProjection = orthoProj; pDistortion = distortion; } }; //----------------------------------------------------------------------------------- // ***** StereoConfig // StereoConfig maintains a scene stereo state and allow switching between different // stereo rendering modes. To support rendering, StereoConfig keeps track of HMD // variables such as screen size, eye-to-screen distance and distortion, and computes // extra data such as FOV and distortion center offsets based on it. Rendering // parameters are returned though StereoEyeParams for each eye. // // Beyond regular 3D projection, this class supports rendering a 2D orthographic // surface for UI and text. The 2D surface will be defined as fitting within a 2D // field of view (85 degrees by default) and used [-1,1] coordinate system with // square pixels. The (0,0) coordinate corresponds to eye center location // that is properly adjusted during rendering through SterepRenderParams::Adjust2D. // Genreally speaking, text outside [-1,1] coordinate range will not be readable. class StereoConfig { public: StereoConfig(StereoMode mode = Stereo_LeftRight_Multipass, const Viewport& fullViewport = Viewport(0,0, 1280,800)); // *** Modifiable State Access // Sets a stereo rendering mode and updates internal cached // state (matrices, per-eye view) based on it. void SetStereoMode(StereoMode mode) { Mode = mode; DirtyFlag = true; } StereoMode GetStereoMode() const { return Mode; } // Sets HMD parameters; also initializes distortion coefficients. void SetHMDInfo(const HMDInfo& hmd); const HMDInfo& GetHMDInfo() const { return HMD; } // Query physical eye-to-screen distance in meters, which combines screen-to-lens and // and lens-to-eye pupil distances. Modifying this value adjusts FOV. float GetEyeToScreenDistance() const { return HMD.EyeToScreenDistance; } void SetEyeToScreenDistance(float esd) { HMD.EyeToScreenDistance = esd; DirtyFlag = true; } // Interpupillary distance used for stereo, in meters. Default is 0.064m (64 mm). void SetIPD(float ipd) { InterpupillaryDistance = ipd; IPDOverride = DirtyFlag = true; } float GetIPD() const { return InterpupillaryDistance; } // Set full render target viewport; for HMD this includes both eyes. void SetFullViewport(const Viewport& vp); const Viewport& GetFullViewport() const { return FullView; } // Aspect ratio defaults to ((w/h)*multiplier) computed per eye. // Aspect multiplier allows adjusting aspect ratio consistently for Stereo/NoStereo. void SetAspectMultiplier(float m) { AspectMultiplier = m; DirtyFlag = true; } float GetAspectMultiplier() const { return AspectMultiplier; } // For the distorted image to fill rendered viewport, input texture render target needs to be // scaled by DistortionScale before sampling. The scale factor is computed by fitting a point // on of specified radius from a distortion center, more easily specified as a coordinate. // SetDistortionFitPointVP sets the (x,y) coordinate of the point that scale will be "fit" to, // assuming [-1,1] coordinate range for full left-eye viewport. A fit point is a location // where source (pre-distortion) and target (post-distortion) image match each other. // For the right eye, the interpretation of 'u' will be inverted. void SetDistortionFitPointVP(float x, float y); // SetDistortionFitPointPixels sets the (x,y) coordinate of the point that scale will be "fit" to, // specified in pixeld for full left-eye texture. void SetDistortionFitPointPixels(float x, float y); // Changes all distortion settings. // Note that setting HMDInfo also changes Distortion coefficients. void SetDistortionConfig(const DistortionConfig& d) { Distortion = d; DirtyFlag = true; } // Modify distortion coefficients; useful for adjustment tweaking. void SetDistortionK(int i, float k) { Distortion.K[i] = k; DirtyFlag = true; } float GetDistortionK(int i) const { return Distortion.K[i]; } // Sets the fieldOfView that the 2D coordinate area stretches to. void Set2DAreaFov(float fovRadians); // *** Computed State // Return current aspect ratio. float GetAspect() { updateIfDirty(); return Aspect; } // Return computed vertical FOV in radians/degrees. float GetYFOVRadians() { updateIfDirty(); return YFov; } float GetYFOVDegrees() { return RadToDegree(GetYFOVRadians()); } // Query horizontal projection center offset as a distance away from the // one-eye [-1,1] unit viewport. // Positive return value should be used for left eye, negative for right eye. float GetProjectionCenterOffset() { updateIfDirty(); return ProjectionCenterOffset; } // GetDistortionConfig isn't const because XCenterOffset bay need to be recomputed. const DistortionConfig& GetDistortionConfig() { updateIfDirty(); return Distortion; } // Returns DistortionScale factor by which input texture size is increased to make // post-distortion result distortion fit the viewport. float GetDistortionScale() { updateIfDirty(); return Distortion.Scale; } // Returns the size of a pixel within 2D coordinate system. float Get2DUnitPixel() { updateIfDirty(); return (2.0f / (FovPixels * Distortion.Scale)); } // Returns full set of Stereo rendering parameters for the specified eye. const StereoEyeParams& GetEyeRenderParams(StereoEye eye); private: void updateIfDirty() { if (DirtyFlag) updateComputedState(); } void updateComputedState(); void updateDistortionOffsetAndScale(); void updateProjectionOffset(); void update2D(); void updateEyeParams(); // *** Modifiable State StereoMode Mode; float InterpupillaryDistance; float AspectMultiplier; // Multiplied into aspect ratio to change it. HMDInfo HMD; DistortionConfig Distortion; float DistortionFitX, DistortionFitY; // In [-1,1] half-screen viewport units. Viewport FullView; // Entire window viewport. float Area2DFov; // FOV range mapping to [-1, 1] 2D area. // *** Computed State bool DirtyFlag; // Set when any if the modifiable state changed. bool IPDOverride; // True after SetIPD was called. float YFov; // Vertical FOV. float Aspect; // Aspect ratio: (w/h)*AspectMultiplier. float ProjectionCenterOffset; StereoEyeParams EyeRenderParams[2]; // ** 2D Rendering // Number of 2D pixels in the FOV. This defines [-1,1] coordinate range for 2D. float FovPixels; Matrix4f OrthoCenter; float OrthoPixelOffset; }; }}} // OVR::Util::Render #endif \ No newline at end of file +/************************************************************************************ + +PublicHeader: OVR.h +Filename : Util_Render_Stereo.h +Content : Sample stereo rendering configuration classes. +Created : October 22, 2012 +Authors : Michael Antonov + +Copyright : Copyright 2012 Oculus, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus Inc license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +*************************************************************************************/ + +#ifndef OVR_Util_Render_Stereo_h +#define OVR_Util_Render_Stereo_h + +#include "../OVR_Device.h" + +namespace OVR { namespace Util { namespace Render { + + +//----------------------------------------------------------------------------------- +// ***** Stereo Enumerations + +// StereoMode describes rendering modes that can be used by StereoConfig. +// These modes control whether stereo rendering is used or not (Stereo_None), +// and how it is implemented. +enum StereoMode +{ + Stereo_None = 0, + Stereo_LeftRight_Multipass = 1 +}; + + +// StereoEye specifies which eye we are rendering for; it is used to +// retrieve StereoEyeParams. +enum StereoEye +{ + StereoEye_Center, + StereoEye_Left, + StereoEye_Right +}; + + +//----------------------------------------------------------------------------------- +// ***** Viewport + +// Viewport describes a rectangular area used for rendering, in pixels. +struct Viewport +{ + int x, y; + int w, h; + + Viewport() {} + Viewport(int x1, int y1, int w1, int h1) : x(x1), y(y1), w(w1), h(h1) { } + + bool operator == (const Viewport& vp) const + { return (x == vp.x) && (y == vp.y) && (w == vp.w) && (h == vp.h); } + bool operator != (const Viewport& vp) const + { return !operator == (vp); } +}; + + +//----------------------------------------------------------------------------------- +// ***** DistortionConfig + +// DistortionConfig Provides controls for the distortion shader. +// - K[0] - K[3] are coefficients for the distortion function. +// - XCenterOffset is the offset of lens distortion center from the +// center of one-eye screen half. [-1, 1] Range. +// - Scale is a factor of how much larger will the input image be, +// with a factor of 1.0f being no scaling. An inverse of this +// value is applied to sampled UV coordinates (1/Scale). +// - ChromaticAberration is an array of parameters for controlling +// additional Red and Blue scaling in order to reduce chromatic aberration +// caused by the Rift lenses. +class DistortionConfig +{ +public: + DistortionConfig(float k0 = 1.0f, float k1 = 0.0f, float k2 = 0.0f, float k3 = 0.0f) + : XCenterOffset(0), YCenterOffset(0), Scale(1.0f) + { + SetCoefficients(k0, k1, k2, k3); + SetChromaticAberration(); + } + + void SetCoefficients(float k0, float k1 = 0.0f, float k2 = 0.0f, float k3 = 0.0f) + { K[0] = k0; K[1] = k1; K[2] = k2; K[3] = k3; } + + void SetChromaticAberration(float red1 = 1.0f, float red2 = 0.0f, float blue1 = 1.0f, float blue2 = 0.0f) + { ChromaticAberration[0] = red1; ChromaticAberration[1] = red2; ChromaticAberration[2] = blue1; ChromaticAberration[3] = blue2; } + + + // DistortionFn applies distortion equation to the argument. The returned + // value should match distortion equation used in shader. + float DistortionFn(float r) const + { + float rsq = r * r; + float scale = r * (K[0] + K[1] * rsq + K[2] * rsq * rsq + K[3] * rsq * rsq * rsq); + return scale; + } + + // DistortionFnInverse computes the inverse of the distortion function on an argument. + float DistortionFnInverse(float r); + + float K[4]; + float XCenterOffset, YCenterOffset; + float Scale; + + float ChromaticAberration[4]; // Additional per-channel scaling is applied after distortion: + // Index [0] - Red channel constant coefficient. + // Index [1] - Red channel r^2 coefficient. + // Index [2] - Blue channel constant coefficient. + // Index [3] - Blue channel r^2 coefficient. +}; + + +//----------------------------------------------------------------------------------- +// ***** StereoEyeParams + +// StereoEyeParams describes RenderDevice configuration needed to render +// the scene for one eye. +class StereoEyeParams +{ +public: + StereoEye Eye; + Viewport VP; // Viewport that we are rendering to + const DistortionConfig* pDistortion; + + Matrix4f ViewAdjust; // Translation to be applied to view matrix. + Matrix4f Projection; // Projection matrix used with this eye. + Matrix4f OrthoProjection; // Orthographic projection used with this eye. + + void Init(StereoEye eye, const Viewport &vp, float vofs, + const Matrix4f& proj, const Matrix4f& orthoProj, + const DistortionConfig* distortion = 0) + { + Eye = eye; + VP = vp; + ViewAdjust = Matrix4f::Translation(Vector3f(vofs,0,0)); + Projection = proj; + OrthoProjection = orthoProj; + pDistortion = distortion; + } +}; + + +//----------------------------------------------------------------------------------- +// ***** StereoConfig + +// StereoConfig maintains a scene stereo state and allow switching between different +// stereo rendering modes. To support rendering, StereoConfig keeps track of HMD +// variables such as screen size, eye-to-screen distance and distortion, and computes +// extra data such as FOV and distortion center offsets based on it. Rendering +// parameters are returned though StereoEyeParams for each eye. +// +// Beyond regular 3D projection, this class supports rendering a 2D orthographic +// surface for UI and text. The 2D surface will be defined as fitting within a 2D +// field of view (85 degrees by default) and used [-1,1] coordinate system with +// square pixels. The (0,0) coordinate corresponds to eye center location +// that is properly adjusted during rendering through SterepRenderParams::Adjust2D. +// Genreally speaking, text outside [-1,1] coordinate range will not be readable. + +class StereoConfig +{ +public: + + StereoConfig(StereoMode mode = Stereo_LeftRight_Multipass, + const Viewport& fullViewport = Viewport(0,0, 1280,800)); + + + // *** Modifiable State Access + + // Sets a stereo rendering mode and updates internal cached + // state (matrices, per-eye view) based on it. + void SetStereoMode(StereoMode mode) { Mode = mode; DirtyFlag = true; } + StereoMode GetStereoMode() const { return Mode; } + + // Sets HMD parameters; also initializes distortion coefficients. + void SetHMDInfo(const HMDInfo& hmd); + const HMDInfo& GetHMDInfo() const { return HMD; } + + // Query physical eye-to-screen distance in meters, which combines screen-to-lens and + // and lens-to-eye pupil distances. Modifying this value adjusts FOV. + float GetEyeToScreenDistance() const { return HMD.EyeToScreenDistance; } + void SetEyeToScreenDistance(float esd) { HMD.EyeToScreenDistance = esd; DirtyFlag = true; } + + // Interpupillary distance used for stereo, in meters. Default is 0.064m (64 mm). + void SetIPD(float ipd) { InterpupillaryDistance = ipd; IPDOverride = DirtyFlag = true; } + float GetIPD() const { return InterpupillaryDistance; } + + // Set full render target viewport; for HMD this includes both eyes. + void SetFullViewport(const Viewport& vp); + const Viewport& GetFullViewport() const { return FullView; } + + // Aspect ratio defaults to ((w/h)*multiplier) computed per eye. + // Aspect multiplier allows adjusting aspect ratio consistently for Stereo/NoStereo. + void SetAspectMultiplier(float m) { AspectMultiplier = m; DirtyFlag = true; } + float GetAspectMultiplier() const { return AspectMultiplier; } + + + // For the distorted image to fill rendered viewport, input texture render target needs to be + // scaled by DistortionScale before sampling. The scale factor is computed by fitting a point + // on of specified radius from a distortion center, more easily specified as a coordinate. + // SetDistortionFitPointVP sets the (x,y) coordinate of the point that scale will be "fit" to, + // assuming [-1,1] coordinate range for full left-eye viewport. A fit point is a location + // where source (pre-distortion) and target (post-distortion) image match each other. + // For the right eye, the interpretation of 'u' will be inverted. + void SetDistortionFitPointVP(float x, float y); + // SetDistortionFitPointPixels sets the (x,y) coordinate of the point that scale will be "fit" to, + // specified in pixeld for full left-eye texture. + void SetDistortionFitPointPixels(float x, float y); + + // Changes all distortion settings. + // Note that setting HMDInfo also changes Distortion coefficients. + void SetDistortionConfig(const DistortionConfig& d) { Distortion = d; DirtyFlag = true; } + + // Modify distortion coefficients; useful for adjustment tweaking. + void SetDistortionK(int i, float k) { Distortion.K[i] = k; DirtyFlag = true; } + float GetDistortionK(int i) const { return Distortion.K[i]; } + + // Sets the fieldOfView that the 2D coordinate area stretches to. + void Set2DAreaFov(float fovRadians); + + + // *** Computed State + + // Return current aspect ratio. + float GetAspect() { updateIfDirty(); return Aspect; } + + // Return computed vertical FOV in radians/degrees. + float GetYFOVRadians() { updateIfDirty(); return YFov; } + float GetYFOVDegrees() { return RadToDegree(GetYFOVRadians()); } + + // Query horizontal projection center offset as a distance away from the + // one-eye [-1,1] unit viewport. + // Positive return value should be used for left eye, negative for right eye. + float GetProjectionCenterOffset() { updateIfDirty(); return ProjectionCenterOffset; } + + // GetDistortionConfig isn't const because XCenterOffset bay need to be recomputed. + const DistortionConfig& GetDistortionConfig() { updateIfDirty(); return Distortion; } + + // Returns DistortionScale factor by which input texture size is increased to make + // post-distortion result distortion fit the viewport. + float GetDistortionScale() { updateIfDirty(); return Distortion.Scale; } + + // Returns the size of a pixel within 2D coordinate system. + float Get2DUnitPixel() { updateIfDirty(); return (2.0f / (FovPixels * Distortion.Scale)); } + + // Returns full set of Stereo rendering parameters for the specified eye. + const StereoEyeParams& GetEyeRenderParams(StereoEye eye); + +private: + + void updateIfDirty() { if (DirtyFlag) updateComputedState(); } + void updateComputedState(); + + void updateDistortionOffsetAndScale(); + void updateProjectionOffset(); + void update2D(); + void updateEyeParams(); + + + // *** Modifiable State + + StereoMode Mode; + float InterpupillaryDistance; + float AspectMultiplier; // Multiplied into aspect ratio to change it. + HMDInfo HMD; + DistortionConfig Distortion; + float DistortionFitX, DistortionFitY; // In [-1,1] half-screen viewport units. + Viewport FullView; // Entire window viewport. + + float Area2DFov; // FOV range mapping to [-1, 1] 2D area. + + // *** Computed State + + bool DirtyFlag; // Set when any if the modifiable state changed. + bool IPDOverride; // True after SetIPD was called. + float YFov; // Vertical FOV. + float Aspect; // Aspect ratio: (w/h)*AspectMultiplier. + float ProjectionCenterOffset; + StereoEyeParams EyeRenderParams[2]; + + + // ** 2D Rendering + + // Number of 2D pixels in the FOV. This defines [-1,1] coordinate range for 2D. + float FovPixels; + Matrix4f OrthoCenter; + float OrthoPixelOffset; +}; + + +}}} // OVR::Util::Render + +#endif diff -r 59fc487ba58e -r b069a5c27388 libovr/Src/linux/OVR_ThreadsPthread.cpp --- a/libovr/Src/linux/OVR_ThreadsPthread.cpp Sat Sep 14 17:51:03 2013 +0300 +++ b/libovr/Src/linux/OVR_ThreadsPthread.cpp Sun Sep 15 04:10:05 2013 +0300 @@ -1,1 +1,795 @@ - #include "OVR_Threads.h" #include "OVR_Hash.h" #ifdef OVR_ENABLE_THREADS #include "OVR_Timer.h" #include "OVR_Log.h" #include #include #ifdef OVR_OS_PS3 #include #include #include #define sleep(x) sys_timer_sleep(x) #define usleep(x) sys_timer_usleep(x) using std::timespec; #else #include #include #include #endif namespace OVR { // ***** Mutex implementation // *** Internal Mutex implementation structure class MutexImpl : public NewOverrideBase { // System mutex or semaphore pthread_mutex_t SMutex; bool Recursive; unsigned LockCount; pthread_t LockedBy; friend class WaitConditionImpl; public: // Constructor/destructor MutexImpl(Mutex* pmutex, bool recursive = 1); ~MutexImpl(); // Locking functions void DoLock(); bool TryLock(); void Unlock(Mutex* pmutex); // Returns 1 if the mutes is currently locked bool IsLockedByAnotherThread(Mutex* pmutex); bool IsSignaled() const; }; pthread_mutexattr_t Lock::RecursiveAttr; bool Lock::RecursiveAttrInit = 0; // *** Constructor/destructor MutexImpl::MutexImpl(Mutex* pmutex, bool recursive) { Recursive = recursive; LockCount = 0; if (Recursive) { if (!Lock::RecursiveAttrInit) { pthread_mutexattr_init(&Lock::RecursiveAttr); pthread_mutexattr_settype(&Lock::RecursiveAttr, PTHREAD_MUTEX_RECURSIVE); Lock::RecursiveAttrInit = 1; } pthread_mutex_init(&SMutex, &Lock::RecursiveAttr); } else pthread_mutex_init(&SMutex, 0); } MutexImpl::~MutexImpl() { pthread_mutex_destroy(&SMutex); } // Lock and try lock void MutexImpl::DoLock() { while (pthread_mutex_lock(&SMutex)); LockCount++; LockedBy = pthread_self(); } bool MutexImpl::TryLock() { if (!pthread_mutex_trylock(&SMutex)) { LockCount++; LockedBy = pthread_self(); return 1; } return 0; } void MutexImpl::Unlock(Mutex* pmutex) { OVR_ASSERT(pthread_self() == LockedBy && LockCount > 0); unsigned lockCount; LockCount--; lockCount = LockCount; pthread_mutex_unlock(&SMutex); } bool MutexImpl::IsLockedByAnotherThread(Mutex* pmutex) { // There could be multiple interpretations of IsLocked with respect to current thread if (LockCount == 0) return 0; if (pthread_self() != LockedBy) return 1; return 0; } bool MutexImpl::IsSignaled() const { // An mutex is signaled if it is not locked ANYWHERE // Note that this is different from IsLockedByAnotherThread function, // that takes current thread into account return LockCount == 0; } // *** Actual Mutex class implementation Mutex::Mutex(bool recursive) { // NOTE: RefCount mode already thread-safe for all waitables. pImpl = new MutexImpl(this, recursive); } Mutex::~Mutex() { delete pImpl; } // Lock and try lock void Mutex::DoLock() { pImpl->DoLock(); } bool Mutex::TryLock() { return pImpl->TryLock(); } void Mutex::Unlock() { pImpl->Unlock(this); } bool Mutex::IsLockedByAnotherThread() { return pImpl->IsLockedByAnotherThread(this); } //----------------------------------------------------------------------------------- // ***** Event bool Event::Wait(unsigned delay) { Mutex::Locker lock(&StateMutex); // Do the correct amount of waiting if (delay == OVR_WAIT_INFINITE) { while(!State) StateWaitCondition.Wait(&StateMutex); } else if (delay) { if (!State) StateWaitCondition.Wait(&StateMutex, delay); } bool state = State; // Take care of temporary 'pulsing' of a state if (Temporary) { Temporary = false; State = false; } return state; } void Event::updateState(bool newState, bool newTemp, bool mustNotify) { Mutex::Locker lock(&StateMutex); State = newState; Temporary = newTemp; if (mustNotify) StateWaitCondition.NotifyAll(); } // ***** Wait Condition Implementation // Internal implementation class class WaitConditionImpl : public NewOverrideBase { pthread_mutex_t SMutex; pthread_cond_t Condv; public: // Constructor/destructor WaitConditionImpl(); ~WaitConditionImpl(); // Release mutex and wait for condition. The mutex is re-aqured after the wait. bool Wait(Mutex *pmutex, unsigned delay = OVR_WAIT_INFINITE); // Notify a condition, releasing at one object waiting void Notify(); // Notify a condition, releasing all objects waiting void NotifyAll(); }; WaitConditionImpl::WaitConditionImpl() { pthread_mutex_init(&SMutex, 0); pthread_cond_init(&Condv, 0); } WaitConditionImpl::~WaitConditionImpl() { pthread_mutex_destroy(&SMutex); pthread_cond_destroy(&Condv); } bool WaitConditionImpl::Wait(Mutex *pmutex, unsigned delay) { bool result = 1; unsigned lockCount = pmutex->pImpl->LockCount; // Mutex must have been locked if (lockCount == 0) return 0; pthread_mutex_lock(&SMutex); // Finally, release a mutex or semaphore if (pmutex->pImpl->Recursive) { // Release the recursive mutex N times pmutex->pImpl->LockCount = 0; for(unsigned i=0; ipImpl->SMutex); } else { pmutex->pImpl->LockCount = 0; pthread_mutex_unlock(&pmutex->pImpl->SMutex); } // Note that there is a gap here between mutex.Unlock() and Wait(). // The other mutex protects this gap. if (delay == OVR_WAIT_INFINITE) pthread_cond_wait(&Condv,&SMutex); else { timespec ts; #ifdef OVR_OS_PS3 sys_time_sec_t s; sys_time_nsec_t ns; sys_time_get_current_time(&s, &ns); ts.tv_sec = s + (delay / 1000); ts.tv_nsec = ns + (delay % 1000) * 1000000; #else struct timeval tv; gettimeofday(&tv, 0); ts.tv_sec = tv.tv_sec + (delay / 1000); ts.tv_nsec = (tv.tv_usec + (delay % 1000) * 1000) * 1000; #endif if (ts.tv_nsec > 999999999) { ts.tv_sec++; ts.tv_nsec -= 1000000000; } int r = pthread_cond_timedwait(&Condv,&SMutex, &ts); OVR_ASSERT(r == 0 || r == ETIMEDOUT); if (r) result = 0; } pthread_mutex_unlock(&SMutex); // Re-aquire the mutex for(unsigned i=0; iDoLock(); // Return the result return result; } // Notify a condition, releasing the least object in a queue void WaitConditionImpl::Notify() { pthread_mutex_lock(&SMutex); pthread_cond_signal(&Condv); pthread_mutex_unlock(&SMutex); } // Notify a condition, releasing all objects waiting void WaitConditionImpl::NotifyAll() { pthread_mutex_lock(&SMutex); pthread_cond_broadcast(&Condv); pthread_mutex_unlock(&SMutex); } // *** Actual implementation of WaitCondition WaitCondition::WaitCondition() { pImpl = new WaitConditionImpl; } WaitCondition::~WaitCondition() { delete pImpl; } bool WaitCondition::Wait(Mutex *pmutex, unsigned delay) { return pImpl->Wait(pmutex, delay); } // Notification void WaitCondition::Notify() { pImpl->Notify(); } void WaitCondition::NotifyAll() { pImpl->NotifyAll(); } // ***** Current thread // Per-thread variable /* static __thread Thread* pCurrentThread = 0; // Static function to return a pointer to the current thread void Thread::InitCurrentThread(Thread *pthread) { pCurrentThread = pthread; } // Static function to return a pointer to the current thread Thread* Thread::GetThread() { return pCurrentThread; } */ // *** Thread constructors. Thread::Thread(UPInt stackSize, int processor) { // NOTE: RefCount mode already thread-safe for all Waitable objects. CreateParams params; params.stackSize = stackSize; params.processor = processor; Init(params); } Thread::Thread(Thread::ThreadFn threadFunction, void* userHandle, UPInt stackSize, int processor, Thread::ThreadState initialState) { CreateParams params(threadFunction, userHandle, stackSize, processor, initialState); Init(params); } Thread::Thread(const CreateParams& params) { Init(params); } void Thread::Init(const CreateParams& params) { // Clear the variables ThreadFlags = 0; ThreadHandle = 0; ExitCode = 0; SuspendCount = 0; StackSize = params.stackSize; Processor = params.processor; Priority = params.priority; // Clear Function pointers ThreadFunction = params.threadFunction; UserHandle = params.userHandle; if (params.initialState != NotRunning) Start(params.initialState); } Thread::~Thread() { // Thread should not running while object is being destroyed, // this would indicate ref-counting issue. //OVR_ASSERT(IsRunning() == 0); // Clean up thread. ThreadHandle = 0; } // *** Overridable User functions. // Default Run implementation int Thread::Run() { // Call pointer to function, if available. return (ThreadFunction) ? ThreadFunction(this, UserHandle) : 0; } void Thread::OnExit() { } // Finishes the thread and releases internal reference to it. void Thread::FinishAndRelease() { // Note: thread must be US. ThreadFlags &= (UInt32)~(OVR_THREAD_STARTED); ThreadFlags |= OVR_THREAD_FINISHED; // Release our reference; this is equivalent to 'delete this' // from the point of view of our thread. Release(); } // *** ThreadList - used to track all created threads class ThreadList : public NewOverrideBase { //------------------------------------------------------------------------ struct ThreadHashOp { size_t operator()(const Thread* ptr) { return (((size_t)ptr) >> 6) ^ (size_t)ptr; } }; HashSet ThreadSet; Mutex ThreadMutex; WaitCondition ThreadsEmpty; // Track the root thread that created us. pthread_t RootThreadId; static ThreadList* volatile pRunningThreads; void addThread(Thread *pthread) { Mutex::Locker lock(&ThreadMutex); ThreadSet.Add(pthread); } void removeThread(Thread *pthread) { Mutex::Locker lock(&ThreadMutex); ThreadSet.Remove(pthread); if (ThreadSet.GetSize() == 0) ThreadsEmpty.Notify(); } void finishAllThreads() { // Only original root thread can call this. OVR_ASSERT(pthread_self() == RootThreadId); Mutex::Locker lock(&ThreadMutex); while (ThreadSet.GetSize() != 0) ThreadsEmpty.Wait(&ThreadMutex); } public: ThreadList() { RootThreadId = pthread_self(); } ~ThreadList() { } static void AddRunningThread(Thread *pthread) { // Non-atomic creation ok since only the root thread if (!pRunningThreads) { pRunningThreads = new ThreadList; OVR_ASSERT(pRunningThreads); } pRunningThreads->addThread(pthread); } // NOTE: 'pthread' might be a dead pointer when this is // called so it should not be accessed; it is only used // for removal. static void RemoveRunningThread(Thread *pthread) { OVR_ASSERT(pRunningThreads); pRunningThreads->removeThread(pthread); } static void FinishAllThreads() { // This is ok because only root thread can wait for other thread finish. if (pRunningThreads) { pRunningThreads->finishAllThreads(); delete pRunningThreads; pRunningThreads = 0; } } }; // By default, we have no thread list. ThreadList* volatile ThreadList::pRunningThreads = 0; // FinishAllThreads - exposed publicly in Thread. void Thread::FinishAllThreads() { ThreadList::FinishAllThreads(); } // *** Run override int Thread::PRun() { // Suspend us on start, if requested if (ThreadFlags & OVR_THREAD_START_SUSPENDED) { Suspend(); ThreadFlags &= (UInt32)~OVR_THREAD_START_SUSPENDED; } // Call the virtual run function ExitCode = Run(); return ExitCode; } // *** User overridables bool Thread::GetExitFlag() const { return (ThreadFlags & OVR_THREAD_EXIT) != 0; } void Thread::SetExitFlag(bool exitFlag) { // The below is atomic since ThreadFlags is AtomicInt. if (exitFlag) ThreadFlags |= OVR_THREAD_EXIT; else ThreadFlags &= (UInt32) ~OVR_THREAD_EXIT; } // Determines whether the thread was running and is now finished bool Thread::IsFinished() const { return (ThreadFlags & OVR_THREAD_FINISHED) != 0; } // Determines whether the thread is suspended bool Thread::IsSuspended() const { return SuspendCount > 0; } // Returns current thread state Thread::ThreadState Thread::GetThreadState() const { if (IsSuspended()) return Suspended; if (ThreadFlags & OVR_THREAD_STARTED) return Running; return NotRunning; } /* static const char* mapsched_policy(int policy) { switch(policy) { case SCHED_OTHER: return "SCHED_OTHER"; case SCHED_RR: return "SCHED_RR"; case SCHED_FIFO: return "SCHED_FIFO"; } return "UNKNOWN"; } int policy; sched_param sparam; pthread_getschedparam(pthread_self(), &policy, &sparam); int max_prior = sched_get_priority_max(policy); int min_prior = sched_get_priority_min(policy); printf(" !!!! policy: %s, priority: %d, max priority: %d, min priority: %d\n", mapsched_policy(policy), sparam.sched_priority, max_prior, min_prior); #include */ // ***** Thread management // The actual first function called on thread start void* Thread_PthreadStartFn(void* phandle) { Thread* pthread = (Thread*)phandle; int result = pthread->PRun(); // Signal the thread as done and release it atomically. pthread->FinishAndRelease(); // At this point Thread object might be dead; however we can still pass // it to RemoveRunningThread since it is only used as a key there. ThreadList::RemoveRunningThread(pthread); return (void*) result; } int Thread::InitAttr = 0; pthread_attr_t Thread::Attr; /* static */ int Thread::GetOSPriority(ThreadPriority p) //static inline int MapToSystemPrority(Thread::ThreadPriority p) { #ifdef OVR_OS_PS3 switch(p) { case Thread::CriticalPriority: return 0; case Thread::HighestPriority: return 300; case Thread::AboveNormalPriority: return 600; case Thread::NormalPriority: return 1000; case Thread::BelowNormalPriority: return 1500; case Thread::LowestPriority: return 2500; case Thread::IdlePriority: return 3071; } return 1000; #else OVR_UNUSED(p); return -1; #endif } bool Thread::Start(ThreadState initialState) { if (initialState == NotRunning) return 0; if (GetThreadState() != NotRunning) { OVR_DEBUG_LOG(("Thread::Start failed - thread %p already running", this)); return 0; } if (!InitAttr) { pthread_attr_init(&Attr); pthread_attr_setdetachstate(&Attr, PTHREAD_CREATE_DETACHED); pthread_attr_setstacksize(&Attr, 128 * 1024); sched_param sparam; sparam.sched_priority = Thread::GetOSPriority(NormalPriority); pthread_attr_setschedparam(&Attr, &sparam); InitAttr = 1; } ExitCode = 0; SuspendCount = 0; ThreadFlags = (initialState == Running) ? 0 : OVR_THREAD_START_SUSPENDED; // AddRef to us until the thread is finished AddRef(); ThreadList::AddRunningThread(this); int result; if (StackSize != 128 * 1024 || Priority != NormalPriority) { pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); pthread_attr_setstacksize(&attr, StackSize); sched_param sparam; sparam.sched_priority = Thread::GetOSPriority(Priority); pthread_attr_setschedparam(&attr, &sparam); result = pthread_create(&ThreadHandle, &attr, Thread_PthreadStartFn, this); pthread_attr_destroy(&attr); } else result = pthread_create(&ThreadHandle, &Attr, Thread_PthreadStartFn, this); if (result) { ThreadFlags = 0; Release(); ThreadList::RemoveRunningThread(this); return 0; } return 1; } // Suspend the thread until resumed bool Thread::Suspend() { OVR_DEBUG_LOG(("Thread::Suspend - cannot suspend threads on this system")); return 0; } // Resumes currently suspended thread bool Thread::Resume() { return 0; } // Quits with an exit code void Thread::Exit(int exitCode) { // Can only exist the current thread // if (GetThread() != this) // return; // Call the virtual OnExit function OnExit(); // Signal this thread object as done and release it's references. FinishAndRelease(); ThreadList::RemoveRunningThread(this); pthread_exit((void *) exitCode); } ThreadId GetCurrentThreadId() { return (void*)pthread_self(); } // *** Sleep functions /* static */ bool Thread::Sleep(unsigned secs) { sleep(secs); return 1; } /* static */ bool Thread::MSleep(unsigned msecs) { usleep(msecs*1000); return 1; } /* static */ int Thread::GetCPUCount() { return 1; } #ifdef OVR_OS_PS3 sys_lwmutex_attribute_t Lock::LockAttr = { SYS_SYNC_PRIORITY, SYS_SYNC_RECURSIVE }; #endif } #endif // OVR_ENABLE_THREADS \ No newline at end of file + +#include "OVR_Threads.h" +#include "OVR_Hash.h" + +#ifdef OVR_ENABLE_THREADS + +#include "OVR_Timer.h" +#include "OVR_Log.h" + +#include +#include + +#ifdef OVR_OS_PS3 +#include +#include +#include +#define sleep(x) sys_timer_sleep(x) +#define usleep(x) sys_timer_usleep(x) +using std::timespec; +#else +#include +#include +#include +#endif + +namespace OVR { + +// ***** Mutex implementation + + +// *** Internal Mutex implementation structure + +class MutexImpl : public NewOverrideBase +{ + // System mutex or semaphore + pthread_mutex_t SMutex; + bool Recursive; + unsigned LockCount; + pthread_t LockedBy; + + friend class WaitConditionImpl; + +public: + // Constructor/destructor + MutexImpl(Mutex* pmutex, bool recursive = 1); + ~MutexImpl(); + + // Locking functions + void DoLock(); + bool TryLock(); + void Unlock(Mutex* pmutex); + // Returns 1 if the mutes is currently locked + bool IsLockedByAnotherThread(Mutex* pmutex); + bool IsSignaled() const; +}; + +pthread_mutexattr_t Lock::RecursiveAttr; +bool Lock::RecursiveAttrInit = 0; + +// *** Constructor/destructor +MutexImpl::MutexImpl(Mutex* pmutex, bool recursive) +{ + Recursive = recursive; + LockCount = 0; + + if (Recursive) + { + if (!Lock::RecursiveAttrInit) + { + pthread_mutexattr_init(&Lock::RecursiveAttr); + pthread_mutexattr_settype(&Lock::RecursiveAttr, PTHREAD_MUTEX_RECURSIVE); + Lock::RecursiveAttrInit = 1; + } + + pthread_mutex_init(&SMutex, &Lock::RecursiveAttr); + } + else + pthread_mutex_init(&SMutex, 0); +} + +MutexImpl::~MutexImpl() +{ + pthread_mutex_destroy(&SMutex); +} + + +// Lock and try lock +void MutexImpl::DoLock() +{ + while (pthread_mutex_lock(&SMutex)); + LockCount++; + LockedBy = pthread_self(); +} + +bool MutexImpl::TryLock() +{ + if (!pthread_mutex_trylock(&SMutex)) + { + LockCount++; + LockedBy = pthread_self(); + return 1; + } + + return 0; +} + +void MutexImpl::Unlock(Mutex* pmutex) +{ + OVR_ASSERT(pthread_self() == LockedBy && LockCount > 0); + + unsigned lockCount; + LockCount--; + lockCount = LockCount; + + pthread_mutex_unlock(&SMutex); +} + +bool MutexImpl::IsLockedByAnotherThread(Mutex* pmutex) +{ + // There could be multiple interpretations of IsLocked with respect to current thread + if (LockCount == 0) + return 0; + if (pthread_self() != LockedBy) + return 1; + return 0; +} + +bool MutexImpl::IsSignaled() const +{ + // An mutex is signaled if it is not locked ANYWHERE + // Note that this is different from IsLockedByAnotherThread function, + // that takes current thread into account + return LockCount == 0; +} + + +// *** Actual Mutex class implementation + +Mutex::Mutex(bool recursive) +{ + // NOTE: RefCount mode already thread-safe for all waitables. + pImpl = new MutexImpl(this, recursive); +} + +Mutex::~Mutex() +{ + delete pImpl; +} + +// Lock and try lock +void Mutex::DoLock() +{ + pImpl->DoLock(); +} +bool Mutex::TryLock() +{ + return pImpl->TryLock(); +} +void Mutex::Unlock() +{ + pImpl->Unlock(this); +} +bool Mutex::IsLockedByAnotherThread() +{ + return pImpl->IsLockedByAnotherThread(this); +} + + + +//----------------------------------------------------------------------------------- +// ***** Event + +bool Event::Wait(unsigned delay) +{ + Mutex::Locker lock(&StateMutex); + + // Do the correct amount of waiting + if (delay == OVR_WAIT_INFINITE) + { + while(!State) + StateWaitCondition.Wait(&StateMutex); + } + else if (delay) + { + if (!State) + StateWaitCondition.Wait(&StateMutex, delay); + } + + bool state = State; + // Take care of temporary 'pulsing' of a state + if (Temporary) + { + Temporary = false; + State = false; + } + return state; +} + +void Event::updateState(bool newState, bool newTemp, bool mustNotify) +{ + Mutex::Locker lock(&StateMutex); + State = newState; + Temporary = newTemp; + if (mustNotify) + StateWaitCondition.NotifyAll(); +} + + + +// ***** Wait Condition Implementation + +// Internal implementation class +class WaitConditionImpl : public NewOverrideBase +{ + pthread_mutex_t SMutex; + pthread_cond_t Condv; + +public: + + // Constructor/destructor + WaitConditionImpl(); + ~WaitConditionImpl(); + + // Release mutex and wait for condition. The mutex is re-aqured after the wait. + bool Wait(Mutex *pmutex, unsigned delay = OVR_WAIT_INFINITE); + + // Notify a condition, releasing at one object waiting + void Notify(); + // Notify a condition, releasing all objects waiting + void NotifyAll(); +}; + + +WaitConditionImpl::WaitConditionImpl() +{ + pthread_mutex_init(&SMutex, 0); + pthread_cond_init(&Condv, 0); +} + +WaitConditionImpl::~WaitConditionImpl() +{ + pthread_mutex_destroy(&SMutex); + pthread_cond_destroy(&Condv); +} + +bool WaitConditionImpl::Wait(Mutex *pmutex, unsigned delay) +{ + bool result = 1; + unsigned lockCount = pmutex->pImpl->LockCount; + + // Mutex must have been locked + if (lockCount == 0) + return 0; + + pthread_mutex_lock(&SMutex); + + // Finally, release a mutex or semaphore + if (pmutex->pImpl->Recursive) + { + // Release the recursive mutex N times + pmutex->pImpl->LockCount = 0; + for(unsigned i=0; ipImpl->SMutex); + } + else + { + pmutex->pImpl->LockCount = 0; + pthread_mutex_unlock(&pmutex->pImpl->SMutex); + } + + // Note that there is a gap here between mutex.Unlock() and Wait(). + // The other mutex protects this gap. + + if (delay == OVR_WAIT_INFINITE) + pthread_cond_wait(&Condv,&SMutex); + else + { + timespec ts; +#ifdef OVR_OS_PS3 + sys_time_sec_t s; + sys_time_nsec_t ns; + sys_time_get_current_time(&s, &ns); + + ts.tv_sec = s + (delay / 1000); + ts.tv_nsec = ns + (delay % 1000) * 1000000; + +#else + struct timeval tv; + gettimeofday(&tv, 0); + + ts.tv_sec = tv.tv_sec + (delay / 1000); + ts.tv_nsec = (tv.tv_usec + (delay % 1000) * 1000) * 1000; +#endif + if (ts.tv_nsec > 999999999) + { + ts.tv_sec++; + ts.tv_nsec -= 1000000000; + } + int r = pthread_cond_timedwait(&Condv,&SMutex, &ts); + OVR_ASSERT(r == 0 || r == ETIMEDOUT); + if (r) + result = 0; + } + + pthread_mutex_unlock(&SMutex); + + // Re-aquire the mutex + for(unsigned i=0; iDoLock(); + + // Return the result + return result; +} + +// Notify a condition, releasing the least object in a queue +void WaitConditionImpl::Notify() +{ + pthread_mutex_lock(&SMutex); + pthread_cond_signal(&Condv); + pthread_mutex_unlock(&SMutex); +} + +// Notify a condition, releasing all objects waiting +void WaitConditionImpl::NotifyAll() +{ + pthread_mutex_lock(&SMutex); + pthread_cond_broadcast(&Condv); + pthread_mutex_unlock(&SMutex); +} + + + +// *** Actual implementation of WaitCondition + +WaitCondition::WaitCondition() +{ + pImpl = new WaitConditionImpl; +} +WaitCondition::~WaitCondition() +{ + delete pImpl; +} + +bool WaitCondition::Wait(Mutex *pmutex, unsigned delay) +{ + return pImpl->Wait(pmutex, delay); +} +// Notification +void WaitCondition::Notify() +{ + pImpl->Notify(); +} +void WaitCondition::NotifyAll() +{ + pImpl->NotifyAll(); +} + + +// ***** Current thread + +// Per-thread variable +/* +static __thread Thread* pCurrentThread = 0; + +// Static function to return a pointer to the current thread +void Thread::InitCurrentThread(Thread *pthread) +{ + pCurrentThread = pthread; +} + +// Static function to return a pointer to the current thread +Thread* Thread::GetThread() +{ + return pCurrentThread; +} +*/ + + +// *** Thread constructors. + +Thread::Thread(UPInt stackSize, int processor) +{ + // NOTE: RefCount mode already thread-safe for all Waitable objects. + CreateParams params; + params.stackSize = stackSize; + params.processor = processor; + Init(params); +} + +Thread::Thread(Thread::ThreadFn threadFunction, void* userHandle, UPInt stackSize, + int processor, Thread::ThreadState initialState) +{ + CreateParams params(threadFunction, userHandle, stackSize, processor, initialState); + Init(params); +} + +Thread::Thread(const CreateParams& params) +{ + Init(params); +} + +void Thread::Init(const CreateParams& params) +{ + // Clear the variables + ThreadFlags = 0; + ThreadHandle = 0; + ExitCode = 0; + SuspendCount = 0; + StackSize = params.stackSize; + Processor = params.processor; + Priority = params.priority; + + // Clear Function pointers + ThreadFunction = params.threadFunction; + UserHandle = params.userHandle; + if (params.initialState != NotRunning) + Start(params.initialState); +} + +Thread::~Thread() +{ + // Thread should not running while object is being destroyed, + // this would indicate ref-counting issue. + //OVR_ASSERT(IsRunning() == 0); + + // Clean up thread. + ThreadHandle = 0; +} + + + +// *** Overridable User functions. + +// Default Run implementation +int Thread::Run() +{ + // Call pointer to function, if available. + return (ThreadFunction) ? ThreadFunction(this, UserHandle) : 0; +} +void Thread::OnExit() +{ +} + + +// Finishes the thread and releases internal reference to it. +void Thread::FinishAndRelease() +{ + // Note: thread must be US. + ThreadFlags &= (UInt32)~(OVR_THREAD_STARTED); + ThreadFlags |= OVR_THREAD_FINISHED; + + // Release our reference; this is equivalent to 'delete this' + // from the point of view of our thread. + Release(); +} + + + +// *** ThreadList - used to track all created threads + +class ThreadList : public NewOverrideBase +{ + //------------------------------------------------------------------------ + struct ThreadHashOp + { + size_t operator()(const Thread* ptr) + { + return (((size_t)ptr) >> 6) ^ (size_t)ptr; + } + }; + + HashSet ThreadSet; + Mutex ThreadMutex; + WaitCondition ThreadsEmpty; + // Track the root thread that created us. + pthread_t RootThreadId; + + static ThreadList* volatile pRunningThreads; + + void addThread(Thread *pthread) + { + Mutex::Locker lock(&ThreadMutex); + ThreadSet.Add(pthread); + } + + void removeThread(Thread *pthread) + { + Mutex::Locker lock(&ThreadMutex); + ThreadSet.Remove(pthread); + if (ThreadSet.GetSize() == 0) + ThreadsEmpty.Notify(); + } + + void finishAllThreads() + { + // Only original root thread can call this. + OVR_ASSERT(pthread_self() == RootThreadId); + + Mutex::Locker lock(&ThreadMutex); + while (ThreadSet.GetSize() != 0) + ThreadsEmpty.Wait(&ThreadMutex); + } + +public: + + ThreadList() + { + RootThreadId = pthread_self(); + } + ~ThreadList() { } + + + static void AddRunningThread(Thread *pthread) + { + // Non-atomic creation ok since only the root thread + if (!pRunningThreads) + { + pRunningThreads = new ThreadList; + OVR_ASSERT(pRunningThreads); + } + pRunningThreads->addThread(pthread); + } + + // NOTE: 'pthread' might be a dead pointer when this is + // called so it should not be accessed; it is only used + // for removal. + static void RemoveRunningThread(Thread *pthread) + { + OVR_ASSERT(pRunningThreads); + pRunningThreads->removeThread(pthread); + } + + static void FinishAllThreads() + { + // This is ok because only root thread can wait for other thread finish. + if (pRunningThreads) + { + pRunningThreads->finishAllThreads(); + delete pRunningThreads; + pRunningThreads = 0; + } + } +}; + +// By default, we have no thread list. +ThreadList* volatile ThreadList::pRunningThreads = 0; + + +// FinishAllThreads - exposed publicly in Thread. +void Thread::FinishAllThreads() +{ + ThreadList::FinishAllThreads(); +} + +// *** Run override + +int Thread::PRun() +{ + // Suspend us on start, if requested + if (ThreadFlags & OVR_THREAD_START_SUSPENDED) + { + Suspend(); + ThreadFlags &= (UInt32)~OVR_THREAD_START_SUSPENDED; + } + + // Call the virtual run function + ExitCode = Run(); + return ExitCode; +} + + + + +// *** User overridables + +bool Thread::GetExitFlag() const +{ + return (ThreadFlags & OVR_THREAD_EXIT) != 0; +} + +void Thread::SetExitFlag(bool exitFlag) +{ + // The below is atomic since ThreadFlags is AtomicInt. + if (exitFlag) + ThreadFlags |= OVR_THREAD_EXIT; + else + ThreadFlags &= (UInt32) ~OVR_THREAD_EXIT; +} + + +// Determines whether the thread was running and is now finished +bool Thread::IsFinished() const +{ + return (ThreadFlags & OVR_THREAD_FINISHED) != 0; +} +// Determines whether the thread is suspended +bool Thread::IsSuspended() const +{ + return SuspendCount > 0; +} +// Returns current thread state +Thread::ThreadState Thread::GetThreadState() const +{ + if (IsSuspended()) + return Suspended; + if (ThreadFlags & OVR_THREAD_STARTED) + return Running; + return NotRunning; +} +/* +static const char* mapsched_policy(int policy) +{ + switch(policy) + { + case SCHED_OTHER: + return "SCHED_OTHER"; + case SCHED_RR: + return "SCHED_RR"; + case SCHED_FIFO: + return "SCHED_FIFO"; + + } + return "UNKNOWN"; +} + int policy; + sched_param sparam; + pthread_getschedparam(pthread_self(), &policy, &sparam); + int max_prior = sched_get_priority_max(policy); + int min_prior = sched_get_priority_min(policy); + printf(" !!!! policy: %s, priority: %d, max priority: %d, min priority: %d\n", mapsched_policy(policy), sparam.sched_priority, max_prior, min_prior); +#include +*/ +// ***** Thread management + +// The actual first function called on thread start +void* Thread_PthreadStartFn(void* phandle) +{ + Thread* pthread = (Thread*)phandle; + int result = pthread->PRun(); + // Signal the thread as done and release it atomically. + pthread->FinishAndRelease(); + // At this point Thread object might be dead; however we can still pass + // it to RemoveRunningThread since it is only used as a key there. + ThreadList::RemoveRunningThread(pthread); + return (void*) result; +} + +int Thread::InitAttr = 0; +pthread_attr_t Thread::Attr; + +/* static */ +int Thread::GetOSPriority(ThreadPriority p) +//static inline int MapToSystemPrority(Thread::ThreadPriority p) +{ +#ifdef OVR_OS_PS3 + switch(p) + { + case Thread::CriticalPriority: return 0; + case Thread::HighestPriority: return 300; + case Thread::AboveNormalPriority: return 600; + case Thread::NormalPriority: return 1000; + case Thread::BelowNormalPriority: return 1500; + case Thread::LowestPriority: return 2500; + case Thread::IdlePriority: return 3071; + } return 1000; +#else + OVR_UNUSED(p); + return -1; +#endif +} + +bool Thread::Start(ThreadState initialState) +{ + if (initialState == NotRunning) + return 0; + if (GetThreadState() != NotRunning) + { + OVR_DEBUG_LOG(("Thread::Start failed - thread %p already running", this)); + return 0; + } + + if (!InitAttr) + { + pthread_attr_init(&Attr); + pthread_attr_setdetachstate(&Attr, PTHREAD_CREATE_DETACHED); + pthread_attr_setstacksize(&Attr, 128 * 1024); + sched_param sparam; + sparam.sched_priority = Thread::GetOSPriority(NormalPriority); + pthread_attr_setschedparam(&Attr, &sparam); + InitAttr = 1; + } + + ExitCode = 0; + SuspendCount = 0; + ThreadFlags = (initialState == Running) ? 0 : OVR_THREAD_START_SUSPENDED; + + // AddRef to us until the thread is finished + AddRef(); + ThreadList::AddRunningThread(this); + + int result; + if (StackSize != 128 * 1024 || Priority != NormalPriority) + { + pthread_attr_t attr; + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_attr_setstacksize(&attr, StackSize); + sched_param sparam; + sparam.sched_priority = Thread::GetOSPriority(Priority); + pthread_attr_setschedparam(&attr, &sparam); + result = pthread_create(&ThreadHandle, &attr, Thread_PthreadStartFn, this); + pthread_attr_destroy(&attr); + } + else + result = pthread_create(&ThreadHandle, &Attr, Thread_PthreadStartFn, this); + + if (result) + { + ThreadFlags = 0; + Release(); + ThreadList::RemoveRunningThread(this); + return 0; + } + return 1; +} + + +// Suspend the thread until resumed +bool Thread::Suspend() +{ + OVR_DEBUG_LOG(("Thread::Suspend - cannot suspend threads on this system")); + return 0; +} + +// Resumes currently suspended thread +bool Thread::Resume() +{ + return 0; +} + + +// Quits with an exit code +void Thread::Exit(int exitCode) +{ + // Can only exist the current thread + // if (GetThread() != this) + // return; + + // Call the virtual OnExit function + OnExit(); + + // Signal this thread object as done and release it's references. + FinishAndRelease(); + ThreadList::RemoveRunningThread(this); + + pthread_exit((void *) exitCode); +} + +ThreadId GetCurrentThreadId() +{ + return (void*)pthread_self(); +} + +// *** Sleep functions + +/* static */ +bool Thread::Sleep(unsigned secs) +{ + sleep(secs); + return 1; +} +/* static */ +bool Thread::MSleep(unsigned msecs) +{ + usleep(msecs*1000); + return 1; +} + +/* static */ +int Thread::GetCPUCount() +{ + return 1; +} + + +#ifdef OVR_OS_PS3 + +sys_lwmutex_attribute_t Lock::LockAttr = { SYS_SYNC_PRIORITY, SYS_SYNC_RECURSIVE }; + +#endif + +} + +#endif // OVR_ENABLE_THREADS diff -r 59fc487ba58e -r b069a5c27388 libovr/Src/osx/OVR_ThreadsPthread.cpp --- a/libovr/Src/osx/OVR_ThreadsPthread.cpp Sat Sep 14 17:51:03 2013 +0300 +++ b/libovr/Src/osx/OVR_ThreadsPthread.cpp Sun Sep 15 04:10:05 2013 +0300 @@ -1,1 +1,795 @@ - #include "OVR_Threads.h" #include "OVR_Hash.h" #ifdef OVR_ENABLE_THREADS #include "OVR_Timer.h" #include "OVR_Log.h" #include #include #ifdef OVR_OS_PS3 #include #include #include #define sleep(x) sys_timer_sleep(x) #define usleep(x) sys_timer_usleep(x) using std::timespec; #else #include #include #include #endif namespace OVR { // ***** Mutex implementation // *** Internal Mutex implementation structure class MutexImpl : public NewOverrideBase { // System mutex or semaphore pthread_mutex_t SMutex; bool Recursive; unsigned LockCount; pthread_t LockedBy; friend class WaitConditionImpl; public: // Constructor/destructor MutexImpl(Mutex* pmutex, bool recursive = 1); ~MutexImpl(); // Locking functions void DoLock(); bool TryLock(); void Unlock(Mutex* pmutex); // Returns 1 if the mutes is currently locked bool IsLockedByAnotherThread(Mutex* pmutex); bool IsSignaled() const; }; pthread_mutexattr_t Lock::RecursiveAttr; bool Lock::RecursiveAttrInit = 0; // *** Constructor/destructor MutexImpl::MutexImpl(Mutex* pmutex, bool recursive) { Recursive = recursive; LockCount = 0; if (Recursive) { if (!Lock::RecursiveAttrInit) { pthread_mutexattr_init(&Lock::RecursiveAttr); pthread_mutexattr_settype(&Lock::RecursiveAttr, PTHREAD_MUTEX_RECURSIVE); Lock::RecursiveAttrInit = 1; } pthread_mutex_init(&SMutex, &Lock::RecursiveAttr); } else pthread_mutex_init(&SMutex, 0); } MutexImpl::~MutexImpl() { pthread_mutex_destroy(&SMutex); } // Lock and try lock void MutexImpl::DoLock() { while (pthread_mutex_lock(&SMutex)); LockCount++; LockedBy = pthread_self(); } bool MutexImpl::TryLock() { if (!pthread_mutex_trylock(&SMutex)) { LockCount++; LockedBy = pthread_self(); return 1; } return 0; } void MutexImpl::Unlock(Mutex* pmutex) { OVR_ASSERT(pthread_self() == LockedBy && LockCount > 0); unsigned lockCount; LockCount--; lockCount = LockCount; pthread_mutex_unlock(&SMutex); } bool MutexImpl::IsLockedByAnotherThread(Mutex* pmutex) { // There could be multiple interpretations of IsLocked with respect to current thread if (LockCount == 0) return 0; if (pthread_self() != LockedBy) return 1; return 0; } bool MutexImpl::IsSignaled() const { // An mutex is signaled if it is not locked ANYWHERE // Note that this is different from IsLockedByAnotherThread function, // that takes current thread into account return LockCount == 0; } // *** Actual Mutex class implementation Mutex::Mutex(bool recursive) { // NOTE: RefCount mode already thread-safe for all waitables. pImpl = new MutexImpl(this, recursive); } Mutex::~Mutex() { delete pImpl; } // Lock and try lock void Mutex::DoLock() { pImpl->DoLock(); } bool Mutex::TryLock() { return pImpl->TryLock(); } void Mutex::Unlock() { pImpl->Unlock(this); } bool Mutex::IsLockedByAnotherThread() { return pImpl->IsLockedByAnotherThread(this); } //----------------------------------------------------------------------------------- // ***** Event bool Event::Wait(unsigned delay) { Mutex::Locker lock(&StateMutex); // Do the correct amount of waiting if (delay == OVR_WAIT_INFINITE) { while(!State) StateWaitCondition.Wait(&StateMutex); } else if (delay) { if (!State) StateWaitCondition.Wait(&StateMutex, delay); } bool state = State; // Take care of temporary 'pulsing' of a state if (Temporary) { Temporary = false; State = false; } return state; } void Event::updateState(bool newState, bool newTemp, bool mustNotify) { Mutex::Locker lock(&StateMutex); State = newState; Temporary = newTemp; if (mustNotify) StateWaitCondition.NotifyAll(); } // ***** Wait Condition Implementation // Internal implementation class class WaitConditionImpl : public NewOverrideBase { pthread_mutex_t SMutex; pthread_cond_t Condv; public: // Constructor/destructor WaitConditionImpl(); ~WaitConditionImpl(); // Release mutex and wait for condition. The mutex is re-aqured after the wait. bool Wait(Mutex *pmutex, unsigned delay = OVR_WAIT_INFINITE); // Notify a condition, releasing at one object waiting void Notify(); // Notify a condition, releasing all objects waiting void NotifyAll(); }; WaitConditionImpl::WaitConditionImpl() { pthread_mutex_init(&SMutex, 0); pthread_cond_init(&Condv, 0); } WaitConditionImpl::~WaitConditionImpl() { pthread_mutex_destroy(&SMutex); pthread_cond_destroy(&Condv); } bool WaitConditionImpl::Wait(Mutex *pmutex, unsigned delay) { bool result = 1; unsigned lockCount = pmutex->pImpl->LockCount; // Mutex must have been locked if (lockCount == 0) return 0; pthread_mutex_lock(&SMutex); // Finally, release a mutex or semaphore if (pmutex->pImpl->Recursive) { // Release the recursive mutex N times pmutex->pImpl->LockCount = 0; for(unsigned i=0; ipImpl->SMutex); } else { pmutex->pImpl->LockCount = 0; pthread_mutex_unlock(&pmutex->pImpl->SMutex); } // Note that there is a gap here between mutex.Unlock() and Wait(). // The other mutex protects this gap. if (delay == OVR_WAIT_INFINITE) pthread_cond_wait(&Condv,&SMutex); else { timespec ts; #ifdef OVR_OS_PS3 sys_time_sec_t s; sys_time_nsec_t ns; sys_time_get_current_time(&s, &ns); ts.tv_sec = s + (delay / 1000); ts.tv_nsec = ns + (delay % 1000) * 1000000; #else struct timeval tv; gettimeofday(&tv, 0); ts.tv_sec = tv.tv_sec + (delay / 1000); ts.tv_nsec = (tv.tv_usec + (delay % 1000) * 1000) * 1000; #endif if (ts.tv_nsec > 999999999) { ts.tv_sec++; ts.tv_nsec -= 1000000000; } int r = pthread_cond_timedwait(&Condv,&SMutex, &ts); OVR_ASSERT(r == 0 || r == ETIMEDOUT); if (r) result = 0; } pthread_mutex_unlock(&SMutex); // Re-aquire the mutex for(unsigned i=0; iDoLock(); // Return the result return result; } // Notify a condition, releasing the least object in a queue void WaitConditionImpl::Notify() { pthread_mutex_lock(&SMutex); pthread_cond_signal(&Condv); pthread_mutex_unlock(&SMutex); } // Notify a condition, releasing all objects waiting void WaitConditionImpl::NotifyAll() { pthread_mutex_lock(&SMutex); pthread_cond_broadcast(&Condv); pthread_mutex_unlock(&SMutex); } // *** Actual implementation of WaitCondition WaitCondition::WaitCondition() { pImpl = new WaitConditionImpl; } WaitCondition::~WaitCondition() { delete pImpl; } bool WaitCondition::Wait(Mutex *pmutex, unsigned delay) { return pImpl->Wait(pmutex, delay); } // Notification void WaitCondition::Notify() { pImpl->Notify(); } void WaitCondition::NotifyAll() { pImpl->NotifyAll(); } // ***** Current thread // Per-thread variable /* static __thread Thread* pCurrentThread = 0; // Static function to return a pointer to the current thread void Thread::InitCurrentThread(Thread *pthread) { pCurrentThread = pthread; } // Static function to return a pointer to the current thread Thread* Thread::GetThread() { return pCurrentThread; } */ // *** Thread constructors. Thread::Thread(UPInt stackSize, int processor) { // NOTE: RefCount mode already thread-safe for all Waitable objects. CreateParams params; params.stackSize = stackSize; params.processor = processor; Init(params); } Thread::Thread(Thread::ThreadFn threadFunction, void* userHandle, UPInt stackSize, int processor, Thread::ThreadState initialState) { CreateParams params(threadFunction, userHandle, stackSize, processor, initialState); Init(params); } Thread::Thread(const CreateParams& params) { Init(params); } void Thread::Init(const CreateParams& params) { // Clear the variables ThreadFlags = 0; ThreadHandle = 0; ExitCode = 0; SuspendCount = 0; StackSize = params.stackSize; Processor = params.processor; Priority = params.priority; // Clear Function pointers ThreadFunction = params.threadFunction; UserHandle = params.userHandle; if (params.initialState != NotRunning) Start(params.initialState); } Thread::~Thread() { // Thread should not running while object is being destroyed, // this would indicate ref-counting issue. //OVR_ASSERT(IsRunning() == 0); // Clean up thread. ThreadHandle = 0; } // *** Overridable User functions. // Default Run implementation int Thread::Run() { // Call pointer to function, if available. return (ThreadFunction) ? ThreadFunction(this, UserHandle) : 0; } void Thread::OnExit() { } // Finishes the thread and releases internal reference to it. void Thread::FinishAndRelease() { // Note: thread must be US. ThreadFlags &= (UInt32)~(OVR_THREAD_STARTED); ThreadFlags |= OVR_THREAD_FINISHED; // Release our reference; this is equivalent to 'delete this' // from the point of view of our thread. Release(); } // *** ThreadList - used to track all created threads class ThreadList : public NewOverrideBase { //------------------------------------------------------------------------ struct ThreadHashOp { size_t operator()(const Thread* ptr) { return (((size_t)ptr) >> 6) ^ (size_t)ptr; } }; HashSet ThreadSet; Mutex ThreadMutex; WaitCondition ThreadsEmpty; // Track the root thread that created us. pthread_t RootThreadId; static ThreadList* volatile pRunningThreads; void addThread(Thread *pthread) { Mutex::Locker lock(&ThreadMutex); ThreadSet.Add(pthread); } void removeThread(Thread *pthread) { Mutex::Locker lock(&ThreadMutex); ThreadSet.Remove(pthread); if (ThreadSet.GetSize() == 0) ThreadsEmpty.Notify(); } void finishAllThreads() { // Only original root thread can call this. OVR_ASSERT(pthread_self() == RootThreadId); Mutex::Locker lock(&ThreadMutex); while (ThreadSet.GetSize() != 0) ThreadsEmpty.Wait(&ThreadMutex); } public: ThreadList() { RootThreadId = pthread_self(); } ~ThreadList() { } static void AddRunningThread(Thread *pthread) { // Non-atomic creation ok since only the root thread if (!pRunningThreads) { pRunningThreads = new ThreadList; OVR_ASSERT(pRunningThreads); } pRunningThreads->addThread(pthread); } // NOTE: 'pthread' might be a dead pointer when this is // called so it should not be accessed; it is only used // for removal. static void RemoveRunningThread(Thread *pthread) { OVR_ASSERT(pRunningThreads); pRunningThreads->removeThread(pthread); } static void FinishAllThreads() { // This is ok because only root thread can wait for other thread finish. if (pRunningThreads) { pRunningThreads->finishAllThreads(); delete pRunningThreads; pRunningThreads = 0; } } }; // By default, we have no thread list. ThreadList* volatile ThreadList::pRunningThreads = 0; // FinishAllThreads - exposed publicly in Thread. void Thread::FinishAllThreads() { ThreadList::FinishAllThreads(); } // *** Run override int Thread::PRun() { // Suspend us on start, if requested if (ThreadFlags & OVR_THREAD_START_SUSPENDED) { Suspend(); ThreadFlags &= (UInt32)~OVR_THREAD_START_SUSPENDED; } // Call the virtual run function ExitCode = Run(); return ExitCode; } // *** User overridables bool Thread::GetExitFlag() const { return (ThreadFlags & OVR_THREAD_EXIT) != 0; } void Thread::SetExitFlag(bool exitFlag) { // The below is atomic since ThreadFlags is AtomicInt. if (exitFlag) ThreadFlags |= OVR_THREAD_EXIT; else ThreadFlags &= (UInt32) ~OVR_THREAD_EXIT; } // Determines whether the thread was running and is now finished bool Thread::IsFinished() const { return (ThreadFlags & OVR_THREAD_FINISHED) != 0; } // Determines whether the thread is suspended bool Thread::IsSuspended() const { return SuspendCount > 0; } // Returns current thread state Thread::ThreadState Thread::GetThreadState() const { if (IsSuspended()) return Suspended; if (ThreadFlags & OVR_THREAD_STARTED) return Running; return NotRunning; } /* static const char* mapsched_policy(int policy) { switch(policy) { case SCHED_OTHER: return "SCHED_OTHER"; case SCHED_RR: return "SCHED_RR"; case SCHED_FIFO: return "SCHED_FIFO"; } return "UNKNOWN"; } int policy; sched_param sparam; pthread_getschedparam(pthread_self(), &policy, &sparam); int max_prior = sched_get_priority_max(policy); int min_prior = sched_get_priority_min(policy); printf(" !!!! policy: %s, priority: %d, max priority: %d, min priority: %d\n", mapsched_policy(policy), sparam.sched_priority, max_prior, min_prior); #include */ // ***** Thread management // The actual first function called on thread start void* Thread_PthreadStartFn(void* phandle) { Thread* pthread = (Thread*)phandle; int result = pthread->PRun(); // Signal the thread as done and release it atomically. pthread->FinishAndRelease(); // At this point Thread object might be dead; however we can still pass // it to RemoveRunningThread since it is only used as a key there. ThreadList::RemoveRunningThread(pthread); return (void*) result; } int Thread::InitAttr = 0; pthread_attr_t Thread::Attr; /* static */ int Thread::GetOSPriority(ThreadPriority p) //static inline int MapToSystemPrority(Thread::ThreadPriority p) { #ifdef OVR_OS_PS3 switch(p) { case Thread::CriticalPriority: return 0; case Thread::HighestPriority: return 300; case Thread::AboveNormalPriority: return 600; case Thread::NormalPriority: return 1000; case Thread::BelowNormalPriority: return 1500; case Thread::LowestPriority: return 2500; case Thread::IdlePriority: return 3071; } return 1000; #else OVR_UNUSED(p); return -1; #endif } bool Thread::Start(ThreadState initialState) { if (initialState == NotRunning) return 0; if (GetThreadState() != NotRunning) { OVR_DEBUG_LOG(("Thread::Start failed - thread %p already running", this)); return 0; } if (!InitAttr) { pthread_attr_init(&Attr); pthread_attr_setdetachstate(&Attr, PTHREAD_CREATE_DETACHED); pthread_attr_setstacksize(&Attr, 128 * 1024); sched_param sparam; sparam.sched_priority = Thread::GetOSPriority(NormalPriority); pthread_attr_setschedparam(&Attr, &sparam); InitAttr = 1; } ExitCode = 0; SuspendCount = 0; ThreadFlags = (initialState == Running) ? 0 : OVR_THREAD_START_SUSPENDED; // AddRef to us until the thread is finished AddRef(); ThreadList::AddRunningThread(this); int result; if (StackSize != 128 * 1024 || Priority != NormalPriority) { pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); pthread_attr_setstacksize(&attr, StackSize); sched_param sparam; sparam.sched_priority = Thread::GetOSPriority(Priority); pthread_attr_setschedparam(&attr, &sparam); result = pthread_create(&ThreadHandle, &attr, Thread_PthreadStartFn, this); pthread_attr_destroy(&attr); } else result = pthread_create(&ThreadHandle, &Attr, Thread_PthreadStartFn, this); if (result) { ThreadFlags = 0; Release(); ThreadList::RemoveRunningThread(this); return 0; } return 1; } // Suspend the thread until resumed bool Thread::Suspend() { OVR_DEBUG_LOG(("Thread::Suspend - cannot suspend threads on this system")); return 0; } // Resumes currently suspended thread bool Thread::Resume() { return 0; } // Quits with an exit code void Thread::Exit(int exitCode) { // Can only exist the current thread // if (GetThread() != this) // return; // Call the virtual OnExit function OnExit(); // Signal this thread object as done and release it's references. FinishAndRelease(); ThreadList::RemoveRunningThread(this); pthread_exit((void *) exitCode); } ThreadId GetCurrentThreadId() { return (void*)pthread_self(); } // *** Sleep functions /* static */ bool Thread::Sleep(unsigned secs) { sleep(secs); return 1; } /* static */ bool Thread::MSleep(unsigned msecs) { usleep(msecs*1000); return 1; } /* static */ int Thread::GetCPUCount() { return 1; } #ifdef OVR_OS_PS3 sys_lwmutex_attribute_t Lock::LockAttr = { SYS_SYNC_PRIORITY, SYS_SYNC_RECURSIVE }; #endif } #endif // OVR_ENABLE_THREADS \ No newline at end of file + +#include "OVR_Threads.h" +#include "OVR_Hash.h" + +#ifdef OVR_ENABLE_THREADS + +#include "OVR_Timer.h" +#include "OVR_Log.h" + +#include +#include + +#ifdef OVR_OS_PS3 +#include +#include +#include +#define sleep(x) sys_timer_sleep(x) +#define usleep(x) sys_timer_usleep(x) +using std::timespec; +#else +#include +#include +#include +#endif + +namespace OVR { + +// ***** Mutex implementation + + +// *** Internal Mutex implementation structure + +class MutexImpl : public NewOverrideBase +{ + // System mutex or semaphore + pthread_mutex_t SMutex; + bool Recursive; + unsigned LockCount; + pthread_t LockedBy; + + friend class WaitConditionImpl; + +public: + // Constructor/destructor + MutexImpl(Mutex* pmutex, bool recursive = 1); + ~MutexImpl(); + + // Locking functions + void DoLock(); + bool TryLock(); + void Unlock(Mutex* pmutex); + // Returns 1 if the mutes is currently locked + bool IsLockedByAnotherThread(Mutex* pmutex); + bool IsSignaled() const; +}; + +pthread_mutexattr_t Lock::RecursiveAttr; +bool Lock::RecursiveAttrInit = 0; + +// *** Constructor/destructor +MutexImpl::MutexImpl(Mutex* pmutex, bool recursive) +{ + Recursive = recursive; + LockCount = 0; + + if (Recursive) + { + if (!Lock::RecursiveAttrInit) + { + pthread_mutexattr_init(&Lock::RecursiveAttr); + pthread_mutexattr_settype(&Lock::RecursiveAttr, PTHREAD_MUTEX_RECURSIVE); + Lock::RecursiveAttrInit = 1; + } + + pthread_mutex_init(&SMutex, &Lock::RecursiveAttr); + } + else + pthread_mutex_init(&SMutex, 0); +} + +MutexImpl::~MutexImpl() +{ + pthread_mutex_destroy(&SMutex); +} + + +// Lock and try lock +void MutexImpl::DoLock() +{ + while (pthread_mutex_lock(&SMutex)); + LockCount++; + LockedBy = pthread_self(); +} + +bool MutexImpl::TryLock() +{ + if (!pthread_mutex_trylock(&SMutex)) + { + LockCount++; + LockedBy = pthread_self(); + return 1; + } + + return 0; +} + +void MutexImpl::Unlock(Mutex* pmutex) +{ + OVR_ASSERT(pthread_self() == LockedBy && LockCount > 0); + + unsigned lockCount; + LockCount--; + lockCount = LockCount; + + pthread_mutex_unlock(&SMutex); +} + +bool MutexImpl::IsLockedByAnotherThread(Mutex* pmutex) +{ + // There could be multiple interpretations of IsLocked with respect to current thread + if (LockCount == 0) + return 0; + if (pthread_self() != LockedBy) + return 1; + return 0; +} + +bool MutexImpl::IsSignaled() const +{ + // An mutex is signaled if it is not locked ANYWHERE + // Note that this is different from IsLockedByAnotherThread function, + // that takes current thread into account + return LockCount == 0; +} + + +// *** Actual Mutex class implementation + +Mutex::Mutex(bool recursive) +{ + // NOTE: RefCount mode already thread-safe for all waitables. + pImpl = new MutexImpl(this, recursive); +} + +Mutex::~Mutex() +{ + delete pImpl; +} + +// Lock and try lock +void Mutex::DoLock() +{ + pImpl->DoLock(); +} +bool Mutex::TryLock() +{ + return pImpl->TryLock(); +} +void Mutex::Unlock() +{ + pImpl->Unlock(this); +} +bool Mutex::IsLockedByAnotherThread() +{ + return pImpl->IsLockedByAnotherThread(this); +} + + + +//----------------------------------------------------------------------------------- +// ***** Event + +bool Event::Wait(unsigned delay) +{ + Mutex::Locker lock(&StateMutex); + + // Do the correct amount of waiting + if (delay == OVR_WAIT_INFINITE) + { + while(!State) + StateWaitCondition.Wait(&StateMutex); + } + else if (delay) + { + if (!State) + StateWaitCondition.Wait(&StateMutex, delay); + } + + bool state = State; + // Take care of temporary 'pulsing' of a state + if (Temporary) + { + Temporary = false; + State = false; + } + return state; +} + +void Event::updateState(bool newState, bool newTemp, bool mustNotify) +{ + Mutex::Locker lock(&StateMutex); + State = newState; + Temporary = newTemp; + if (mustNotify) + StateWaitCondition.NotifyAll(); +} + + + +// ***** Wait Condition Implementation + +// Internal implementation class +class WaitConditionImpl : public NewOverrideBase +{ + pthread_mutex_t SMutex; + pthread_cond_t Condv; + +public: + + // Constructor/destructor + WaitConditionImpl(); + ~WaitConditionImpl(); + + // Release mutex and wait for condition. The mutex is re-aqured after the wait. + bool Wait(Mutex *pmutex, unsigned delay = OVR_WAIT_INFINITE); + + // Notify a condition, releasing at one object waiting + void Notify(); + // Notify a condition, releasing all objects waiting + void NotifyAll(); +}; + + +WaitConditionImpl::WaitConditionImpl() +{ + pthread_mutex_init(&SMutex, 0); + pthread_cond_init(&Condv, 0); +} + +WaitConditionImpl::~WaitConditionImpl() +{ + pthread_mutex_destroy(&SMutex); + pthread_cond_destroy(&Condv); +} + +bool WaitConditionImpl::Wait(Mutex *pmutex, unsigned delay) +{ + bool result = 1; + unsigned lockCount = pmutex->pImpl->LockCount; + + // Mutex must have been locked + if (lockCount == 0) + return 0; + + pthread_mutex_lock(&SMutex); + + // Finally, release a mutex or semaphore + if (pmutex->pImpl->Recursive) + { + // Release the recursive mutex N times + pmutex->pImpl->LockCount = 0; + for(unsigned i=0; ipImpl->SMutex); + } + else + { + pmutex->pImpl->LockCount = 0; + pthread_mutex_unlock(&pmutex->pImpl->SMutex); + } + + // Note that there is a gap here between mutex.Unlock() and Wait(). + // The other mutex protects this gap. + + if (delay == OVR_WAIT_INFINITE) + pthread_cond_wait(&Condv,&SMutex); + else + { + timespec ts; +#ifdef OVR_OS_PS3 + sys_time_sec_t s; + sys_time_nsec_t ns; + sys_time_get_current_time(&s, &ns); + + ts.tv_sec = s + (delay / 1000); + ts.tv_nsec = ns + (delay % 1000) * 1000000; + +#else + struct timeval tv; + gettimeofday(&tv, 0); + + ts.tv_sec = tv.tv_sec + (delay / 1000); + ts.tv_nsec = (tv.tv_usec + (delay % 1000) * 1000) * 1000; +#endif + if (ts.tv_nsec > 999999999) + { + ts.tv_sec++; + ts.tv_nsec -= 1000000000; + } + int r = pthread_cond_timedwait(&Condv,&SMutex, &ts); + OVR_ASSERT(r == 0 || r == ETIMEDOUT); + if (r) + result = 0; + } + + pthread_mutex_unlock(&SMutex); + + // Re-aquire the mutex + for(unsigned i=0; iDoLock(); + + // Return the result + return result; +} + +// Notify a condition, releasing the least object in a queue +void WaitConditionImpl::Notify() +{ + pthread_mutex_lock(&SMutex); + pthread_cond_signal(&Condv); + pthread_mutex_unlock(&SMutex); +} + +// Notify a condition, releasing all objects waiting +void WaitConditionImpl::NotifyAll() +{ + pthread_mutex_lock(&SMutex); + pthread_cond_broadcast(&Condv); + pthread_mutex_unlock(&SMutex); +} + + + +// *** Actual implementation of WaitCondition + +WaitCondition::WaitCondition() +{ + pImpl = new WaitConditionImpl; +} +WaitCondition::~WaitCondition() +{ + delete pImpl; +} + +bool WaitCondition::Wait(Mutex *pmutex, unsigned delay) +{ + return pImpl->Wait(pmutex, delay); +} +// Notification +void WaitCondition::Notify() +{ + pImpl->Notify(); +} +void WaitCondition::NotifyAll() +{ + pImpl->NotifyAll(); +} + + +// ***** Current thread + +// Per-thread variable +/* +static __thread Thread* pCurrentThread = 0; + +// Static function to return a pointer to the current thread +void Thread::InitCurrentThread(Thread *pthread) +{ + pCurrentThread = pthread; +} + +// Static function to return a pointer to the current thread +Thread* Thread::GetThread() +{ + return pCurrentThread; +} +*/ + + +// *** Thread constructors. + +Thread::Thread(UPInt stackSize, int processor) +{ + // NOTE: RefCount mode already thread-safe for all Waitable objects. + CreateParams params; + params.stackSize = stackSize; + params.processor = processor; + Init(params); +} + +Thread::Thread(Thread::ThreadFn threadFunction, void* userHandle, UPInt stackSize, + int processor, Thread::ThreadState initialState) +{ + CreateParams params(threadFunction, userHandle, stackSize, processor, initialState); + Init(params); +} + +Thread::Thread(const CreateParams& params) +{ + Init(params); +} + +void Thread::Init(const CreateParams& params) +{ + // Clear the variables + ThreadFlags = 0; + ThreadHandle = 0; + ExitCode = 0; + SuspendCount = 0; + StackSize = params.stackSize; + Processor = params.processor; + Priority = params.priority; + + // Clear Function pointers + ThreadFunction = params.threadFunction; + UserHandle = params.userHandle; + if (params.initialState != NotRunning) + Start(params.initialState); +} + +Thread::~Thread() +{ + // Thread should not running while object is being destroyed, + // this would indicate ref-counting issue. + //OVR_ASSERT(IsRunning() == 0); + + // Clean up thread. + ThreadHandle = 0; +} + + + +// *** Overridable User functions. + +// Default Run implementation +int Thread::Run() +{ + // Call pointer to function, if available. + return (ThreadFunction) ? ThreadFunction(this, UserHandle) : 0; +} +void Thread::OnExit() +{ +} + + +// Finishes the thread and releases internal reference to it. +void Thread::FinishAndRelease() +{ + // Note: thread must be US. + ThreadFlags &= (UInt32)~(OVR_THREAD_STARTED); + ThreadFlags |= OVR_THREAD_FINISHED; + + // Release our reference; this is equivalent to 'delete this' + // from the point of view of our thread. + Release(); +} + + + +// *** ThreadList - used to track all created threads + +class ThreadList : public NewOverrideBase +{ + //------------------------------------------------------------------------ + struct ThreadHashOp + { + size_t operator()(const Thread* ptr) + { + return (((size_t)ptr) >> 6) ^ (size_t)ptr; + } + }; + + HashSet ThreadSet; + Mutex ThreadMutex; + WaitCondition ThreadsEmpty; + // Track the root thread that created us. + pthread_t RootThreadId; + + static ThreadList* volatile pRunningThreads; + + void addThread(Thread *pthread) + { + Mutex::Locker lock(&ThreadMutex); + ThreadSet.Add(pthread); + } + + void removeThread(Thread *pthread) + { + Mutex::Locker lock(&ThreadMutex); + ThreadSet.Remove(pthread); + if (ThreadSet.GetSize() == 0) + ThreadsEmpty.Notify(); + } + + void finishAllThreads() + { + // Only original root thread can call this. + OVR_ASSERT(pthread_self() == RootThreadId); + + Mutex::Locker lock(&ThreadMutex); + while (ThreadSet.GetSize() != 0) + ThreadsEmpty.Wait(&ThreadMutex); + } + +public: + + ThreadList() + { + RootThreadId = pthread_self(); + } + ~ThreadList() { } + + + static void AddRunningThread(Thread *pthread) + { + // Non-atomic creation ok since only the root thread + if (!pRunningThreads) + { + pRunningThreads = new ThreadList; + OVR_ASSERT(pRunningThreads); + } + pRunningThreads->addThread(pthread); + } + + // NOTE: 'pthread' might be a dead pointer when this is + // called so it should not be accessed; it is only used + // for removal. + static void RemoveRunningThread(Thread *pthread) + { + OVR_ASSERT(pRunningThreads); + pRunningThreads->removeThread(pthread); + } + + static void FinishAllThreads() + { + // This is ok because only root thread can wait for other thread finish. + if (pRunningThreads) + { + pRunningThreads->finishAllThreads(); + delete pRunningThreads; + pRunningThreads = 0; + } + } +}; + +// By default, we have no thread list. +ThreadList* volatile ThreadList::pRunningThreads = 0; + + +// FinishAllThreads - exposed publicly in Thread. +void Thread::FinishAllThreads() +{ + ThreadList::FinishAllThreads(); +} + +// *** Run override + +int Thread::PRun() +{ + // Suspend us on start, if requested + if (ThreadFlags & OVR_THREAD_START_SUSPENDED) + { + Suspend(); + ThreadFlags &= (UInt32)~OVR_THREAD_START_SUSPENDED; + } + + // Call the virtual run function + ExitCode = Run(); + return ExitCode; +} + + + + +// *** User overridables + +bool Thread::GetExitFlag() const +{ + return (ThreadFlags & OVR_THREAD_EXIT) != 0; +} + +void Thread::SetExitFlag(bool exitFlag) +{ + // The below is atomic since ThreadFlags is AtomicInt. + if (exitFlag) + ThreadFlags |= OVR_THREAD_EXIT; + else + ThreadFlags &= (UInt32) ~OVR_THREAD_EXIT; +} + + +// Determines whether the thread was running and is now finished +bool Thread::IsFinished() const +{ + return (ThreadFlags & OVR_THREAD_FINISHED) != 0; +} +// Determines whether the thread is suspended +bool Thread::IsSuspended() const +{ + return SuspendCount > 0; +} +// Returns current thread state +Thread::ThreadState Thread::GetThreadState() const +{ + if (IsSuspended()) + return Suspended; + if (ThreadFlags & OVR_THREAD_STARTED) + return Running; + return NotRunning; +} +/* +static const char* mapsched_policy(int policy) +{ + switch(policy) + { + case SCHED_OTHER: + return "SCHED_OTHER"; + case SCHED_RR: + return "SCHED_RR"; + case SCHED_FIFO: + return "SCHED_FIFO"; + + } + return "UNKNOWN"; +} + int policy; + sched_param sparam; + pthread_getschedparam(pthread_self(), &policy, &sparam); + int max_prior = sched_get_priority_max(policy); + int min_prior = sched_get_priority_min(policy); + printf(" !!!! policy: %s, priority: %d, max priority: %d, min priority: %d\n", mapsched_policy(policy), sparam.sched_priority, max_prior, min_prior); +#include +*/ +// ***** Thread management + +// The actual first function called on thread start +void* Thread_PthreadStartFn(void* phandle) +{ + Thread* pthread = (Thread*)phandle; + int result = pthread->PRun(); + // Signal the thread as done and release it atomically. + pthread->FinishAndRelease(); + // At this point Thread object might be dead; however we can still pass + // it to RemoveRunningThread since it is only used as a key there. + ThreadList::RemoveRunningThread(pthread); + return (void*) result; +} + +int Thread::InitAttr = 0; +pthread_attr_t Thread::Attr; + +/* static */ +int Thread::GetOSPriority(ThreadPriority p) +//static inline int MapToSystemPrority(Thread::ThreadPriority p) +{ +#ifdef OVR_OS_PS3 + switch(p) + { + case Thread::CriticalPriority: return 0; + case Thread::HighestPriority: return 300; + case Thread::AboveNormalPriority: return 600; + case Thread::NormalPriority: return 1000; + case Thread::BelowNormalPriority: return 1500; + case Thread::LowestPriority: return 2500; + case Thread::IdlePriority: return 3071; + } return 1000; +#else + OVR_UNUSED(p); + return -1; +#endif +} + +bool Thread::Start(ThreadState initialState) +{ + if (initialState == NotRunning) + return 0; + if (GetThreadState() != NotRunning) + { + OVR_DEBUG_LOG(("Thread::Start failed - thread %p already running", this)); + return 0; + } + + if (!InitAttr) + { + pthread_attr_init(&Attr); + pthread_attr_setdetachstate(&Attr, PTHREAD_CREATE_DETACHED); + pthread_attr_setstacksize(&Attr, 128 * 1024); + sched_param sparam; + sparam.sched_priority = Thread::GetOSPriority(NormalPriority); + pthread_attr_setschedparam(&Attr, &sparam); + InitAttr = 1; + } + + ExitCode = 0; + SuspendCount = 0; + ThreadFlags = (initialState == Running) ? 0 : OVR_THREAD_START_SUSPENDED; + + // AddRef to us until the thread is finished + AddRef(); + ThreadList::AddRunningThread(this); + + int result; + if (StackSize != 128 * 1024 || Priority != NormalPriority) + { + pthread_attr_t attr; + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_attr_setstacksize(&attr, StackSize); + sched_param sparam; + sparam.sched_priority = Thread::GetOSPriority(Priority); + pthread_attr_setschedparam(&attr, &sparam); + result = pthread_create(&ThreadHandle, &attr, Thread_PthreadStartFn, this); + pthread_attr_destroy(&attr); + } + else + result = pthread_create(&ThreadHandle, &Attr, Thread_PthreadStartFn, this); + + if (result) + { + ThreadFlags = 0; + Release(); + ThreadList::RemoveRunningThread(this); + return 0; + } + return 1; +} + + +// Suspend the thread until resumed +bool Thread::Suspend() +{ + OVR_DEBUG_LOG(("Thread::Suspend - cannot suspend threads on this system")); + return 0; +} + +// Resumes currently suspended thread +bool Thread::Resume() +{ + return 0; +} + + +// Quits with an exit code +void Thread::Exit(int exitCode) +{ + // Can only exist the current thread + // if (GetThread() != this) + // return; + + // Call the virtual OnExit function + OnExit(); + + // Signal this thread object as done and release it's references. + FinishAndRelease(); + ThreadList::RemoveRunningThread(this); + + pthread_exit((void *) exitCode); +} + +ThreadId GetCurrentThreadId() +{ + return (void*)pthread_self(); +} + +// *** Sleep functions + +/* static */ +bool Thread::Sleep(unsigned secs) +{ + sleep(secs); + return 1; +} +/* static */ +bool Thread::MSleep(unsigned msecs) +{ + usleep(msecs*1000); + return 1; +} + +/* static */ +int Thread::GetCPUCount() +{ + return 1; +} + + +#ifdef OVR_OS_PS3 + +sys_lwmutex_attribute_t Lock::LockAttr = { SYS_SYNC_PRIORITY, SYS_SYNC_RECURSIVE }; + +#endif + +} + +#endif // OVR_ENABLE_THREADS diff -r 59fc487ba58e -r b069a5c27388 libovr/Src/win32/OVR_ThreadsWinAPI.cpp --- a/libovr/Src/win32/OVR_ThreadsWinAPI.cpp Sat Sep 14 17:51:03 2013 +0300 +++ b/libovr/Src/win32/OVR_ThreadsWinAPI.cpp Sun Sep 15 04:10:05 2013 +0300 @@ -1,1 +1,994 @@ -/************************************************************************************ Filename : OVR_ThreadsWinAPI.cpp Platform : WinAPI Content : Windows specific thread-related (safe) functionality Created : September 19, 2012 Notes : Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #include "OVR_Threads.h" #include "OVR_Hash.h" #include "OVR_Log.h" #ifdef OVR_ENABLE_THREADS // For _beginthreadex / _endtheadex #include namespace OVR { //----------------------------------------------------------------------------------- // *** Internal Mutex implementation class class MutexImpl : public NewOverrideBase { // System mutex or semaphore HANDLE hMutexOrSemaphore; bool Recursive; volatile unsigned LockCount; friend class WaitConditionImpl; public: // Constructor/destructor MutexImpl(bool recursive = 1); ~MutexImpl(); // Locking functions void DoLock(); bool TryLock(); void Unlock(Mutex* pmutex); // Returns 1 if the mutes is currently locked bool IsLockedByAnotherThread(Mutex* pmutex); }; // *** Constructor/destructor MutexImpl::MutexImpl(bool recursive) { Recursive = recursive; LockCount = 0; hMutexOrSemaphore = Recursive ? CreateMutex(NULL, 0, NULL) : CreateSemaphore(NULL, 1, 1, NULL); } MutexImpl::~MutexImpl() { CloseHandle(hMutexOrSemaphore); } // Lock and try lock void MutexImpl::DoLock() { if (::WaitForSingleObject(hMutexOrSemaphore, INFINITE) != WAIT_OBJECT_0) return; LockCount++; } bool MutexImpl::TryLock() { DWORD ret; if ((ret=::WaitForSingleObject(hMutexOrSemaphore, 0)) != WAIT_OBJECT_0) return 0; LockCount++; return 1; } void MutexImpl::Unlock(Mutex* pmutex) { OVR_UNUSED(pmutex); unsigned lockCount; LockCount--; lockCount = LockCount; // Release mutex if ((Recursive ? ReleaseMutex(hMutexOrSemaphore) : ReleaseSemaphore(hMutexOrSemaphore, 1, NULL)) != 0) { // This used to call Wait handlers if lockCount == 0. } } bool MutexImpl::IsLockedByAnotherThread(Mutex* pmutex) { // There could be multiple interpretations of IsLocked with respect to current thread if (LockCount == 0) return 0; if (!TryLock()) return 1; Unlock(pmutex); return 0; } /* bool MutexImpl::IsSignaled() const { // An mutex is signaled if it is not locked ANYWHERE // Note that this is different from IsLockedByAnotherThread function, // that takes current thread into account return LockCount == 0; } */ // *** Actual Mutex class implementation Mutex::Mutex(bool recursive) { pImpl = new MutexImpl(recursive); } Mutex::~Mutex() { delete pImpl; } // Lock and try lock void Mutex::DoLock() { pImpl->DoLock(); } bool Mutex::TryLock() { return pImpl->TryLock(); } void Mutex::Unlock() { pImpl->Unlock(this); } bool Mutex::IsLockedByAnotherThread() { return pImpl->IsLockedByAnotherThread(this); } //----------------------------------------------------------------------------------- // ***** Event bool Event::Wait(unsigned delay) { Mutex::Locker lock(&StateMutex); // Do the correct amount of waiting if (delay == OVR_WAIT_INFINITE) { while(!State) StateWaitCondition.Wait(&StateMutex); } else if (delay) { if (!State) StateWaitCondition.Wait(&StateMutex, delay); } bool state = State; // Take care of temporary 'pulsing' of a state if (Temporary) { Temporary = false; State = false; } return state; } void Event::updateState(bool newState, bool newTemp, bool mustNotify) { Mutex::Locker lock(&StateMutex); State = newState; Temporary = newTemp; if (mustNotify) StateWaitCondition.NotifyAll(); } //----------------------------------------------------------------------------------- // ***** Win32 Wait Condition Implementation // Internal implementation class class WaitConditionImpl : public NewOverrideBase { // Event pool entries for extra events struct EventPoolEntry : public NewOverrideBase { HANDLE hEvent; EventPoolEntry *pNext; EventPoolEntry *pPrev; }; Lock WaitQueueLoc; // Stores free events that can be used later EventPoolEntry * pFreeEventList; // A queue of waiting objects to be signaled EventPoolEntry* pQueueHead; EventPoolEntry* pQueueTail; // Allocation functions for free events EventPoolEntry* GetNewEvent(); void ReleaseEvent(EventPoolEntry* pevent); // Queue operations void QueuePush(EventPoolEntry* pentry); EventPoolEntry* QueuePop(); void QueueFindAndRemove(EventPoolEntry* pentry); public: // Constructor/destructor WaitConditionImpl(); ~WaitConditionImpl(); // Release mutex and wait for condition. The mutex is re-acqured after the wait. bool Wait(Mutex *pmutex, unsigned delay = OVR_WAIT_INFINITE); // Notify a condition, releasing at one object waiting void Notify(); // Notify a condition, releasing all objects waiting void NotifyAll(); }; WaitConditionImpl::WaitConditionImpl() { pFreeEventList = 0; pQueueHead = pQueueTail = 0; } WaitConditionImpl::~WaitConditionImpl() { // Free all the resources EventPoolEntry* p = pFreeEventList; EventPoolEntry* pentry; while(p) { // Move to next pentry = p; p = p->pNext; // Delete old ::CloseHandle(pentry->hEvent); delete pentry; } // Shouldn't we also consider the queue? // To be safe pFreeEventList = 0; pQueueHead = pQueueTail = 0; } // Allocation functions for free events WaitConditionImpl::EventPoolEntry* WaitConditionImpl::GetNewEvent() { EventPoolEntry* pentry; // If there are any free nodes, use them if (pFreeEventList) { pentry = pFreeEventList; pFreeEventList = pFreeEventList->pNext; } else { // Allocate a new node pentry = new EventPoolEntry; pentry->pNext = 0; pentry->pPrev = 0; // Non-signaled manual event pentry->hEvent = ::CreateEvent(NULL, TRUE, 0, NULL); } return pentry; } void WaitConditionImpl::ReleaseEvent(EventPoolEntry* pevent) { // Mark event as non-signaled ::ResetEvent(pevent->hEvent); // And add it to free pool pevent->pNext = pFreeEventList; pevent->pPrev = 0; pFreeEventList = pevent; } // Queue operations void WaitConditionImpl::QueuePush(EventPoolEntry* pentry) { // Items already exist? Just add to tail if (pQueueTail) { pentry->pPrev = pQueueTail; pQueueTail->pNext = pentry; pentry->pNext = 0; pQueueTail = pentry; } else { // No items in queue pentry->pNext = pentry->pPrev = 0; pQueueHead = pQueueTail = pentry; } } WaitConditionImpl::EventPoolEntry* WaitConditionImpl::QueuePop() { EventPoolEntry* pentry = pQueueHead; // No items, null pointer if (pentry) { // More items after this one? just grab the first item if (pQueueHead->pNext) { pQueueHead = pentry->pNext; pQueueHead->pPrev = 0; } else { // Last item left pQueueTail = pQueueHead = 0; } } return pentry; } void WaitConditionImpl::QueueFindAndRemove(EventPoolEntry* pentry) { // Do an exhaustive search looking for an entry EventPoolEntry* p = pQueueHead; while(p) { // Entry found? Remove. if (p == pentry) { // Remove the node form the list // Prev link if (pentry->pPrev) pentry->pPrev->pNext = pentry->pNext; else pQueueHead = pentry->pNext; // Next link if (pentry->pNext) pentry->pNext->pPrev = pentry->pPrev; else pQueueTail = pentry->pPrev; // Done return; } // Move to next item p = p->pNext; } } bool WaitConditionImpl::Wait(Mutex *pmutex, unsigned delay) { bool result = 0; unsigned i; unsigned lockCount = pmutex->pImpl->LockCount; EventPoolEntry* pentry; // Mutex must have been locked if (lockCount == 0) return 0; // Add an object to the wait queue WaitQueueLoc.DoLock(); QueuePush(pentry = GetNewEvent()); WaitQueueLoc.Unlock(); // Finally, release a mutex or semaphore if (pmutex->pImpl->Recursive) { // Release the recursive mutex N times pmutex->pImpl->LockCount = 0; for(i=0; ipImpl->hMutexOrSemaphore); } else { pmutex->pImpl->LockCount = 0; ::ReleaseSemaphore(pmutex->pImpl->hMutexOrSemaphore, 1, NULL); } // Note that there is a gap here between mutex.Unlock() and Wait(). However, // if notify() comes in at this point in the other thread it will set our // corresponding event so wait will just fall through, as expected. // Block and wait on the event DWORD waitResult = ::WaitForSingleObject(pentry->hEvent, (delay == OVR_WAIT_INFINITE) ? INFINITE : delay); /* repeat_wait: DWORD waitResult = ::MsgWaitForMultipleObjects(1, &pentry->hEvent, FALSE, (delay == OVR_WAIT_INFINITE) ? INFINITE : delay, QS_ALLINPUT); */ WaitQueueLoc.DoLock(); switch(waitResult) { case WAIT_ABANDONED: case WAIT_OBJECT_0: result = 1; // Wait was successful, therefore the event entry should already be removed // So just add entry back to a free list ReleaseEvent(pentry); break; /* case WAIT_OBJECT_0 + 1: // Messages in WINDOWS queue { MSG msg; PeekMessage(&msg, NULL, 0U, 0U, PM_NOREMOVE); WaitQueueLoc.Unlock(); goto repeat_wait; } break; */ default: // Timeout, our entry should still be in a queue QueueFindAndRemove(pentry); ReleaseEvent(pentry); } WaitQueueLoc.Unlock(); // Re-aquire the mutex for(i=0; iDoLock(); // Return the result return result; } // Notify a condition, releasing the least object in a queue void WaitConditionImpl::Notify() { Lock::Locker lock(&WaitQueueLoc); // Pop last entry & signal it EventPoolEntry* pentry = QueuePop(); if (pentry) ::SetEvent(pentry->hEvent); } // Notify a condition, releasing all objects waiting void WaitConditionImpl::NotifyAll() { Lock::Locker lock(&WaitQueueLoc); // Pop and signal all events // NOTE : There is no need to release the events, it's the waiters job to do so EventPoolEntry* pentry = QueuePop(); while (pentry) { ::SetEvent(pentry->hEvent); pentry = QueuePop(); } } // *** Actual implementation of WaitCondition WaitCondition::WaitCondition() { pImpl = new WaitConditionImpl; } WaitCondition::~WaitCondition() { delete pImpl; } // Wait without a mutex bool WaitCondition::Wait(Mutex *pmutex, unsigned delay) { return pImpl->Wait(pmutex, delay); } // Notification void WaitCondition::Notify() { pImpl->Notify(); } void WaitCondition::NotifyAll() { pImpl->NotifyAll(); } //----------------------------------------------------------------------------------- // ***** Thread Class // Per-thread variable // MA: Don't use TLS for now - portability issues with DLLs, etc. /* #if !defined(OVR_CC_MSVC) || (OVR_CC_MSVC < 1300) __declspec(thread) Thread* pCurrentThread = 0; #else #pragma data_seg(".tls$") __declspec(thread) Thread* pCurrentThread = 0; #pragma data_seg(".rwdata") #endif */ // *** Thread constructors. Thread::Thread(UPInt stackSize, int processor) { CreateParams params; params.stackSize = stackSize; params.processor = processor; Init(params); } Thread::Thread(Thread::ThreadFn threadFunction, void* userHandle, UPInt stackSize, int processor, Thread::ThreadState initialState) { CreateParams params(threadFunction, userHandle, stackSize, processor, initialState); Init(params); } Thread::Thread(const CreateParams& params) { Init(params); } void Thread::Init(const CreateParams& params) { // Clear the variables ThreadFlags = 0; ThreadHandle = 0; IdValue = 0; ExitCode = 0; SuspendCount = 0; StackSize = params.stackSize; Processor = params.processor; Priority = params.priority; // Clear Function pointers ThreadFunction = params.threadFunction; UserHandle = params.userHandle; if (params.initialState != NotRunning) Start(params.initialState); } Thread::~Thread() { // Thread should not running while object is being destroyed, // this would indicate ref-counting issue. //OVR_ASSERT(IsRunning() == 0); // Clean up thread. CleanupSystemThread(); ThreadHandle = 0; } // *** Overridable User functions. // Default Run implementation int Thread::Run() { // Call pointer to function, if available. return (ThreadFunction) ? ThreadFunction(this, UserHandle) : 0; } void Thread::OnExit() { } // Finishes the thread and releases internal reference to it. void Thread::FinishAndRelease() { // Note: thread must be US. ThreadFlags &= (UInt32)~(OVR_THREAD_STARTED); ThreadFlags |= OVR_THREAD_FINISHED; // Release our reference; this is equivalent to 'delete this' // from the point of view of our thread. Release(); } // *** ThreadList - used to tack all created threads class ThreadList : public NewOverrideBase { //------------------------------------------------------------------------ struct ThreadHashOp { UPInt operator()(const Thread* ptr) { return (((UPInt)ptr) >> 6) ^ (UPInt)ptr; } }; HashSet ThreadSet; Mutex ThreadMutex; WaitCondition ThreadsEmpty; // Track the root thread that created us. ThreadId RootThreadId; static ThreadList* volatile pRunningThreads; void addThread(Thread *pthread) { Mutex::Locker lock(&ThreadMutex); ThreadSet.Add(pthread); } void removeThread(Thread *pthread) { Mutex::Locker lock(&ThreadMutex); ThreadSet.Remove(pthread); if (ThreadSet.GetSize() == 0) ThreadsEmpty.Notify(); } void finishAllThreads() { // Only original root thread can call this. OVR_ASSERT(GetCurrentThreadId() == RootThreadId); Mutex::Locker lock(&ThreadMutex); while (ThreadSet.GetSize() != 0) ThreadsEmpty.Wait(&ThreadMutex); } public: ThreadList() { RootThreadId = GetCurrentThreadId(); } ~ThreadList() { } static void AddRunningThread(Thread *pthread) { // Non-atomic creation ok since only the root thread if (!pRunningThreads) { pRunningThreads = new ThreadList; OVR_ASSERT(pRunningThreads); } pRunningThreads->addThread(pthread); } // NOTE: 'pthread' might be a dead pointer when this is // called so it should not be accessed; it is only used // for removal. static void RemoveRunningThread(Thread *pthread) { OVR_ASSERT(pRunningThreads); pRunningThreads->removeThread(pthread); } static void FinishAllThreads() { // This is ok because only root thread can wait for other thread finish. if (pRunningThreads) { pRunningThreads->finishAllThreads(); delete pRunningThreads; pRunningThreads = 0; } } }; // By default, we have no thread list. ThreadList* volatile ThreadList::pRunningThreads = 0; // FinishAllThreads - exposed publicly in Thread. void Thread::FinishAllThreads() { ThreadList::FinishAllThreads(); } // *** Run override int Thread::PRun() { // Suspend us on start, if requested if (ThreadFlags & OVR_THREAD_START_SUSPENDED) { Suspend(); ThreadFlags &= (UInt32)~OVR_THREAD_START_SUSPENDED; } // Call the virtual run function ExitCode = Run(); return ExitCode; } /* MA: Don't use TLS for now. // Static function to return a pointer to the current thread void Thread::InitCurrentThread(Thread *pthread) { pCurrentThread = pthread; } // Static function to return a pointer to the current thread Thread* Thread::GetThread() { return pCurrentThread; } */ // *** User overridables bool Thread::GetExitFlag() const { return (ThreadFlags & OVR_THREAD_EXIT) != 0; } void Thread::SetExitFlag(bool exitFlag) { // The below is atomic since ThreadFlags is AtomicInt. if (exitFlag) ThreadFlags |= OVR_THREAD_EXIT; else ThreadFlags &= (UInt32) ~OVR_THREAD_EXIT; } // Determines whether the thread was running and is now finished bool Thread::IsFinished() const { return (ThreadFlags & OVR_THREAD_FINISHED) != 0; } // Determines whether the thread is suspended bool Thread::IsSuspended() const { return SuspendCount > 0; } // Returns current thread state Thread::ThreadState Thread::GetThreadState() const { if (IsSuspended()) return Suspended; if (ThreadFlags & OVR_THREAD_STARTED) return Running; return NotRunning; } // ***** Thread management /* static */ int Thread::GetOSPriority(ThreadPriority p) { switch(p) { case Thread::CriticalPriority: return THREAD_PRIORITY_TIME_CRITICAL; case Thread::HighestPriority: return THREAD_PRIORITY_HIGHEST; case Thread::AboveNormalPriority: return THREAD_PRIORITY_ABOVE_NORMAL; case Thread::NormalPriority: return THREAD_PRIORITY_NORMAL; case Thread::BelowNormalPriority: return THREAD_PRIORITY_BELOW_NORMAL; case Thread::LowestPriority: return THREAD_PRIORITY_LOWEST; case Thread::IdlePriority: return THREAD_PRIORITY_IDLE; } return THREAD_PRIORITY_NORMAL; } // The actual first function called on thread start unsigned WINAPI Thread_Win32StartFn(void * phandle) { Thread * pthread = (Thread*)phandle; if (pthread->Processor != -1) { DWORD_PTR ret = SetThreadAffinityMask(GetCurrentThread(), (DWORD)pthread->Processor); if (ret == 0) OVR_DEBUG_LOG(("Could not set hardware processor for the thread")); } BOOL ret = ::SetThreadPriority(GetCurrentThread(), Thread::GetOSPriority(pthread->Priority)); if (ret == 0) OVR_DEBUG_LOG(("Could not set thread priority")); OVR_UNUSED(ret); // Ensure that ThreadId is assigned once thread is running, in case // beginthread hasn't filled it in yet. pthread->IdValue = (ThreadId)::GetCurrentThreadId(); DWORD result = pthread->PRun(); // Signal the thread as done and release it atomically. pthread->FinishAndRelease(); // At this point Thread object might be dead; however we can still pass // it to RemoveRunningThread since it is only used as a key there. ThreadList::RemoveRunningThread(pthread); return (unsigned) result; } bool Thread::Start(ThreadState initialState) { if (initialState == NotRunning) return 0; if (GetThreadState() != NotRunning) { OVR_DEBUG_LOG(("Thread::Start failed - thread %p already running", this)); return 0; } // Free old thread handle before creating the new one CleanupSystemThread(); // AddRef to us until the thread is finished. AddRef(); ThreadList::AddRunningThread(this); ExitCode = 0; SuspendCount = 0; ThreadFlags = (initialState == Running) ? 0 : OVR_THREAD_START_SUSPENDED; ThreadHandle = (HANDLE) _beginthreadex(0, (unsigned)StackSize, Thread_Win32StartFn, this, 0, (unsigned*)&IdValue); // Failed? Fail the function if (ThreadHandle == 0) { ThreadFlags = 0; Release(); ThreadList::RemoveRunningThread(this); return 0; } return 1; } // Suspend the thread until resumed bool Thread::Suspend() { // Can't suspend a thread that wasn't started if (!(ThreadFlags & OVR_THREAD_STARTED)) return 0; if (::SuspendThread(ThreadHandle) != 0xFFFFFFFF) { SuspendCount++; return 1; } return 0; } // Resumes currently suspended thread bool Thread::Resume() { // Can't suspend a thread that wasn't started if (!(ThreadFlags & OVR_THREAD_STARTED)) return 0; // Decrement count, and resume thread if it is 0 SInt32 oldCount = SuspendCount.ExchangeAdd_Acquire(-1); if (oldCount >= 1) { if (oldCount == 1) { if (::ResumeThread(ThreadHandle) != 0xFFFFFFFF) return 1; } else { return 1; } } return 0; } // Quits with an exit code void Thread::Exit(int exitCode) { // Can only exist the current thread. // MA: Don't use TLS for now. //if (GetThread() != this) // return; // Call the virtual OnExit function. OnExit(); // Signal this thread object as done and release it's references. FinishAndRelease(); ThreadList::RemoveRunningThread(this); // Call the exit function. _endthreadex((unsigned)exitCode); } void Thread::CleanupSystemThread() { if (ThreadHandle != 0) { ::CloseHandle(ThreadHandle); ThreadHandle = 0; } } // *** Sleep functions // static bool Thread::Sleep(unsigned secs) { ::Sleep(secs*1000); return 1; } // static bool Thread::MSleep(unsigned msecs) { ::Sleep(msecs); return 1; } void Thread::SetThreadName( const char* name ) { #if !defined(OVR_BUILD_SHIPPING) || defined(OVR_BUILD_PROFILING) // Looks ugly, but it is the recommended way to name a thread. typedef struct tagTHREADNAME_INFO { DWORD dwType; // Must be 0x1000 LPCSTR szName; // Pointer to name (in user address space) DWORD dwThreadID; // Thread ID (-1 for caller thread) DWORD dwFlags; // Reserved for future use; must be zero } THREADNAME_INFO; THREADNAME_INFO info; info.dwType = 0x1000; info.szName = name; info.dwThreadID = reinterpret_cast(GetThreadId()); info.dwFlags = 0; __try { #ifdef _WIN64 RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(DWORD), (const ULONG_PTR *)&info ); #else RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(DWORD), (DWORD *)&info ); #endif } __except( GetExceptionCode()==0x406D1388 ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_EXECUTE_HANDLER ) { } #endif // OVR_BUILD_SHIPPING } // static int Thread::GetCPUCount() { SYSTEM_INFO sysInfo; GetSystemInfo(&sysInfo); return (int) sysInfo.dwNumberOfProcessors; } // Returns the unique Id of a thread it is called on, intended for // comparison purposes. ThreadId GetCurrentThreadId() { return (ThreadId)::GetCurrentThreadId(); } } // OVR #endif \ No newline at end of file +/************************************************************************************ + +Filename : OVR_ThreadsWinAPI.cpp +Platform : WinAPI +Content : Windows specific thread-related (safe) functionality +Created : September 19, 2012 +Notes : + +Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. + +Use of this software is subject to the terms of the Oculus license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#include "OVR_Threads.h" +#include "OVR_Hash.h" +#include "OVR_Log.h" + +#ifdef OVR_ENABLE_THREADS + +// For _beginthreadex / _endtheadex +#include + +namespace OVR { + + +//----------------------------------------------------------------------------------- +// *** Internal Mutex implementation class + +class MutexImpl : public NewOverrideBase +{ + // System mutex or semaphore + HANDLE hMutexOrSemaphore; + bool Recursive; + volatile unsigned LockCount; + + friend class WaitConditionImpl; + +public: + // Constructor/destructor + MutexImpl(bool recursive = 1); + ~MutexImpl(); + + // Locking functions + void DoLock(); + bool TryLock(); + void Unlock(Mutex* pmutex); + // Returns 1 if the mutes is currently locked + bool IsLockedByAnotherThread(Mutex* pmutex); +}; + +// *** Constructor/destructor +MutexImpl::MutexImpl(bool recursive) +{ + Recursive = recursive; + LockCount = 0; + hMutexOrSemaphore = Recursive ? CreateMutex(NULL, 0, NULL) : CreateSemaphore(NULL, 1, 1, NULL); +} +MutexImpl::~MutexImpl() +{ + CloseHandle(hMutexOrSemaphore); +} + + +// Lock and try lock +void MutexImpl::DoLock() +{ + if (::WaitForSingleObject(hMutexOrSemaphore, INFINITE) != WAIT_OBJECT_0) + return; + LockCount++; +} + +bool MutexImpl::TryLock() +{ + DWORD ret; + if ((ret=::WaitForSingleObject(hMutexOrSemaphore, 0)) != WAIT_OBJECT_0) + return 0; + LockCount++; + return 1; +} + +void MutexImpl::Unlock(Mutex* pmutex) +{ + OVR_UNUSED(pmutex); + + unsigned lockCount; + LockCount--; + lockCount = LockCount; + + // Release mutex + if ((Recursive ? ReleaseMutex(hMutexOrSemaphore) : + ReleaseSemaphore(hMutexOrSemaphore, 1, NULL)) != 0) + { + // This used to call Wait handlers if lockCount == 0. + } +} + +bool MutexImpl::IsLockedByAnotherThread(Mutex* pmutex) +{ + // There could be multiple interpretations of IsLocked with respect to current thread + if (LockCount == 0) + return 0; + if (!TryLock()) + return 1; + Unlock(pmutex); + return 0; +} + +/* +bool MutexImpl::IsSignaled() const +{ + // An mutex is signaled if it is not locked ANYWHERE + // Note that this is different from IsLockedByAnotherThread function, + // that takes current thread into account + return LockCount == 0; +} +*/ + + +// *** Actual Mutex class implementation + +Mutex::Mutex(bool recursive) +{ + pImpl = new MutexImpl(recursive); +} +Mutex::~Mutex() +{ + delete pImpl; +} + +// Lock and try lock +void Mutex::DoLock() +{ + pImpl->DoLock(); +} +bool Mutex::TryLock() +{ + return pImpl->TryLock(); +} +void Mutex::Unlock() +{ + pImpl->Unlock(this); +} +bool Mutex::IsLockedByAnotherThread() +{ + return pImpl->IsLockedByAnotherThread(this); +} + +//----------------------------------------------------------------------------------- +// ***** Event + +bool Event::Wait(unsigned delay) +{ + Mutex::Locker lock(&StateMutex); + + // Do the correct amount of waiting + if (delay == OVR_WAIT_INFINITE) + { + while(!State) + StateWaitCondition.Wait(&StateMutex); + } + else if (delay) + { + if (!State) + StateWaitCondition.Wait(&StateMutex, delay); + } + + bool state = State; + // Take care of temporary 'pulsing' of a state + if (Temporary) + { + Temporary = false; + State = false; + } + return state; +} + +void Event::updateState(bool newState, bool newTemp, bool mustNotify) +{ + Mutex::Locker lock(&StateMutex); + State = newState; + Temporary = newTemp; + if (mustNotify) + StateWaitCondition.NotifyAll(); +} + + +//----------------------------------------------------------------------------------- +// ***** Win32 Wait Condition Implementation + +// Internal implementation class +class WaitConditionImpl : public NewOverrideBase +{ + // Event pool entries for extra events + struct EventPoolEntry : public NewOverrideBase + { + HANDLE hEvent; + EventPoolEntry *pNext; + EventPoolEntry *pPrev; + }; + + Lock WaitQueueLoc; + // Stores free events that can be used later + EventPoolEntry * pFreeEventList; + + // A queue of waiting objects to be signaled + EventPoolEntry* pQueueHead; + EventPoolEntry* pQueueTail; + + // Allocation functions for free events + EventPoolEntry* GetNewEvent(); + void ReleaseEvent(EventPoolEntry* pevent); + + // Queue operations + void QueuePush(EventPoolEntry* pentry); + EventPoolEntry* QueuePop(); + void QueueFindAndRemove(EventPoolEntry* pentry); + +public: + + // Constructor/destructor + WaitConditionImpl(); + ~WaitConditionImpl(); + + // Release mutex and wait for condition. The mutex is re-acqured after the wait. + bool Wait(Mutex *pmutex, unsigned delay = OVR_WAIT_INFINITE); + + // Notify a condition, releasing at one object waiting + void Notify(); + // Notify a condition, releasing all objects waiting + void NotifyAll(); +}; + + + +WaitConditionImpl::WaitConditionImpl() +{ + pFreeEventList = 0; + pQueueHead = + pQueueTail = 0; +} + +WaitConditionImpl::~WaitConditionImpl() +{ + // Free all the resources + EventPoolEntry* p = pFreeEventList; + EventPoolEntry* pentry; + + while(p) + { + // Move to next + pentry = p; + p = p->pNext; + // Delete old + ::CloseHandle(pentry->hEvent); + delete pentry; + } + // Shouldn't we also consider the queue? + + // To be safe + pFreeEventList = 0; + pQueueHead = + pQueueTail = 0; +} + + +// Allocation functions for free events +WaitConditionImpl::EventPoolEntry* WaitConditionImpl::GetNewEvent() +{ + EventPoolEntry* pentry; + + // If there are any free nodes, use them + if (pFreeEventList) + { + pentry = pFreeEventList; + pFreeEventList = pFreeEventList->pNext; + } + else + { + // Allocate a new node + pentry = new EventPoolEntry; + pentry->pNext = 0; + pentry->pPrev = 0; + // Non-signaled manual event + pentry->hEvent = ::CreateEvent(NULL, TRUE, 0, NULL); + } + + return pentry; +} + +void WaitConditionImpl::ReleaseEvent(EventPoolEntry* pevent) +{ + // Mark event as non-signaled + ::ResetEvent(pevent->hEvent); + // And add it to free pool + pevent->pNext = pFreeEventList; + pevent->pPrev = 0; + pFreeEventList = pevent; +} + +// Queue operations +void WaitConditionImpl::QueuePush(EventPoolEntry* pentry) +{ + // Items already exist? Just add to tail + if (pQueueTail) + { + pentry->pPrev = pQueueTail; + pQueueTail->pNext = pentry; + pentry->pNext = 0; + pQueueTail = pentry; + } + else + { + // No items in queue + pentry->pNext = + pentry->pPrev = 0; + pQueueHead = + pQueueTail = pentry; + } +} + +WaitConditionImpl::EventPoolEntry* WaitConditionImpl::QueuePop() +{ + EventPoolEntry* pentry = pQueueHead; + + // No items, null pointer + if (pentry) + { + // More items after this one? just grab the first item + if (pQueueHead->pNext) + { + pQueueHead = pentry->pNext; + pQueueHead->pPrev = 0; + } + else + { + // Last item left + pQueueTail = + pQueueHead = 0; + } + } + return pentry; +} + +void WaitConditionImpl::QueueFindAndRemove(EventPoolEntry* pentry) +{ + // Do an exhaustive search looking for an entry + EventPoolEntry* p = pQueueHead; + + while(p) + { + // Entry found? Remove. + if (p == pentry) + { + + // Remove the node form the list + // Prev link + if (pentry->pPrev) + pentry->pPrev->pNext = pentry->pNext; + else + pQueueHead = pentry->pNext; + // Next link + if (pentry->pNext) + pentry->pNext->pPrev = pentry->pPrev; + else + pQueueTail = pentry->pPrev; + // Done + return; + } + + // Move to next item + p = p->pNext; + } +} + + +bool WaitConditionImpl::Wait(Mutex *pmutex, unsigned delay) +{ + bool result = 0; + unsigned i; + unsigned lockCount = pmutex->pImpl->LockCount; + EventPoolEntry* pentry; + + // Mutex must have been locked + if (lockCount == 0) + return 0; + + // Add an object to the wait queue + WaitQueueLoc.DoLock(); + QueuePush(pentry = GetNewEvent()); + WaitQueueLoc.Unlock(); + + // Finally, release a mutex or semaphore + if (pmutex->pImpl->Recursive) + { + // Release the recursive mutex N times + pmutex->pImpl->LockCount = 0; + for(i=0; ipImpl->hMutexOrSemaphore); + } + else + { + pmutex->pImpl->LockCount = 0; + ::ReleaseSemaphore(pmutex->pImpl->hMutexOrSemaphore, 1, NULL); + } + + // Note that there is a gap here between mutex.Unlock() and Wait(). However, + // if notify() comes in at this point in the other thread it will set our + // corresponding event so wait will just fall through, as expected. + + // Block and wait on the event + DWORD waitResult = ::WaitForSingleObject(pentry->hEvent, + (delay == OVR_WAIT_INFINITE) ? INFINITE : delay); + /* +repeat_wait: + DWORD waitResult = + + ::MsgWaitForMultipleObjects(1, &pentry->hEvent, FALSE, + (delay == OVR_WAIT_INFINITE) ? INFINITE : delay, + QS_ALLINPUT); + */ + + WaitQueueLoc.DoLock(); + switch(waitResult) + { + case WAIT_ABANDONED: + case WAIT_OBJECT_0: + result = 1; + // Wait was successful, therefore the event entry should already be removed + // So just add entry back to a free list + ReleaseEvent(pentry); + break; + /* + case WAIT_OBJECT_0 + 1: + // Messages in WINDOWS queue + { + MSG msg; + PeekMessage(&msg, NULL, 0U, 0U, PM_NOREMOVE); + WaitQueueLoc.Unlock(); + goto repeat_wait; + } + break; */ + default: + // Timeout, our entry should still be in a queue + QueueFindAndRemove(pentry); + ReleaseEvent(pentry); + } + WaitQueueLoc.Unlock(); + + // Re-aquire the mutex + for(i=0; iDoLock(); + + // Return the result + return result; +} + +// Notify a condition, releasing the least object in a queue +void WaitConditionImpl::Notify() +{ + Lock::Locker lock(&WaitQueueLoc); + + // Pop last entry & signal it + EventPoolEntry* pentry = QueuePop(); + if (pentry) + ::SetEvent(pentry->hEvent); +} + +// Notify a condition, releasing all objects waiting +void WaitConditionImpl::NotifyAll() +{ + Lock::Locker lock(&WaitQueueLoc); + + // Pop and signal all events + // NOTE : There is no need to release the events, it's the waiters job to do so + EventPoolEntry* pentry = QueuePop(); + while (pentry) + { + ::SetEvent(pentry->hEvent); + pentry = QueuePop(); + } +} + + + +// *** Actual implementation of WaitCondition + +WaitCondition::WaitCondition() +{ + pImpl = new WaitConditionImpl; +} +WaitCondition::~WaitCondition() +{ + delete pImpl; +} + +// Wait without a mutex +bool WaitCondition::Wait(Mutex *pmutex, unsigned delay) +{ + return pImpl->Wait(pmutex, delay); +} +// Notification +void WaitCondition::Notify() +{ + pImpl->Notify(); +} +void WaitCondition::NotifyAll() +{ + pImpl->NotifyAll(); +} + + + +//----------------------------------------------------------------------------------- +// ***** Thread Class + +// Per-thread variable +// MA: Don't use TLS for now - portability issues with DLLs, etc. +/* +#if !defined(OVR_CC_MSVC) || (OVR_CC_MSVC < 1300) +__declspec(thread) Thread* pCurrentThread = 0; +#else +#pragma data_seg(".tls$") +__declspec(thread) Thread* pCurrentThread = 0; +#pragma data_seg(".rwdata") +#endif +*/ + +// *** Thread constructors. + +Thread::Thread(UPInt stackSize, int processor) +{ + CreateParams params; + params.stackSize = stackSize; + params.processor = processor; + Init(params); +} + +Thread::Thread(Thread::ThreadFn threadFunction, void* userHandle, UPInt stackSize, + int processor, Thread::ThreadState initialState) +{ + CreateParams params(threadFunction, userHandle, stackSize, processor, initialState); + Init(params); +} + +Thread::Thread(const CreateParams& params) +{ + Init(params); +} +void Thread::Init(const CreateParams& params) +{ + // Clear the variables + ThreadFlags = 0; + ThreadHandle = 0; + IdValue = 0; + ExitCode = 0; + SuspendCount = 0; + StackSize = params.stackSize; + Processor = params.processor; + Priority = params.priority; + + // Clear Function pointers + ThreadFunction = params.threadFunction; + UserHandle = params.userHandle; + if (params.initialState != NotRunning) + Start(params.initialState); + +} + +Thread::~Thread() +{ + // Thread should not running while object is being destroyed, + // this would indicate ref-counting issue. + //OVR_ASSERT(IsRunning() == 0); + + // Clean up thread. + CleanupSystemThread(); + ThreadHandle = 0; +} + + +// *** Overridable User functions. + +// Default Run implementation +int Thread::Run() +{ + // Call pointer to function, if available. + return (ThreadFunction) ? ThreadFunction(this, UserHandle) : 0; +} +void Thread::OnExit() +{ +} + +// Finishes the thread and releases internal reference to it. +void Thread::FinishAndRelease() +{ + // Note: thread must be US. + ThreadFlags &= (UInt32)~(OVR_THREAD_STARTED); + ThreadFlags |= OVR_THREAD_FINISHED; + + // Release our reference; this is equivalent to 'delete this' + // from the point of view of our thread. + Release(); +} + + +// *** ThreadList - used to tack all created threads + +class ThreadList : public NewOverrideBase +{ + //------------------------------------------------------------------------ + struct ThreadHashOp + { + UPInt operator()(const Thread* ptr) + { + return (((UPInt)ptr) >> 6) ^ (UPInt)ptr; + } + }; + + HashSet ThreadSet; + Mutex ThreadMutex; + WaitCondition ThreadsEmpty; + // Track the root thread that created us. + ThreadId RootThreadId; + + static ThreadList* volatile pRunningThreads; + + void addThread(Thread *pthread) + { + Mutex::Locker lock(&ThreadMutex); + ThreadSet.Add(pthread); + } + + void removeThread(Thread *pthread) + { + Mutex::Locker lock(&ThreadMutex); + ThreadSet.Remove(pthread); + if (ThreadSet.GetSize() == 0) + ThreadsEmpty.Notify(); + } + + void finishAllThreads() + { + // Only original root thread can call this. + OVR_ASSERT(GetCurrentThreadId() == RootThreadId); + + Mutex::Locker lock(&ThreadMutex); + while (ThreadSet.GetSize() != 0) + ThreadsEmpty.Wait(&ThreadMutex); + } + +public: + + ThreadList() + { + RootThreadId = GetCurrentThreadId(); + } + ~ThreadList() { } + + + static void AddRunningThread(Thread *pthread) + { + // Non-atomic creation ok since only the root thread + if (!pRunningThreads) + { + pRunningThreads = new ThreadList; + OVR_ASSERT(pRunningThreads); + } + pRunningThreads->addThread(pthread); + } + + // NOTE: 'pthread' might be a dead pointer when this is + // called so it should not be accessed; it is only used + // for removal. + static void RemoveRunningThread(Thread *pthread) + { + OVR_ASSERT(pRunningThreads); + pRunningThreads->removeThread(pthread); + } + + static void FinishAllThreads() + { + // This is ok because only root thread can wait for other thread finish. + if (pRunningThreads) + { + pRunningThreads->finishAllThreads(); + delete pRunningThreads; + pRunningThreads = 0; + } + } +}; + +// By default, we have no thread list. +ThreadList* volatile ThreadList::pRunningThreads = 0; + + +// FinishAllThreads - exposed publicly in Thread. +void Thread::FinishAllThreads() +{ + ThreadList::FinishAllThreads(); +} + + +// *** Run override + +int Thread::PRun() +{ + // Suspend us on start, if requested + if (ThreadFlags & OVR_THREAD_START_SUSPENDED) + { + Suspend(); + ThreadFlags &= (UInt32)~OVR_THREAD_START_SUSPENDED; + } + + // Call the virtual run function + ExitCode = Run(); + return ExitCode; +} + + + +/* MA: Don't use TLS for now. + +// Static function to return a pointer to the current thread +void Thread::InitCurrentThread(Thread *pthread) +{ + pCurrentThread = pthread; +} + +// Static function to return a pointer to the current thread +Thread* Thread::GetThread() +{ + return pCurrentThread; +} +*/ + + +// *** User overridables + +bool Thread::GetExitFlag() const +{ + return (ThreadFlags & OVR_THREAD_EXIT) != 0; +} + +void Thread::SetExitFlag(bool exitFlag) +{ + // The below is atomic since ThreadFlags is AtomicInt. + if (exitFlag) + ThreadFlags |= OVR_THREAD_EXIT; + else + ThreadFlags &= (UInt32) ~OVR_THREAD_EXIT; +} + + +// Determines whether the thread was running and is now finished +bool Thread::IsFinished() const +{ + return (ThreadFlags & OVR_THREAD_FINISHED) != 0; +} +// Determines whether the thread is suspended +bool Thread::IsSuspended() const +{ + return SuspendCount > 0; +} +// Returns current thread state +Thread::ThreadState Thread::GetThreadState() const +{ + if (IsSuspended()) + return Suspended; + if (ThreadFlags & OVR_THREAD_STARTED) + return Running; + return NotRunning; +} + + + +// ***** Thread management +/* static */ +int Thread::GetOSPriority(ThreadPriority p) +{ + switch(p) + { + case Thread::CriticalPriority: return THREAD_PRIORITY_TIME_CRITICAL; + case Thread::HighestPriority: return THREAD_PRIORITY_HIGHEST; + case Thread::AboveNormalPriority: return THREAD_PRIORITY_ABOVE_NORMAL; + case Thread::NormalPriority: return THREAD_PRIORITY_NORMAL; + case Thread::BelowNormalPriority: return THREAD_PRIORITY_BELOW_NORMAL; + case Thread::LowestPriority: return THREAD_PRIORITY_LOWEST; + case Thread::IdlePriority: return THREAD_PRIORITY_IDLE; + } + return THREAD_PRIORITY_NORMAL; +} + +// The actual first function called on thread start +unsigned WINAPI Thread_Win32StartFn(void * phandle) +{ + Thread * pthread = (Thread*)phandle; + if (pthread->Processor != -1) + { + DWORD_PTR ret = SetThreadAffinityMask(GetCurrentThread(), (DWORD)pthread->Processor); + if (ret == 0) + OVR_DEBUG_LOG(("Could not set hardware processor for the thread")); + } + BOOL ret = ::SetThreadPriority(GetCurrentThread(), Thread::GetOSPriority(pthread->Priority)); + if (ret == 0) + OVR_DEBUG_LOG(("Could not set thread priority")); + OVR_UNUSED(ret); + + // Ensure that ThreadId is assigned once thread is running, in case + // beginthread hasn't filled it in yet. + pthread->IdValue = (ThreadId)::GetCurrentThreadId(); + + DWORD result = pthread->PRun(); + // Signal the thread as done and release it atomically. + pthread->FinishAndRelease(); + // At this point Thread object might be dead; however we can still pass + // it to RemoveRunningThread since it is only used as a key there. + ThreadList::RemoveRunningThread(pthread); + return (unsigned) result; +} + +bool Thread::Start(ThreadState initialState) +{ + if (initialState == NotRunning) + return 0; + if (GetThreadState() != NotRunning) + { + OVR_DEBUG_LOG(("Thread::Start failed - thread %p already running", this)); + return 0; + } + + // Free old thread handle before creating the new one + CleanupSystemThread(); + + // AddRef to us until the thread is finished. + AddRef(); + ThreadList::AddRunningThread(this); + + ExitCode = 0; + SuspendCount = 0; + ThreadFlags = (initialState == Running) ? 0 : OVR_THREAD_START_SUSPENDED; + ThreadHandle = (HANDLE) _beginthreadex(0, (unsigned)StackSize, + Thread_Win32StartFn, this, 0, (unsigned*)&IdValue); + + // Failed? Fail the function + if (ThreadHandle == 0) + { + ThreadFlags = 0; + Release(); + ThreadList::RemoveRunningThread(this); + return 0; + } + return 1; +} + + +// Suspend the thread until resumed +bool Thread::Suspend() +{ + // Can't suspend a thread that wasn't started + if (!(ThreadFlags & OVR_THREAD_STARTED)) + return 0; + + if (::SuspendThread(ThreadHandle) != 0xFFFFFFFF) + { + SuspendCount++; + return 1; + } + return 0; +} + +// Resumes currently suspended thread +bool Thread::Resume() +{ + // Can't suspend a thread that wasn't started + if (!(ThreadFlags & OVR_THREAD_STARTED)) + return 0; + + // Decrement count, and resume thread if it is 0 + SInt32 oldCount = SuspendCount.ExchangeAdd_Acquire(-1); + if (oldCount >= 1) + { + if (oldCount == 1) + { + if (::ResumeThread(ThreadHandle) != 0xFFFFFFFF) + return 1; + } + else + { + return 1; + } + } + return 0; +} + + +// Quits with an exit code +void Thread::Exit(int exitCode) +{ + // Can only exist the current thread. + // MA: Don't use TLS for now. + //if (GetThread() != this) + // return; + + // Call the virtual OnExit function. + OnExit(); + + // Signal this thread object as done and release it's references. + FinishAndRelease(); + ThreadList::RemoveRunningThread(this); + + // Call the exit function. + _endthreadex((unsigned)exitCode); +} + + +void Thread::CleanupSystemThread() +{ + if (ThreadHandle != 0) + { + ::CloseHandle(ThreadHandle); + ThreadHandle = 0; + } +} + +// *** Sleep functions +// static +bool Thread::Sleep(unsigned secs) +{ + ::Sleep(secs*1000); + return 1; +} + +// static +bool Thread::MSleep(unsigned msecs) +{ + ::Sleep(msecs); + return 1; +} + +void Thread::SetThreadName( const char* name ) +{ +#if !defined(OVR_BUILD_SHIPPING) || defined(OVR_BUILD_PROFILING) + // Looks ugly, but it is the recommended way to name a thread. + typedef struct tagTHREADNAME_INFO { + DWORD dwType; // Must be 0x1000 + LPCSTR szName; // Pointer to name (in user address space) + DWORD dwThreadID; // Thread ID (-1 for caller thread) + DWORD dwFlags; // Reserved for future use; must be zero + } THREADNAME_INFO; + + THREADNAME_INFO info; + + info.dwType = 0x1000; + info.szName = name; + info.dwThreadID = reinterpret_cast(GetThreadId()); + info.dwFlags = 0; + + __try + { +#ifdef _WIN64 + RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(DWORD), (const ULONG_PTR *)&info ); +#else + RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(DWORD), (DWORD *)&info ); +#endif + } + __except( GetExceptionCode()==0x406D1388 ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_EXECUTE_HANDLER ) + { + } +#endif // OVR_BUILD_SHIPPING +} + +// static +int Thread::GetCPUCount() +{ + SYSTEM_INFO sysInfo; + GetSystemInfo(&sysInfo); + return (int) sysInfo.dwNumberOfProcessors; +} + +// Returns the unique Id of a thread it is called on, intended for +// comparison purposes. +ThreadId GetCurrentThreadId() +{ + return (ThreadId)::GetCurrentThreadId(); +} + +} // OVR + +#endif + + diff -r 59fc487ba58e -r b069a5c27388 src/main.cc --- a/src/main.cc Sat Sep 14 17:51:03 2013 +0300 +++ b/src/main.cc Sun Sep 15 04:10:05 2013 +0300 @@ -26,13 +26,13 @@ int main(int argc, char **argv) { + glutInitWindowSize(1280, 800); glutInit(&argc, argv); if(!parse_args(argc, argv)) { return 1; } - glutInitWindowSize(1280, 800); glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE); glutCreateWindow("oculus test 01"); @@ -63,7 +63,7 @@ glEnable(GL_LIGHTING); if(vr_init(VR_INIT_OCULUS) == -1) { - return false; + //return false; } return true; } @@ -75,6 +75,15 @@ static void disp() { + // test rift sensor + float quat[4], euler[3]; + + vr_get_rotation(quat); + vr_get_rotation_euler(euler); + + printf("q(%.3f + %.3fi + %.3fj %.3fk)", quat[3], quat[0], quat[1], quat[2]); + printf(" - euler(%.3f %.3f %.3f)\n", euler[0], euler[1], euler[2]); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_PROJECTION); diff -r 59fc487ba58e -r b069a5c27388 src/vr.cc --- a/src/vr.cc Sat Sep 14 17:51:03 2013 +0300 +++ b/src/vr.cc Sun Sep 15 04:10:05 2013 +0300 @@ -4,7 +4,7 @@ static bool init_ovr(); -VRData vr_data; +VRContext vr_ctx; extern "C" int vr_init(enum vr_init_mode mode) { @@ -16,28 +16,26 @@ extern "C" void vr_shutdown(void) { - System::Destroy(); + //System::Destroy(); } - - static bool init_ovr() { // initialize Oculus SDK System::Init(); - if(!(vr_data.ovr_devman = DeviceManager::Create())) { + if(!(vr_ctx.ovr_devman = DeviceManager::Create())) { fprintf(stderr, "failed to create OVR device manager\n"); return false; } // create the display device - if(!(vr_data.ovr_hmd_dev = vr_data.ovr_devman->EnumerateDevices().CreateDevice())) { + if(!(vr_ctx.ovr_hmd_dev = vr_ctx.ovr_devman->EnumerateDevices().CreateDevice())) { fprintf(stderr, "no oculus rift devices found\n"); return false; } HMDInfo info; - if(vr_data.ovr_hmd_dev->GetDeviceInfo(&info)) { + if(vr_ctx.ovr_hmd_dev->GetDeviceInfo(&info)) { printf("oculus device info:\n"); printf(" name: %s\n", info.DisplayDeviceName); printf(" ipd: %f\n", info.InterpupillaryDistance); @@ -46,16 +44,102 @@ } // get the sensor device - if(!(vr_data.ovr_sensor_dev = vr_data.ovr_hmd_dev->GetSensor())) { + if(!(vr_ctx.ovr_sensor_dev = vr_ctx.ovr_hmd_dev->GetSensor())) { fprintf(stderr, "failed to get oculus sensor device\n"); return false; } SensorInfo sinfo; - if(vr_data.ovr_sensor_dev->GetDeviceInfo(&sinfo)) { + if(vr_ctx.ovr_sensor_dev->GetDeviceInfo(&sinfo)) { printf("oculus sensor device info:\n"); printf(" name: %s\n", sinfo.ProductName); } + vr_ctx.ovr_sfusion.AttachToSensor(vr_ctx.ovr_sensor_dev); + + // calculate and store viewing parameters + float vhalfsz = info.VScreenSize * 0.5; + vr_ctx.info.fov = 2.0 * atan(vhalfsz / info.EyeToScreenDistance); + + vr_ctx.info.width = info.HResolution; + vr_ctx.info.height = info.VResolution; + vr_ctx.info.aspect = (float)vr_ctx.info.width / (float)vr_ctx.info.height; + + vr_ctx.info.ipd = info.InterpupillaryDistance; + for(int i=0; i<4; i++) { + vr_ctx.info.distort[i] = info.DistortionK[i]; + } + return true; } + +extern "C" int vr_get_width(void) +{ + return vr_ctx.info.width; +} + +extern "C" int vr_get_height(void) +{ + return vr_ctx.info.height; +} + +extern "C" float vr_get_fov(void) +{ + return vr_ctx.info.fov; +} + +extern "C" float vr_get_aspect(void) +{ + return vr_ctx.info.aspect; +} + +extern "C" void vr_set_eyedist(float ipd) +{ + vr_ctx.info.ipd = ipd; +} + +extern "C" float vr_get_eyedist(void) +{ + return vr_ctx.info.ipd; +} + +extern "C" void vr_set_distort(const float *coef) +{ + memcpy(vr_ctx.info.distort, coef, sizeof vr_ctx.info.distort); +} + +extern "C" void vr_get_distort(float *coef) +{ + memcpy(coef, vr_ctx.info.distort, sizeof vr_ctx.info.distort); +} + +extern "C" void vr_set_prediction_sec(float dt) +{ + vr_ctx.ovr_sfusion.SetPrediction(dt); +} + +extern "C" float vr_get_prediction_sec(void) +{ + return vr_ctx.ovr_sfusion.GetPredictionDelta(); +} + +extern "C" void vr_get_translation(float *offs) +{ + // current oculus devkit doesn't do translation + offs[0] = offs[1] = offs[2] = 0.0f; +} + +extern "C" void vr_get_rotation(float *quat) +{ + Quatf oq = vr_ctx.ovr_sfusion.GetPredictedOrientation(); + quat[0] = oq.x; + quat[1] = oq.y; + quat[2] = oq.z; + quat[3] = oq.w; +} + +extern "C" void vr_get_rotation_euler(float *euler) +{ + Quatf oq = vr_ctx.ovr_sfusion.GetPredictedOrientation(); + oq.GetEulerAngles(euler + 1, euler, euler + 2); +} diff -r 59fc487ba58e -r b069a5c27388 src/vr.h --- a/src/vr.h Sat Sep 14 17:51:03 2013 +0300 +++ b/src/vr.h Sun Sep 15 04:10:05 2013 +0300 @@ -22,10 +22,7 @@ int vr_init(enum vr_init_mode mode); void vr_shutdown(void); -void vr_set_fov(float fov); float vr_get_fov(void); - -void vr_set_aspect(float aspect); float vr_get_aspect(void); void vr_set_eyedist(float ipd); @@ -37,6 +34,9 @@ void vr_set_distort(const float *coef); void vr_get_distort(float *coef); +void vr_set_prediction_sec(float dt); +float vr_get_prediction_sec(void); + void vr_get_view_matrix(float *res, int eye); void vr_get_proj_matrix(float *res, int eye); diff -r 59fc487ba58e -r b069a5c27388 src/vr_impl.h --- a/src/vr_impl.h Sat Sep 14 17:51:03 2013 +0300 +++ b/src/vr_impl.h Sun Sep 15 04:10:05 2013 +0300 @@ -5,17 +5,23 @@ using namespace OVR; -struct VRData { +struct VRContext { DeviceManager *ovr_devman; HMDDevice *ovr_hmd_dev; SensorDevice *ovr_sensor_dev; + SensorFusion ovr_sfusion; - float fov; - float aspect; - float ipd; - float distort[4]; + struct { + // the full width and height of the display (both eyes) + int width, height; + float fov; + // the full aspect ratio of the display (both eyes) + float aspect; + float ipd; + float distort[4]; + } info; }; -extern VRData vr_data; +extern VRContext vr_ctx; #endif // VR_IMPL_H_