diff options
Diffstat (limited to 'thirdparty/thekla_atlas/nvcore')
28 files changed, 7965 insertions, 0 deletions
diff --git a/thirdparty/thekla_atlas/nvcore/Array.h b/thirdparty/thekla_atlas/nvcore/Array.h new file mode 100644 index 0000000000..b295cb2b0c --- /dev/null +++ b/thirdparty/thekla_atlas/nvcore/Array.h @@ -0,0 +1,182 @@ +// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> + +#pragma once +#ifndef NV_CORE_ARRAY_H +#define NV_CORE_ARRAY_H + +/* +This array class requires the elements to be relocable; it uses memmove and realloc. Ideally I should be +using swap, but I honestly don't care. The only thing that you should be aware of is that internal pointers +are not supported. + +Note also that push_back and resize does not support inserting arguments elements that are in the same +container. This is forbidden to prevent an extra copy. +*/ + + +#include "Memory.h" +#include "Debug.h" +#include "ForEach.h" // PseudoIndex + + +namespace nv +{ + class Stream; + + /** + * Replacement for std::vector that is easier to debug and provides + * some nice foreach enumerators. + */ + template<typename T> + class NVCORE_CLASS Array { + public: + typedef uint size_type; + + // Default constructor. + NV_FORCEINLINE Array() : m_buffer(NULL), m_capacity(0), m_size(0) {} + + // Copy constructor. + NV_FORCEINLINE Array(const Array & a) : m_buffer(NULL), m_capacity(0), m_size(0) { + copy(a.m_buffer, a.m_size); + } + + // Constructor that initializes the vector with the given elements. + NV_FORCEINLINE Array(const T * ptr, uint num) : m_buffer(NULL), m_capacity(0), m_size(0) { + copy(ptr, num); + } + + // Allocate array. + NV_FORCEINLINE explicit Array(uint capacity) : m_buffer(NULL), m_capacity(0), m_size(0) { + setArrayCapacity(capacity); + } + + // Destructor. + NV_FORCEINLINE ~Array() { + clear(); + free<T>(m_buffer); + } + + + /// Const element access. + NV_FORCEINLINE const T & operator[]( uint index ) const + { + nvDebugCheck(index < m_size); + return m_buffer[index]; + } + NV_FORCEINLINE const T & at( uint index ) const + { + nvDebugCheck(index < m_size); + return m_buffer[index]; + } + + /// Element access. + NV_FORCEINLINE T & operator[] ( uint index ) + { + nvDebugCheck(index < m_size); + return m_buffer[index]; + } + NV_FORCEINLINE T & at( uint index ) + { + nvDebugCheck(index < m_size); + return m_buffer[index]; + } + + /// Get vector size. + NV_FORCEINLINE uint size() const { return m_size; } + + /// Get vector size. + NV_FORCEINLINE uint count() const { return m_size; } + + /// Get vector capacity. + NV_FORCEINLINE uint capacity() const { return m_capacity; } + + /// Get const vector pointer. + NV_FORCEINLINE const T * buffer() const { return m_buffer; } + + /// Get vector pointer. + NV_FORCEINLINE T * buffer() { return m_buffer; } + + /// Provide begin/end pointers for C++11 range-based for loops. + NV_FORCEINLINE T * begin() { return m_buffer; } + NV_FORCEINLINE T * end() { return m_buffer + m_size; } + NV_FORCEINLINE const T * begin() const { return m_buffer; } + NV_FORCEINLINE const T * end() const { return m_buffer + m_size; } + + /// Is vector empty. + NV_FORCEINLINE bool isEmpty() const { return m_size == 0; } + + /// Is a null vector. + NV_FORCEINLINE bool isNull() const { return m_buffer == NULL; } + + + T & append(); + void push_back( const T & val ); + void pushBack( const T & val ); + Array<T> & append( const T & val ); + Array<T> & operator<< ( T & t ); + void pop_back(); + void popBack(uint count = 1); + void popFront(uint count = 1); + const T & back() const; + T & back(); + const T & front() const; + T & front(); + bool contains(const T & e) const; + bool find(const T & element, uint * indexPtr) const; + bool find(const T & element, uint begin, uint end, uint * indexPtr) const; + void removeAt(uint index); + bool remove(const T & element); + void insertAt(uint index, const T & val = T()); + void append(const Array<T> & other); + void append(const T other[], uint count); + void replaceWithLast(uint index); + void resize(uint new_size); + void resize(uint new_size, const T & elem); + void fill(const T & elem); + void clear(); + void shrink(); + void reserve(uint desired_size); + void copy(const T * data, uint count); + Array<T> & operator=( const Array<T> & a ); + T * release(); + + + // Array enumerator. + typedef uint PseudoIndex; + + NV_FORCEINLINE PseudoIndex start() const { return 0; } + NV_FORCEINLINE bool isDone(const PseudoIndex & i) const { nvDebugCheck(i <= this->m_size); return i == this->m_size; } + NV_FORCEINLINE void advance(PseudoIndex & i) const { nvDebugCheck(i <= this->m_size); i++; } + +#if NV_NEED_PSEUDOINDEX_WRAPPER + NV_FORCEINLINE T & operator[]( const PseudoIndexWrapper & i ) { + return m_buffer[i(this)]; + } + NV_FORCEINLINE const T & operator[]( const PseudoIndexWrapper & i ) const { + return m_buffer[i(this)]; + } +#endif + + // Friends. + template <typename Typ> + friend Stream & operator<< ( Stream & s, Array<Typ> & p ); + + template <typename Typ> + friend void swap(Array<Typ> & a, Array<Typ> & b); + + + protected: + + void setArraySize(uint new_size); + void setArrayCapacity(uint new_capacity); + + T * m_buffer; + uint m_capacity; + uint m_size; + + }; + + +} // nv namespace + +#endif // NV_CORE_ARRAY_H diff --git a/thirdparty/thekla_atlas/nvcore/Array.inl b/thirdparty/thekla_atlas/nvcore/Array.inl new file mode 100644 index 0000000000..0b4de28ba9 --- /dev/null +++ b/thirdparty/thekla_atlas/nvcore/Array.inl @@ -0,0 +1,452 @@ +// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> + +#pragma once +#ifndef NV_CORE_ARRAY_INL +#define NV_CORE_ARRAY_INL + +#include "Array.h" + +#include "Stream.h" +#include "Utils.h" // swap + +#include <string.h> // memmove +#include <new> // for placement new + + + +namespace nv +{ + template <typename T> + NV_FORCEINLINE T & Array<T>::append() + { + uint old_size = m_size; + uint new_size = m_size + 1; + + setArraySize(new_size); + + construct_range(m_buffer, new_size, old_size); + + return m_buffer[old_size]; // Return reference to last element. + } + + // Push an element at the end of the vector. + template <typename T> + NV_FORCEINLINE void Array<T>::push_back( const T & val ) + { +#if 1 + nvDebugCheck(&val < m_buffer || &val >= m_buffer+m_size); + + uint old_size = m_size; + uint new_size = m_size + 1; + + setArraySize(new_size); + + construct_range(m_buffer, new_size, old_size, val); +#else + uint new_size = m_size + 1; + + if (new_size > m_capacity) + { + // @@ Is there any way to avoid this copy? + // @@ Can we create a copy without side effects? Ie. without calls to constructor/destructor. Use alloca + memcpy? + // @@ Assert instead of copy? + const T copy(val); // create a copy in case value is inside of this array. + + setArraySize(new_size); + + new (m_buffer+new_size-1) T(copy); + } + else + { + m_size = new_size; + new(m_buffer+new_size-1) T(val); + } +#endif // 0/1 + } + template <typename T> + NV_FORCEINLINE void Array<T>::pushBack( const T & val ) + { + push_back(val); + } + template <typename T> + NV_FORCEINLINE Array<T> & Array<T>::append( const T & val ) + { + push_back(val); + return *this; + } + + // Qt like push operator. + template <typename T> + NV_FORCEINLINE Array<T> & Array<T>::operator<< ( T & t ) + { + push_back(t); + return *this; + } + + // Pop the element at the end of the vector. + template <typename T> + NV_FORCEINLINE void Array<T>::pop_back() + { + nvDebugCheck( m_size > 0 ); + resize( m_size - 1 ); + } + template <typename T> + NV_FORCEINLINE void Array<T>::popBack(uint count) + { + nvDebugCheck(m_size >= count); + resize(m_size - count); + } + + template <typename T> + NV_FORCEINLINE void Array<T>::popFront(uint count) + { + nvDebugCheck(m_size >= count); + //resize(m_size - count); + + if (m_size == count) { + clear(); + } + else { + destroy_range(m_buffer, 0, count); + + memmove(m_buffer, m_buffer + count, sizeof(T) * (m_size - count)); + + m_size -= count; + } + + } + + + // Get back element. + template <typename T> + NV_FORCEINLINE const T & Array<T>::back() const + { + nvDebugCheck( m_size > 0 ); + return m_buffer[m_size-1]; + } + + // Get back element. + template <typename T> + NV_FORCEINLINE T & Array<T>::back() + { + nvDebugCheck( m_size > 0 ); + return m_buffer[m_size-1]; + } + + // Get front element. + template <typename T> + NV_FORCEINLINE const T & Array<T>::front() const + { + nvDebugCheck( m_size > 0 ); + return m_buffer[0]; + } + + // Get front element. + template <typename T> + NV_FORCEINLINE T & Array<T>::front() + { + nvDebugCheck( m_size > 0 ); + return m_buffer[0]; + } + + // Check if the given element is contained in the array. + template <typename T> + NV_FORCEINLINE bool Array<T>::contains(const T & e) const + { + return find(e, NULL); + } + + // Return true if element found. + template <typename T> + NV_FORCEINLINE bool Array<T>::find(const T & element, uint * indexPtr) const + { + return find(element, 0, m_size, indexPtr); + } + + // Return true if element found within the given range. + template <typename T> + NV_FORCEINLINE bool Array<T>::find(const T & element, uint begin, uint end, uint * indexPtr) const + { + return ::nv::find(element, m_buffer, begin, end, indexPtr); + } + + + // Remove the element at the given index. This is an expensive operation! + template <typename T> + void Array<T>::removeAt(uint index) + { + nvDebugCheck(index >= 0 && index < m_size); + + if (m_size == 1) { + clear(); + } + else { + m_buffer[index].~T(); + + memmove(m_buffer+index, m_buffer+index+1, sizeof(T) * (m_size - 1 - index)); + m_size--; + } + } + + // Remove the first instance of the given element. + template <typename T> + bool Array<T>::remove(const T & element) + { + uint index; + if (find(element, &index)) { + removeAt(index); + return true; + } + return false; + } + + // Insert the given element at the given index shifting all the elements up. + template <typename T> + void Array<T>::insertAt(uint index, const T & val/*=T()*/) + { + nvDebugCheck( index >= 0 && index <= m_size ); + + setArraySize(m_size + 1); + + if (index < m_size - 1) { + memmove(m_buffer+index+1, m_buffer+index, sizeof(T) * (m_size - 1 - index)); + } + + // Copy-construct into the newly opened slot. + new(m_buffer+index) T(val); + } + + // Append the given data to our vector. + template <typename T> + NV_FORCEINLINE void Array<T>::append(const Array<T> & other) + { + append(other.m_buffer, other.m_size); + } + + // Append the given data to our vector. + template <typename T> + void Array<T>::append(const T other[], uint count) + { + if (count > 0) { + const uint old_size = m_size; + + setArraySize(m_size + count); + + for (uint i = 0; i < count; i++ ) { + new(m_buffer + old_size + i) T(other[i]); + } + } + } + + + // Remove the given element by replacing it with the last one. + template <typename T> + void Array<T>::replaceWithLast(uint index) + { + nvDebugCheck( index < m_size ); + nv::swap(m_buffer[index], back()); // @@ Is this OK when index == size-1? + (m_buffer+m_size-1)->~T(); + m_size--; + } + + // Resize the vector preserving existing elements. + template <typename T> + void Array<T>::resize(uint new_size) + { + uint old_size = m_size; + + // Destruct old elements (if we're shrinking). + destroy_range(m_buffer, new_size, old_size); + + setArraySize(new_size); + + // Call default constructors + construct_range(m_buffer, new_size, old_size); + } + + + // Resize the vector preserving existing elements and initializing the + // new ones with the given value. + template <typename T> + void Array<T>::resize(uint new_size, const T & elem) + { + nvDebugCheck(&elem < m_buffer || &elem > m_buffer+m_size); + + uint old_size = m_size; + + // Destruct old elements (if we're shrinking). + destroy_range(m_buffer, new_size, old_size); + + setArraySize(new_size); + + // Call copy constructors + construct_range(m_buffer, new_size, old_size, elem); + } + + // Fill array with the given value. + template <typename T> + void Array<T>::fill(const T & elem) + { + fill(m_buffer, m_size, elem); + } + + // Clear the buffer. + template <typename T> + NV_FORCEINLINE void Array<T>::clear() + { + nvDebugCheck(isValidPtr(m_buffer)); + + // Destruct old elements + destroy_range(m_buffer, 0, m_size); + + m_size = 0; + } + + // Shrink the allocated vector. + template <typename T> + NV_FORCEINLINE void Array<T>::shrink() + { + if (m_size < m_capacity) { + setArrayCapacity(m_size); + } + } + + // Preallocate space. + template <typename T> + NV_FORCEINLINE void Array<T>::reserve(uint desired_size) + { + if (desired_size > m_capacity) { + setArrayCapacity(desired_size); + } + } + + // Copy elements to this array. Resizes it if needed. + template <typename T> + NV_FORCEINLINE void Array<T>::copy(const T * data, uint count) + { +#if 1 // More simple, but maybe not be as efficient? + destroy_range(m_buffer, 0, m_size); + + setArraySize(count); + + construct_range(m_buffer, count, 0, data); +#else + const uint old_size = m_size; + + destroy_range(m_buffer, count, old_size); + + setArraySize(count); + + copy_range(m_buffer, data, old_size); + + construct_range(m_buffer, count, old_size, data); +#endif + } + + // Assignment operator. + template <typename T> + NV_FORCEINLINE Array<T> & Array<T>::operator=( const Array<T> & a ) + { + copy(a.m_buffer, a.m_size); + return *this; + } + + // Release ownership of allocated memory and returns pointer to it. + template <typename T> + T * Array<T>::release() { + T * tmp = m_buffer; + m_buffer = NULL; + m_capacity = 0; + m_size = 0; + return tmp; + } + + + + // Change array size. + template <typename T> + inline void Array<T>::setArraySize(uint new_size) { + m_size = new_size; + + if (new_size > m_capacity) { + uint new_buffer_size; + if (m_capacity == 0) { + // first allocation is exact + new_buffer_size = new_size; + } + else { + // following allocations grow array by 25% + new_buffer_size = new_size + (new_size >> 2); + } + + setArrayCapacity( new_buffer_size ); + } + } + + // Change array capacity. + template <typename T> + inline void Array<T>::setArrayCapacity(uint new_capacity) { + nvDebugCheck(new_capacity >= m_size); + + if (new_capacity == 0) { + // free the buffer. + if (m_buffer != NULL) { + free<T>(m_buffer); + m_buffer = NULL; + } + } + else { + // realloc the buffer + m_buffer = realloc<T>(m_buffer, new_capacity); + } + + m_capacity = new_capacity; + } + + // Array serialization. + template <typename Typ> + inline Stream & operator<< ( Stream & s, Array<Typ> & p ) + { + if (s.isLoading()) { + uint size; + s << size; + p.resize( size ); + } + else { + s << p.m_size; + } + + for (uint i = 0; i < p.m_size; i++) { + s << p.m_buffer[i]; + } + + return s; + } + + // Swap the members of the two given vectors. + template <typename Typ> + inline void swap(Array<Typ> & a, Array<Typ> & b) + { + nv::swap(a.m_buffer, b.m_buffer); + nv::swap(a.m_capacity, b.m_capacity); + nv::swap(a.m_size, b.m_size); + } + + +} // nv namespace + +// IC: These functions are for compatibility with the Foreach macro in The Witness. +template <typename T> inline int item_count(const nv::Array<T> & array) { return array.count(); } +template <typename T> inline const T & item_at(const nv::Array<T> & array, int i) { return array.at(i); } +template <typename T> inline T & item_at(nv::Array<T> & array, int i) { return array.at(i); } +template <typename T> inline int item_advance(const nv::Array<T> & array, int i) { return ++i; } +template <typename T> inline int item_remove(nv::Array<T> & array, int i) { array.replaceWithLast(i); return i - 1; } + +template <typename T> inline int item_count(const nv::Array<T> * array) { return array->count(); } +template <typename T> inline const T & item_at(const nv::Array<T> * array, int i) { return array->at(i); } +template <typename T> inline T & item_at(nv::Array<T> * array, int i) { return array->at(i); } +template <typename T> inline int item_advance(const nv::Array<T> * array, int i) { return ++i; } +template <typename T> inline int item_remove(nv::Array<T> * array, int i) { array->replaceWithLast(i); return i - 1; } + + +#endif // NV_CORE_ARRAY_INL diff --git a/thirdparty/thekla_atlas/nvcore/BitArray.h b/thirdparty/thekla_atlas/nvcore/BitArray.h new file mode 100644 index 0000000000..23cf880694 --- /dev/null +++ b/thirdparty/thekla_atlas/nvcore/BitArray.h @@ -0,0 +1,250 @@ +// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> + +#pragma once +#ifndef NV_CORE_BITARRAY_H +#define NV_CORE_BITARRAY_H + +#include "nvcore.h" +#include "Array.inl" + +namespace nv +{ + + // @@ Uh, this could be much faster. + inline uint countSetBits(uint32 x) { + uint count = 0; + for(; x != 0; x >>= 1) { + count += (x & 1); + } + return count; + } + + // @@ This is even more lame. What was I thinking? + inline uint countSetBits(uint32 x, int bits) { + uint count = 0; + for(; x != 0 && bits != 0; x >>= 1, bits--) { + count += (x & 1); + } + return count; + } + + // See "Conditionally set or clear bits without branching" at http://graphics.stanford.edu/~seander/bithacks.html + inline uint setBits(uint w, uint m, bool b) { + return (w & ~m) | (-int(b) & m); + } + + + + // Simple bit array. + class BitArray + { + public: + + BitArray() {} + BitArray(uint sz) { + resize(sz); + } + + uint size() const { return m_size; } + void clear() { resize(0); } + + void resize(uint new_size) + { + m_size = new_size; + m_wordArray.resize( (m_size + 31) >> 5 ); + } + + void resize(uint new_size, bool init) + { + //if (new_size == m_size) return; + + uint old_size = m_size; + uint size_mod_32 = old_size & 31; + uint last_word_index = ((old_size + 31) >> 5) - 1; + uint mask = (1 << size_mod_32) - 1; + + uint init_dword; + if (init) { + if (size_mod_32) m_wordArray[last_word_index] |= ~mask; + init_dword = ~0; + } + else { + if (size_mod_32) m_wordArray[last_word_index] &= mask; + init_dword = 0; + } + + m_size = new_size; + m_wordArray.resize((new_size + 31) >> 5, init_dword); + + // Make sure new bits are initialized correctly. + /*for (uint i = old_size; i < new_size; i++) { + nvCheck(bitAt(i) == init); + }*/ + } + + + /// Get bit. + bool bitAt(uint b) const + { + nvDebugCheck( b < m_size ); + return (m_wordArray[b >> 5] & (1 << (b & 31))) != 0; + } + + // It may be useful to pack mulitple bit arrays together interleaving their bits. + uint bitsAt(uint idx, uint count) const + { + //nvDebugCheck(count == 2 || count == 4 || count == 8 || count == 16 || count == 32); + nvDebugCheck(count == 2); // @@ Hardcoded for two. + uint b = idx * count; + nvDebugCheck(b < m_size); + return (m_wordArray[b >> 5] & (0x3 << (b & 31))) >> (b & 31); + } + + // It would be useful to have a function to set two bits simultaneously. + /*void setBitsAt(uint idx, uint count, uint bits) const + { + //nvDebugCheck(count == 2 || count == 4 || count == 8 || count == 16 || count == 32); + nvDebugCheck(count == 2); // @@ Hardcoded for two. + uint b = idx * count; + nvDebugCheck(b < m_size); + return (m_wordArray[b >> 5] & (0x3 << (b & 31))) >> (b & 31); + }*/ + + + + // Set a bit. + void setBitAt(uint idx) + { + nvDebugCheck(idx < m_size); + m_wordArray[idx >> 5] |= (1 << (idx & 31)); + } + + // Clear a bit. + void clearBitAt(uint idx) + { + nvDebugCheck(idx < m_size); + m_wordArray[idx >> 5] &= ~(1 << (idx & 31)); + } + + // Toggle a bit. + void toggleBitAt(uint idx) + { + nvDebugCheck(idx < m_size); + m_wordArray[idx >> 5] ^= (1 << (idx & 31)); + } + + // Set a bit to the given value. @@ Rename modifyBitAt? + void setBitAt(uint idx, bool b) + { + nvDebugCheck(idx < m_size); + m_wordArray[idx >> 5] = setBits(m_wordArray[idx >> 5], 1 << (idx & 31), b); + nvDebugCheck(bitAt(idx) == b); + } + + void append(bool value) + { + resize(m_size + 1); + setBitAt(m_size - 1, value); + } + + + // Clear all the bits. + void clearAll() + { + memset(m_wordArray.buffer(), 0, m_wordArray.size() * sizeof(uint)); + } + + // Set all the bits. + void setAll() + { + memset(m_wordArray.buffer(), 0xFF, m_wordArray.size() * sizeof(uint)); + } + + // Toggle all the bits. + void toggleAll() + { + const uint wordCount = m_wordArray.count(); + for(uint b = 0; b < wordCount; b++) { + m_wordArray[b] ^= 0xFFFFFFFF; + } + } + + // Count the number of bits set. + uint countSetBits() const + { + const uint num = m_wordArray.size(); + if( num == 0 ) { + return 0; + } + + uint count = 0; + for(uint i = 0; i < num - 1; i++) { + count += nv::countSetBits(m_wordArray[i]); + } + count += nv::countSetBits(m_wordArray[num - 1], m_size & 31); + + //piDebugCheck(count + countClearBits() == m_size); + return count; + } + + // Count the number of bits clear. + uint countClearBits() const { + + const uint num = m_wordArray.size(); + if( num == 0 ) { + return 0; + } + + uint count = 0; + for(uint i = 0; i < num - 1; i++) { + count += nv::countSetBits(~m_wordArray[i]); + } + count += nv::countSetBits(~m_wordArray[num - 1], m_size & 31); + + //piDebugCheck(count + countSetBits() == m_size); + return count; + } + + friend void swap(BitArray & a, BitArray & b) + { + swap(a.m_size, b.m_size); + swap(a.m_wordArray, b.m_wordArray); + } + + void operator &= (const BitArray & other) { + if (other.m_size != m_size) { + resize(other.m_size); + } + + const uint wordCount = m_wordArray.count(); + for (uint i = 0; i < wordCount; i++) { + m_wordArray[i] &= other.m_wordArray[i]; + } + } + + void operator |= (const BitArray & other) { + if (other.m_size != m_size) { + resize(other.m_size); + } + + const uint wordCount = m_wordArray.count(); + for (uint i = 0; i < wordCount; i++) { + m_wordArray[i] |= other.m_wordArray[i]; + } + } + + + private: + + // Number of bits stored. + uint m_size; + + // Array of bits. + Array<uint> m_wordArray; + + }; + +} // nv namespace + +#endif // NV_CORE_BITARRAY_H + diff --git a/thirdparty/thekla_atlas/nvcore/Debug.cpp b/thirdparty/thekla_atlas/nvcore/Debug.cpp new file mode 100644 index 0000000000..75ac6beb75 --- /dev/null +++ b/thirdparty/thekla_atlas/nvcore/Debug.cpp @@ -0,0 +1,1347 @@ +// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> + +#include "Debug.h" +#include "Array.inl" +#include "StrLib.h" // StringBuilder + +#include "StdStream.h" // fileOpen + +#include <stdlib.h> + +// Extern +#if NV_OS_WIN32 //&& NV_CC_MSVC +# define WIN32_LEAN_AND_MEAN +# define VC_EXTRALEAN +# include <windows.h> +# include <direct.h> +# if NV_CC_MSVC +# include <crtdbg.h> +# if _MSC_VER < 1300 +# define DECLSPEC_DEPRECATED +// VC6: change this path to your Platform SDK headers +# include <dbghelp.h> // must be XP version of file +// include "M:\\dev7\\vs\\devtools\\common\\win32sdk\\include\\dbghelp.h" +# else +// VC7: ships with updated headers +# include <dbghelp.h> +# endif +# endif +# pragma comment(lib,"dbghelp.lib") +#endif + +#if NV_OS_XBOX +# include <Xtl.h> +# ifdef _DEBUG +# include <xbdm.h> +# endif //_DEBUG +#endif //NV_OS_XBOX + +#if !NV_OS_WIN32 && defined(NV_HAVE_SIGNAL_H) +# include <signal.h> +#endif + +#if NV_OS_UNIX +# include <unistd.h> // getpid +#endif + +#if NV_OS_LINUX && defined(NV_HAVE_EXECINFO_H) +# include <execinfo.h> // backtrace +# if NV_CC_GNUC // defined(NV_HAVE_CXXABI_H) +# include <cxxabi.h> +# endif +#endif + +#if NV_OS_DARWIN || NV_OS_FREEBSD || NV_OS_OPENBSD +# include <sys/types.h> +# include <sys/param.h> +# include <sys/sysctl.h> // sysctl +# if !defined(NV_OS_OPENBSD) +# include <sys/ucontext.h> +# endif +# if defined(NV_HAVE_EXECINFO_H) // only after OSX 10.5 +# include <execinfo.h> // backtrace +# if NV_CC_GNUC // defined(NV_HAVE_CXXABI_H) +# include <cxxabi.h> +# endif +# endif +#endif + +#if NV_OS_ORBIS +#include <libdbg.h> +#endif + +#if NV_OS_DURANGO +#include "Windows.h" +#include <winnt.h> +#include <crtdbg.h> +#include <dbghelp.h> +#include <errhandlingapi.h> +#define NV_USE_SEPARATE_THREAD 0 +#else +#define NV_USE_SEPARATE_THREAD 1 +#endif + + + +using namespace nv; + +namespace +{ + + static MessageHandler * s_message_handler = NULL; + static AssertHandler * s_assert_handler = NULL; + + static bool s_sig_handler_enabled = false; + static bool s_interactive = true; + +#if (NV_OS_WIN32 && NV_CC_MSVC) || NV_OS_DURANGO + + // Old exception filter. + static LPTOP_LEVEL_EXCEPTION_FILTER s_old_exception_filter = NULL; + +#elif !NV_OS_WIN32 && defined(NV_HAVE_SIGNAL_H) + + // Old signal handlers. + struct sigaction s_old_sigsegv; + struct sigaction s_old_sigtrap; + struct sigaction s_old_sigfpe; + struct sigaction s_old_sigbus; + +#endif + + +#if (NV_OS_WIN32 && NV_CC_MSVC) || NV_OS_DURANGO + + // We should try to simplify the top level filter as much as possible. + // http://www.nynaeve.net/?p=128 + + // The critical section enforcing the requirement that only one exception be + // handled by a handler at a time. + static CRITICAL_SECTION s_handler_critical_section; + +#if NV_USE_SEPARATE_THREAD + // Semaphores used to move exception handling between the exception thread + // and the handler thread. handler_start_semaphore_ is signalled by the + // exception thread to wake up the handler thread when an exception occurs. + // handler_finish_semaphore_ is signalled by the handler thread to wake up + // the exception thread when handling is complete. + static HANDLE s_handler_start_semaphore = NULL; + static HANDLE s_handler_finish_semaphore = NULL; + + // The exception handler thread. + static HANDLE s_handler_thread = NULL; + + static DWORD s_requesting_thread_id = 0; + static EXCEPTION_POINTERS * s_exception_info = NULL; + +#endif // NV_USE_SEPARATE_THREAD + + + struct MinidumpCallbackContext { + ULONG64 memory_base; + ULONG memory_size; + bool finished; + }; + +#if NV_OS_WIN32 + // static + static BOOL CALLBACK miniDumpWriteDumpCallback(PVOID context, const PMINIDUMP_CALLBACK_INPUT callback_input, PMINIDUMP_CALLBACK_OUTPUT callback_output) + { + switch (callback_input->CallbackType) + { + case MemoryCallback: { + MinidumpCallbackContext* callback_context = reinterpret_cast<MinidumpCallbackContext*>(context); + if (callback_context->finished) + return FALSE; + + // Include the specified memory region. + callback_output->MemoryBase = callback_context->memory_base; + callback_output->MemorySize = callback_context->memory_size; + callback_context->finished = true; + return TRUE; + } + + // Include all modules. + case IncludeModuleCallback: + case ModuleCallback: + return TRUE; + + // Include all threads. + case IncludeThreadCallback: + case ThreadCallback: + return TRUE; + + // Stop receiving cancel callbacks. + case CancelCallback: + callback_output->CheckCancel = FALSE; + callback_output->Cancel = FALSE; + return TRUE; + } + + // Ignore other callback types. + return FALSE; + } +#endif + + static bool writeMiniDump(EXCEPTION_POINTERS * pExceptionInfo) + { +#if NV_OS_DURANGO + // Get a handle to the minidump method. + typedef BOOL(WINAPI* MiniDumpWriteDumpPfn) ( + _In_ HANDLE hProcess, + _In_ DWORD ProcessId, + _In_ HANDLE hFile, + _In_ MINIDUMP_TYPE DumpType, + _In_opt_ PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, + _In_opt_ PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, + _Reserved_ PVOID CallbackParam + ); + MiniDumpWriteDumpPfn MiniDumpWriteDump = NULL; + HMODULE hToolHelpModule = ::LoadLibraryW(L"toolhelpx.dll"); + if (hToolHelpModule != INVALID_HANDLE_VALUE) { + MiniDumpWriteDump = reinterpret_cast<MiniDumpWriteDumpPfn>(::GetProcAddress(hToolHelpModule, "MiniDumpWriteDump")); + if (!MiniDumpWriteDump) { + FreeLibrary(hToolHelpModule); + return false; + } + } + else + return false; + + // Generate a decent filename. + nv::Path application_path(256); + HINSTANCE hinstance = GetModuleHandle(NULL); + GetModuleFileName(hinstance, application_path.str(), 256); + application_path.stripExtension(); + const char * application_name = application_path.fileName(); + + SYSTEMTIME local_time; + GetLocalTime(&local_time); + + char dump_filename[MAX_PATH] = {}; + sprintf_s(dump_filename, "d:\\%s-%04d%02d%02d-%02d%02d%02d.dmp", + application_name, + local_time.wYear, local_time.wMonth, local_time.wDay, + local_time.wHour, local_time.wMinute, local_time.wSecond ); +#else + const char* dump_filename = "crash.dmp"; +#endif + + // create the file + HANDLE hFile = CreateFileA(dump_filename, GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) { + //nvDebug("*** Failed to create dump file.\n"); +#if NV_OS_DURANGO + FreeLibrary(hToolHelpModule); +#endif + return false; + } + + MINIDUMP_EXCEPTION_INFORMATION * pExInfo = NULL; +#if NV_OS_WIN32 + MINIDUMP_CALLBACK_INFORMATION * pCallback = NULL; +#else + void * pCallback = NULL; +#endif + + MINIDUMP_EXCEPTION_INFORMATION ExInfo; + if (pExceptionInfo != NULL) { + ExInfo.ThreadId = ::GetCurrentThreadId(); + ExInfo.ExceptionPointers = pExceptionInfo; + ExInfo.ClientPointers = NULL; + pExInfo = &ExInfo; + +#if NV_OS_WIN32 + MINIDUMP_CALLBACK_INFORMATION callback; + MinidumpCallbackContext context; + + // Find a memory region of 256 bytes centered on the + // faulting instruction pointer. + const ULONG64 instruction_pointer = + #if defined(_M_IX86) + pExceptionInfo->ContextRecord->Eip; + #elif defined(_M_AMD64) + pExceptionInfo->ContextRecord->Rip; + #else + #error Unsupported platform + #endif + + MEMORY_BASIC_INFORMATION info; + + if (VirtualQuery(reinterpret_cast<LPCVOID>(instruction_pointer), &info, sizeof(MEMORY_BASIC_INFORMATION)) != 0 && info.State == MEM_COMMIT) + { + // Attempt to get 128 bytes before and after the instruction + // pointer, but settle for whatever's available up to the + // boundaries of the memory region. + const ULONG64 kIPMemorySize = 256; + context.memory_base = max(reinterpret_cast<ULONG64>(info.BaseAddress), instruction_pointer - (kIPMemorySize / 2)); + ULONG64 end_of_range = min(instruction_pointer + (kIPMemorySize / 2), reinterpret_cast<ULONG64>(info.BaseAddress) + info.RegionSize); + context.memory_size = static_cast<ULONG>(end_of_range - context.memory_base); + context.finished = false; + + callback.CallbackRoutine = miniDumpWriteDumpCallback; + callback.CallbackParam = reinterpret_cast<void*>(&context); + pCallback = &callback; + } +#endif + } + + MINIDUMP_TYPE miniDumpType = (MINIDUMP_TYPE)(MiniDumpNormal|MiniDumpWithHandleData|MiniDumpWithThreadInfo); + + // write the dump + BOOL ok = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, miniDumpType, pExInfo, NULL, pCallback) != 0; + CloseHandle(hFile); +#if NV_OS_DURANGO + FreeLibrary(hToolHelpModule); +#endif + + if (ok == FALSE) { + //nvDebug("*** Failed to save dump file.\n"); + return false; + } + + //nvDebug("\nDump file saved.\n"); + + return true; + } + +#if NV_USE_SEPARATE_THREAD + + static DWORD WINAPI ExceptionHandlerThreadMain(void* lpParameter) { + nvDebugCheck(s_handler_start_semaphore != NULL); + nvDebugCheck(s_handler_finish_semaphore != NULL); + + while (true) { + if (WaitForSingleObject(s_handler_start_semaphore, INFINITE) == WAIT_OBJECT_0) { + writeMiniDump(s_exception_info); + + // Allow the requesting thread to proceed. + ReleaseSemaphore(s_handler_finish_semaphore, 1, NULL); + } + } + + // This statement is not reached when the thread is unconditionally + // terminated by the ExceptionHandler destructor. + return 0; + } + +#endif // NV_USE_SEPARATE_THREAD + + static bool hasStackTrace() { + return true; + } + + /*static NV_NOINLINE int backtrace(void * trace[], int maxcount) { + + // In Windows XP and Windows Server 2003, the sum of the FramesToSkip and FramesToCapture parameters must be less than 63. + int xp_maxcount = min(63-1, maxcount); + + int count = RtlCaptureStackBackTrace(1, xp_maxcount, trace, NULL); + nvDebugCheck(count <= maxcount); + + return count; + }*/ + +#if NV_OS_WIN32 + static NV_NOINLINE int backtraceWithSymbols(CONTEXT * ctx, void * trace[], int maxcount, int skip = 0) { + + // Init the stack frame for this function + STACKFRAME64 stackFrame = { 0 }; + + #if NV_CPU_X86_64 + DWORD dwMachineType = IMAGE_FILE_MACHINE_AMD64; + stackFrame.AddrPC.Offset = ctx->Rip; + stackFrame.AddrFrame.Offset = ctx->Rbp; + stackFrame.AddrStack.Offset = ctx->Rsp; + #elif NV_CPU_X86 + DWORD dwMachineType = IMAGE_FILE_MACHINE_I386; + stackFrame.AddrPC.Offset = ctx->Eip; + stackFrame.AddrFrame.Offset = ctx->Ebp; + stackFrame.AddrStack.Offset = ctx->Esp; + #else + #error "Platform not supported!" + #endif + stackFrame.AddrPC.Mode = AddrModeFlat; + stackFrame.AddrFrame.Mode = AddrModeFlat; + stackFrame.AddrStack.Mode = AddrModeFlat; + + // Walk up the stack + const HANDLE hThread = GetCurrentThread(); + const HANDLE hProcess = GetCurrentProcess(); + int i; + for (i = 0; i < maxcount; i++) + { + // walking once first makes us skip self + if (!StackWalk64(dwMachineType, hProcess, hThread, &stackFrame, ctx, NULL, &SymFunctionTableAccess64, &SymGetModuleBase64, NULL)) { + break; + } + + /*if (stackFrame.AddrPC.Offset == stackFrame.AddrReturn.Offset || stackFrame.AddrPC.Offset == 0) { + break; + }*/ + + if (i >= skip) { + trace[i - skip] = (PVOID)stackFrame.AddrPC.Offset; + } + } + + return i - skip; + } + +#pragma warning(push) +#pragma warning(disable:4748) + static NV_NOINLINE int backtrace(void * trace[], int maxcount) { + CONTEXT ctx = { 0 }; +#if NV_CPU_X86 && !NV_CPU_X86_64 + ctx.ContextFlags = CONTEXT_CONTROL; + _asm { + call x + x: pop eax + mov ctx.Eip, eax + mov ctx.Ebp, ebp + mov ctx.Esp, esp + } +#else + RtlCaptureContext(&ctx); // Not implemented correctly in x86. +#endif + + return backtraceWithSymbols(&ctx, trace, maxcount, 1); + } +#pragma warning(pop) + + static NV_NOINLINE void writeStackTrace(void * trace[], int size, int start, Array<const char *> & lines) + { + StringBuilder builder(512); + + HANDLE hProcess = GetCurrentProcess(); + + // Resolve PC to function names + for (int i = start; i < size; i++) + { + // Check for end of stack walk + DWORD64 ip = (DWORD64)trace[i]; + if (ip == NULL) + break; + + // Get function name + #define MAX_STRING_LEN (512) + unsigned char byBuffer[sizeof(IMAGEHLP_SYMBOL64) + MAX_STRING_LEN] = { 0 }; + IMAGEHLP_SYMBOL64 * pSymbol = (IMAGEHLP_SYMBOL64*)byBuffer; + pSymbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64); + pSymbol->MaxNameLength = MAX_STRING_LEN; + + DWORD64 dwDisplacement; + + if (SymGetSymFromAddr64(hProcess, ip, &dwDisplacement, pSymbol)) + { + pSymbol->Name[MAX_STRING_LEN-1] = 0; + + /* + // Make the symbol readable for humans + UnDecorateSymbolName( pSym->Name, lpszNonUnicodeUnDSymbol, BUFFERSIZE, + UNDNAME_COMPLETE | + UNDNAME_NO_THISTYPE | + UNDNAME_NO_SPECIAL_SYMS | + UNDNAME_NO_MEMBER_TYPE | + UNDNAME_NO_MS_KEYWORDS | + UNDNAME_NO_ACCESS_SPECIFIERS ); + */ + + // pSymbol->Name + const char * pFunc = pSymbol->Name; + + // Get file/line number + IMAGEHLP_LINE64 theLine = { 0 }; + theLine.SizeOfStruct = sizeof(theLine); + + DWORD dwDisplacement; + if (!SymGetLineFromAddr64(hProcess, ip, &dwDisplacement, &theLine)) + { + // Do not print unknown symbols anymore. + //break; + builder.format("unknown(%08X) : %s\n", (uint32)ip, pFunc); + } + else + { + /* + const char* pFile = strrchr(theLine.FileName, '\\'); + if ( pFile == NULL ) pFile = theLine.FileName; + else pFile++; + */ + const char * pFile = theLine.FileName; + + int line = theLine.LineNumber; + + builder.format("%s(%d) : %s\n", pFile, line, pFunc); + } + + lines.append(builder.release()); + + if (pFunc != NULL && strcmp(pFunc, "WinMain") == 0) { + break; + } + } + } + } +#endif + + // Write mini dump and print stack trace. + static LONG WINAPI handleException(EXCEPTION_POINTERS * pExceptionInfo) + { + EnterCriticalSection(&s_handler_critical_section); +#if NV_USE_SEPARATE_THREAD + s_requesting_thread_id = GetCurrentThreadId(); + s_exception_info = pExceptionInfo; + + // This causes the handler thread to call writeMiniDump. + ReleaseSemaphore(s_handler_start_semaphore, 1, NULL); + + // Wait until WriteMinidumpWithException is done and collect its return value. + WaitForSingleObject(s_handler_finish_semaphore, INFINITE); + //bool status = s_handler_return_value; + + // Clean up. + s_requesting_thread_id = 0; + s_exception_info = NULL; +#else + // First of all, write mini dump. + writeMiniDump(pExceptionInfo); +#endif + LeaveCriticalSection(&s_handler_critical_section); + + nvDebug("\nDump file saved.\n"); + + // Try to attach to debugger. + if (s_interactive && debug::attachToDebugger()) { + nvDebugBreak(); + return EXCEPTION_CONTINUE_EXECUTION; + } + +#if NV_OS_WIN32 + // If that fails, then try to pretty print a stack trace and terminate. + void * trace[64]; + + int size = backtraceWithSymbols(pExceptionInfo->ContextRecord, trace, 64); + + // @@ Use win32's CreateFile? + FILE * fp = fileOpen("crash.txt", "wb"); + if (fp != NULL) { + Array<const char *> lines; + writeStackTrace(trace, size, 0, lines); + + for (uint i = 0; i < lines.count(); i++) { + fputs(lines[i], fp); + delete lines[i]; + } + + // @@ Add more info to crash.txt? + + fclose(fp); + } +#endif + + // This should terminate the process and set the error exit code. + TerminateProcess(GetCurrentProcess(), EXIT_FAILURE + 2); + + return EXCEPTION_EXECUTE_HANDLER; // Terminate app. In case terminate process did not succeed. + } + + static void handlePureVirtualCall() { + nvDebugBreak(); + TerminateProcess(GetCurrentProcess(), EXIT_FAILURE + 8); + } + + static void handleInvalidParameter(const wchar_t * wexpresion, const wchar_t * wfunction, const wchar_t * wfile, unsigned int line, uintptr_t reserved) { + + size_t convertedCharCount = 0; + + StringBuilder expresion; + if (wexpresion != NULL) { + uint size = U32(wcslen(wexpresion) + 1); + expresion.reserve(size); + wcstombs_s(&convertedCharCount, expresion.str(), size, wexpresion, _TRUNCATE); + } + + StringBuilder file; + if (wfile != NULL) { + uint size = U32(wcslen(wfile) + 1); + file.reserve(size); + wcstombs_s(&convertedCharCount, file.str(), size, wfile, _TRUNCATE); + } + + StringBuilder function; + if (wfunction != NULL) { + uint size = U32(wcslen(wfunction) + 1); + function.reserve(size); + wcstombs_s(&convertedCharCount, function.str(), size, wfunction, _TRUNCATE); + } + + int result = nvAbort(expresion.str(), file.str(), line, function.str()); + if (result == NV_ABORT_DEBUG) { + nvDebugBreak(); + } + } + +#elif !NV_OS_WIN32 && defined(NV_HAVE_SIGNAL_H) // NV_OS_LINUX || NV_OS_DARWIN + +#if defined(NV_HAVE_EXECINFO_H) + + static bool hasStackTrace() { + return true; + } + + + static void writeStackTrace(void * trace[], int size, int start, Array<const char *> & lines) { + StringBuilder builder(512); + char ** string_array = backtrace_symbols(trace, size); + + for(int i = start; i < size-1; i++ ) { + // IC: Just in case. + if (string_array[i] == NULL || string_array[i][0] == '\0') break; + +# if NV_CC_GNUC // defined(NV_HAVE_CXXABI_H) + // @@ Write a better parser for the possible formats. + char * begin = strchr(string_array[i], '('); + char * end = strrchr(string_array[i], '+'); + char * module = string_array[i]; + + if (begin == 0 && end != 0) { + *(end - 1) = '\0'; + begin = strrchr(string_array[i], ' '); + module = NULL; // Ignore module. + } + + if (begin != 0 && begin < end) { + int stat; + *end = '\0'; + *begin = '\0'; + char * name = abi::__cxa_demangle(begin+1, 0, 0, &stat); + if (module == NULL) { + if (name == NULL || stat != 0) { + builder.format(" In: '%s'\n", begin+1); + } + else { + builder.format(" In: '%s'\n", name); + } + } + else { + if (name == NULL || stat != 0) { + builder.format(" In: [%s] '%s'\n", module, begin+1); + } + else { + builder.format(" In: [%s] '%s'\n", module, name); + } + } + free(name); + } + else { + builder.format(" In: '%s'\n", string_array[i]); + } +# else + builder.format(" In: '%s'\n", string_array[i]); +# endif + lines.append(builder.release()); + } + + free(string_array); + } + + static void printStackTrace(void * trace[], int size, int start=0) { + nvDebug( "\nDumping stacktrace:\n" ); + + Array<const char *> lines; + writeStackTrace(trace, size, 1, lines); + + for (uint i = 0; i < lines.count(); i++) { + nvDebug("%s", lines[i]); + delete lines[i]; + } + + nvDebug("\n"); + } + +#endif // defined(NV_HAVE_EXECINFO_H) + + static void * callerAddress(void * secret) + { +#if NV_OS_DARWIN +# if defined(_STRUCT_MCONTEXT) +# if NV_CPU_PPC + ucontext_t * ucp = (ucontext_t *)secret; + return (void *) ucp->uc_mcontext->__ss.__srr0; +# elif NV_CPU_X86_64 + ucontext_t * ucp = (ucontext_t *)secret; + return (void *) ucp->uc_mcontext->__ss.__rip; +# elif NV_CPU_X86 + ucontext_t * ucp = (ucontext_t *)secret; + return (void *) ucp->uc_mcontext->__ss.__eip; +# elif NV_CPU_ARM + ucontext_t * ucp = (ucontext_t *)secret; + return (void *) ucp->uc_mcontext->__ss.__pc; +# else +# error "Unknown CPU" +# endif +# else +# if NV_CPU_PPC + ucontext_t * ucp = (ucontext_t *)secret; + return (void *) ucp->uc_mcontext->ss.srr0; +# elif NV_CPU_X86 + ucontext_t * ucp = (ucontext_t *)secret; + return (void *) ucp->uc_mcontext->ss.eip; +# else +# error "Unknown CPU" +# endif +# endif +#elif NV_OS_FREEBSD +# if NV_CPU_X86_64 + ucontext_t * ucp = (ucontext_t *)secret; + return (void *)ucp->uc_mcontext.mc_rip; +# elif NV_CPU_X86 + ucontext_t * ucp = (ucontext_t *)secret; + return (void *)ucp->uc_mcontext.mc_eip; +# else +# error "Unknown CPU" +# endif +#elif NV_OS_OPENBSD +# if NV_CPU_X86_64 + ucontext_t * ucp = (ucontext_t *)secret; + return (void *)ucp->sc_rip; +# elif NV_CPU_X86 + ucontext_t * ucp = (ucontext_t *)secret; + return (void *)ucp->sc_eip; +# else +# error "Unknown CPU" +# endif +#else +# if NV_CPU_X86_64 + // #define REG_RIP REG_INDEX(rip) // seems to be 16 + ucontext_t * ucp = (ucontext_t *)secret; + return (void *)ucp->uc_mcontext.gregs[REG_RIP]; +# elif NV_CPU_X86 + ucontext_t * ucp = (ucontext_t *)secret; + return (void *)ucp->uc_mcontext.gregs[14/*REG_EIP*/]; +# elif NV_CPU_PPC + ucontext_t * ucp = (ucontext_t *)secret; + return (void *) ucp->uc_mcontext.regs->nip; +# else +# error "Unknown CPU" +# endif +#endif + + // How to obtain the instruction pointers in different platforms, from mlton's source code. + // http://mlton.org/ + // OpenBSD && NetBSD + // ucp->sc_eip + // FreeBSD: + // ucp->uc_mcontext.mc_eip + // HPUX: + // ucp->uc_link + // Solaris: + // ucp->uc_mcontext.gregs[REG_PC] + // Linux hppa: + // uc->uc_mcontext.sc_iaoq[0] & ~0x3UL + // Linux sparc: + // ((struct sigcontext*) secret)->sigc_regs.tpc + // Linux sparc64: + // ((struct sigcontext*) secret)->si_regs.pc + + // potentially correct for other archs: + // Linux alpha: ucp->m_context.sc_pc + // Linux arm: ucp->m_context.ctx.arm_pc + // Linux ia64: ucp->m_context.sc_ip & ~0x3UL + // Linux mips: ucp->m_context.sc_pc + // Linux s390: ucp->m_context.sregs->regs.psw.addr + } + + static void nvSigHandler(int sig, siginfo_t *info, void *secret) + { + void * pnt = callerAddress(secret); + + // Do something useful with siginfo_t + if (sig == SIGSEGV) { + if (pnt != NULL) nvDebug("Got signal %d, faulty address is %p, from %p\n", sig, info->si_addr, pnt); + else nvDebug("Got signal %d, faulty address is %p\n", sig, info->si_addr); + } + else if(sig == SIGTRAP) { + nvDebug("Breakpoint hit.\n"); + } + else { + nvDebug("Got signal %d\n", sig); + } + +#if defined(NV_HAVE_EXECINFO_H) + if (hasStackTrace()) // in case of weak linking + { + void * trace[64]; + int size = backtrace(trace, 64); + + if (pnt != NULL) { + // Overwrite sigaction with caller's address. + trace[1] = pnt; + } + + printStackTrace(trace, size, 1); + } +#endif // defined(NV_HAVE_EXECINFO_H) + + exit(0); + } + +#endif // defined(NV_HAVE_SIGNAL_H) + + + +#if NV_OS_WIN32 //&& NV_CC_MSVC + + /** Win32 assert handler. */ + struct Win32AssertHandler : public AssertHandler + { + // Flush the message queue. This is necessary for the message box to show up. + static void flushMessageQueue() + { + MSG msg; + while( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) { + //if( msg.message == WM_QUIT ) break; + TranslateMessage( &msg ); + DispatchMessage( &msg ); + } + } + + // Assert handler method. + virtual int assertion(const char * exp, const char * file, int line, const char * func, const char * msg, va_list arg) + { + int ret = NV_ABORT_EXIT; + + StringBuilder error_string; + error_string.format("*** Assertion failed: %s\n On file: %s\n On line: %d\n", exp, file, line ); + if (func != NULL) { + error_string.appendFormat(" On function: %s\n", func); + } + if (msg != NULL) { + error_string.append(" Message: "); + va_list tmp; + va_copy(tmp, arg); + error_string.appendFormatList(msg, tmp); + va_end(tmp); + error_string.append("\n"); + } + nvDebug( error_string.str() ); + + // Print stack trace: + debug::dumpInfo(); + + if (debug::isDebuggerPresent()) { + return NV_ABORT_DEBUG; + } + + if (s_interactive) { + flushMessageQueue(); + int action = MessageBoxA(NULL, error_string.str(), "Assertion failed", MB_ABORTRETRYIGNORE | MB_ICONERROR | MB_TOPMOST); + switch( action ) { + case IDRETRY: + ret = NV_ABORT_DEBUG; + break; + case IDIGNORE: + ret = NV_ABORT_IGNORE; + break; + case IDABORT: + default: + ret = NV_ABORT_EXIT; + break; + } + /*if( _CrtDbgReport( _CRT_ASSERT, file, line, module, exp ) == 1 ) { + return NV_ABORT_DEBUG; + }*/ + } + + if (ret == NV_ABORT_EXIT) { + // Exit cleanly. + exit(EXIT_FAILURE + 1); + } + + return ret; + } + }; +#elif NV_OS_XBOX + + /** Xbox360 assert handler. */ + struct Xbox360AssertHandler : public AssertHandler + { + // Assert handler method. + virtual int assertion(const char * exp, const char * file, int line, const char * func, const char * msg, va_list arg) + { + int ret = NV_ABORT_EXIT; + + StringBuilder error_string; + if( func != NULL ) { + error_string.format( "*** Assertion failed: %s\n On file: %s\n On function: %s\n On line: %d\n ", exp, file, func, line ); + nvDebug( error_string.str() ); + } + else { + error_string.format( "*** Assertion failed: %s\n On file: %s\n On line: %d\n ", exp, file, line ); + nvDebug( error_string.str() ); + } + + if (debug::isDebuggerPresent()) { + return NV_ABORT_DEBUG; + } + + if( ret == NV_ABORT_EXIT ) { + // Exit cleanly. + exit(EXIT_FAILURE + 1); + } + + return ret; + } + }; +#elif NV_OS_ORBIS || NV_OS_DURANGO + + /** Console assert handler. */ + struct ConsoleAssertHandler : public AssertHandler + { + // Assert handler method. + virtual int assertion(const char * exp, const char * file, int line, const char * func, const char * msg, va_list arg) + { + if( func != NULL ) { + nvDebug( "*** Assertion failed: %s\n On file: %s\n On function: %s\n On line: %d\n ", exp, file, func, line ); + } + else { + nvDebug( "*** Assertion failed: %s\n On file: %s\n On line: %d\n ", exp, file, line ); + } + + //SBtodoORBIS print stack trace + /*if (hasStackTrace()) + { + void * trace[64]; + int size = backtrace(trace, 64); + printStackTrace(trace, size, 2); + }*/ + + if (debug::isDebuggerPresent()) + return NV_ABORT_DEBUG; + + return NV_ABORT_IGNORE; + } + }; + +#else + + /** Unix assert handler. */ + struct UnixAssertHandler : public AssertHandler + { + // Assert handler method. + virtual int assertion(const char * exp, const char * file, int line, const char * func, const char * msg, va_list arg) + { + int ret = NV_ABORT_EXIT; + + if( func != NULL ) { + nvDebug( "*** Assertion failed: %s\n On file: %s\n On function: %s\n On line: %d\n ", exp, file, func, line ); + } + else { + nvDebug( "*** Assertion failed: %s\n On file: %s\n On line: %d\n ", exp, file, line ); + } + +#if _DEBUG + if (debug::isDebuggerPresent()) { + return NV_ABORT_DEBUG; + } +#endif + +#if defined(NV_HAVE_EXECINFO_H) + if (hasStackTrace()) + { + void * trace[64]; + int size = backtrace(trace, 64); + printStackTrace(trace, size, 2); + } +#endif + + if( ret == NV_ABORT_EXIT ) { + // Exit cleanly. + exit(EXIT_FAILURE + 1); + } + + return ret; + } + }; + +#endif + +} // namespace + + +/// Handle assertion through the assert handler. +int nvAbort(const char * exp, const char * file, int line, const char * func/*=NULL*/, const char * msg/*= NULL*/, ...) +{ +#if NV_OS_WIN32 //&& NV_CC_MSVC + static Win32AssertHandler s_default_assert_handler; +#elif NV_OS_XBOX + static Xbox360AssertHandler s_default_assert_handler; +#elif NV_OS_ORBIS || NV_OS_DURANGO + static ConsoleAssertHandler s_default_assert_handler; +#else + static UnixAssertHandler s_default_assert_handler; +#endif + + va_list arg; + va_start(arg,msg); + + AssertHandler * handler = s_assert_handler != NULL ? s_assert_handler : &s_default_assert_handler; + int result = handler->assertion(exp, file, line, func, msg, arg); + + va_end(arg); + + return result; +} + +// Abnormal termination. Create mini dump and output call stack. +void debug::terminate(int code) +{ +#if NV_OS_WIN32 || NV_OS_DURANGO + EnterCriticalSection(&s_handler_critical_section); + + writeMiniDump(NULL); + +#if NV_OS_WIN32 + const int max_stack_size = 64; + void * trace[max_stack_size]; + int size = backtrace(trace, max_stack_size); + + // @@ Use win32's CreateFile? + FILE * fp = fileOpen("crash.txt", "wb"); + if (fp != NULL) { + Array<const char *> lines; + writeStackTrace(trace, size, 0, lines); + + for (uint i = 0; i < lines.count(); i++) { + fputs(lines[i], fp); + delete lines[i]; + } + + // @@ Add more info to crash.txt? + + fclose(fp); + } +#endif + + LeaveCriticalSection(&s_handler_critical_section); +#endif + + exit(code); +} + + +/// Shows a message through the message handler. +void NV_CDECL nvDebugPrint(const char *msg, ...) +{ + va_list arg; + va_start(arg,msg); + if (s_message_handler != NULL) { + s_message_handler->log( msg, arg ); + } + else { + vprintf(msg, arg); + } + va_end(arg); +} + + +/// Dump debug info. +void debug::dumpInfo() +{ +#if (NV_OS_WIN32 && NV_CC_MSVC) || (defined(NV_HAVE_SIGNAL_H) && defined(NV_HAVE_EXECINFO_H)) + if (hasStackTrace()) + { + void * trace[64]; + int size = backtrace(trace, 64); + + nvDebug( "\nDumping stacktrace:\n" ); + + Array<const char *> lines; + writeStackTrace(trace, size, 1, lines); + + for (uint i = 0; i < lines.count(); i++) { + nvDebug("%s", lines[i]); + delete lines[i]; + } + } +#endif +} + +/// Dump callstack using the specified handler. +void debug::dumpCallstack(MessageHandler *messageHandler, int callstackLevelsToSkip /*= 0*/) +{ +#if (NV_OS_WIN32 && NV_CC_MSVC) || (defined(NV_HAVE_SIGNAL_H) && defined(NV_HAVE_EXECINFO_H)) + if (hasStackTrace()) + { + void * trace[64]; + int size = backtrace(trace, 64); + + Array<const char *> lines; + writeStackTrace(trace, size, callstackLevelsToSkip + 1, lines); // + 1 to skip the call to dumpCallstack + + for (uint i = 0; i < lines.count(); i++) { + messageHandler->log(lines[i], NULL); + delete lines[i]; + } + } +#endif +} + + +/// Set the debug message handler. +void debug::setMessageHandler(MessageHandler * message_handler) +{ + s_message_handler = message_handler; +} + +/// Reset the debug message handler. +void debug::resetMessageHandler() +{ + s_message_handler = NULL; +} + +/// Set the assert handler. +void debug::setAssertHandler(AssertHandler * assert_handler) +{ + s_assert_handler = assert_handler; +} + +/// Reset the assert handler. +void debug::resetAssertHandler() +{ + s_assert_handler = NULL; +} + +#if NV_OS_WIN32 || NV_OS_DURANGO +#if NV_USE_SEPARATE_THREAD + +static void initHandlerThread() +{ + static const int kExceptionHandlerThreadInitialStackSize = 64 * 1024; + + // Set synchronization primitives and the handler thread. Each + // ExceptionHandler object gets its own handler thread because that's the + // only way to reliably guarantee sufficient stack space in an exception, + // and it allows an easy way to get a snapshot of the requesting thread's + // context outside of an exception. + InitializeCriticalSection(&s_handler_critical_section); + + s_handler_start_semaphore = CreateSemaphoreExW(NULL, 0, 1, NULL, 0, + SEMAPHORE_MODIFY_STATE | DELETE | SYNCHRONIZE); + nvDebugCheck(s_handler_start_semaphore != NULL); + + s_handler_finish_semaphore = CreateSemaphoreExW(NULL, 0, 1, NULL, 0, + SEMAPHORE_MODIFY_STATE | DELETE | SYNCHRONIZE); + nvDebugCheck(s_handler_finish_semaphore != NULL); + + // Don't attempt to create the thread if we could not create the semaphores. + if (s_handler_finish_semaphore != NULL && s_handler_start_semaphore != NULL) { + DWORD thread_id; + s_handler_thread = CreateThread(NULL, // lpThreadAttributes + kExceptionHandlerThreadInitialStackSize, + ExceptionHandlerThreadMain, + NULL, // lpParameter + 0, // dwCreationFlags + &thread_id); + nvDebugCheck(s_handler_thread != NULL); + } + + /* @@ We should avoid loading modules in the exception handler! + dbghelp_module_ = LoadLibrary(L"dbghelp.dll"); + if (dbghelp_module_) { + minidump_write_dump_ = reinterpret_cast<MiniDumpWriteDump_type>(GetProcAddress(dbghelp_module_, "MiniDumpWriteDump")); + } + */ +} + +static void shutHandlerThread() { + // @@ Free stuff. Terminate thread. +} + +#endif // NV_USE_SEPARATE_THREAD +#endif // NV_OS_WIN32 + + +// Enable signal handler. +void debug::enableSigHandler(bool interactive) +{ + if (s_sig_handler_enabled) return; + + s_sig_handler_enabled = true; + s_interactive = interactive; + +#if (NV_OS_WIN32 && NV_CC_MSVC) || NV_OS_DURANGO + if (interactive) { +#if NV_OS_WIN32 + // Do not display message boxes on error. + // http://msdn.microsoft.com/en-us/library/windows/desktop/ms680621(v=vs.85).aspx + SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOGPFAULTERRORBOX|SEM_NOOPENFILEERRORBOX); +#endif + + // CRT reports errors to debug output only. + // http://msdn.microsoft.com/en-us/library/1y71x448(v=vs.80).aspx + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG); + _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG); + _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG); + } + + +#if NV_USE_SEPARATE_THREAD + initHandlerThread(); +#else + InitializeCriticalSection(&s_handler_critical_section); +#endif + + s_old_exception_filter = ::SetUnhandledExceptionFilter( handleException ); + +#if _MSC_VER >= 1400 // MSVC 2005/8 + _set_invalid_parameter_handler(handleInvalidParameter); +#endif // _MSC_VER >= 1400 + + _set_purecall_handler(handlePureVirtualCall); + +#if NV_OS_WIN32 + // SYMOPT_DEFERRED_LOADS make us not take a ton of time unless we actual log traces + SymSetOptions(SYMOPT_DEFERRED_LOADS|SYMOPT_FAIL_CRITICAL_ERRORS|SYMOPT_LOAD_LINES|SYMOPT_UNDNAME); + + if (!SymInitialize(GetCurrentProcess(), NULL, TRUE)) { + DWORD error = GetLastError(); + nvDebug("SymInitialize returned error : %d\n", error); + } +#endif + +#elif !NV_OS_WIN32 && defined(NV_HAVE_SIGNAL_H) + + // Install our signal handler + struct sigaction sa; + sa.sa_sigaction = nvSigHandler; + sigemptyset (&sa.sa_mask); + sa.sa_flags = SA_ONSTACK | SA_RESTART | SA_SIGINFO; + + sigaction(SIGSEGV, &sa, &s_old_sigsegv); + sigaction(SIGTRAP, &sa, &s_old_sigtrap); + sigaction(SIGFPE, &sa, &s_old_sigfpe); + sigaction(SIGBUS, &sa, &s_old_sigbus); + +#endif +} + +/// Disable signal handler. +void debug::disableSigHandler() +{ + nvCheck(s_sig_handler_enabled == true); + s_sig_handler_enabled = false; + +#if (NV_OS_WIN32 && NV_CC_MSVC) || NV_OS_DURANGO + + ::SetUnhandledExceptionFilter( s_old_exception_filter ); + s_old_exception_filter = NULL; + +#if NV_OS_WIN32 + SymCleanup(GetCurrentProcess()); +#endif + +#elif !NV_OS_WIN32 && defined(NV_HAVE_SIGNAL_H) + + sigaction(SIGSEGV, &s_old_sigsegv, NULL); + sigaction(SIGTRAP, &s_old_sigtrap, NULL); + sigaction(SIGFPE, &s_old_sigfpe, NULL); + sigaction(SIGBUS, &s_old_sigbus, NULL); + +#endif +} + + +bool debug::isDebuggerPresent() +{ +#if NV_OS_WIN32 + HINSTANCE kernel32 = GetModuleHandleA("kernel32.dll"); + if (kernel32) { + FARPROC IsDebuggerPresent = GetProcAddress(kernel32, "IsDebuggerPresent"); + if (IsDebuggerPresent != NULL && IsDebuggerPresent()) { + return true; + } + } + return false; +#elif NV_OS_XBOX +#ifdef _DEBUG + return DmIsDebuggerPresent() == TRUE; +#else + return false; +#endif +#elif NV_OS_ORBIS + #if PS4_FINAL_REQUIREMENTS + return false; + #else + return sceDbgIsDebuggerAttached() == 1; + #endif +#elif NV_OS_DURANGO + #if XB1_FINAL_REQUIREMENTS + return false; + #else + return IsDebuggerPresent() == TRUE; + #endif +#elif NV_OS_DARWIN + int mib[4]; + struct kinfo_proc info; + size_t size; + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = getpid(); + size = sizeof(info); + info.kp_proc.p_flag = 0; + sysctl(mib,4,&info,&size,NULL,0); + return ((info.kp_proc.p_flag & P_TRACED) == P_TRACED); +#else + // if ppid != sid, some process spawned our app, probably a debugger. + return getsid(getpid()) != getppid(); +#endif +} + +bool debug::attachToDebugger() +{ +#if NV_OS_WIN32 + if (isDebuggerPresent() == FALSE) { + Path process(1024); + process.copy("\""); + GetSystemDirectoryA(process.str() + 1, 1024 - 1); + + process.appendSeparator(); + + process.appendFormat("VSJitDebugger.exe\" -p %lu", ::GetCurrentProcessId()); + + STARTUPINFOA sSi; + memset(&sSi, 0, sizeof(sSi)); + + PROCESS_INFORMATION sPi; + memset(&sPi, 0, sizeof(sPi)); + + BOOL b = CreateProcessA(NULL, process.str(), NULL, NULL, FALSE, 0, NULL, NULL, &sSi, &sPi); + if (b != FALSE) { + ::WaitForSingleObject(sPi.hProcess, INFINITE); + + DWORD dwExitCode; + ::GetExitCodeProcess(sPi.hProcess, &dwExitCode); + if (dwExitCode != 0) //if exit code is zero, a debugger was selected + b = FALSE; + } + + if (sPi.hThread != NULL) ::CloseHandle(sPi.hThread); + if (sPi.hProcess != NULL) ::CloseHandle(sPi.hProcess); + + if (b == FALSE) + return false; + + for (int i = 0; i < 5*60; i++) { + if (isDebuggerPresent()) + break; + ::Sleep(200); + } + } +#endif // NV_OS_WIN32 + + return true; +} diff --git a/thirdparty/thekla_atlas/nvcore/Debug.h b/thirdparty/thekla_atlas/nvcore/Debug.h new file mode 100644 index 0000000000..f37a05c453 --- /dev/null +++ b/thirdparty/thekla_atlas/nvcore/Debug.h @@ -0,0 +1,246 @@ +// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> + +#pragma once +#ifndef NV_CORE_DEBUG_H +#define NV_CORE_DEBUG_H + +#include "nvcore.h" + +#include <stdarg.h> // va_list + +#if NV_OS_IOS //ACS: maybe we want this for OSX too? +# ifdef __APPLE__ +# include <TargetConditionals.h> +# include <signal.h> +# endif +#endif + +// Make sure we are using our assert. +#undef assert + +#define NV_ABORT_DEBUG 1 +#define NV_ABORT_IGNORE 2 +#define NV_ABORT_EXIT 3 + +#define nvNoAssert(exp) \ + NV_MULTI_LINE_MACRO_BEGIN \ + (void)sizeof(exp); \ + NV_MULTI_LINE_MACRO_END + +#if NV_NO_ASSERT + +# define nvAssert(exp) nvNoAssert(exp) +# define nvCheck(exp) nvNoAssert(exp) +# define nvDebugAssert(exp) nvNoAssert(exp) +# define nvDebugCheck(exp) nvNoAssert(exp) +# define nvDebugBreak() nvNoAssert(0) + +#else // NV_NO_ASSERT + +# if NV_CC_MSVC + // @@ Does this work in msvc-6 and earlier? +# define nvDebugBreak() __debugbreak() +//# define nvDebugBreak() __asm { int 3 } +# elif NV_OS_ORBIS +# define nvDebugBreak() __debugbreak() +# elif NV_OS_IOS && TARGET_OS_IPHONE +# define nvDebugBreak() raise(SIGINT) +# elif NV_CC_CLANG +# define nvDebugBreak() __builtin_debugtrap() +# elif NV_CC_GNUC +//# define nvDebugBreak() __builtin_debugtrap() // Does GCC have debugtrap? +# define nvDebugBreak() __builtin_trap() +/* +# elif NV_CC_GNUC && NV_CPU_PPC && NV_OS_DARWIN +// @@ Use __builtin_trap() on GCC +# define nvDebugBreak() __asm__ volatile ("trap") +# elif NV_CC_GNUC && NV_CPU_X86 && NV_OS_DARWIN +# define nvDebugBreak() __asm__ volatile ("int3") +# elif NV_CC_GNUC && NV_CPU_X86 +# define nvDebugBreak() __asm__ ( "int %0" : :"I"(3) ) +# elif NV_OS_ORBIS +# define nvDebugBreak() __asm volatile ("int $0x41") +# else +# include <signal.h> +# define nvDebugBreak() raise(SIGTRAP); +// define nvDebugBreak() *((int *)(0)) = 0 +*/ +# endif + +# if NV_CC_MSVC +# define nvExpect(expr) (expr) +#else +# define nvExpect(expr) __builtin_expect((expr) != 0, true) +#endif + +#if NV_CC_CLANG +# if __has_feature(attribute_analyzer_noreturn) +# define NV_ANALYZER_NORETURN __attribute__((analyzer_noreturn)) +# else +# define NV_ANALYZER_NORETURN +# endif +#else +# define NV_ANALYZER_NORETURN +#endif + +#define nvDebugBreakOnce() \ + NV_MULTI_LINE_MACRO_BEGIN \ + static bool firstTime = true; \ + if (firstTime) { firstTime = false; nvDebugBreak(); } \ + NV_MULTI_LINE_MACRO_END + +#define nvAssertMacro(exp) \ + NV_MULTI_LINE_MACRO_BEGIN \ + if (!nvExpect(exp)) { \ + if (nvAbort(#exp, __FILE__, __LINE__, __FUNC__) == NV_ABORT_DEBUG) { \ + nvDebugBreak(); \ + } \ + } \ + NV_MULTI_LINE_MACRO_END + +// GCC, LLVM need "##" before the __VA_ARGS__, MSVC doesn't care +#define nvAssertMacroWithIgnoreAll(exp,...) \ + NV_MULTI_LINE_MACRO_BEGIN \ + static bool ignoreAll = false; \ + if (!ignoreAll && !nvExpect(exp)) { \ + int _result = nvAbort(#exp, __FILE__, __LINE__, __FUNC__, ##__VA_ARGS__); \ + if (_result == NV_ABORT_DEBUG) { \ + nvDebugBreak(); \ + } else if (_result == NV_ABORT_IGNORE) { \ + ignoreAll = true; \ + } \ + } \ + NV_MULTI_LINE_MACRO_END + +// Interesting assert macro from Insomniac: +// http://www.gdcvault.com/play/1015319/Developing-Imperfect-Software-How-to +// Used as follows: +// if (nvCheck(i < count)) { +// normal path +// } else { +// fixup code. +// } +// This style of macro could be combined with __builtin_expect to let the compiler know failure is unlikely. +#define nvCheckMacro(exp) \ + (\ + (exp) ? true : ( \ + (nvAbort(#exp, __FILE__, __LINE__, __FUNC__) == NV_ABORT_DEBUG) ? (nvDebugBreak(), true) : ( false ) \ + ) \ + ) + + +#define nvAssert(exp) nvAssertMacro(exp) +#define nvCheck(exp) nvAssertMacro(exp) + +#if defined(_DEBUG) +# define nvDebugAssert(exp) nvAssertMacro(exp) +# define nvDebugCheck(exp) nvAssertMacro(exp) +#else // _DEBUG +# define nvDebugAssert(exp) nvNoAssert(exp) +# define nvDebugCheck(exp) nvNoAssert(exp) +#endif // _DEBUG + +#endif // NV_NO_ASSERT + +// Use nvAssume for very simple expresions only: nvAssume(0), nvAssume(value == true), etc. +/*#if !defined(_DEBUG) +# if NV_CC_MSVC +# define nvAssume(exp) __assume(exp) +# else +# define nvAssume(exp) nvCheck(exp) +# endif +#else +# define nvAssume(exp) nvCheck(exp) +#endif*/ + +#if defined(_DEBUG) +# if NV_CC_MSVC +# define nvUnreachable() nvAssert(0 && "unreachable"); __assume(0) +# else +# define nvUnreachable() nvAssert(0 && "unreachable"); __builtin_unreachable() +# endif +#else +# if NV_CC_MSVC +# define nvUnreachable() __assume(0) +# else +# define nvUnreachable() __builtin_unreachable() +# endif +#endif + +#define nvError(x) nvAbort(x, __FILE__, __LINE__, __FUNC__) +#define nvWarning(x) nvDebugPrint("*** Warning %s/%d: %s\n", __FILE__, __LINE__, (x)) + +#ifndef NV_DEBUG_PRINT +#define NV_DEBUG_PRINT 1 //defined(_DEBUG) +#endif + +#if NV_DEBUG_PRINT +#define nvDebug(...) nvDebugPrint(__VA_ARGS__) +#else +#if NV_CC_MSVC +#define nvDebug(...) __noop(__VA_ARGS__) +#else +#define nvDebug(...) ((void)0) // Non-msvc platforms do not evaluate arguments? +#endif +#endif + + +NVCORE_API int nvAbort(const char *exp, const char *file, int line, const char * func = NULL, const char * msg = NULL, ...) __attribute__((format (printf, 5, 6))) NV_ANALYZER_NORETURN; +NVCORE_API void NV_CDECL nvDebugPrint( const char *msg, ... ) __attribute__((format (printf, 1, 2))); + +namespace nv +{ + inline bool isValidPtr(const void * ptr) { + #if NV_OS_DARWIN + return true; // IC: Not sure what ranges are OK on OSX. + #endif + + #if NV_CPU_X86_64 + if (ptr == NULL) return true; + if (reinterpret_cast<uint64>(ptr) < 0x10000ULL) return false; + if (reinterpret_cast<uint64>(ptr) >= 0x000007FFFFFEFFFFULL) return false; + #else + if (reinterpret_cast<uint32>(ptr) == 0xcccccccc) return false; + if (reinterpret_cast<uint32>(ptr) == 0xcdcdcdcd) return false; + if (reinterpret_cast<uint32>(ptr) == 0xdddddddd) return false; + if (reinterpret_cast<uint32>(ptr) == 0xffffffff) return false; + #endif + return true; + } + + // Message handler interface. + struct MessageHandler { + virtual void log(const char * str, va_list arg) = 0; + virtual ~MessageHandler() {} + }; + + // Assert handler interface. + struct AssertHandler { + virtual int assertion(const char *exp, const char *file, int line, const char *func, const char *msg, va_list arg) = 0; + virtual ~AssertHandler() {} + }; + + + namespace debug + { + NVCORE_API void dumpInfo(); + NVCORE_API void dumpCallstack( MessageHandler *messageHandler, int callstackLevelsToSkip = 0 ); + + NVCORE_API void setMessageHandler( MessageHandler * messageHandler ); + NVCORE_API void resetMessageHandler(); + + NVCORE_API void setAssertHandler( AssertHandler * assertHanlder ); + NVCORE_API void resetAssertHandler(); + + NVCORE_API void enableSigHandler(bool interactive); + NVCORE_API void disableSigHandler(); + + NVCORE_API bool isDebuggerPresent(); + NVCORE_API bool attachToDebugger(); + + NVCORE_API void terminate(int code); + } + +} // nv namespace + +#endif // NV_CORE_DEBUG_H diff --git a/thirdparty/thekla_atlas/nvcore/DefsGnucDarwin.h b/thirdparty/thekla_atlas/nvcore/DefsGnucDarwin.h new file mode 100644 index 0000000000..afb21c3d25 --- /dev/null +++ b/thirdparty/thekla_atlas/nvcore/DefsGnucDarwin.h @@ -0,0 +1,57 @@ +#ifndef NV_CORE_H +#error "Do not include this file directly." +#endif + +#include <stdint.h> // uint8_t, int8_t, ... uintptr_t +#include <stddef.h> // operator new, size_t, NULL + +// Function linkage +#define DLL_IMPORT +#if __GNUC__ >= 4 +# define DLL_EXPORT __attribute__((visibility("default"))) +# define DLL_EXPORT_CLASS DLL_EXPORT +#else +# define DLL_EXPORT +# define DLL_EXPORT_CLASS +#endif + +// Function calling modes +#if NV_CPU_X86 +# define NV_CDECL __attribute__((cdecl)) +# define NV_STDCALL __attribute__((stdcall)) +#else +# define NV_CDECL +# define NV_STDCALL +#endif + +#define NV_FASTCALL __attribute__((fastcall)) +#define NV_FORCEINLINE __attribute__((always_inline)) inline +#define NV_DEPRECATED __attribute__((deprecated)) +#if NV_OS_IOS +#define NV_THREAD_LOCAL // @@ IC: Looks like iOS does not have support for TLS declarations. +#else +#define NV_THREAD_LOCAL __thread +#endif + +#if __GNUC__ > 2 +#define NV_PURE __attribute__((pure)) +#define NV_CONST __attribute__((const)) +#else +#define NV_PURE +#define NV_CONST +#endif + +#define NV_NOINLINE __attribute__((noinline)) + +// Define __FUNC__ properly. +#if __STDC_VERSION__ < 199901L +# if __GNUC__ >= 2 +# define __FUNC__ __PRETTY_FUNCTION__ // __FUNCTION__ +# else +# define __FUNC__ "<unknown>" +# endif +#else +# define __FUNC__ __PRETTY_FUNCTION__ +#endif + +#define restrict __restrict__ diff --git a/thirdparty/thekla_atlas/nvcore/DefsGnucLinux.h b/thirdparty/thekla_atlas/nvcore/DefsGnucLinux.h new file mode 100644 index 0000000000..2126d866f5 --- /dev/null +++ b/thirdparty/thekla_atlas/nvcore/DefsGnucLinux.h @@ -0,0 +1,59 @@ +#ifndef NV_CORE_H +#error "Do not include this file directly." +#endif + +#include <stdint.h> // uint8_t, int8_t, ... uintptr_t +#include <stddef.h> // operator new, size_t, NULL + +// Function linkage +#define DLL_IMPORT +#if __GNUC__ >= 4 +# define DLL_EXPORT __attribute__((visibility("default"))) +# define DLL_EXPORT_CLASS DLL_EXPORT +#else +# define DLL_EXPORT +# define DLL_EXPORT_CLASS +#endif + +// Function calling modes +#if NV_CPU_X86 +# define NV_CDECL __attribute__((cdecl)) +# define NV_STDCALL __attribute__((stdcall)) +#else +# define NV_CDECL +# define NV_STDCALL +#endif + +#define NV_FASTCALL __attribute__((fastcall)) +//#if __GNUC__ > 3 +// It seems that GCC does not assume always_inline implies inline. I think this depends on the GCC version :( +#define NV_FORCEINLINE inline __attribute__((always_inline)) +//#else +// Some compilers complain that inline and always_inline are redundant. +//#define NV_FORCEINLINE __attribute__((always_inline)) +//#endif +#define NV_DEPRECATED __attribute__((deprecated)) +#define NV_THREAD_LOCAL __thread + +#if __GNUC__ > 2 +#define NV_PURE __attribute__((pure)) +#define NV_CONST __attribute__((const)) +#else +#define NV_PURE +#define NV_CONST +#endif + +#define NV_NOINLINE __attribute__((noinline)) + +// Define __FUNC__ properly. +#if __STDC_VERSION__ < 199901L +# if __GNUC__ >= 2 +# define __FUNC__ __PRETTY_FUNCTION__ // __FUNCTION__ +# else +# define __FUNC__ "<unknown>" +# endif +#else +# define __FUNC__ __PRETTY_FUNCTION__ +#endif + +#define restrict __restrict__ diff --git a/thirdparty/thekla_atlas/nvcore/DefsGnucWin32.h b/thirdparty/thekla_atlas/nvcore/DefsGnucWin32.h new file mode 100644 index 0000000000..f35ed88575 --- /dev/null +++ b/thirdparty/thekla_atlas/nvcore/DefsGnucWin32.h @@ -0,0 +1,65 @@ +#ifndef NV_CORE_H +#error "Do not include this file directly." +#endif + +//#include <cstddef> // size_t, NULL + +// Function linkage +#define DLL_IMPORT __declspec(dllimport) +#define DLL_EXPORT __declspec(dllexport) +#define DLL_EXPORT_CLASS DLL_EXPORT + +// Function calling modes +#if NV_CPU_X86 +# define NV_CDECL __attribute__((cdecl)) +# define NV_STDCALL __attribute__((stdcall)) +#else +# define NV_CDECL +# define NV_STDCALL +#endif + +#define NV_FASTCALL __attribute__((fastcall)) +#define NV_FORCEINLINE __attribute__((always_inline)) +#define NV_DEPRECATED __attribute__((deprecated)) + +#if __GNUC__ > 2 +#define NV_PURE __attribute__((pure)) +#define NV_CONST __attribute__((const)) +#else +#define NV_PURE +#define NV_CONST +#endif + +#define NV_NOINLINE __attribute__((noinline)) + +// Define __FUNC__ properly. +#if __STDC_VERSION__ < 199901L +# if __GNUC__ >= 2 +# define __FUNC__ __PRETTY_FUNCTION__ // __FUNCTION__ +# else +# define __FUNC__ "<unknown>" +# endif +#else +# define __FUNC__ __PRETTY_FUNCTION__ +#endif + +#define restrict __restrict__ + +/* +// Type definitions +typedef unsigned char uint8; +typedef signed char int8; + +typedef unsigned short uint16; +typedef signed short int16; + +typedef unsigned int uint32; +typedef signed int int32; + +typedef unsigned long long uint64; +typedef signed long long int64; + +// Aliases +typedef uint32 uint; +*/ + diff --git a/thirdparty/thekla_atlas/nvcore/DefsVcWin32.h b/thirdparty/thekla_atlas/nvcore/DefsVcWin32.h new file mode 100644 index 0000000000..a915f3791a --- /dev/null +++ b/thirdparty/thekla_atlas/nvcore/DefsVcWin32.h @@ -0,0 +1,94 @@ +// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> + +#ifndef NV_CORE_H +#error "Do not include this file directly." +#endif + +// Function linkage +#define DLL_IMPORT __declspec(dllimport) +#define DLL_EXPORT __declspec(dllexport) +#define DLL_EXPORT_CLASS DLL_EXPORT + +// Function calling modes +#define NV_CDECL __cdecl +#define NV_STDCALL __stdcall +#define NV_FASTCALL __fastcall +#define NV_DEPRECATED + +#define NV_PURE +#define NV_CONST + +// Set standard function names. +#if _MSC_VER < 1900 +# define snprintf _snprintf +#endif +#if _MSC_VER < 1500 +# define vsnprintf _vsnprintf +#endif +#if _MSC_VER < 1700 +# define strtoll _strtoi64 +# define strtoull _strtoui64 +#endif +//#define chdir _chdir +#define getcwd _getcwd + +#if _MSC_VER <= 1600 +#define va_copy(a, b) (a) = (b) +#endif + +#if !defined restrict +#define restrict +#endif + +// Ignore gcc attributes. +#define __attribute__(X) + +#if !defined __FUNC__ +#define __FUNC__ __FUNCTION__ +#endif + +#define NV_NOINLINE __declspec(noinline) +#define NV_FORCEINLINE __forceinline + +#define NV_THREAD_LOCAL __declspec(thread) + +/* +// Type definitions +typedef unsigned char uint8; +typedef signed char int8; + +typedef unsigned short uint16; +typedef signed short int16; + +typedef unsigned int uint32; +typedef signed int int32; + +typedef unsigned __int64 uint64; +typedef signed __int64 int64; + +// Aliases +typedef uint32 uint; +*/ + +// Unwanted VC++ warnings to disable. +/* +#pragma warning(disable : 4244) // conversion to float, possible loss of data +#pragma warning(disable : 4245) // conversion from 'enum ' to 'unsigned long', signed/unsigned mismatch +#pragma warning(disable : 4100) // unreferenced formal parameter +#pragma warning(disable : 4514) // unreferenced inline function has been removed +#pragma warning(disable : 4710) // inline function not expanded +#pragma warning(disable : 4127) // Conditional expression is constant +#pragma warning(disable : 4305) // truncation from 'const double' to 'float' +#pragma warning(disable : 4505) // unreferenced local function has been removed + +#pragma warning(disable : 4702) // unreachable code in inline expanded function +#pragma warning(disable : 4711) // function selected for automatic inlining +#pragma warning(disable : 4725) // Pentium fdiv bug + +#pragma warning(disable : 4786) // Identifier was truncated and cannot be debugged. + +#pragma warning(disable : 4675) // resolved overload was found by argument-dependent lookup +*/ + +#pragma warning(1 : 4705) // Report unused local variables. +#pragma warning(1 : 4555) // Expression has no effect. diff --git a/thirdparty/thekla_atlas/nvcore/FileSystem.cpp b/thirdparty/thekla_atlas/nvcore/FileSystem.cpp new file mode 100644 index 0000000000..5ed0ca074f --- /dev/null +++ b/thirdparty/thekla_atlas/nvcore/FileSystem.cpp @@ -0,0 +1,75 @@ +// This code is in the public domain -- castano@gmail.com + +#include "FileSystem.h" + +#if NV_OS_WIN32 +#define _CRT_NONSTDC_NO_WARNINGS // _chdir is defined deprecated, but that's a bug, chdir is deprecated, _chdir is *not*. +//#include <shlwapi.h> // PathFileExists +#include <windows.h> // GetFileAttributes +#include <direct.h> // _mkdir +#elif NV_OS_XBOX +#include <Xtl.h> +#elif NV_OS_DURANGO +#include <Windows.h> +#else +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> +#endif +#include <stdio.h> // remove, unlink + +using namespace nv; + + +bool FileSystem::exists(const char * path) +{ +#if NV_OS_UNIX + return access(path, F_OK|R_OK) == 0; + //struct stat buf; + //return stat(path, &buf) == 0; +#elif NV_OS_WIN32 || NV_OS_XBOX || NV_OS_DURANGO + // PathFileExists requires linking to shlwapi.lib + //return PathFileExists(path) != 0; + return GetFileAttributesA(path) != INVALID_FILE_ATTRIBUTES; +#else + if (FILE * fp = fopen(path, "r")) + { + fclose(fp); + return true; + } + return false; +#endif +} + +bool FileSystem::createDirectory(const char * path) +{ +#if NV_OS_WIN32 || NV_OS_XBOX || NV_OS_DURANGO + return CreateDirectoryA(path, NULL) != 0; +#elif NV_OS_ORBIS + // not implemented + return false; +#else + return mkdir(path, 0777) != -1; +#endif +} + +bool FileSystem::changeDirectory(const char * path) +{ +#if NV_OS_WIN32 + return _chdir(path) != -1; +#elif NV_OS_XBOX || NV_OS_DURANGO + // Xbox doesn't support Current Working Directory! + return false; +#elif NV_OS_ORBIS + // Orbis doesn't support Current Working Directory! + return false; +#else + return chdir(path) != -1; +#endif +} + +bool FileSystem::removeFile(const char * path) +{ + // @@ Use unlink or remove? + return remove(path) == 0; +} diff --git a/thirdparty/thekla_atlas/nvcore/FileSystem.h b/thirdparty/thekla_atlas/nvcore/FileSystem.h new file mode 100644 index 0000000000..afd0f449d3 --- /dev/null +++ b/thirdparty/thekla_atlas/nvcore/FileSystem.h @@ -0,0 +1,24 @@ +// This code is in the public domain -- castano@gmail.com + +#pragma once +#ifndef NV_CORE_FILESYSTEM_H +#define NV_CORE_FILESYSTEM_H + +#include "nvcore.h" + +namespace nv +{ + + namespace FileSystem + { + NVCORE_API bool exists(const char * path); + NVCORE_API bool createDirectory(const char * path); + NVCORE_API bool changeDirectory(const char * path); + NVCORE_API bool removeFile(const char * path); + + } // FileSystem namespace + +} // nv namespace + + +#endif // NV_CORE_FILESYSTEM_H diff --git a/thirdparty/thekla_atlas/nvcore/ForEach.h b/thirdparty/thekla_atlas/nvcore/ForEach.h new file mode 100644 index 0000000000..bc66f424ef --- /dev/null +++ b/thirdparty/thekla_atlas/nvcore/ForEach.h @@ -0,0 +1,71 @@ +// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> + +#pragma once +#ifndef NV_CORE_FOREACH_H +#define NV_CORE_FOREACH_H + +/* +These foreach macros are very non-standard and somewhat confusing, but I like them. +*/ + +#include "nvcore.h" + + +#if NV_CC_CPP11 + +#define NV_FOREACH(i, container) \ + for (auto i = (container).start(); !(container).isDone(i); (container).advance(i)) + +#elif NV_CC_GNUC // If typeof is available: + +/* +Ideally we would like to write this: + +#define NV_FOREACH(i, container) \ + for(decltype(container)::PseudoIndex i((container).start()); !(container).isDone(i); (container).advance(i)) + +But gcc versions prior to 4.7 required an intermediate type. See: +https://gcc.gnu.org/bugzilla/show_bug.cgi?id=6709 +*/ + +#define NV_FOREACH(i, container) \ + typedef typeof(container) NV_STRING_JOIN2(cont,__LINE__); \ + for(NV_STRING_JOIN2(cont,__LINE__)::PseudoIndex i((container).start()); !(container).isDone(i); (container).advance(i)) + +#else // If typeof not available: + +#define NV_NEED_PSEUDOINDEX_WRAPPER 1 + +#include <new> // placement new + +struct PseudoIndexWrapper { + template <typename T> + PseudoIndexWrapper(const T & container) { + nvStaticCheck(sizeof(typename T::PseudoIndex) <= sizeof(memory)); + new (memory) typename T::PseudoIndex(container.start()); + } + // PseudoIndex cannot have a dtor! + + template <typename T> typename T::PseudoIndex & operator()(const T * /*container*/) { + return *reinterpret_cast<typename T::PseudoIndex *>(memory); + } + template <typename T> const typename T::PseudoIndex & operator()(const T * /*container*/) const { + return *reinterpret_cast<const typename T::PseudoIndex *>(memory); + } + + uint8 memory[4]; // Increase the size if we have bigger enumerators. +}; + +#define NV_FOREACH(i, container) \ + for(PseudoIndexWrapper i(container); !(container).isDone(i(&(container))); (container).advance(i(&(container)))) + +#endif + +// Declare foreach keyword. +#if !defined NV_NO_USE_KEYWORDS +# define foreach NV_FOREACH +# define foreach_index NV_FOREACH +#endif + + +#endif // NV_CORE_FOREACH_H diff --git a/thirdparty/thekla_atlas/nvcore/Hash.h b/thirdparty/thekla_atlas/nvcore/Hash.h new file mode 100644 index 0000000000..a8b0b2c63b --- /dev/null +++ b/thirdparty/thekla_atlas/nvcore/Hash.h @@ -0,0 +1,83 @@ +// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> + +#pragma once +#ifndef NV_CORE_HASH_H +#define NV_CORE_HASH_H + +#include "nvcore.h" + +namespace nv +{ + inline uint sdbmHash(const void * data_in, uint size, uint h = 5381) + { + const uint8 * data = (const uint8 *) data_in; + uint i = 0; + while (i < size) { + h = (h << 16) + (h << 6) - h + (uint) data[i++]; + } + return h; + } + + // Note that this hash does not handle NaN properly. + inline uint sdbmFloatHash(const float * f, uint count, uint h = 5381) + { + for (uint i = 0; i < count; i++) { + //nvDebugCheck(nv::isFinite(*f)); + union { float f; uint32 i; } x = { f[i] }; + if (x.i == 0x80000000) x.i = 0; + h = sdbmHash(&x, 4, h); + } + return h; + } + + + template <typename T> + inline uint hash(const T & t, uint h = 5381) + { + return sdbmHash(&t, sizeof(T), h); + } + + template <> + inline uint hash(const float & f, uint h) + { + return sdbmFloatHash(&f, 1, h); + } + + + // Functors for hash table: + template <typename Key> struct Hash + { + uint operator()(const Key & k) const { + return hash(k); + } + }; + + template <typename Key> struct Equal + { + bool operator()(const Key & k0, const Key & k1) const { + return k0 == k1; + } + }; + + + // @@ Move to Utils.h? + template <typename T1, typename T2> + struct Pair { + T1 first; + T2 second; + }; + + template <typename T1, typename T2> + bool operator==(const Pair<T1,T2> & p0, const Pair<T1,T2> & p1) { + return p0.first == p1.first && p0.second == p1.second; + } + + template <typename T1, typename T2> + uint hash(const Pair<T1,T2> & p, uint h = 5381) { + return hash(p.second, hash(p.first)); + } + + +} // nv namespace + +#endif // NV_CORE_HASH_H diff --git a/thirdparty/thekla_atlas/nvcore/HashMap.h b/thirdparty/thekla_atlas/nvcore/HashMap.h new file mode 100644 index 0000000000..7856d6a8c9 --- /dev/null +++ b/thirdparty/thekla_atlas/nvcore/HashMap.h @@ -0,0 +1,174 @@ +// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> + +#pragma once +#ifndef NV_CORE_HASHMAP_H +#define NV_CORE_HASHMAP_H + +/* +HashMap based on Thatcher Ulrich <tu@tulrich.com> container, donated to the Public Domain. + +I'd like to do something to reduce the amount of code generated with this template. The type of +U is largely irrelevant to the generated code, except for calls to constructors and destructors, +but the combination of all T and U pairs, generate a large amounts of code. + +HashMap is not used in NVTT, so it could be removed from the repository. +*/ + + +#include "Memory.h" +#include "Debug.h" +#include "ForEach.h" +#include "Hash.h" + +namespace nv +{ + class Stream; + + /** Thatcher Ulrich's hash table. + * + * 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. Could + * come in handy. + * + * 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. + */ + template<typename T, typename U, typename H = Hash<T>, typename E = Equal<T> > + class NVCORE_CLASS HashMap + { + NV_FORBID_COPY(HashMap); + public: + + /// Default ctor. + HashMap() : entry_count(0), size_mask(-1), table(NULL) { } + + /// Ctor with size hint. + explicit HashMap(int size_hint) : entry_count(0), size_mask(-1), table(NULL) { setCapacity(size_hint); } + + /// Dtor. + ~HashMap() { clear(); } + + + void set(const T& key, const U& value); + void add(const T& key, const U& value); + bool remove(const T& key); + void clear(); + bool isEmpty() const; + bool get(const T& key, U* value = NULL, T* other_key = NULL) const; + bool contains(const T & key) const; + int size() const; + int count() const; + int capacity() const; + void checkExpand(); + void resize(int n); + + void setCapacity(int new_size); + + // Behaves much like std::pair. + struct Entry + { + int next_in_chain; // internal chaining for collisions + uint hash_value; // avoids recomputing. Worthwhile? + T key; + U value; + + Entry() : next_in_chain(-2) {} + Entry(const Entry& e) : next_in_chain(e.next_in_chain), hash_value(e.hash_value), key(e.key), value(e.value) {} + Entry(const T& k, const U& v, int next, int hash) : next_in_chain(next), hash_value(hash), key(k), value(v) {} + + bool isEmpty() const { return next_in_chain == -2; } + bool isEndOfChain() const { return next_in_chain == -1; } + bool isTombstone() const { return hash_value == TOMBSTONE_HASH; } + + void clear() { + key.~T(); // placement delete + value.~U(); // placement delete + next_in_chain = -2; + hash_value = ~TOMBSTONE_HASH; + } + + void makeTombstone() { + key.~T(); + value.~U(); + hash_value = TOMBSTONE_HASH; + } + }; + + + // HashMap enumerator. + typedef int PseudoIndex; + PseudoIndex start() const { PseudoIndex i = 0; findNext(i); return i; } + bool isDone(const PseudoIndex & i) const { nvDebugCheck(i <= size_mask+1); return i == size_mask+1; }; + void advance(PseudoIndex & i) const { nvDebugCheck(i <= size_mask+1); i++; findNext(i); } + +#if NV_NEED_PSEUDOINDEX_WRAPPER + Entry & operator[]( const PseudoIndexWrapper & i ) { + Entry & e = entry(i(this)); + nvDebugCheck(e.isTombstone() == false); + return e; + } + const Entry & operator[]( const PseudoIndexWrapper & i ) const { + const Entry & e = entry(i(this)); + nvDebugCheck(e.isTombstone() == false); + return e; + } +#else + Entry & operator[](const PseudoIndex & i) { + Entry & e = entry(i); + nvDebugCheck(e.isTombstone() == false); + return e; + } + const Entry & operator[](const PseudoIndex & i) const { + const Entry & e = entry(i); + nvDebugCheck(e.isTombstone() == false); + return e; + } +#endif + + + // By default we serialize the key-value pairs compactl y. + template<typename _T, typename _U, typename _H, typename _E> + friend Stream & operator<< (Stream & s, HashMap<_T, _U, _H, _E> & map); + + // This requires more storage, but saves us from rehashing the elements. + template<typename _T, typename _U, typename _H, typename _E> + friend Stream & rawSerialize(Stream & s, HashMap<_T, _U, _H, _E> & map); + + /// Swap the members of this vector and the given vector. + template<typename _T, typename _U, typename _H, typename _E> + friend void swap(HashMap<_T, _U, _H, _E> & a, HashMap<_T, _U, _H, _E> & b); + + private: + static const uint TOMBSTONE_HASH = (uint) -1; + + uint compute_hash(const T& key) const; + + // Find the index of the matching entry. If no match, then return -1. + int findIndex(const T& key) const; + + // Return the index of the newly cleared element. + int removeTombstone(int index); + + // Helpers. + Entry & entry(int index); + const Entry & entry(int index) const; + + void setRawCapacity(int new_size); + + // Move the enumerator to the next valid element. + void findNext(PseudoIndex & i) const; + + + int entry_count; + int size_mask; + Entry * table; + + }; + +} // nv namespace + +#endif // NV_CORE_HASHMAP_H diff --git a/thirdparty/thekla_atlas/nvcore/HashMap.inl b/thirdparty/thekla_atlas/nvcore/HashMap.inl new file mode 100644 index 0000000000..f0b6bfea62 --- /dev/null +++ b/thirdparty/thekla_atlas/nvcore/HashMap.inl @@ -0,0 +1,550 @@ +// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> + +#pragma once +#ifndef NV_CORE_HASHMAP_INL +#define NV_CORE_HASHMAP_INL + +#include "HashMap.h" + +#include "Stream.h" +#include "Utils.h" // swap + +#include <new> // for placement new + + +namespace nv +{ + + // Set a new or existing value under the key, to the value. + template<typename T, typename U, typename H, typename E> + void HashMap<T, U, H, E>::set(const T& key, const U& value) + { + int index = findIndex(key); + if (index >= 0) + { + entry(index).value = value; + return; + } + + // Entry under key doesn't exist. + add(key, value); + } + + + // Add a new value to the hash table, under the specified key. + template<typename T, typename U, typename H, typename E> + void HashMap<T, U, H, E>::add(const T& key, const U& value) + { + nvCheck(findIndex(key) == -1); + + checkExpand(); + nvCheck(table != NULL); + entry_count++; + + const uint hash_value = compute_hash(key); + const int index = hash_value & size_mask; + + Entry * natural_entry = &(entry(index)); + + if (natural_entry->isEmpty()) + { + // Put the new entry in. + new (natural_entry) Entry(key, value, -1, hash_value); + } + else if (natural_entry->isTombstone()) { + // Put the new entry in, without disturbing the rest of the chain. + int next_in_chain = natural_entry->next_in_chain; + new (natural_entry) Entry(key, value, next_in_chain, hash_value); + } + else + { + // Find a blank spot. + int blank_index = index; + for (int search_count = 0; ; search_count++) + { + blank_index = (blank_index + 1) & size_mask; + if (entry(blank_index).isEmpty()) break; // found it + if (entry(blank_index).isTombstone()) { + blank_index = removeTombstone(blank_index); + break; + } + nvCheck(search_count < this->size_mask); + } + Entry * blank_entry = &entry(blank_index); + + if (int(natural_entry->hash_value & size_mask) == index) + { + // Collision. Link into this chain. + + // Move existing list head. + new (blank_entry) Entry(*natural_entry); // placement new, copy ctor + + // Put the new info in the natural entry. + natural_entry->key = key; + natural_entry->value = value; + natural_entry->next_in_chain = blank_index; + natural_entry->hash_value = hash_value; + } + 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) + int collided_index = natural_entry->hash_value & size_mask; + for (int search_count = 0; ; search_count++) + { + Entry * e = &entry(collided_index); + if (e->next_in_chain == index) + { + // Here's where we need to splice. + new (blank_entry) Entry(*natural_entry); + e->next_in_chain = blank_index; + break; + } + collided_index = e->next_in_chain; + nvCheck(collided_index >= 0 && collided_index <= size_mask); + nvCheck(search_count <= size_mask); + } + + // Put the new data in the natural entry. + natural_entry->key = key; + natural_entry->value = value; + natural_entry->hash_value = hash_value; + natural_entry->next_in_chain = -1; + } + } + } + + + // Remove the first value under the specified key. + template<typename T, typename U, typename H, typename E> + bool HashMap<T, U, H, E>::remove(const T& key) + { + if (table == NULL) + { + return false; + } + + int index = findIndex(key); + if (index < 0) + { + return false; + } + + Entry * pos = &entry(index); + + int natural_index = (int) (pos->hash_value & size_mask); + + if (index != natural_index) { + // We're not the head of our chain, so we can + // be spliced out of it. + + // Iterate up the chain, and splice out when + // we get to m_index. + Entry* e = &entry(natural_index); + while (e->next_in_chain != index) { + nvDebugCheck(e->isEndOfChain() == false); + e = &entry(e->next_in_chain); + } + + if (e->isTombstone() && pos->isEndOfChain()) { + // Tombstone has nothing else to point + // to, so mark it empty. + e->next_in_chain = -2; + } else { + e->next_in_chain = pos->next_in_chain; + } + + pos->clear(); + } + else if (pos->isEndOfChain() == false) { + // We're the head of our chain, and there are + // additional elements. + // + // We need to put a tombstone here. + // + // We can't clear the element, because the + // rest of the elements in the chain must be + // linked to this position. + // + // We can't move any of the succeeding + // elements in the chain (i.e. to fill this + // entry), because we don't want to invalidate + // any other existing iterators. + pos->makeTombstone(); + } else { + // We're the head of the chain, but we're the + // only member of the chain. + pos->clear(); + } + + entry_count--; + + return true; + } + + + // Remove all entries from the hash table. + template<typename T, typename U, typename H, typename E> + void HashMap<T, U, H, E>::clear() + { + if (table != NULL) + { + // Delete the entries. + for (int i = 0, n = size_mask; i <= n; i++) + { + Entry * e = &entry(i); + if (e->isEmpty() == false && e->isTombstone() == false) + { + e->clear(); + } + } + free(table); + table = NULL; + entry_count = 0; + size_mask = -1; + } + } + + + // Returns true if the hash is empty. + template<typename T, typename U, typename H, typename E> + bool HashMap<T, U, H, E>::isEmpty() const + { + return table == NULL || entry_count == 0; + } + + + // Retrieve the value under the given key. + // - If there's no value under the key, then return false and leave *value alone. + // - If there is a value, return true, and set *value to the entry's value. + // - If value == NULL, return true or false according to the presence of the key, but don't touch *value. + template<typename T, typename U, typename H, typename E> + bool HashMap<T, U, H, E>::get(const T& key, U* value/*= NULL*/, T* other_key/*= NULL*/) const + { + int index = findIndex(key); + if (index >= 0) + { + if (value != NULL) { + *value = entry(index).value; // take care with side-effects! + } + if (other_key != NULL) { + *other_key = entry(index).key; + } + return true; + } + return false; + } + + // Determine if the given key is contained in the hash. + template<typename T, typename U, typename H, typename E> + bool HashMap<T, U, H, E>::contains(const T & key) const + { + return get(key); + } + + // Number of entries in the hash. + template<typename T, typename U, typename H, typename E> + int HashMap<T, U, H, E>::size() const + { + return entry_count; + } + + // Number of entries in the hash. + template<typename T, typename U, typename H, typename E> + int HashMap<T, U, H, E>::count() const + { + return size(); + } + + template<typename T, typename U, typename H, typename E> + int HashMap<T, U, H, E>::capacity() const + { + return size_mask+1; + } + + + // Resize the hash table to fit one more entry. Often this doesn't involve any action. + template<typename T, typename U, typename H, typename E> + void HashMap<T, U, H, E>::checkExpand() + { + if (table == NULL) { + // Initial creation of table. Make a minimum-sized table. + setRawCapacity(16); + } + else if (entry_count * 3 > (size_mask + 1) * 2) { + // Table is more than 2/3rds full. Expand. + setRawCapacity(entry_count * 2); + } + } + + + // Hint the bucket count to >= n. + template<typename T, typename U, typename H, typename E> + void HashMap<T, U, H, E>::resize(int 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 hash so that it can comfortably contain the given number of elements. If the hash already contains more + // elements than new_size, then this may be a no-op. + template<typename T, typename U, typename H, typename E> + void HashMap<T, U, H, E>::setCapacity(int new_size) + { + int new_raw_size = (new_size * 3) / 2; + if (new_raw_size < size()) { return; } + + setRawCapacity(new_raw_size); + } + + + // By default we serialize the key-value pairs compactly. + template<typename _T, typename _U, typename _H, typename _E> + Stream & operator<< (Stream & s, HashMap<_T, _U, _H, _E> & map) + { + typedef typename HashMap<_T, _U, _H, _E>::Entry HashMapEntry; + + int entry_count = map.entry_count; + s << entry_count; + + if (s.isLoading()) { + map.clear(); + if(entry_count == 0) { + return s; + } + map.entry_count = entry_count; + map.size_mask = nextPowerOfTwo(U32(entry_count)) - 1; + map.table = malloc<HashMapEntry>(map.size_mask + 1); + + for (int i = 0; i <= map.size_mask; i++) { + map.table[i].next_in_chain = -2; // mark empty + } + + _T key; + _U value; + for (int i = 0; i < entry_count; i++) { + s << key << value; + map.add(key, value); + } + } + else { + int i = 0; + map.findNext(i); + while (i != map.size_mask+1) { + HashMapEntry & e = map.entry(i); + + s << e.key << e.value; + + i++; + map.findNext(i); + } + //for(HashMap<_T, _U, _H, _E>::PseudoIndex i((map).start()); !(map).isDone(i); (map).advance(i)) { + //foreach(i, map) { + // s << map[i].key << map[i].value; + //} + } + + return s; + } + + // This requires more storage, but saves us from rehashing the elements. + template<typename _T, typename _U, typename _H, typename _E> + Stream & rawSerialize(Stream & s, HashMap<_T, _U, _H, _E> & map) + { + typedef typename HashMap<_T, _U, _H, _E>::Entry HashMapEntry; + + if (s.isLoading()) { + map.clear(); + } + + s << map.size_mask; + + if (map.size_mask != -1) { + s << map.entry_count; + + if (s.isLoading()) { + map.table = new HashMapEntry[map.size_mask+1]; + } + + for (int i = 0; i <= map.size_mask; i++) { + HashMapEntry & e = map.table[i]; + s << e.next_in_chain << e.hash_value; + s << e.key; + s << e.value; + } + } + + return s; + } + + // Swap the members of this vector and the given vector. + template<typename _T, typename _U, typename _H, typename _E> + void swap(HashMap<_T, _U, _H, _E> & a, HashMap<_T, _U, _H, _E> & b) + { + swap(a.entry_count, b.entry_count); + swap(a.size_mask, b.size_mask); + swap(a.table, b.table); + } + + + template<typename T, typename U, typename H, typename E> + uint HashMap<T, U, H, E>::compute_hash(const T& key) const + { + H hash; + uint hash_value = hash(key); + if (hash_value == TOMBSTONE_HASH) { + hash_value ^= 0x8000; + } + return hash_value; + } + + // Find the index of the matching entry. If no match, then return -1. + template<typename T, typename U, typename H, typename E> + int HashMap<T, U, H, E>::findIndex(const T& key) const + { + if (table == NULL) return -1; + + E equal; + + uint hash_value = compute_hash(key); + int index = hash_value & size_mask; + + const Entry * e = &entry(index); + if (e->isEmpty()) return -1; + if (e->isTombstone() == false && int(e->hash_value & size_mask) != index) { + // occupied by a collider + return -1; + } + + for (;;) + { + nvCheck(e->isTombstone() || (e->hash_value & size_mask) == (hash_value & size_mask)); + + if (e->hash_value == hash_value && equal(e->key, key)) + { + // Found it. + return index; + } + nvDebugCheck(e->isTombstone() || !equal(e->key, key)); // keys are equal, but hash differs! + + // Keep looking through the chain. + index = e->next_in_chain; + if (index == -1) break; // end of chain + + nvCheck(index >= 0 && index <= size_mask); + e = &entry(index); + + nvCheck(e->isEmpty() == false || e->isTombstone()); + } + return -1; + } + + // Return the index of the newly cleared element. + template<typename T, typename U, typename H, typename E> + int HashMap<T, U, H, E>::removeTombstone(int index) { + Entry* e = &entry(index); + nvCheck(e->isTombstone()); + nvCheck(!e->isEndOfChain()); + + // Move the next element of the chain into the + // tombstone slot, and return the vacated element. + int new_blank_index = e->next_in_chain; + Entry* new_blank = &entry(new_blank_index); + new (e) Entry(*new_blank); + new_blank->clear(); + return new_blank_index; + } + + // Helpers. + template<typename T, typename U, typename H, typename E> + typename HashMap<T, U, H, E>::Entry & HashMap<T, U, H, E>::entry(int index) + { + nvDebugCheck(table != NULL); + nvDebugCheck(index >= 0 && index <= size_mask); + return table[index]; + } + template<typename T, typename U, typename H, typename E> + const typename HashMap<T, U, H, E>::Entry & HashMap<T, U, H, E>::entry(int index) const + { + nvDebugCheck(table != NULL); + nvDebugCheck(index >= 0 && index <= size_mask); + return table[index]; + } + + + // Resize the hash table to the given size (Rehash the contents of the current table). The arg is the number of + // hash table entries, not the number of elements we should actually contain (which will be less than this). + template<typename T, typename U, typename H, typename E> + void HashMap<T, U, H, E>::setRawCapacity(int new_size) + { + if (new_size <= 0) { + // Special case. + clear(); + return; + } + + // Force new_size to be a power of two. + new_size = nextPowerOfTwo(U32(new_size)); + + HashMap<T, U, H, E> new_hash; + new_hash.table = malloc<Entry>(new_size); + nvDebugCheck(new_hash.table != NULL); + + new_hash.entry_count = 0; + new_hash.size_mask = new_size - 1; + for (int i = 0; i < new_size; i++) + { + new_hash.entry(i).next_in_chain = -2; // mark empty + } + + // Copy stuff to new_hash + if (table != NULL) + { + for (int i = 0, n = size_mask; i <= n; i++) + { + Entry * e = &entry(i); + if (e->isEmpty() == false && e->isTombstone() == false) + { + // Insert old entry into new hash. + new_hash.add(e->key, e->value); + e->clear(); // placement delete of old element + } + } + + // Delete our old data buffer. + free(table); + } + + // Steal new_hash's data. + entry_count = new_hash.entry_count; + size_mask = new_hash.size_mask; + table = new_hash.table; + new_hash.entry_count = 0; + new_hash.size_mask = -1; + new_hash.table = NULL; + } + + // Move the enumerator to the next valid element. + template<typename T, typename U, typename H, typename E> + void HashMap<T, U, H, E>::findNext(PseudoIndex & i) const { + while (i <= size_mask) { + const Entry & e = entry(i); + if (e.isEmpty() == false && e.isTombstone() == false) { + break; + } + i++; + } + } + +} // nv namespace + +#endif // NV_CORE_HASHMAP_INL diff --git a/thirdparty/thekla_atlas/nvcore/Memory.cpp b/thirdparty/thekla_atlas/nvcore/Memory.cpp new file mode 100644 index 0000000000..302a2d84cb --- /dev/null +++ b/thirdparty/thekla_atlas/nvcore/Memory.cpp @@ -0,0 +1,153 @@ +// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> + +#include "Memory.h" +#include "Debug.h" +#include "Utils.h" + +#include <stdlib.h> + +#ifdef NV_OS_LINUX +#include <malloc.h> +#endif + +#define USE_EFENCE 0 + +#if USE_EFENCE +extern "C" void *EF_malloc(size_t size); +extern "C" void *EF_realloc(void * oldBuffer, size_t newSize); +extern "C" void EF_free(void * address); +#endif + +using namespace nv; + +#if NV_OVERRIDE_ALLOC + +void * malloc(size_t size) +{ +#if USE_EFENCE + return EF_malloc(size); +#else + return ::malloc(size); +#endif +} + +void * debug_malloc(size_t size, const char * file, int line) +{ + NV_UNUSED(file); + NV_UNUSED(line); +#if USE_EFENCE + return EF_malloc(size); +#else + return ::malloc(size); +#endif +} + +void free(void * ptr) +{ +#if USE_EFENCE + return EF_free(const_cast<void *>(ptr)); +#else + ::free(const_cast<void *>(ptr)); +#endif +} + +void * realloc(void * ptr, size_t size) +{ + nvDebugCheck(ptr != NULL || size != 0); // undefined realloc behavior. +#if USE_EFENCE + return EF_realloc(ptr, size); +#else + return ::realloc(ptr, size); +#endif +} + + +/* No need to override this unless we want line info. +void * operator new (size_t size) throw() +{ + return malloc(size); +} + +void operator delete (void *p) throw() +{ + free(p); +} + +void * operator new [] (size_t size) throw() +{ + return malloc(size); +} + +void operator delete [] (void * p) throw() +{ + free(p); +} +*/ + +#if 0 // Code from Apple: +void* operator new(std::size_t sz) throw (std::bad_alloc) +{ + void *result = std::malloc (sz == 0 ? 1 : sz); + if (result == NULL) + throw std::bad_alloc(); + gNewCounter++; + return result; +} +void operator delete(void* p) throw() +{ + if (p == NULL) + return; + std::free (p); + gDeleteCounter++; +} + +/* These are the 'nothrow' versions of the above operators. + The system version will try to call a std::new_handler if they + fail, but your overriding versions are not required to do this. */ +void* operator new(std::size_t sz, const std::nothrow_t&) throw() +{ + try { + void * result = ::operator new (sz); // calls our overridden operator new + return result; + } catch (std::bad_alloc &) { + return NULL; + } +} +void operator delete(void* p, const std::nothrow_t&) throw() +{ + ::operator delete (p); +} + +#endif // 0 + +#endif // NV_OVERRIDE_ALLOC + +void * nv::aligned_malloc(size_t size, size_t alignment) +{ + // alignment must be a power of two, multiple of sizeof(void*) + nvDebugCheck(isPowerOfTwo(alignment)); + nvDebugCheck((alignment & (sizeof(void*) - 1)) == 0); + +#if NV_OS_WIN32 || NV_OS_DURANGO + return _aligned_malloc(size, alignment); +#elif NV_OS_DARWIN && !NV_OS_IOS + void * ptr = NULL; + posix_memalign(&ptr, alignment, size); + return ptr; +#elif NV_OS_LINUX + return memalign(alignment, size); +#else // NV_OS_ORBIS || NV_OS_IOS + // @@ IC: iOS appears to be 16 byte aligned, should we check alignment and assert if we request a higher alignment factor? + return ::malloc(size); +#endif +} + +void nv::aligned_free(void * ptr) +{ +#if NV_OS_WIN32 || NV_OS_DURANGO + _aligned_free(ptr); +#else + ::free(ptr); +#endif +} + diff --git a/thirdparty/thekla_atlas/nvcore/Memory.h b/thirdparty/thekla_atlas/nvcore/Memory.h new file mode 100644 index 0000000000..1f71b60947 --- /dev/null +++ b/thirdparty/thekla_atlas/nvcore/Memory.h @@ -0,0 +1,72 @@ +// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> + +#pragma once +#ifndef NV_CORE_MEMORY_H +#define NV_CORE_MEMORY_H + +#include "nvcore.h" + +#include <stdlib.h> // malloc(), realloc() and free() +#include <string.h> // memset +//#include <stddef.h> // size_t + +//#include <new> // new and delete + +#define TRACK_MEMORY_LEAKS 0 +#if TRACK_MEMORY_LEAKS +#include <vld.h> +#endif + + +#if NV_CC_GNUC +# define NV_ALIGN_16 __attribute__ ((__aligned__ (16))) +#else +# define NV_ALIGN_16 __declspec(align(16)) +#endif + + +#define NV_OVERRIDE_ALLOC 0 + +#if NV_OVERRIDE_ALLOC + +// Custom memory allocator +extern "C" { + NVCORE_API void * malloc(size_t size); + NVCORE_API void * debug_malloc(size_t size, const char * file, int line); + NVCORE_API void free(void * ptr); + NVCORE_API void * realloc(void * ptr, size_t size); +} + +/* +#ifdef _DEBUG +#define new new(__FILE__, __LINE__) +#define malloc(i) debug_malloc(i, __FILE__, __LINE__) +#endif +*/ + +#endif + +namespace nv { + NVCORE_API void * aligned_malloc(size_t size, size_t alignment); + NVCORE_API void aligned_free(void * ); + + // C++ helpers. + template <typename T> NV_FORCEINLINE T * malloc(size_t count) { + return (T *)::malloc(sizeof(T) * count); + } + + template <typename T> NV_FORCEINLINE T * realloc(T * ptr, size_t count) { + return (T *)::realloc(ptr, sizeof(T) * count); + } + + template <typename T> NV_FORCEINLINE void free(const T * ptr) { + ::free((void *)ptr); + } + + template <typename T> NV_FORCEINLINE void zero(T & data) { + memset(&data, 0, sizeof(T)); + } + +} // nv namespace + +#endif // NV_CORE_MEMORY_H diff --git a/thirdparty/thekla_atlas/nvcore/Ptr.h b/thirdparty/thekla_atlas/nvcore/Ptr.h new file mode 100644 index 0000000000..b43039274b --- /dev/null +++ b/thirdparty/thekla_atlas/nvcore/Ptr.h @@ -0,0 +1,322 @@ +// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> + +#ifndef NV_CORE_PTR_H +#define NV_CORE_PTR_H + +#include "nvcore.h" +#include "Debug.h" + +#include "RefCounted.h" + +namespace nv +{ + class WeakProxy; + + /** Simple auto pointer template class. + * + * This is very similar to the standard auto_ptr class, but with some + * additional limitations to make its use less error prone: + * - Copy constructor and assignment operator are disabled. + * - reset method is removed. + * + * The semantics of the standard auto_ptr are not clear and change depending + * on the std implementation. For a discussion of the problems of auto_ptr read: + * http://www.awprofessional.com/content/images/020163371X/autoptrupdate\auto_ptr_update.html + */ + template <class T> + class AutoPtr + { + NV_FORBID_COPY(AutoPtr); + NV_FORBID_HEAPALLOC(); + public: + + /// Ctor. + AutoPtr(T * p = NULL) : m_ptr(p) { } + + template <class Q> + AutoPtr(Q * p) : m_ptr(static_cast<T *>(p)) { } + + /// Dtor. Deletes owned pointer. + ~AutoPtr() { + delete m_ptr; + m_ptr = NULL; + } + + /// Delete owned pointer and assign new one. + void operator=( T * p ) { + if (p != m_ptr) + { + delete m_ptr; + m_ptr = p; + } + } + + template <class Q> + void operator=( Q * p ) { + if (p != m_ptr) + { + delete m_ptr; + m_ptr = static_cast<T *>(p); + } + } + + /// Member access. + T * operator -> () const { + nvDebugCheck(m_ptr != NULL); + return m_ptr; + } + + /// Get reference. + T & operator*() const { + nvDebugCheck(m_ptr != NULL); + return *m_ptr; + } + + /// Get pointer. + T * ptr() const { return m_ptr; } + + /// Relinquish ownership of the underlying pointer and returns that pointer. + T * release() { + T * tmp = m_ptr; + m_ptr = NULL; + return tmp; + } + + /// Const pointer equal comparation. + friend bool operator == (const AutoPtr<T> & ap, const T * const p) { + return (ap.ptr() == p); + } + + /// Const pointer nequal comparation. + friend bool operator != (const AutoPtr<T> & ap, const T * const p) { + return (ap.ptr() != p); + } + + /// Const pointer equal comparation. + friend bool operator == (const T * const p, const AutoPtr<T> & ap) { + return (ap.ptr() == p); + } + + /// Const pointer nequal comparation. + friend bool operator != (const T * const p, const AutoPtr<T> & ap) { + return (ap.ptr() != p); + } + + private: + T * m_ptr; + }; + + + /// Smart pointer template class. + template <class BaseClass> + class SmartPtr { + public: + + // BaseClass must implement addRef() and release(). + typedef SmartPtr<BaseClass> ThisType; + + /// Default ctor. + SmartPtr() : m_ptr(NULL) + { + } + + /// Other type assignment. + template <class OtherBase> + SmartPtr( const SmartPtr<OtherBase> & tc ) + { + m_ptr = static_cast<BaseClass *>( tc.ptr() ); + if (m_ptr) { + m_ptr->addRef(); + } + } + + /// Copy ctor. + SmartPtr( const ThisType & bc ) + { + m_ptr = bc.ptr(); + if (m_ptr) { + m_ptr->addRef(); + } + } + + /// Copy cast ctor. SmartPtr(NULL) is valid. + explicit SmartPtr( BaseClass * bc ) + { + m_ptr = bc; + if (m_ptr) { + m_ptr->addRef(); + } + } + + /// Dtor. + ~SmartPtr() + { + set(NULL); + } + + + /// -> operator. + BaseClass * operator -> () const + { + nvCheck( m_ptr != NULL ); + return m_ptr; + } + + /// * operator. + BaseClass & operator*() const + { + nvCheck( m_ptr != NULL ); + return *m_ptr; + } + + /// Get pointer. + BaseClass * ptr() const + { + return m_ptr; + } + + /// Other type assignment. + template <class OtherBase> + void operator = ( const SmartPtr<OtherBase> & tc ) + { + set( static_cast<BaseClass *>(tc.ptr()) ); + } + + /// This type assignment. + void operator = ( const ThisType & bc ) + { + set( bc.ptr() ); + } + + /// Pointer assignment. + void operator = ( BaseClass * bc ) + { + set( bc ); + } + + + /// Other type equal comparation. + template <class OtherBase> + bool operator == ( const SmartPtr<OtherBase> & other ) const + { + return m_ptr == other.ptr(); + } + + /// This type equal comparation. + bool operator == ( const ThisType & bc ) const + { + return m_ptr == bc.ptr(); + } + + /// Const pointer equal comparation. + bool operator == ( const BaseClass * const bc ) const + { + return m_ptr == bc; + } + + /// Other type not equal comparation. + template <class OtherBase> + bool operator != ( const SmartPtr<OtherBase> & other ) const + { + return m_ptr != other.ptr(); + } + + /// Other type not equal comparation. + bool operator != ( const ThisType & bc ) const + { + return m_ptr != bc.ptr(); + } + + /// Const pointer not equal comparation. + bool operator != (const BaseClass * const bc) const + { + return m_ptr != bc; + } + + /// This type lower than comparation. + bool operator < (const ThisType & p) const + { + return m_ptr < p.ptr(); + } + + bool isValid() const { + return isValidPtr(m_ptr); + } + + private: + + // Set this pointer. + void set( BaseClass * p ) + { + if (p) p->addRef(); + if (m_ptr) m_ptr->release(); + m_ptr = p; + } + + private: + + BaseClass * m_ptr; + + }; + + + /// Smart pointer template class. + template <class T> + class WeakPtr { + public: + + WeakPtr() {} + + WeakPtr(T * p) { operator=(p); } + WeakPtr(const SmartPtr<T> & p) { operator=(p.ptr()); } + + // Default constructor and assignment from weak_ptr<T> are OK. + + void operator=(T * p) + { + if (p) { + m_proxy = p->getWeakProxy(); + nvDebugCheck(m_proxy != NULL); + nvDebugCheck(m_proxy->ptr() == p); + } + else { + m_proxy = NULL; + } + } + + void operator=(const SmartPtr<T> & ptr) { operator=(ptr.ptr()); } + + bool operator==(const SmartPtr<T> & p) const { return ptr() == p.ptr(); } + bool operator!=(const SmartPtr<T> & p) const { return ptr() != p.ptr(); } + + bool operator==(const WeakPtr<T> & p) const { return ptr() == p.ptr(); } + bool operator!=(const WeakPtr<T> & p) const { return ptr() != p.ptr(); } + + bool operator==(T * p) const { return ptr() == p; } + bool operator!=(T * p) const { return ptr() != p; } + + T * operator->() const + { + T * p = ptr(); + nvDebugCheck(p != NULL); + return p; + } + + T * ptr() const + { + if (m_proxy != NULL) { + return static_cast<T *>(m_proxy->ptr()); + } + return NULL; + } + + private: + + mutable SmartPtr<WeakProxy> m_proxy; + + }; + + +} // nv namespace + +#endif // NV_CORE_PTR_H diff --git a/thirdparty/thekla_atlas/nvcore/RadixSort.cpp b/thirdparty/thekla_atlas/nvcore/RadixSort.cpp new file mode 100644 index 0000000000..3f44620c99 --- /dev/null +++ b/thirdparty/thekla_atlas/nvcore/RadixSort.cpp @@ -0,0 +1,285 @@ +// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> + +#include "RadixSort.h" + +#include "Utils.h" + +#include <string.h> // memset + +using namespace nv; + +static inline void FloatFlip(uint32 & f) { + //uint32 mask = -int32(f >> 31) | 0x80000000; // Michael Herf. + int32 mask = (int32(f) >> 31) | 0x80000000; // Warren Hunt, Manchor Ko. + f ^= mask; +} + +static inline void IFloatFlip(uint32 & f) { + uint32 mask = ((f >> 31) - 1) | 0x80000000; // Michael Herf. + //uint32 mask = (int32(f ^ 0x80000000) >> 31) | 0x80000000; // Warren Hunt, Manchor Ko. @@ Correct, but fails in release on gcc-4.2.1 + f ^= mask; +} + + +template<typename T> +void createHistograms(const T * buffer, uint count, uint * histogram) +{ + const uint bucketCount = sizeof(T); // (8 * sizeof(T)) / log2(radix) + + // Init bucket pointers. + uint * h[bucketCount]; + for (uint i = 0; i < bucketCount; i++) { +#if NV_BIG_ENDIAN + h[sizeof(T)-1-i] = histogram + 256 * i; +#else + h[i] = histogram + 256 * i; +#endif + } + + // Clear histograms. + memset(histogram, 0, 256 * bucketCount * sizeof(uint)); + + // @@ Add support for signed integers. + + // Build histograms. + const uint8 * p = (const uint8 *)buffer; // @@ Does this break aliasing rules? + const uint8 * pe = p + count * sizeof(T); + + while (p != pe) { + h[0][*p++]++, h[1][*p++]++, h[2][*p++]++, h[3][*p++]++; + if (bucketCount == 8) h[4][*p++]++, h[5][*p++]++, h[6][*p++]++, h[7][*p++]++; + } +} + +/* +template <> +void createHistograms<float>(const float * buffer, uint count, uint * histogram) +{ + // Init bucket pointers. + uint32 * h[4]; + for (uint i = 0; i < 4; i++) { +#if NV_BIG_ENDIAN + h[3-i] = histogram + 256 * i; +#else + h[i] = histogram + 256 * i; +#endif + } + + // Clear histograms. + memset(histogram, 0, 256 * 4 * sizeof(uint32)); + + // Build histograms. + for (uint i = 0; i < count; i++) { + uint32 fi = FloatFlip(buffer[i]); + + h[0][fi & 0xFF]++; + h[1][(fi >> 8) & 0xFF]++; + h[2][(fi >> 16) & 0xFF]++; + h[3][fi >> 24]++; + } +} +*/ + +RadixSort::RadixSort() : m_size(0), m_ranks(NULL), m_ranks2(NULL), m_validRanks(false) +{ +} + +RadixSort::RadixSort(uint reserve_count) : m_size(0), m_ranks(NULL), m_ranks2(NULL), m_validRanks(false) +{ + checkResize(reserve_count); +} + +RadixSort::~RadixSort() +{ + // Release everything + free(m_ranks2); + free(m_ranks); +} + + +void RadixSort::resize(uint count) +{ + m_ranks2 = realloc<uint>(m_ranks2, count); + m_ranks = realloc<uint>(m_ranks, count); +} + +inline void RadixSort::checkResize(uint count) +{ + if (count != m_size) + { + if (count > m_size) resize(count); + m_size = count; + m_validRanks = false; + } +} + +template <typename T> inline void RadixSort::insertionSort(const T * input, uint count) +{ + if (!m_validRanks) { + /*for (uint i = 0; i < count; i++) { + m_ranks[i] = i; + }*/ + + m_ranks[0] = 0; + for (uint i = 1; i != count; ++i) + { + int rank = m_ranks[i] = i; + + uint j = i; + while (j != 0 && input[rank] < input[m_ranks[j-1]]) + { + m_ranks[j] = m_ranks[j-1]; + --j; + } + if (i != j) + { + m_ranks[j] = rank; + } + } + + m_validRanks = true; + } + else { + for (uint i = 1; i != count; ++i) + { + int rank = m_ranks[i]; + + uint j = i; + while (j != 0 && input[rank] < input[m_ranks[j-1]]) + { + m_ranks[j] = m_ranks[j-1]; + --j; + } + if (i != j) + { + m_ranks[j] = rank; + } + } + } +} + +template <typename T> inline void RadixSort::radixSort(const T * input, uint count) +{ + const uint P = sizeof(T); // pass count + + // Allocate histograms & offsets on the stack + uint histogram[256 * P]; + uint * link[256]; + + createHistograms(input, count, histogram); + + // Radix sort, j is the pass number (0=LSB, P=MSB) + for (uint j = 0; j < P; j++) + { + // Pointer to this bucket. + const uint * h = &histogram[j * 256]; + + const uint8 * inputBytes = (const uint8*)input; // @@ Is this aliasing legal? + +#if NV_BIG_ENDIAN + inputBytes += P - 1 - j; +#else + inputBytes += j; +#endif + + if (h[inputBytes[0]] == count) { + // Skip this pass, all values are the same. + continue; + } + + // Create offsets + link[0] = m_ranks2; + for (uint i = 1; i < 256; i++) link[i] = link[i-1] + h[i-1]; + + // Perform Radix Sort + if (!m_validRanks) + { + for (uint i = 0; i < count; i++) + { + *link[inputBytes[i*P]]++ = i; + } + m_validRanks = true; + } + else + { + for (uint i = 0; i < count; i++) + { + const uint idx = m_ranks[i]; + *link[inputBytes[idx*P]]++ = idx; + } + } + + // Swap pointers for next pass. Valid indices - the most recent ones - are in m_ranks after the swap. + swap(m_ranks, m_ranks2); + } + + // All values were equal, generate linear ranks. + if (!m_validRanks) + { + for (uint i = 0; i < count; i++) + { + m_ranks[i] = i; + } + m_validRanks = true; + } +} + + +RadixSort & RadixSort::sort(const uint32 * input, uint count) +{ + if (input == NULL || count == 0) return *this; + + // Resize lists if needed + checkResize(count); + + if (count < 32) { + insertionSort(input, count); + } + else { + radixSort<uint32>(input, count); + } + return *this; +} + + +RadixSort & RadixSort::sort(const uint64 * input, uint count) +{ + if (input == NULL || count == 0) return *this; + + // Resize lists if needed + checkResize(count); + + if (count < 64) { + insertionSort(input, count); + } + else { + radixSort(input, count); + } + return *this; +} + +RadixSort& RadixSort::sort(const float * input, uint count) +{ + if (input == NULL || count == 0) return *this; + + // Resize lists if needed + checkResize(count); + + if (count < 32) { + insertionSort(input, count); + } + else { + // @@ Avoid touching the input multiple times. + for (uint i = 0; i < count; i++) { + FloatFlip((uint32 &)input[i]); + } + + radixSort<uint32>((const uint32 *)input, count); + + for (uint i = 0; i < count; i++) { + IFloatFlip((uint32 &)input[i]); + } + } + + return *this; +} diff --git a/thirdparty/thekla_atlas/nvcore/RadixSort.h b/thirdparty/thekla_atlas/nvcore/RadixSort.h new file mode 100644 index 0000000000..82325ebb24 --- /dev/null +++ b/thirdparty/thekla_atlas/nvcore/RadixSort.h @@ -0,0 +1,75 @@ +#pragma once +#ifndef NV_CORE_RADIXSORT_H +#define NV_CORE_RADIXSORT_H + +// Based on Pierre Terdiman's and Michael Herf's source code. +// http://www.codercorner.com/RadixSortRevisited.htm +// http://www.stereopsis.com/radix.html + +#include "nvcore.h" +#include "Array.h" + +namespace nv +{ + + class NVCORE_CLASS RadixSort + { + NV_FORBID_COPY(RadixSort); + public: + // Constructor/Destructor + RadixSort(); + RadixSort(uint reserve_count); + ~RadixSort(); + + // Invalidate ranks. + RadixSort & reset() { m_validRanks = false; return *this; } + + // Sorting methods. + RadixSort & sort(const uint32 * input, uint count); + RadixSort & sort(const uint64 * input, uint count); + RadixSort & sort(const float * input, uint count); + + // Helpers. + RadixSort & sort(const Array<uint32> & input); + RadixSort & sort(const Array<uint64> & input); + RadixSort & sort(const Array<float> & input); + + // Access to results. m_ranks is a list of indices in sorted order, i.e. in the order you may further process your data + inline const uint * ranks() const { nvDebugCheck(m_validRanks); return m_ranks; } + inline uint * ranks() { nvDebugCheck(m_validRanks); return m_ranks; } + inline uint rank(uint i) const { nvDebugCheck(m_validRanks); return m_ranks[i]; } + + // query whether the sort has been performed + inline bool valid() const { return m_validRanks; } + + private: + uint m_size; + uint * m_ranks; + uint * m_ranks2; + bool m_validRanks; + + // Internal methods + template <typename T> void insertionSort(const T * input, uint count); + template <typename T> void radixSort(const T * input, uint count); + + void checkResize(uint nb); + void resize(uint nb); + }; + + inline RadixSort & RadixSort::sort(const Array<uint32> & input) { + return sort(input.buffer(), input.count()); + } + + inline RadixSort & RadixSort::sort(const Array<uint64> & input) { + return sort(input.buffer(), input.count()); + } + + inline RadixSort & RadixSort::sort(const Array<float> & input) { + return sort(input.buffer(), input.count()); + } + +} // nv namespace + + + +#endif // NV_CORE_RADIXSORT_H diff --git a/thirdparty/thekla_atlas/nvcore/RefCounted.h b/thirdparty/thekla_atlas/nvcore/RefCounted.h new file mode 100644 index 0000000000..b8d68edee3 --- /dev/null +++ b/thirdparty/thekla_atlas/nvcore/RefCounted.h @@ -0,0 +1,149 @@ +// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> + +#ifndef NV_CORE_REFCOUNTED_H +#define NV_CORE_REFCOUNTED_H + +#include "nvcore.h" +#include "Debug.h" + +#define NV_DECLARE_PTR(Class) \ + template <class T> class SmartPtr; \ + typedef SmartPtr<class Class> Class ## Ptr; \ + typedef SmartPtr<const class Class> Class ## ConstPtr + + +namespace nv +{ + /// Weak proxy. + class WeakProxy + { + NV_FORBID_COPY(WeakProxy); + public: + /// Ctor. + WeakProxy(void * ptr) : m_count(0), m_ptr(ptr) { } + + /// Dtor. + ~WeakProxy() + { + nvCheck( m_count == 0 ); + } + + /// Increase reference count. + uint addRef() const + { + m_count++; + return m_count; + } + + /// Decrease reference count and remove when 0. + uint release() const + { + nvCheck( m_count > 0 ); + + m_count--; + if( m_count == 0 ) { + delete this; + return 0; + } + return m_count; + } + + /// WeakPtr's call this to determine if their pointer is valid or not. + bool isAlive() const { + return m_ptr != NULL; + } + + /// Only the actual object should call this. + void notifyObjectDied() { + m_ptr = NULL; + } + + /// Return proxy pointer. + void * ptr() const { + return m_ptr; + } + + private: + mutable int m_count; + void * m_ptr; + }; + + + /// Reference counted base class to be used with SmartPtr and WeakPtr. + class RefCounted + { + NV_FORBID_COPY(RefCounted); + public: + + /// Ctor. + RefCounted() : m_count(0), m_weak_proxy(NULL) + { + } + + /// Virtual dtor. + virtual ~RefCounted() + { + nvCheck( m_count == 0 ); + releaseWeakProxy(); + } + + + /// Increase reference count. + uint addRef() const + { + m_count++; + return m_count; + } + + + /// Decrease reference count and remove when 0. + uint release() const + { + nvCheck( m_count > 0 ); + + m_count--; + if( m_count == 0 ) { + delete this; + return 0; + } + return m_count; + } + + /// Get weak proxy. + WeakProxy * getWeakProxy() const + { + if (m_weak_proxy == NULL) { + m_weak_proxy = new WeakProxy((void *)this); + m_weak_proxy->addRef(); + } + return m_weak_proxy; + } + + /// Release the weak proxy. + void releaseWeakProxy() const + { + if (m_weak_proxy != NULL) { + m_weak_proxy->notifyObjectDied(); + m_weak_proxy->release(); + m_weak_proxy = NULL; + } + } + + /// Get reference count. + int refCount() const + { + return m_count; + } + + + private: + + mutable int m_count; + mutable WeakProxy * m_weak_proxy; + + }; + +} // nv namespace + + +#endif // NV_CORE_REFCOUNTED_H diff --git a/thirdparty/thekla_atlas/nvcore/StdStream.h b/thirdparty/thekla_atlas/nvcore/StdStream.h new file mode 100644 index 0000000000..f65d6dab59 --- /dev/null +++ b/thirdparty/thekla_atlas/nvcore/StdStream.h @@ -0,0 +1,474 @@ +// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> + +//#pragma once +//#ifndef NV_CORE_STDSTREAM_H +//#define NV_CORE_STDSTREAM_H + +#include "nvcore.h" +#include "Stream.h" +#include "Array.h" + +#include <stdio.h> // fopen +#include <string.h> // memcpy + +namespace nv +{ + + // Portable version of fopen. + inline FILE * fileOpen(const char * fileName, const char * mode) + { + nvCheck(fileName != NULL); +#if NV_CC_MSVC && _MSC_VER >= 1400 + FILE * fp; + if (fopen_s(&fp, fileName, mode) == 0) { + return fp; + } + return NULL; +#else + return fopen(fileName, mode); +#endif + } + + + /// Base stdio stream. + class NVCORE_CLASS StdStream : public Stream + { + NV_FORBID_COPY(StdStream); + public: + + /// Ctor. + StdStream( FILE * fp, bool autoclose ) : m_fp(fp), m_autoclose(autoclose) { } + + /// Dtor. + virtual ~StdStream() + { + if( m_fp != NULL && m_autoclose ) { +#if NV_OS_WIN32 + _fclose_nolock( m_fp ); +#else + fclose( m_fp ); +#endif + } + } + + + /** @name Stream implementation. */ + //@{ + virtual void seek( uint pos ) + { + nvDebugCheck(m_fp != NULL); + nvDebugCheck(pos <= size()); +#if NV_OS_WIN32 + _fseek_nolock(m_fp, pos, SEEK_SET); +#else + fseek(m_fp, pos, SEEK_SET); +#endif + } + + virtual uint tell() const + { + nvDebugCheck(m_fp != NULL); +#if NV_OS_WIN32 + return _ftell_nolock(m_fp); +#else + return (uint)ftell(m_fp); +#endif + } + + virtual uint size() const + { + nvDebugCheck(m_fp != NULL); +#if NV_OS_WIN32 + uint pos = _ftell_nolock(m_fp); + _fseek_nolock(m_fp, 0, SEEK_END); + uint end = _ftell_nolock(m_fp); + _fseek_nolock(m_fp, pos, SEEK_SET); +#else + uint pos = (uint)ftell(m_fp); + fseek(m_fp, 0, SEEK_END); + uint end = (uint)ftell(m_fp); + fseek(m_fp, pos, SEEK_SET); +#endif + return end; + } + + virtual bool isError() const + { + return m_fp == NULL || ferror( m_fp ) != 0; + } + + virtual void clearError() + { + nvDebugCheck(m_fp != NULL); + clearerr(m_fp); + } + + // @@ The original implementation uses feof, which only returns true when we attempt to read *past* the end of the stream. + // That is, if we read the last byte of a file, then isAtEnd would still return false, even though the stream pointer is at the file end. This is not the intent and was inconsistent with the implementation of the MemoryStream, a better + // implementation uses use ftell and fseek to determine our location within the file. + virtual bool isAtEnd() const + { + if (m_fp == NULL) return true; + //nvDebugCheck(m_fp != NULL); + //return feof( m_fp ) != 0; +#if NV_OS_WIN32 + uint pos = _ftell_nolock(m_fp); + _fseek_nolock(m_fp, 0, SEEK_END); + uint end = _ftell_nolock(m_fp); + _fseek_nolock(m_fp, pos, SEEK_SET); +#else + uint pos = (uint)ftell(m_fp); + fseek(m_fp, 0, SEEK_END); + uint end = (uint)ftell(m_fp); + fseek(m_fp, pos, SEEK_SET); +#endif + return pos == end; + } + + /// Always true. + virtual bool isSeekable() const { return true; } + //@} + + protected: + + FILE * m_fp; + bool m_autoclose; + + }; + + + /// Standard output stream. + class NVCORE_CLASS StdOutputStream : public StdStream + { + NV_FORBID_COPY(StdOutputStream); + public: + + /// Construct stream by file name. + StdOutputStream( const char * name ) : StdStream(fileOpen(name, "wb"), /*autoclose=*/true) { } + + /// Construct stream by file handle. + StdOutputStream( FILE * fp, bool autoclose ) : StdStream(fp, autoclose) + { + } + + /** @name Stream implementation. */ + //@{ + /// Write data. + virtual uint serialize( void * data, uint len ) + { + nvDebugCheck(data != NULL); + nvDebugCheck(m_fp != NULL); +#if NV_OS_WIN32 + return (uint)_fwrite_nolock(data, 1, len, m_fp); +#elif NV_OS_LINUX + return (uint)fwrite_unlocked(data, 1, len, m_fp); +#elif NV_OS_DARWIN + // @@ No error checking, always returns len. + for (uint i = 0; i < len; i++) { + putc_unlocked(((char *)data)[i], m_fp); + } + return len; +#else + return (uint)fwrite(data, 1, len, m_fp); +#endif + } + + virtual bool isLoading() const + { + return false; + } + + virtual bool isSaving() const + { + return true; + } + //@} + + }; + + + /// Standard input stream. + class NVCORE_CLASS StdInputStream : public StdStream + { + NV_FORBID_COPY(StdInputStream); + public: + + /// Construct stream by file name. + StdInputStream( const char * name ) : StdStream(fileOpen(name, "rb"), /*autoclose=*/true) { } + + /// Construct stream by file handle. + StdInputStream( FILE * fp, bool autoclose=true ) : StdStream(fp, autoclose) + { + } + + /** @name Stream implementation. */ + //@{ + /// Read data. + virtual uint serialize( void * data, uint len ) + { + nvDebugCheck(data != NULL); + nvDebugCheck(m_fp != NULL); +#if NV_OS_WIN32 + return (uint)_fread_nolock(data, 1, len, m_fp); +#elif NV_OS_LINUX + return (uint)fread_unlocked(data, 1, len, m_fp); +#elif NV_OS_DARWIN + // This is rather lame. Not sure if it's faster than the locked version. + for (uint i = 0; i < len; i++) { + ((char *)data)[i] = getc_unlocked(m_fp); + if (feof_unlocked(m_fp) != 0) { + return i; + } + } + return len; +#else + return (uint)fread(data, 1, len, m_fp); +#endif + + } + + virtual bool isLoading() const + { + return true; + } + + virtual bool isSaving() const + { + return false; + } + //@} + }; + + + + /// Memory input stream. + class NVCORE_CLASS MemoryInputStream : public Stream + { + NV_FORBID_COPY(MemoryInputStream); + public: + + /// Ctor. + MemoryInputStream( const uint8 * mem, uint size ) : m_mem(mem), m_ptr(mem), m_size(size) { } + + /** @name Stream implementation. */ + //@{ + /// Read data. + virtual uint serialize( void * data, uint len ) + { + nvDebugCheck(data != NULL); + nvDebugCheck(!isError()); + + uint left = m_size - tell(); + if (len > left) len = left; + + memcpy( data, m_ptr, len ); + m_ptr += len; + + return len; + } + + virtual void seek( uint pos ) + { + nvDebugCheck(!isError()); + m_ptr = m_mem + pos; + nvDebugCheck(!isError()); + } + + virtual uint tell() const + { + nvDebugCheck(m_ptr >= m_mem); + return uint(m_ptr - m_mem); + } + + virtual uint size() const + { + return m_size; + } + + virtual bool isError() const + { + return m_mem == NULL || m_ptr > m_mem + m_size || m_ptr < m_mem; + } + + virtual void clearError() + { + // Nothing to do. + } + + virtual bool isAtEnd() const + { + return m_ptr == m_mem + m_size; + } + + /// Always true. + virtual bool isSeekable() const + { + return true; + } + + virtual bool isLoading() const + { + return true; + } + + virtual bool isSaving() const + { + return false; + } + //@} + + const uint8 * ptr() const { return m_ptr; } + + + private: + + const uint8 * m_mem; + const uint8 * m_ptr; + uint m_size; + + }; + + + /// Buffer output stream. + class NVCORE_CLASS BufferOutputStream : public Stream + { + NV_FORBID_COPY(BufferOutputStream); + public: + + BufferOutputStream(Array<uint8> & buffer) : m_buffer(buffer) { } + + virtual uint serialize( void * data, uint len ) + { + nvDebugCheck(data != NULL); + m_buffer.append((uint8 *)data, len); + return len; + } + + virtual void seek( uint /*pos*/ ) { /*Not implemented*/ } + virtual uint tell() const { return m_buffer.size(); } + virtual uint size() const { return m_buffer.size(); } + + virtual bool isError() const { return false; } + virtual void clearError() {} + + virtual bool isAtEnd() const { return true; } + virtual bool isSeekable() const { return false; } + virtual bool isLoading() const { return false; } + virtual bool isSaving() const { return true; } + + private: + Array<uint8> & m_buffer; + }; + + + /// Protected input stream. + class NVCORE_CLASS ProtectedStream : public Stream + { + NV_FORBID_COPY(ProtectedStream); + public: + + /// Ctor. + ProtectedStream( Stream & s ) : m_s(&s), m_autodelete(false) + { + } + + /// Ctor. + ProtectedStream( Stream * s, bool autodelete = true ) : + m_s(s), m_autodelete(autodelete) + { + nvDebugCheck(m_s != NULL); + } + + /// Dtor. + virtual ~ProtectedStream() + { + if( m_autodelete ) { + delete m_s; + } + } + + /** @name Stream implementation. */ + //@{ + /// Read data. + virtual uint serialize( void * data, uint len ) + { + nvDebugCheck(data != NULL); + len = m_s->serialize( data, len ); + + if( m_s->isError() ) { +#if NV_OS_ORBIS + //SBtodoORBIS disabled (no exceptions) +#else + throw; +#endif + } + + return len; + } + + virtual void seek( uint pos ) + { + m_s->seek( pos ); + + if( m_s->isError() ) { +#if NV_OS_ORBIS + //SBtodoORBIS disabled (no exceptions) +#else + throw; +#endif + } + } + + virtual uint tell() const + { + return m_s->tell(); + } + + virtual uint size() const + { + return m_s->size(); + } + + virtual bool isError() const + { + return m_s->isError(); + } + + virtual void clearError() + { + m_s->clearError(); + } + + virtual bool isAtEnd() const + { + return m_s->isAtEnd(); + } + + virtual bool isSeekable() const + { + return m_s->isSeekable(); + } + + virtual bool isLoading() const + { + return m_s->isLoading(); + } + + virtual bool isSaving() const + { + return m_s->isSaving(); + } + //@} + + + private: + + Stream * const m_s; + bool const m_autodelete; + + }; + +} // nv namespace + + +//#endif // NV_CORE_STDSTREAM_H diff --git a/thirdparty/thekla_atlas/nvcore/StrLib.cpp b/thirdparty/thekla_atlas/nvcore/StrLib.cpp new file mode 100644 index 0000000000..7ec6c70136 --- /dev/null +++ b/thirdparty/thekla_atlas/nvcore/StrLib.cpp @@ -0,0 +1,796 @@ +// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> + +#include "StrLib.h" + +#include "Memory.h" +#include "Utils.h" // swap + +#include <math.h> // log +#include <stdio.h> // vsnprintf +#include <string.h> // strlen, strcmp, etc. + +#if NV_CC_MSVC +#include <stdarg.h> // vsnprintf +#endif + +using namespace nv; + +namespace +{ + static char * strAlloc(uint size) + { + return malloc<char>(size); + } + + static char * strReAlloc(char * str, uint size) + { + return realloc<char>(str, size); + } + + static void strFree(const char * str) + { + return free<char>(str); + } + + /*static char * strDup( const char * str ) + { + nvDebugCheck( str != NULL ); + uint len = uint(strlen( str ) + 1); + char * dup = strAlloc( len ); + memcpy( dup, str, len ); + return dup; + }*/ + + // helper function for integer to string conversion. + static char * i2a( uint i, char *a, uint r ) + { + if( i / r > 0 ) { + a = i2a( i / r, a, r ); + } + *a = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[i % r]; + return a + 1; + } + + // Locale independent functions. + static inline char toUpper( char c ) { + return (c<'a' || c>'z') ? (c) : (c+'A'-'a'); + } + static inline char toLower( char c ) { + return (c<'A' || c>'Z') ? (c) : (c+'a'-'A'); + } + static inline bool isAlpha( char c ) { + return (c>='a' && c<='z') || (c>='A' && c<='Z'); + } + static inline bool isDigit( char c ) { + return c>='0' && c<='9'; + } + static inline bool isAlnum( char c ) { + return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9'); + } + +} + +uint nv::strLen(const char * str) +{ + nvDebugCheck(str != NULL); + return U32(strlen(str)); +} + +int nv::strDiff(const char * s1, const char * s2) +{ + nvDebugCheck(s1 != NULL); + nvDebugCheck(s2 != NULL); + return strcmp(s1, s2); +} + +int nv::strCaseDiff(const char * s1, const char * s2) +{ + nvDebugCheck(s1 != NULL); + nvDebugCheck(s1 != NULL); +#if NV_CC_MSVC + return _stricmp(s1, s2); +#else + return strcasecmp(s1, s2); +#endif +} + +bool nv::strEqual(const char * s1, const char * s2) +{ + if (s1 == s2) return true; + if (s1 == NULL || s2 == NULL) return false; + return strcmp(s1, s2) == 0; +} + +bool nv::strCaseEqual(const char * s1, const char * s2) +{ + if (s1 == s2) return true; + if (s1 == NULL || s2 == NULL) return false; + return strCaseDiff(s1, s2) == 0; +} + +bool nv::strBeginsWith(const char * str, const char * prefix) +{ + //return strstr(str, prefix) == dst; + return strncmp(str, prefix, strlen(prefix)) == 0; +} + +bool nv::strEndsWith(const char * str, const char * suffix) +{ + uint ml = strLen(str); + uint sl = strLen(suffix); + if (ml < sl) return false; + return strncmp(str + ml - sl, suffix, sl) == 0; +} + +// @@ Add asserts to detect overlap between dst and src? +void nv::strCpy(char * dst, uint size, const char * src) +{ + nvDebugCheck(dst != NULL); + nvDebugCheck(src != NULL); +#if NV_CC_MSVC && _MSC_VER >= 1400 + strcpy_s(dst, size, src); +#else + NV_UNUSED(size); + strcpy(dst, src); +#endif +} + +void nv::strCpy(char * dst, uint size, const char * src, uint len) +{ + nvDebugCheck(dst != NULL); + nvDebugCheck(src != NULL); +#if NV_CC_MSVC && _MSC_VER >= 1400 + strncpy_s(dst, size, src, len); +#else + int n = min(len+1, size); + strncpy(dst, src, n); + dst[n-1] = '\0'; +#endif +} + +void nv::strCat(char * dst, uint size, const char * src) +{ + nvDebugCheck(dst != NULL); + nvDebugCheck(src != NULL); +#if NV_CC_MSVC && _MSC_VER >= 1400 + strcat_s(dst, size, src); +#else + NV_UNUSED(size); + strcat(dst, src); +#endif +} + +NVCORE_API const char * nv::strSkipWhiteSpace(const char * str) +{ + nvDebugCheck(str != NULL); + while (*str == ' ') str++; + return str; +} + +NVCORE_API char * nv::strSkipWhiteSpace(char * str) +{ + nvDebugCheck(str != NULL); + while (*str == ' ') str++; + return str; +} + + +/** Pattern matching routine. I don't remember where did I get this. */ +bool nv::strMatch(const char * str, const char * pat) +{ + nvDebugCheck(str != NULL); + nvDebugCheck(pat != NULL); + + char c2; + + while (true) { + if (*pat==0) { + if (*str==0) return true; + else return false; + } + if ((*str==0) && (*pat!='*')) return false; + if (*pat=='*') { + pat++; + if (*pat==0) return true; + while (true) { + if (strMatch(str, pat)) return true; + if (*str==0) return false; + str++; + } + } + if (*pat=='?') goto match; + if (*pat=='[') { + pat++; + while (true) { + if ((*pat==']') || (*pat==0)) return false; + if (*pat==*str) break; + if (pat[1] == '-') { + c2 = pat[2]; + if (c2==0) return false; + if ((*pat<=*str) && (c2>=*str)) break; + if ((*pat>=*str) && (c2<=*str)) break; + pat+=2; + } + pat++; + } + while (*pat!=']') { + if (*pat==0) { + pat--; + break; + } + pat++; + } + goto match; + } + + if (*pat == NV_PATH_SEPARATOR) { + pat++; + if (*pat==0) return false; + } + if (*pat!=*str) return false; + +match: + pat++; + str++; + } +} + +bool nv::isNumber(const char * str) { + while(*str != '\0') { + if (!isDigit(*str)) return false; + str++; + } + return true; +} + + +/** Empty string. */ +StringBuilder::StringBuilder() : m_size(0), m_str(NULL) +{ +} + +/** Preallocate space. */ +StringBuilder::StringBuilder( uint size_hint ) : m_size(size_hint) +{ + nvDebugCheck(m_size > 0); + m_str = strAlloc(m_size); + *m_str = '\0'; +} + +/** Copy ctor. */ +StringBuilder::StringBuilder( const StringBuilder & s ) : m_size(0), m_str(NULL) +{ + copy(s); +} + +/** Copy string. */ +StringBuilder::StringBuilder(const char * s) : m_size(0), m_str(NULL) +{ + if (s != NULL) { + copy(s); + } +} + +/** Copy string. */ +StringBuilder::StringBuilder(const char * s, uint len) : m_size(0), m_str(NULL) +{ + copy(s, len); +} + +/** Delete the string. */ +StringBuilder::~StringBuilder() +{ + strFree(m_str); +} + + +/** Format a string safely. */ +StringBuilder & StringBuilder::format( const char * fmt, ... ) +{ + nvDebugCheck(fmt != NULL); + va_list arg; + va_start( arg, fmt ); + + formatList( fmt, arg ); + + va_end( arg ); + + return *this; +} + + +/** Format a string safely. */ +StringBuilder & StringBuilder::formatList( const char * fmt, va_list arg ) +{ + nvDebugCheck(fmt != NULL); + + if (m_size == 0) { + m_size = 64; + m_str = strAlloc( m_size ); + } + + va_list tmp; + va_copy(tmp, arg); +#if NV_CC_MSVC && _MSC_VER >= 1400 + int n = vsnprintf_s(m_str, m_size, _TRUNCATE, fmt, tmp); +#else + int n = vsnprintf(m_str, m_size, fmt, tmp); +#endif + va_end(tmp); + + while( n < 0 || n >= int(m_size) ) { + if( n > -1 ) { + m_size = n + 1; + } + else { + m_size *= 2; + } + + m_str = strReAlloc(m_str, m_size); + + va_copy(tmp, arg); +#if NV_CC_MSVC && _MSC_VER >= 1400 + n = vsnprintf_s(m_str, m_size, _TRUNCATE, fmt, tmp); +#else + n = vsnprintf(m_str, m_size, fmt, tmp); +#endif + va_end(tmp); + } + + nvDebugCheck(n < int(m_size)); + + // Make sure it's null terminated. + nvDebugCheck(m_str[n] == '\0'); + //str[n] = '\0'; + + return *this; +} + + +// Append a character. +StringBuilder & StringBuilder::append( char c ) +{ + return append(&c, 1); +} + +// Append a string. +StringBuilder & StringBuilder::append( const char * s ) +{ + return append(s, U32(strlen( s ))); +} + +// Append a string. +StringBuilder & StringBuilder::append(const char * s, uint len) +{ + nvDebugCheck(s != NULL); + + uint offset = length(); + const uint size = offset + len + 1; + reserve(size); + strCpy(m_str + offset, len + 1, s, len); + + return *this; +} + +StringBuilder & StringBuilder::append(const StringBuilder & str) +{ + return append(str.m_str, str.length()); +} + + +/** Append a formatted string. */ +StringBuilder & StringBuilder::appendFormat( const char * fmt, ... ) +{ + nvDebugCheck( fmt != NULL ); + + va_list arg; + va_start( arg, fmt ); + + appendFormatList( fmt, arg ); + + va_end( arg ); + + return *this; +} + + +/** Append a formatted string. */ +StringBuilder & StringBuilder::appendFormatList( const char * fmt, va_list arg ) +{ + nvDebugCheck( fmt != NULL ); + + va_list tmp; + va_copy(tmp, arg); + + if (m_size == 0) { + formatList(fmt, arg); + } + else { + StringBuilder tmp_str; + tmp_str.formatList( fmt, tmp ); + append( tmp_str.str() ); + } + + va_end(tmp); + + return *this; +} + +// Append n spaces. +StringBuilder & StringBuilder::appendSpace(uint n) +{ + if (m_str == NULL) { + m_size = n + 1; + m_str = strAlloc(m_size); + memset(m_str, ' ', m_size); + m_str[n] = '\0'; + } + else { + const uint len = strLen(m_str); + if (m_size < len + n + 1) { + m_size = len + n + 1; + m_str = strReAlloc(m_str, m_size); + } + memset(m_str + len, ' ', n); + m_str[len+n] = '\0'; + } + + return *this; +} + + +/** Convert number to string in the given base. */ +StringBuilder & StringBuilder::number( int i, int base ) +{ + nvCheck( base >= 2 ); + nvCheck( base <= 36 ); + + // @@ This needs to be done correctly. + // length = floor(log(i, base)); + uint len = uint(log(float(i)) / log(float(base)) + 2); // one more if negative + reserve(len); + + if( i < 0 ) { + *m_str = '-'; + *i2a(uint(-i), m_str+1, base) = 0; + } + else { + *i2a(i, m_str, base) = 0; + } + + return *this; +} + + +/** Convert number to string in the given base. */ +StringBuilder & StringBuilder::number( uint i, int base ) +{ + nvCheck( base >= 2 ); + nvCheck( base <= 36 ); + + // @@ This needs to be done correctly. + // length = floor(log(i, base)); + uint len = uint(log(float(i)) / log(float(base)) - 0.5f + 1); + reserve(len); + + *i2a(i, m_str, base) = 0; + + return *this; +} + + +/** Resize the string preserving the contents. */ +StringBuilder & StringBuilder::reserve( uint size_hint ) +{ + nvCheck(size_hint != 0); + if (size_hint > m_size) { + m_str = strReAlloc(m_str, size_hint); + m_size = size_hint; + } + return *this; +} + + +/** Copy a string safely. */ +StringBuilder & StringBuilder::copy(const char * s) +{ + nvCheck( s != NULL ); + const uint str_size = uint(strlen( s )) + 1; + reserve(str_size); + memcpy(m_str, s, str_size); + return *this; +} + +/** Copy a string safely. */ +StringBuilder & StringBuilder::copy(const char * s, uint len) +{ + nvCheck( s != NULL ); + const uint str_size = len + 1; + reserve(str_size); + strCpy(m_str, str_size, s, len); + return *this; +} + + +/** Copy an StringBuilder. */ +StringBuilder & StringBuilder::copy( const StringBuilder & s ) +{ + if (s.m_str == NULL) { + nvCheck( s.m_size == 0 ); + reset(); + } + else { + reserve( s.m_size ); + strCpy( m_str, s.m_size, s.m_str ); + } + return *this; +} + +bool StringBuilder::endsWith(const char * str) const +{ + uint l = uint(strlen(str)); + uint ml = uint(strlen(m_str)); + if (ml < l) return false; + return strncmp(m_str + ml - l, str, l) == 0; +} + +bool StringBuilder::beginsWith(const char * str) const +{ + size_t l = strlen(str); + return strncmp(m_str, str, l) == 0; +} + +// Find given char starting from the end. +char * StringBuilder::reverseFind(char c) +{ + int length = (int)strlen(m_str) - 1; + while (length >= 0 && m_str[length] != c) { + length--; + } + if (length >= 0) { + return m_str + length; + } + else { + return NULL; + } +} + + +/** Reset the string. */ +void StringBuilder::reset() +{ + m_size = 0; + strFree( m_str ); + m_str = NULL; +} + +/** Release the allocated string. */ +char * StringBuilder::release() +{ + char * str = m_str; + m_size = 0; + m_str = NULL; + return str; +} + +// Take ownership of string. +void StringBuilder::acquire(char * str) +{ + if (str) { + m_size = strLen(str) + 1; + m_str = str; + } + else { + m_size = 0; + m_str = NULL; + } +} + +// Swap strings. +void nv::swap(StringBuilder & a, StringBuilder & b) { + swap(a.m_size, b.m_size); + swap(a.m_str, b.m_str); +} + + +/// Get the file name from a path. +const char * Path::fileName() const +{ + return fileName(m_str); +} + + +/// Get the extension from a file path. +const char * Path::extension() const +{ + return extension(m_str); +} + + +/*static */void Path::translatePath(char * path, char pathSeparator/*= NV_PATH_SEPARATOR*/) { + if (path != NULL) { + for (int i = 0;; i++) { + if (path[i] == '\0') break; + if (path[i] == '\\' || path[i] == '/') path[i] = pathSeparator; + } + } +} + +/// Toggles path separators (ie. \\ into /). +void Path::translatePath(char pathSeparator/*=NV_PATH_SEPARATOR*/) +{ + if (!isNull()) { + translatePath(m_str, pathSeparator); + } +} + +void Path::appendSeparator(char pathSeparator/*=NV_PATH_SEPARATOR*/) +{ + nvCheck(!isNull()); + + const uint l = length(); + + if (m_str[l] != '\\' && m_str[l] != '/') { + char separatorString[] = { pathSeparator, '\0' }; + append(separatorString); + } +} + + +/** +* Strip the file name from a path. +* @warning path cannot end with '/' o '\\', can't it? +*/ +void Path::stripFileName() +{ + nvCheck( m_str != NULL ); + + int length = (int)strlen(m_str) - 1; + while (length > 0 && m_str[length] != '/' && m_str[length] != '\\'){ + length--; + } + if( length ) { + m_str[length+1] = 0; + } + else { + m_str[0] = 0; + } +} + + +/// Strip the extension from a path name. +void Path::stripExtension() +{ + nvCheck( m_str != NULL ); + + int length = (int)strlen(m_str) - 1; + while (length > 0 && m_str[length] != '.') { + length--; + if( m_str[length] == NV_PATH_SEPARATOR ) { + return; // no extension + } + } + if (length > 0) { + m_str[length] = 0; + } +} + + +/// Get the path separator. +// static +char Path::separator() +{ + return NV_PATH_SEPARATOR; +} + +// static +const char * Path::fileName(const char * str) +{ + nvCheck( str != NULL ); + + int length = (int)strlen(str) - 1; + while (length >= 0 && str[length] != '\\' && str[length] != '/') { + length--; + } + + return &str[length+1]; +} + +// static +const char * Path::extension(const char * str) +{ + nvCheck( str != NULL ); + + int length, l; + l = length = (int)strlen( str ); + while (length > 0 && str[length] != '.') { + length--; + if (str[length] == '\\' || str[length] == '/') { + return &str[l]; // no extension + } + } + if (length == 0) { + return &str[l]; + } + return &str[length]; +} + + + +/// Clone this string +String String::clone() const +{ + String str(data); + return str; +} + +void String::setString(const char * str) +{ + if (str == NULL) { + data = NULL; + } + else { + allocString( str ); + addRef(); + } +} + +void String::setString(const char * str, uint length) +{ + nvDebugCheck(str != NULL); + + allocString(str, length); + addRef(); +} + +void String::setString(const StringBuilder & str) +{ + if (str.str() == NULL) { + data = NULL; + } + else { + allocString(str.str()); + addRef(); + } +} + +// Add reference count. +void String::addRef() +{ + if (data != NULL) + { + setRefCount(getRefCount() + 1); + } +} + +// Decrease reference count. +void String::release() +{ + if (data != NULL) + { + const uint16 count = getRefCount(); + setRefCount(count - 1); + if (count - 1 == 0) { + free(data - 2); + data = NULL; + } + } +} + +void String::allocString(const char * str, uint len) +{ + const char * ptr = malloc<char>(2 + len + 1); + + setData( ptr ); + setRefCount( 0 ); + + // Copy string. + strCpy(const_cast<char *>(data), len+1, str, len); + + // Add terminating character. + const_cast<char *>(data)[len] = '\0'; +} + +void nv::swap(String & a, String & b) { + swap(a.data, b.data); +} diff --git a/thirdparty/thekla_atlas/nvcore/StrLib.h b/thirdparty/thekla_atlas/nvcore/StrLib.h new file mode 100644 index 0000000000..ae4b5d12a0 --- /dev/null +++ b/thirdparty/thekla_atlas/nvcore/StrLib.h @@ -0,0 +1,433 @@ +// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> + +#pragma once +#ifndef NV_CORE_STRING_H +#define NV_CORE_STRING_H + +#include "Debug.h" +#include "Hash.h" // hash + +//#include <string.h> // strlen, etc. + +#if NV_OS_WIN32 +#define NV_PATH_SEPARATOR '\\' +#else +#define NV_PATH_SEPARATOR '/' +#endif + +namespace nv +{ + + NVCORE_API uint strHash(const char * str, uint h) NV_PURE; + + /// String hash based on Bernstein's hash. + inline uint strHash(const char * data, uint h = 5381) + { + uint i = 0; + while(data[i] != 0) { + h = (33 * h) ^ uint(data[i]); + i++; + } + return h; + } + + template <> struct Hash<const char *> { + uint operator()(const char * str) const { return strHash(str); } + }; + + NVCORE_API uint strLen(const char * str) NV_PURE; // Asserts on NULL strings. + + NVCORE_API int strDiff(const char * s1, const char * s2) NV_PURE; // Asserts on NULL strings. + NVCORE_API int strCaseDiff(const char * s1, const char * s2) NV_PURE; // Asserts on NULL strings. + NVCORE_API bool strEqual(const char * s1, const char * s2) NV_PURE; // Accepts NULL strings. + NVCORE_API bool strCaseEqual(const char * s1, const char * s2) NV_PURE; // Accepts NULL strings. + + template <> struct Equal<const char *> { + bool operator()(const char * a, const char * b) const { return strEqual(a, b); } + }; + + NVCORE_API bool strBeginsWith(const char * dst, const char * prefix) NV_PURE; + NVCORE_API bool strEndsWith(const char * dst, const char * suffix) NV_PURE; + + + NVCORE_API void strCpy(char * dst, uint size, const char * src); + NVCORE_API void strCpy(char * dst, uint size, const char * src, uint len); + NVCORE_API void strCat(char * dst, uint size, const char * src); + + NVCORE_API const char * strSkipWhiteSpace(const char * str); + NVCORE_API char * strSkipWhiteSpace(char * str); + + NVCORE_API bool strMatch(const char * str, const char * pat) NV_PURE; + + NVCORE_API bool isNumber(const char * str) NV_PURE; + + /* @@ Implement these two functions and modify StringBuilder to use them? + NVCORE_API void strFormat(const char * dst, const char * fmt, ...); + NVCORE_API void strFormatList(const char * dst, const char * fmt, va_list arg); + + template <size_t count> void strFormatSafe(char (&buffer)[count], const char *fmt, ...) __attribute__((format (printf, 2, 3))); + template <size_t count> void strFormatSafe(char (&buffer)[count], const char *fmt, ...) { + va_list args; + va_start(args, fmt); + strFormatList(buffer, count, fmt, args); + va_end(args); + } + template <size_t count> void strFormatListSafe(char (&buffer)[count], const char *fmt, va_list arg) { + va_list tmp; + va_copy(tmp, args); + strFormatList(buffer, count, fmt, tmp); + va_end(tmp); + }*/ + + template <int count> void strCpySafe(char (&buffer)[count], const char *src) { + strCpy(buffer, count, src); + } + + template <int count> void strCatSafe(char (&buffer)[count], const char * src) { + strCat(buffer, count, src); + } + + + + /// String builder. + class NVCORE_CLASS StringBuilder + { + public: + + StringBuilder(); + explicit StringBuilder( uint size_hint ); + StringBuilder(const char * str); + StringBuilder(const char * str, uint len); + StringBuilder(const StringBuilder & other); + + ~StringBuilder(); + + StringBuilder & format( const char * format, ... ) __attribute__((format (printf, 2, 3))); + StringBuilder & formatList( const char * format, va_list arg ); + + StringBuilder & append(char c); + StringBuilder & append(const char * str); + StringBuilder & append(const char * str, uint len); + StringBuilder & append(const StringBuilder & str); + StringBuilder & appendFormat(const char * format, ...) __attribute__((format (printf, 2, 3))); + StringBuilder & appendFormatList(const char * format, va_list arg); + + StringBuilder & appendSpace(uint n); + + StringBuilder & number( int i, int base = 10 ); + StringBuilder & number( uint i, int base = 10 ); + + StringBuilder & reserve(uint size_hint); + StringBuilder & copy(const char * str); + StringBuilder & copy(const char * str, uint len); + StringBuilder & copy(const StringBuilder & str); + + StringBuilder & toLower(); + StringBuilder & toUpper(); + + bool endsWith(const char * str) const; + bool beginsWith(const char * str) const; + + char * reverseFind(char c); + + void reset(); + bool isNull() const { return m_size == 0; } + + // const char * accessors + //operator const char * () const { return m_str; } + //operator char * () { return m_str; } + const char * str() const { return m_str; } + char * str() { return m_str; } + + char * release(); // Release ownership of string. + void acquire(char *); // Take ownership of string. + + /// Implement value semantics. + StringBuilder & operator=( const StringBuilder & s ) { + return copy(s); + } + + /// Implement value semantics. + StringBuilder & operator=( const char * s ) { + return copy(s); + } + + /// Equal operator. + bool operator==( const StringBuilder & s ) const { + return strMatch(s.m_str, m_str); + } + + /// Return the exact length. + uint length() const { return isNull() ? 0 : strLen(m_str); } + + /// Return the size of the string container. + uint capacity() const { return m_size; } + + /// Return the hash of the string. + uint hash() const { return isNull() ? 0 : strHash(m_str); } + + // Swap strings. + friend void swap(StringBuilder & a, StringBuilder & b); + + protected: + + /// Size of the string container. + uint m_size; + + /// String. + char * m_str; + + }; + + + /// Path string. @@ This should be called PathBuilder. + class NVCORE_CLASS Path : public StringBuilder + { + public: + Path() : StringBuilder() {} + explicit Path(int size_hint) : StringBuilder(size_hint) {} + Path(const char * str) : StringBuilder(str) {} + Path(const Path & path) : StringBuilder(path) {} + + const char * fileName() const; + const char * extension() const; + + void translatePath(char pathSeparator = NV_PATH_SEPARATOR); + + void appendSeparator(char pathSeparator = NV_PATH_SEPARATOR); + + void stripFileName(); + void stripExtension(); + + // statics + NVCORE_API static char separator(); + NVCORE_API static const char * fileName(const char *); + NVCORE_API static const char * extension(const char *); + + NVCORE_API static void translatePath(char * path, char pathSeparator = NV_PATH_SEPARATOR); + }; + + + /// String class. + class NVCORE_CLASS String + { + public: + + /// Constructs a null string. @sa isNull() + String() + { + data = NULL; + } + + /// Constructs a shared copy of str. + String(const String & str) + { + data = str.data; + if (data != NULL) addRef(); + } + + /// Constructs a shared string from a standard string. + String(const char * str) + { + setString(str); + } + + /// Constructs a shared string from a standard string. + String(const char * str, int length) + { + setString(str, length); + } + + /// Constructs a shared string from a StringBuilder. + String(const StringBuilder & str) + { + setString(str); + } + + /// Dtor. + ~String() + { + release(); + } + + String clone() const; + + /// Release the current string and allocate a new one. + const String & operator=( const char * str ) + { + release(); + setString( str ); + return *this; + } + + /// Release the current string and allocate a new one. + const String & operator=( const StringBuilder & str ) + { + release(); + setString( str ); + return *this; + } + + /// Implement value semantics. + String & operator=( const String & str ) + { + if (str.data != data) + { + release(); + data = str.data; + addRef(); + } + return *this; + } + + /// Equal operator. + bool operator==( const String & str ) const + { + return strMatch(str.data, data); + } + + /// Equal operator. + bool operator==( const char * str ) const + { + return strMatch(str, data); + } + + /// Not equal operator. + bool operator!=( const String & str ) const + { + return !strMatch(str.data, data); + } + + /// Not equal operator. + bool operator!=( const char * str ) const + { + return !strMatch(str, data); + } + + /// Returns true if this string is the null string. + bool isNull() const { return data == NULL; } + + /// Return the exact length. + uint length() const { nvDebugCheck(data != NULL); return strLen(data); } + + /// Return the hash of the string. + uint hash() const { nvDebugCheck(data != NULL); return strHash(data); } + + /// const char * cast operator. + operator const char * () const { return data; } + + /// Get string pointer. + const char * str() const { return data; } + + + private: + + // Add reference count. + void addRef(); + + // Decrease reference count. + void release(); + + uint16 getRefCount() const + { + nvDebugCheck(data != NULL); + return *reinterpret_cast<const uint16 *>(data - 2); + } + + void setRefCount(uint16 count) { + nvDebugCheck(data != NULL); + nvCheck(count < 0xFFFF); + *reinterpret_cast<uint16 *>(const_cast<char *>(data - 2)) = uint16(count); + } + + void setData(const char * str) { + data = str + 2; + } + + void allocString(const char * str) + { + allocString(str, strLen(str)); + } + + void allocString(const char * str, uint length); + + void setString(const char * str); + void setString(const char * str, uint length); + void setString(const StringBuilder & str); + + // Swap strings. + friend void swap(String & a, String & b); + + private: + + const char * data; + + }; + + template <> struct Hash<String> { + uint operator()(const String & str) const { return str.hash(); } + }; + + + // Like AutoPtr, but for const char strings. + class AutoString + { + NV_FORBID_COPY(AutoString); + NV_FORBID_HEAPALLOC(); + public: + + // Ctor. + AutoString(const char * p = NULL) : m_ptr(p) { } + +#if NV_CC_CPP11 + // Move ctor. + AutoString(AutoString && ap) : m_ptr(ap.m_ptr) { ap.m_ptr = NULL; } +#endif + + // Dtor. Deletes owned pointer. + ~AutoString() { + delete [] m_ptr; + m_ptr = NULL; + } + + // Delete owned pointer and assign new one. + void operator=(const char * p) { + if (p != m_ptr) + { + delete [] m_ptr; + m_ptr = p; + } + } + + // Get pointer. + const char * ptr() const { return m_ptr; } + operator const char *() const { return m_ptr; } + + // Relinquish ownership of the underlying pointer and returns that pointer. + const char * release() { + const char * tmp = m_ptr; + m_ptr = NULL; + return tmp; + } + + // comparison operators. + friend bool operator == (const AutoString & ap, const char * const p) { + return (ap.ptr() == p); + } + friend bool operator != (const AutoString & ap, const char * const p) { + return (ap.ptr() != p); + } + friend bool operator == (const char * const p, const AutoString & ap) { + return (ap.ptr() == p); + } + friend bool operator != (const char * const p, const AutoString & ap) { + return (ap.ptr() != p); + } + + private: + const char * m_ptr; + }; + +} // nv namespace + +#endif // NV_CORE_STRING_H diff --git a/thirdparty/thekla_atlas/nvcore/Stream.h b/thirdparty/thekla_atlas/nvcore/Stream.h new file mode 100644 index 0000000000..c35c0d0c78 --- /dev/null +++ b/thirdparty/thekla_atlas/nvcore/Stream.h @@ -0,0 +1,164 @@ +// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> + +#pragma once +#ifndef NV_CORE_STREAM_H +#define NV_CORE_STREAM_H + +#include "nvcore.h" +#include "Debug.h" + +namespace nv +{ + + /// Base stream class. + class NVCORE_CLASS Stream { + public: + + enum ByteOrder { + LittleEndian = false, + BigEndian = true, + }; + + /// Get the byte order of the system. + static ByteOrder getSystemByteOrder() { +#if NV_LITTLE_ENDIAN + return LittleEndian; +#else + return BigEndian; +#endif + } + + + /// Ctor. + Stream() : m_byteOrder(LittleEndian) { } + + /// Virtual destructor. + virtual ~Stream() {} + + /// Set byte order. + void setByteOrder(ByteOrder bo) { m_byteOrder = bo; } + + /// Get byte order. + ByteOrder byteOrder() const { return m_byteOrder; } + + + /// Serialize the given data. + virtual uint serialize( void * data, uint len ) = 0; + + /// Move to the given position in the archive. + virtual void seek( uint pos ) = 0; + + /// Return the current position in the archive. + virtual uint tell() const = 0; + + /// Return the current size of the archive. + virtual uint size() const = 0; + + /// Determine if there has been any error. + virtual bool isError() const = 0; + + /// Clear errors. + virtual void clearError() = 0; + + /// Return true if the stream is at the end. + virtual bool isAtEnd() const = 0; + + /// Return true if the stream is seekable. + virtual bool isSeekable() const = 0; + + /// Return true if this is an input stream. + virtual bool isLoading() const = 0; + + /// Return true if this is an output stream. + virtual bool isSaving() const = 0; + + + void advance(uint offset) { seek(tell() + offset); } + + + // friends + friend Stream & operator<<( Stream & s, bool & c ) { +#if NV_OS_DARWIN && !NV_CC_CPP11 + nvStaticCheck(sizeof(bool) == 4); + uint8 b = c ? 1 : 0; + s.serialize( &b, 1 ); + c = (b != 0); +#else + nvStaticCheck(sizeof(bool) == 1); + s.serialize( &c, 1 ); +#endif + return s; + } + friend Stream & operator<<( Stream & s, char & c ) { + nvStaticCheck(sizeof(char) == 1); + s.serialize( &c, 1 ); + return s; + } + friend Stream & operator<<( Stream & s, uint8 & c ) { + nvStaticCheck(sizeof(uint8) == 1); + s.serialize( &c, 1 ); + return s; + } + friend Stream & operator<<( Stream & s, int8 & c ) { + nvStaticCheck(sizeof(int8) == 1); + s.serialize( &c, 1 ); + return s; + } + friend Stream & operator<<( Stream & s, uint16 & c ) { + nvStaticCheck(sizeof(uint16) == 2); + return s.byteOrderSerialize( &c, 2 ); + } + friend Stream & operator<<( Stream & s, int16 & c ) { + nvStaticCheck(sizeof(int16) == 2); + return s.byteOrderSerialize( &c, 2 ); + } + friend Stream & operator<<( Stream & s, uint32 & c ) { + nvStaticCheck(sizeof(uint32) == 4); + return s.byteOrderSerialize( &c, 4 ); + } + friend Stream & operator<<( Stream & s, int32 & c ) { + nvStaticCheck(sizeof(int32) == 4); + return s.byteOrderSerialize( &c, 4 ); + } + friend Stream & operator<<( Stream & s, uint64 & c ) { + nvStaticCheck(sizeof(uint64) == 8); + return s.byteOrderSerialize( &c, 8 ); + } + friend Stream & operator<<( Stream & s, int64 & c ) { + nvStaticCheck(sizeof(int64) == 8); + return s.byteOrderSerialize( &c, 8 ); + } + friend Stream & operator<<( Stream & s, float & c ) { + nvStaticCheck(sizeof(float) == 4); + return s.byteOrderSerialize( &c, 4 ); + } + friend Stream & operator<<( Stream & s, double & c ) { + nvStaticCheck(sizeof(double) == 8); + return s.byteOrderSerialize( &c, 8 ); + } + + protected: + + /// Serialize in the stream byte order. + Stream & byteOrderSerialize( void * v, uint len ) { + if( m_byteOrder == getSystemByteOrder() ) { + serialize( v, len ); + } + else { + for( uint i = len; i > 0; i-- ) { + serialize( (uint8 *)v + i - 1, 1 ); + } + } + return *this; + } + + + private: + + ByteOrder m_byteOrder; + + }; + +} // nv namespace + +#endif // NV_CORE_STREAM_H diff --git a/thirdparty/thekla_atlas/nvcore/Utils.h b/thirdparty/thekla_atlas/nvcore/Utils.h new file mode 100644 index 0000000000..f20e42cda8 --- /dev/null +++ b/thirdparty/thekla_atlas/nvcore/Utils.h @@ -0,0 +1,315 @@ +// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> + +#pragma once +#ifndef NV_CORE_UTILS_H +#define NV_CORE_UTILS_H + +#include "Debug.h" // nvDebugCheck + +#include <new> // for placement new + + +// Just in case. Grrr. +#undef min +#undef max + +#define NV_INT8_MIN (-128) +#define NV_INT8_MAX 127 +#define NV_UINT8_MAX 255 +#define NV_INT16_MIN (-32767-1) +#define NV_INT16_MAX 32767 +#define NV_UINT16_MAX 0xffff +#define NV_INT32_MIN (-2147483647-1) +#define NV_INT32_MAX 2147483647 +#define NV_UINT32_MAX 0xffffffff +#define NV_INT64_MAX POSH_I64(9223372036854775807) +#define NV_INT64_MIN (-POSH_I64(9223372036854775807)-1) +#define NV_UINT64_MAX POSH_U64(0xffffffffffffffff) + +#define NV_HALF_MAX 65504.0F +#define NV_FLOAT_MAX 3.402823466e+38F + +#define NV_INTEGER_TO_FLOAT_MAX 16777217 // Largest integer such that it and all smaller integers can be stored in a 32bit float. + + +namespace nv +{ + // Less error prone than casting. From CB: + // http://cbloomrants.blogspot.com/2011/06/06-17-11-c-casting-is-devil.html + + // These intentionally look like casts. + + // uint64 casts: + template <typename T> inline uint64 U64(T x) { return x; } + //template <> inline uint64 U64<uint64>(uint64 x) { return x; } + template <> inline uint64 U64<int64>(int64 x) { nvDebugCheck(x >= 0); return (uint64)x; } + //template <> inline uint64 U32<uint32>(uint32 x) { return x; } + template <> inline uint64 U64<int32>(int32 x) { nvDebugCheck(x >= 0); return (uint64)x; } + //template <> inline uint64 U64<uint16>(uint16 x) { return x; } + template <> inline uint64 U64<int16>(int16 x) { nvDebugCheck(x >= 0); return (uint64)x; } + //template <> inline uint64 U64<uint8>(uint8 x) { return x; } + template <> inline uint64 U64<int8>(int8 x) { nvDebugCheck(x >= 0); return (uint64)x; } + + // int64 casts: + template <typename T> inline int64 I64(T x) { return x; } + template <> inline int64 I64<uint64>(uint64 x) { nvDebugCheck(x <= NV_INT64_MAX); return (int64)x; } + //template <> inline uint64 U64<int64>(int64 x) { return x; } + //template <> inline uint64 U32<uint32>(uint32 x) { return x; } + //template <> inline uint64 U64<int32>(int32 x) { return x; } + //template <> inline uint64 U64<uint16>(uint16 x) { return x; } + //template <> inline uint64 U64<int16>(int16 x) { return x; } + //template <> inline uint64 U64<uint8>(uint8 x) { return x; } + //template <> inline uint64 U64<int8>(int8 x) { return x; } + + // uint32 casts: + template <typename T> inline uint32 U32(T x) { return x; } + template <> inline uint32 U32<uint64>(uint64 x) { nvDebugCheck(x <= NV_UINT32_MAX); return (uint32)x; } + template <> inline uint32 U32<int64>(int64 x) { nvDebugCheck(x >= 0 && x <= NV_UINT32_MAX); return (uint32)x; } + //template <> inline uint32 U32<uint32>(uint32 x) { return x; } + template <> inline uint32 U32<int32>(int32 x) { nvDebugCheck(x >= 0); return (uint32)x; } + //template <> inline uint32 U32<uint16>(uint16 x) { return x; } + template <> inline uint32 U32<int16>(int16 x) { nvDebugCheck(x >= 0); return (uint32)x; } + //template <> inline uint32 U32<uint8>(uint8 x) { return x; } + template <> inline uint32 U32<int8>(int8 x) { nvDebugCheck(x >= 0); return (uint32)x; } + + // int32 casts: + template <typename T> inline int32 I32(T x) { return x; } + template <> inline int32 I32<uint64>(uint64 x) { nvDebugCheck(x <= NV_INT32_MAX); return (int32)x; } + template <> inline int32 I32<int64>(int64 x) { nvDebugCheck(x >= NV_INT32_MIN && x <= NV_UINT32_MAX); return (int32)x; } + template <> inline int32 I32<uint32>(uint32 x) { nvDebugCheck(x <= NV_INT32_MAX); return (int32)x; } + //template <> inline int32 I32<int32>(int32 x) { return x; } + //template <> inline int32 I32<uint16>(uint16 x) { return x; } + //template <> inline int32 I32<int16>(int16 x) { return x; } + //template <> inline int32 I32<uint8>(uint8 x) { return x; } + //template <> inline int32 I32<int8>(int8 x) { return x; } + + // uint16 casts: + template <typename T> inline uint16 U16(T x) { return x; } + template <> inline uint16 U16<uint64>(uint64 x) { nvDebugCheck(x <= NV_UINT16_MAX); return (uint16)x; } + template <> inline uint16 U16<int64>(int64 x) { nvDebugCheck(x >= 0 && x <= NV_UINT16_MAX); return (uint16)x; } + template <> inline uint16 U16<uint32>(uint32 x) { nvDebugCheck(x <= NV_UINT16_MAX); return (uint16)x; } + template <> inline uint16 U16<int32>(int32 x) { nvDebugCheck(x >= 0 && x <= NV_UINT16_MAX); return (uint16)x; } + //template <> inline uint16 U16<uint16>(uint16 x) { return x; } + template <> inline uint16 U16<int16>(int16 x) { nvDebugCheck(x >= 0); return (uint16)x; } + //template <> inline uint16 U16<uint8>(uint8 x) { return x; } + template <> inline uint16 U16<int8>(int8 x) { nvDebugCheck(x >= 0); return (uint16)x; } + + // int16 casts: + template <typename T> inline int16 I16(T x) { return x; } + template <> inline int16 I16<uint64>(uint64 x) { nvDebugCheck(x <= NV_INT16_MAX); return (int16)x; } + template <> inline int16 I16<int64>(int64 x) { nvDebugCheck(x >= NV_INT16_MIN && x <= NV_UINT16_MAX); return (int16)x; } + template <> inline int16 I16<uint32>(uint32 x) { nvDebugCheck(x <= NV_INT16_MAX); return (int16)x; } + template <> inline int16 I16<int32>(int32 x) { nvDebugCheck(x >= NV_INT16_MIN && x <= NV_UINT16_MAX); return (int16)x; } + template <> inline int16 I16<uint16>(uint16 x) { nvDebugCheck(x <= NV_INT16_MAX); return (int16)x; } + //template <> inline int16 I16<int16>(int16 x) { return x; } + //template <> inline int16 I16<uint8>(uint8 x) { return x; } + //template <> inline int16 I16<int8>(int8 x) { return x; } + + // uint8 casts: + template <typename T> inline uint8 U8(T x) { return x; } + template <> inline uint8 U8<uint64>(uint64 x) { nvDebugCheck(x <= NV_UINT8_MAX); return (uint8)x; } + template <> inline uint8 U8<int64>(int64 x) { nvDebugCheck(x >= 0 && x <= NV_UINT8_MAX); return (uint8)x; } + template <> inline uint8 U8<uint32>(uint32 x) { nvDebugCheck(x <= NV_UINT8_MAX); return (uint8)x; } + template <> inline uint8 U8<int32>(int32 x) { nvDebugCheck(x >= 0 && x <= NV_UINT8_MAX); return (uint8)x; } + template <> inline uint8 U8<uint16>(uint16 x) { nvDebugCheck(x <= NV_UINT8_MAX); return (uint8)x; } + template <> inline uint8 U8<int16>(int16 x) { nvDebugCheck(x >= 0 && x <= NV_UINT8_MAX); return (uint8)x; } + //template <> inline uint8 U8<uint8>(uint8 x) { return x; } + template <> inline uint8 U8<int8>(int8 x) { nvDebugCheck(x >= 0); return (uint8)x; } + //template <> inline uint8 U8<float>(int8 x) { nvDebugCheck(x >= 0.0f && x <= 255.0f); return (uint8)x; } + + // int8 casts: + template <typename T> inline int8 I8(T x) { return x; } + template <> inline int8 I8<uint64>(uint64 x) { nvDebugCheck(x <= NV_INT8_MAX); return (int8)x; } + template <> inline int8 I8<int64>(int64 x) { nvDebugCheck(x >= NV_INT8_MIN && x <= NV_UINT8_MAX); return (int8)x; } + template <> inline int8 I8<uint32>(uint32 x) { nvDebugCheck(x <= NV_INT8_MAX); return (int8)x; } + template <> inline int8 I8<int32>(int32 x) { nvDebugCheck(x >= NV_INT8_MIN && x <= NV_UINT8_MAX); return (int8)x; } + template <> inline int8 I8<uint16>(uint16 x) { nvDebugCheck(x <= NV_INT8_MAX); return (int8)x; } + template <> inline int8 I8<int16>(int16 x) { nvDebugCheck(x >= NV_INT8_MIN && x <= NV_UINT8_MAX); return (int8)x; } + template <> inline int8 I8<uint8>(uint8 x) { nvDebugCheck(x <= NV_INT8_MAX); return (int8)x; } + //template <> inline int8 I8<int8>(int8 x) { return x; } + + // float casts: + template <typename T> inline float F32(T x) { return x; } + template <> inline float F32<uint64>(uint64 x) { nvDebugCheck(x <= NV_INTEGER_TO_FLOAT_MAX); return (float)x; } + template <> inline float F32<int64>(int64 x) { nvDebugCheck(x >= -NV_INTEGER_TO_FLOAT_MAX && x <= NV_INTEGER_TO_FLOAT_MAX); return (float)x; } + template <> inline float F32<uint32>(uint32 x) { nvDebugCheck(x <= NV_INTEGER_TO_FLOAT_MAX); return (float)x; } + template <> inline float F32<int32>(int32 x) { nvDebugCheck(x >= -NV_INTEGER_TO_FLOAT_MAX && x <= NV_INTEGER_TO_FLOAT_MAX); return (float)x; } + // The compiler should not complain about these conversions: + //template <> inline float F32<uint16>(uint16 x) { nvDebugCheck(return (float)x; } + //template <> inline float F32<int16>(int16 x) { nvDebugCheck(return (float)x; } + //template <> inline float F32<uint8>(uint8 x) { nvDebugCheck(return (float)x; } + //template <> inline float F32<int8>(int8 x) { nvDebugCheck(return (float)x; } + + + /// Swap two values. + template <typename T> + inline void swap(T & a, T & b) + { + T temp(a); + a = b; + b = temp; + } + + /// Return the maximum of the two arguments. For floating point values, it returns the second value if the first is NaN. + template <typename T> + //inline const T & max(const T & a, const T & b) + inline T max(const T & a, const T & b) + { + return (b < a) ? a : b; + } + + /// Return the maximum of the four arguments. + template <typename T> + //inline const T & max4(const T & a, const T & b, const T & c) + inline T max4(const T & a, const T & b, const T & c, const T & d) + { + return max(max(a, b), max(c, d)); + } + + /// Return the maximum of the three arguments. + template <typename T> + //inline const T & max3(const T & a, const T & b, const T & c) + inline T max3(const T & a, const T & b, const T & c) + { + return max(a, max(b, c)); + } + + /// Return the minimum of two values. + template <typename T> + //inline const T & min(const T & a, const T & b) + inline T min(const T & a, const T & b) + { + return (a < b) ? a : b; + } + + /// Return the maximum of the three arguments. + template <typename T> + //inline const T & min3(const T & a, const T & b, const T & c) + inline T min3(const T & a, const T & b, const T & c) + { + return min(a, min(b, c)); + } + + /// Clamp between two values. + template <typename T> + //inline const T & clamp(const T & x, const T & a, const T & b) + inline T clamp(const T & x, const T & a, const T & b) + { + return min(max(x, a), b); + } + + /** Return the next power of two. + * @see http://graphics.stanford.edu/~seander/bithacks.html + * @warning Behaviour for 0 is undefined. + * @note isPowerOfTwo(x) == true -> nextPowerOfTwo(x) == x + * @note nextPowerOfTwo(x) = 2 << log2(x-1) + */ + inline uint32 nextPowerOfTwo(uint32 x) + { + nvDebugCheck( x != 0 ); +#if 1 // On modern CPUs this is supposed to be as fast as using the bsr instruction. + x--; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + return x+1; +#else + uint p = 1; + while( x > p ) { + p += p; + } + return p; +#endif + } + + inline uint64 nextPowerOfTwo(uint64 x) + { + nvDebugCheck(x != 0); + uint p = 1; + while (x > p) { + p += p; + } + return p; + } + + // @@ Should I just use a macro instead? + template <typename T> + inline bool isPowerOfTwo(T n) + { + return (n & (n-1)) == 0; + } + + + // @@ Move this to utils? + /// Delete all the elements of a container. + template <typename T> + void deleteAll(T & container) + { + for (typename T::PseudoIndex i = container.start(); !container.isDone(i); container.advance(i)) + { + delete container[i]; + } + } + + + + // @@ Specialize these methods for numeric, pointer, and pod types. + + template <typename T> + void construct_range(T * restrict ptr, uint new_size, uint old_size) { + for (uint i = old_size; i < new_size; i++) { + new(ptr+i) T; // placement new + } + } + + template <typename T> + void construct_range(T * restrict ptr, uint new_size, uint old_size, const T & elem) { + for (uint i = old_size; i < new_size; i++) { + new(ptr+i) T(elem); // placement new + } + } + + template <typename T> + void construct_range(T * restrict ptr, uint new_size, uint old_size, const T * src) { + for (uint i = old_size; i < new_size; i++) { + new(ptr+i) T(src[i]); // placement new + } + } + + template <typename T> + void destroy_range(T * restrict ptr, uint new_size, uint old_size) { + for (uint i = new_size; i < old_size; i++) { + (ptr+i)->~T(); // Explicit call to the destructor + } + } + + template <typename T> + void fill(T * restrict dst, uint count, const T & value) { + for (uint i = 0; i < count; i++) { + dst[i] = value; + } + } + + template <typename T> + void copy_range(T * restrict dst, const T * restrict src, uint count) { + for (uint i = 0; i < count; i++) { + dst[i] = src[i]; + } + } + + template <typename T> + bool find(const T & element, const T * restrict ptr, uint begin, uint end, uint * index) { + for (uint i = begin; i < end; i++) { + if (ptr[i] == element) { + if (index != NULL) *index = i; + return true; + } + } + return false; + } + +} // nv namespace + +#endif // NV_CORE_UTILS_H diff --git a/thirdparty/thekla_atlas/nvcore/nvcore.h b/thirdparty/thekla_atlas/nvcore/nvcore.h new file mode 100644 index 0000000000..a3deb66be2 --- /dev/null +++ b/thirdparty/thekla_atlas/nvcore/nvcore.h @@ -0,0 +1,357 @@ +// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> + +#pragma once +#ifndef NV_CORE_H +#define NV_CORE_H + +// Function linkage +#if NVCORE_SHARED +#ifdef NVCORE_EXPORTS +#define NVCORE_API DLL_EXPORT +#define NVCORE_CLASS DLL_EXPORT_CLASS +#else +#define NVCORE_API DLL_IMPORT +#define NVCORE_CLASS DLL_IMPORT +#endif +#else // NVCORE_SHARED +#define NVCORE_API +#define NVCORE_CLASS +#endif // NVCORE_SHARED + + +// Platform definitions +#include <posh.h> + +// OS: +// NV_OS_WIN32 +// NV_OS_WIN64 +// NV_OS_MINGW +// NV_OS_CYGWIN +// NV_OS_LINUX +// NV_OS_UNIX +// NV_OS_DARWIN +// NV_OS_XBOX +// NV_OS_ORBIS +// NV_OS_IOS + +#define NV_OS_STRING POSH_OS_STRING + +#if defined POSH_OS_LINUX +# define NV_OS_LINUX 1 +# define NV_OS_UNIX 1 +#elif defined POSH_OS_ORBIS +# define NV_OS_ORBIS 1 +#elif defined POSH_OS_FREEBSD +# define NV_OS_FREEBSD 1 +# define NV_OS_UNIX 1 +#elif defined POSH_OS_OPENBSD +# define NV_OS_OPENBSD 1 +# define NV_OS_UNIX 1 +#elif defined POSH_OS_CYGWIN32 +# define NV_OS_CYGWIN 1 +#elif defined POSH_OS_MINGW +# define NV_OS_MINGW 1 +# define NV_OS_WIN32 1 +#elif defined POSH_OS_OSX +# define NV_OS_OSX 1 // IC: Adding this, because iOS defines NV_OS_DARWIN too. +# define NV_OS_DARWIN 1 +# define NV_OS_UNIX 1 +#elif defined POSH_OS_IOS +# define NV_OS_DARWIN 1 //ACS should we keep this on IOS? +# define NV_OS_UNIX 1 +# define NV_OS_IOS 1 +#elif defined POSH_OS_UNIX +# define NV_OS_UNIX 1 +#elif defined POSH_OS_WIN64 +# define NV_OS_WIN32 1 +# define NV_OS_WIN64 1 +#elif defined POSH_OS_WIN32 +# define NV_OS_WIN32 1 +#elif defined POSH_OS_XBOX +# define NV_OS_XBOX 1 +#elif defined POSH_OS_DURANGO +# define NV_OS_DURANGO 1 +#else +# error "Unsupported OS" +#endif + + +// Is this a console OS? (i.e. connected to a TV) +#if NV_OS_ORBIS || NV_OS_XBOX || NV_OS_DURANGO +# define NV_OS_CONSOLE 1 +#endif + + +// Threading: +// some platforms don't implement __thread or similar for thread-local-storage +#if NV_OS_UNIX || NV_OS_ORBIS || NV_OS_IOS //ACStodoIOS darwin instead of ios? +# define NV_OS_USE_PTHREAD 1 +# if NV_OS_IOS +# define NV_OS_HAS_TLS_QUALIFIER 0 +# else +# define NV_OS_HAS_TLS_QUALIFIER 1 +# endif +#else +# define NV_OS_USE_PTHREAD 0 +# define NV_OS_HAS_TLS_QUALIFIER 1 +#endif + + +// CPUs: +// NV_CPU_X86 +// NV_CPU_X86_64 +// NV_CPU_PPC +// NV_CPU_ARM + +#define NV_CPU_STRING POSH_CPU_STRING + +#if defined POSH_CPU_X86_64 +//# define NV_CPU_X86 1 +# define NV_CPU_X86_64 1 +#elif defined POSH_CPU_X86 +# define NV_CPU_X86 1 +#elif defined POSH_CPU_PPC +# define NV_CPU_PPC 1 +#elif defined POSH_CPU_STRONGARM +# define NV_CPU_ARM 1 +#else +# error "Unsupported CPU" +#endif + + +// Compiler: +// NV_CC_GNUC +// NV_CC_MSVC +// NV_CC_CLANG + +#if defined POSH_COMPILER_CLANG +# define NV_CC_CLANG 1 +# define NV_CC_GNUC 1 // Clang is compatible with GCC. +# define NV_CC_STRING "clang" +#elif defined POSH_COMPILER_GCC +# define NV_CC_GNUC 1 +# define NV_CC_STRING "gcc" +#elif defined POSH_COMPILER_MSVC +# define NV_CC_MSVC 1 +# define NV_CC_STRING "msvc" +#else +# error "Unsupported compiler" +#endif + +#if NV_CC_MSVC +#define NV_CC_CPP11 (__cplusplus > 199711L || _MSC_VER >= 1800) // Visual Studio 2013 has all the features we use, but doesn't advertise full C++11 support yet. +#else +// @@ IC: This works in CLANG, about GCC? +// @@ ES: Doesn't work in gcc. These 3 features are available in GCC >= 4.4. +#ifdef __clang__ +#define NV_CC_CPP11 (__has_feature(cxx_deleted_functions) && __has_feature(cxx_rvalue_references) && __has_feature(cxx_static_assert)) +#elif defined __GNUC__ +#define NV_CC_CPP11 ( __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4)) +#endif +#endif + +// Endiannes: +#define NV_LITTLE_ENDIAN POSH_LITTLE_ENDIAN +#define NV_BIG_ENDIAN POSH_BIG_ENDIAN +#define NV_ENDIAN_STRING POSH_ENDIAN_STRING + + +// Define the right printf prefix for size_t arguments: +#if POSH_64BIT_POINTER +# define NV_SIZET_PRINTF_PREFIX POSH_I64_PRINTF_PREFIX +#else +# define NV_SIZET_PRINTF_PREFIX +#endif + + +// cmake config +#include "nvconfig.h" + +#if NV_OS_DARWIN +#include <stdint.h> +//#include <inttypes.h> + +// Type definitions: +typedef uint8_t uint8; +typedef int8_t int8; + +typedef uint16_t uint16; +typedef int16_t int16; + +typedef uint32_t uint32; +typedef int32_t int32; + +typedef uint64_t uint64; +typedef int64_t int64; + +// POSH gets this wrong due to __LP64__ +#undef POSH_I64_PRINTF_PREFIX +#define POSH_I64_PRINTF_PREFIX "ll" + +#else + +// Type definitions: +typedef posh_u8_t uint8; +typedef posh_i8_t int8; + +typedef posh_u16_t uint16; +typedef posh_i16_t int16; + +typedef posh_u32_t uint32; +typedef posh_i32_t int32; + +//#if NV_OS_DARWIN +// OSX-64 is supposed to be LP64 (longs and pointers are 64 bits), thus uint64 is defined as +// unsigned long. However, some OSX headers define it as unsigned long long, producing errors, +// even though both types are 64 bit. Ideally posh should handle that, but it has not been +// updated in ages, so here I'm just falling back to the standard C99 types defined in inttypes.h +//#include <inttypes.h> +//typedef posh_u64_t uint64_t; +//typedef posh_i64_t int64_t; +//#else +typedef posh_u64_t uint64; +typedef posh_i64_t int64; +//#endif +#if NV_OS_DARWIN +// To avoid duplicate definitions. +#define _UINT64 +#endif +#endif + +// Aliases +typedef uint32 uint; + + +// Version string: +#define NV_VERSION_STRING \ + NV_OS_STRING "/" NV_CC_STRING "/" NV_CPU_STRING"/" \ + NV_ENDIAN_STRING"-endian - " __DATE__ "-" __TIME__ + + +// Disable copy constructor and assignment operator. +#if NV_CC_CPP11 +#define NV_FORBID_COPY(C) \ + C( const C & ) = delete; \ + C &operator=( const C & ) = delete +#else +#define NV_FORBID_COPY(C) \ + private: \ + C( const C & ); \ + C &operator=( const C & ) +#endif + +// Disable dynamic allocation on the heap. +// See Prohibiting Heap-Based Objects in More Effective C++. +#define NV_FORBID_HEAPALLOC() \ + private: \ + void *operator new(size_t size); \ + void *operator new[](size_t size) + //static void *operator new(size_t size); \ + //static void *operator new[](size_t size); + +// String concatenation macros. +#define NV_STRING_JOIN2(arg1, arg2) NV_DO_STRING_JOIN2(arg1, arg2) +#define NV_DO_STRING_JOIN2(arg1, arg2) arg1 ## arg2 +#define NV_STRING_JOIN3(arg1, arg2, arg3) NV_DO_STRING_JOIN3(arg1, arg2, arg3) +#define NV_DO_STRING_JOIN3(arg1, arg2, arg3) arg1 ## arg2 ## arg3 +#define NV_STRING2(x) #x +#define NV_STRING(x) NV_STRING2(x) + +#if NV_CC_MSVC +#define NV_MULTI_LINE_MACRO_BEGIN do { +#define NV_MULTI_LINE_MACRO_END \ + __pragma(warning(push)) \ + __pragma(warning(disable:4127)) \ + } while(false) \ + __pragma(warning(pop)) +#else +#define NV_MULTI_LINE_MACRO_BEGIN do { +#define NV_MULTI_LINE_MACRO_END } while(false) +#endif + +#if NV_CC_CPP11 +#define nvStaticCheck(x) static_assert((x), "Static assert "#x" failed") +#else +#define nvStaticCheck(x) typedef char NV_STRING_JOIN2(__static_assert_,__LINE__)[(x)] +#endif +#define NV_COMPILER_CHECK(x) nvStaticCheck(x) // I like this name best. + +// Make sure type definitions are fine. +NV_COMPILER_CHECK(sizeof(int8) == 1); +NV_COMPILER_CHECK(sizeof(uint8) == 1); +NV_COMPILER_CHECK(sizeof(int16) == 2); +NV_COMPILER_CHECK(sizeof(uint16) == 2); +NV_COMPILER_CHECK(sizeof(int32) == 4); +NV_COMPILER_CHECK(sizeof(uint32) == 4); +NV_COMPILER_CHECK(sizeof(int32) == 4); +NV_COMPILER_CHECK(sizeof(uint32) == 4); + +#include <stddef.h> // for size_t +template <typename T, size_t N> char (&ArraySizeHelper(T (&array)[N]))[N]; +#define NV_ARRAY_SIZE(x) sizeof(ArraySizeHelper(x)) +//#define NV_ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) + +#if 0 // Disabled in The Witness. +#if NV_CC_MSVC +#define NV_MESSAGE(x) message(__FILE__ "(" NV_STRING(__LINE__) ") : " x) +#else +#define NV_MESSAGE(x) message(x) +#endif +#else +#define NV_MESSAGE(x) +#endif + + +// Startup initialization macro. +#define NV_AT_STARTUP(some_code) \ + namespace { \ + static struct NV_STRING_JOIN2(AtStartup_, __LINE__) { \ + NV_STRING_JOIN2(AtStartup_, __LINE__)() { some_code; } \ + } \ + NV_STRING_JOIN3(AtStartup_, __LINE__, Instance); \ + } + +// Indicate the compiler that the parameter is not used to suppress compier warnings. +#if NV_CC_MSVC +#define NV_UNUSED(a) ((a)=(a)) +#else +#define NV_UNUSED(a) _Pragma(NV_STRING(unused(a))) +#endif + +// Null index. @@ Move this somewhere else... it's only used by nvmesh. +//const unsigned int NIL = unsigned int(~0); +#define NIL uint(~0) + +// Null pointer. +#ifndef NULL +#define NULL 0 +#endif + +// Platform includes +#if NV_CC_MSVC +# if NV_OS_WIN32 +# include "DefsVcWin32.h" +# elif NV_OS_XBOX +# include "DefsVcXBox.h" +# elif NV_OS_DURANGO +# include "DefsVcDurango.h" +# else +# error "MSVC: Platform not supported" +# endif +#elif NV_CC_GNUC +# if NV_OS_LINUX +# include "DefsGnucLinux.h" +# elif NV_OS_DARWIN || NV_OS_FREEBSD || NV_OS_OPENBSD +# include "DefsGnucDarwin.h" +# elif NV_OS_ORBIS +# include "DefsOrbis.h" +# elif NV_OS_MINGW +# include "DefsGnucWin32.h" +# elif NV_OS_CYGWIN +# error "GCC: Cygwin not supported" +# else +# error "GCC: Platform not supported" +# endif +#endif + +#endif // NV_CORE_H diff --git a/thirdparty/thekla_atlas/nvcore/scanf.c b/thirdparty/thekla_atlas/nvcore/scanf.c new file mode 100644 index 0000000000..bf9d293154 --- /dev/null +++ b/thirdparty/thekla_atlas/nvcore/scanf.c @@ -0,0 +1,641 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * From: Id: vfscanf.c,v 1.13 1998/09/25 12:20:27 obrien Exp + * From: static char sccsid[] = "@(#)strtol.c 8.1 (Berkeley) 6/4/93"; + * From: static char sccsid[] = "@(#)strtoul.c 8.1 (Berkeley) 6/4/93"; + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <ctype.h> +#include <string.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#pragma warning(disable : 4244) // conversion from '*' to '*', possible loss of data +#pragma warning(disable : 4018) // signed/unsigned mismatch +#pragma warning(disable : 4267) // '=' : conversion from 'size_t' to 'int', possible loss of data + +#define strtoq _strtoi64 +#define strtouq _strtoui64 +#define bcopy(b1,b2,len) (memmove((b2), (b1), (len)), (void) 0) + +typedef int long long quad_t; +typedef unsigned long long u_quad_t; +typedef unsigned char u_char; + +#define BUF 32 /* Maximum length of numeric string. */ + +/* + * Flags used during conversion. + */ +#define LONG 0x01 /* l: long or double */ +#define SHORT 0x04 /* h: short */ +#define SUPPRESS 0x08 /* suppress assignment */ +#define POINTER 0x10 /* weird %p pointer (`fake hex') */ +#define NOSKIP 0x20 /* do not skip blanks */ +#define QUAD 0x400 + +/* + * The following are used in numeric conversions only: + * SIGNOK, NDIGITS, DPTOK, and EXPOK are for floating point; + * SIGNOK, NDIGITS, PFXOK, and NZDIGITS are for integral. + */ +#define SIGNOK 0x40 /* +/- is (still) legal */ +#define NDIGITS 0x80 /* no digits detected */ + +#define DPTOK 0x100 /* (float) decimal point is still legal */ +#define EXPOK 0x200 /* (float) exponent (e+3, etc) still legal */ + +#define PFXOK 0x100 /* 0x prefix is (still) legal */ +#define NZDIGITS 0x200 /* no zero digits detected */ + +/* + * Conversion types. + */ +#define CT_CHAR 0 /* %c conversion */ +#define CT_CCL 1 /* %[...] conversion */ +#define CT_STRING 2 /* %s conversion */ +#define CT_INT 3 /* integer, i.e., strtoq or strtouq */ +typedef u_quad_t (*ccfntype)(const char *, char **, int); + +static const u_char *__sccl(char *, const u_char *); + +int +vsscanf(const char *inp, char const *fmt0, va_list ap) +{ + int inr; + const u_char *fmt = (const u_char *)fmt0; + int c; /* character from format, or conversion */ + size_t width; /* field width, or 0 */ + char *p; /* points into all kinds of strings */ + int n; /* handy integer */ + int flags; /* flags as defined above */ + char *p0; /* saves original value of p when necessary */ + int nassigned; /* number of fields assigned */ + int nconversions; /* number of conversions */ + int nread; /* number of characters consumed from fp */ + int base; /* base argument to strtoq/strtouq */ + ccfntype ccfn; /* conversion function (strtoq/strtouq) */ + char ccltab[256]; /* character class table for %[...] */ + char buf[BUF]; /* buffer for numeric conversions */ + + /* `basefix' is used to avoid `if' tests in the integer scanner */ + static short basefix[17] = + { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; + + inr = strlen(inp); + + nassigned = 0; + nconversions = 0; + nread = 0; + base = 0; /* XXX just to keep gcc happy */ + ccfn = NULL; /* XXX just to keep gcc happy */ + for (;;) { + c = *fmt++; + if (c == 0) + return (nassigned); + if (isspace(c)) { + while (inr > 0 && isspace(*inp)) + nread++, inr--, inp++; + continue; + } + if (c != '%') + goto literal; + width = 0; + flags = 0; + /* + * switch on the format. continue if done; + * break once format type is derived. + */ +again: c = *fmt++; + switch (c) { + case '%': +literal: + if (inr <= 0) + goto input_failure; + if (*inp != c) + goto match_failure; + inr--, inp++; + nread++; + continue; + + case '*': + flags |= SUPPRESS; + goto again; + case 'l': + flags |= LONG; + goto again; + case 'q': + flags |= QUAD; + goto again; + case 'h': + flags |= SHORT; + goto again; + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + width = width * 10 + c - '0'; + goto again; + + /* + * Conversions. + * + */ + case 'd': + c = CT_INT; + ccfn = (ccfntype)strtoq; + base = 10; + break; + + case 'i': + c = CT_INT; + ccfn = (ccfntype)strtoq; + base = 0; + break; + + case 'o': + c = CT_INT; + ccfn = strtouq; + base = 8; + break; + + case 'u': + c = CT_INT; + ccfn = strtouq; + base = 10; + break; + + case 'x': + flags |= PFXOK; /* enable 0x prefixing */ + c = CT_INT; + ccfn = strtouq; + base = 16; + break; + + case 's': + c = CT_STRING; + break; + + case '[': + fmt = __sccl(ccltab, fmt); + flags |= NOSKIP; + c = CT_CCL; + break; + + case 'c': + flags |= NOSKIP; + c = CT_CHAR; + break; + + case 'p': /* pointer format is like hex */ + flags |= POINTER | PFXOK; + c = CT_INT; + ccfn = strtouq; + base = 16; + break; + + case 'n': + nconversions++; + if (flags & SUPPRESS) /* ??? */ + continue; + if (flags & SHORT) + *va_arg(ap, short *) = nread; + else if (flags & LONG) + *va_arg(ap, long *) = nread; + else if (flags & QUAD) + *va_arg(ap, quad_t *) = nread; + else + *va_arg(ap, int *) = nread; + continue; + } + + /* + * We have a conversion that requires input. + */ + if (inr <= 0) + goto input_failure; + + /* + * Consume leading white space, except for formats + * that suppress this. + */ + if ((flags & NOSKIP) == 0) { + while (isspace(*inp)) { + nread++; + if (--inr > 0) + inp++; + else + goto input_failure; + } + /* + * Note that there is at least one character in + * the buffer, so conversions that do not set NOSKIP + * can no longer result in an input failure. + */ + } + + /* + * Do the conversion. + */ + switch (c) { + + case CT_CHAR: + /* scan arbitrary characters (sets NOSKIP) */ + if (width == 0) + width = 1; + if (flags & SUPPRESS) { + size_t sum = 0; + for (;;) { + if ((n = inr) < width) { + sum += n; + width -= n; + inp += n; + if (sum == 0) + goto input_failure; + break; + } else { + sum += width; + inr -= width; + inp += width; + break; + } + } + nread += sum; + } else { + bcopy(inp, va_arg(ap, char *), width); + inr -= width; + inp += width; + nread += width; + nassigned++; + } + nconversions++; + break; + + case CT_CCL: + /* scan a (nonempty) character class (sets NOSKIP) */ + if (width == 0) + width = (size_t)~0; /* `infinity' */ + /* take only those things in the class */ + if (flags & SUPPRESS) { + n = 0; + while (ccltab[(unsigned char)*inp]) { + n++, inr--, inp++; + if (--width == 0) + break; + if (inr <= 0) { + if (n == 0) + goto input_failure; + break; + } + } + if (n == 0) + goto match_failure; + } else { + p0 = p = va_arg(ap, char *); + while (ccltab[(unsigned char)*inp]) { + inr--; + *p++ = *inp++; + if (--width == 0) + break; + if (inr <= 0) { + if (p == p0) + goto input_failure; + break; + } + } + n = p - p0; + if (n == 0) + goto match_failure; + *p = 0; + nassigned++; + } + nread += n; + nconversions++; + break; + + case CT_STRING: + /* like CCL, but zero-length string OK, & no NOSKIP */ + if (width == 0) + width = (size_t)~0; + if (flags & SUPPRESS) { + n = 0; + while (!isspace(*inp)) { + n++, inr--, inp++; + if (--width == 0) + break; + if (inr <= 0) + break; + } + nread += n; + } else { + p0 = p = va_arg(ap, char *); + while (!isspace(*inp)) { + inr--; + *p++ = *inp++; + if (--width == 0) + break; + if (inr <= 0) + break; + } + *p = 0; + nread += p - p0; + nassigned++; + } + nconversions++; + continue; + + case CT_INT: + /* scan an integer as if by strtoq/strtouq */ +#ifdef hardway + if (width == 0 || width > sizeof(buf) - 1) + width = sizeof(buf) - 1; +#else + /* size_t is unsigned, hence this optimisation */ + if (--width > sizeof(buf) - 2) + width = sizeof(buf) - 2; + width++; +#endif + flags |= SIGNOK | NDIGITS | NZDIGITS; + for (p = buf; width; width--) { + c = *inp; + /* + * Switch on the character; `goto ok' + * if we accept it as a part of number. + */ + switch (c) { + + /* + * The digit 0 is always legal, but is + * special. For %i conversions, if no + * digits (zero or nonzero) have been + * scanned (only signs), we will have + * base==0. In that case, we should set + * it to 8 and enable 0x prefixing. + * Also, if we have not scanned zero digits + * before this, do not turn off prefixing + * (someone else will turn it off if we + * have scanned any nonzero digits). + */ + case '0': + if (base == 0) { + base = 8; + flags |= PFXOK; + } + if (flags & NZDIGITS) + flags &= ~(SIGNOK|NZDIGITS|NDIGITS); + else + flags &= ~(SIGNOK|PFXOK|NDIGITS); + goto ok; + + /* 1 through 7 always legal */ + case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + base = basefix[base]; + flags &= ~(SIGNOK | PFXOK | NDIGITS); + goto ok; + + /* digits 8 and 9 ok iff decimal or hex */ + case '8': case '9': + base = basefix[base]; + if (base <= 8) + break; /* not legal here */ + flags &= ~(SIGNOK | PFXOK | NDIGITS); + goto ok; + + /* letters ok iff hex */ + case 'A': case 'B': case 'C': + case 'D': case 'E': case 'F': + case 'a': case 'b': case 'c': + case 'd': case 'e': case 'f': + /* no need to fix base here */ + if (base <= 10) + break; /* not legal here */ + flags &= ~(SIGNOK | PFXOK | NDIGITS); + goto ok; + + /* sign ok only as first character */ + case '+': case '-': + if (flags & SIGNOK) { + flags &= ~SIGNOK; + goto ok; + } + break; + + /* x ok iff flag still set & 2nd char */ + case 'x': case 'X': + if (flags & PFXOK && p == buf + 1) { + base = 16; /* if %i */ + flags &= ~PFXOK; + goto ok; + } + break; + } + + /* + * If we got here, c is not a legal character + * for a number. Stop accumulating digits. + */ + break; + ok: + /* + * c is legal: store it and look at the next. + */ + *p++ = c; + if (--inr > 0) + inp++; + else + break; /* end of input */ + } + /* + * If we had only a sign, it is no good; push + * back the sign. If the number ends in `x', + * it was [sign] '0' 'x', so push back the x + * and treat it as [sign] '0'. + */ + if (flags & NDIGITS) { + if (p > buf) { + inp--; + inr++; + } + goto match_failure; + } + c = ((u_char *)p)[-1]; + if (c == 'x' || c == 'X') { + --p; + inp--; + inr++; + } + if ((flags & SUPPRESS) == 0) { + u_quad_t res; + + *p = 0; + res = (*ccfn)(buf, (char **)NULL, base); + if (flags & POINTER) + *va_arg(ap, void **) = + (void *)(uintptr_t)res; + else if (flags & SHORT) + *va_arg(ap, short *) = res; + else if (flags & LONG) + *va_arg(ap, long *) = res; + else if (flags & QUAD) + *va_arg(ap, quad_t *) = res; + else + *va_arg(ap, int *) = res; + nassigned++; + } + nread += p - buf; + nconversions++; + break; + + } + } +input_failure: + return (nconversions != 0 ? nassigned : -1); +match_failure: + return (nassigned); +} + + +/* + * Fill in the given table from the scanset at the given format + * (just after `['). Return a pointer to the character past the + * closing `]'. The table has a 1 wherever characters should be + * considered part of the scanset. + */ +static const u_char * +__sccl(char *tab, const u_char *fmt) +{ + int c, n, v; + + /* first `clear' the whole table */ + c = *fmt++; /* first char hat => negated scanset */ + if (c == '^') { + v = 1; /* default => accept */ + c = *fmt++; /* get new first char */ + } else + v = 0; /* default => reject */ + + /* XXX: Will not work if sizeof(tab*) > sizeof(char) */ + for (n = 0; n < 256; n++) + tab[n] = v; /* memset(tab, v, 256) */ + + if (c == 0) + return (fmt - 1);/* format ended before closing ] */ + + /* + * Now set the entries corresponding to the actual scanset + * to the opposite of the above. + * + * The first character may be ']' (or '-') without being special; + * the last character may be '-'. + */ + v = 1 - v; + for (;;) { + tab[c] = v; /* take character c */ +doswitch: + n = *fmt++; /* and examine the next */ + switch (n) { + + case 0: /* format ended too soon */ + return (fmt - 1); + + case '-': + /* + * A scanset of the form + * [01+-] + * is defined as `the digit 0, the digit 1, + * the character +, the character -', but + * the effect of a scanset such as + * [a-zA-Z0-9] + * is implementation defined. The V7 Unix + * scanf treats `a-z' as `the letters a through + * z', but treats `a-a' as `the letter a, the + * character -, and the letter a'. + * + * For compatibility, the `-' is not considerd + * to define a range if the character following + * it is either a close bracket (required by ANSI) + * or is not numerically greater than the character + * we just stored in the table (c). + */ + n = *fmt; + if (n == ']' || n < c) { + c = '-'; + break; /* resume the for(;;) */ + } + fmt++; + /* fill in the range */ + do { + tab[++c] = v; + } while (c < n); + c = n; + /* + * Alas, the V7 Unix scanf also treats formats + * such as [a-c-e] as `the letters a through e'. + * This too is permitted by the standard.... + */ + goto doswitch; + break; + + case ']': /* end of scanset */ + return (fmt); + + default: /* just another character */ + c = n; + break; + } + } + /* NOTREACHED */ +} + +/* +int +sscanf(const char *ibuf, const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = vsscanf(ibuf, fmt, ap); + va_end(ap); + + return(ret); +} +*/ + +#ifdef __cplusplus +} +#endif |