Merge private branch.

This commit is contained in:
castano
2010-05-27 23:18:08 +00:00
parent 56401d5f7d
commit e7f2d1e2bc
27 changed files with 3429 additions and 3263 deletions

View File

@ -1,154 +0,0 @@
// This code is in the public domain -- castanyo@yahoo.es
#ifndef NV_CORE_ALGORITHMS_H
#define NV_CORE_ALGORITHMS_H
#include "nvcore.h"
namespace nv
{
/// Return the maximum of two values.
template <typename T>
inline const T & max(const T & a, const T & b)
{
//return std::max(a, b);
if( a < b ) {
return b;
}
return a;
}
/// Return the minimum of two values.
template <typename T>
inline const T & min(const T & a, const T & b)
{
//return std::min(a, b);
if( b < a ) {
return b;
}
return a;
}
/// Clamp between two values.
template <typename T>
inline const T & clamp(const T & x, const T & a, const T & b)
{
return min(max(x, a), b);
}
/// 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];
}
}
// @@ Should swap be implemented here?
template <typename T, template <typename T> class C>
void sort(C<T> & container)
{
introsortLoop(container, 0, container.count());
insertionSort(container, 0, container.count());
}
template <typename T, template <typename T> class C>
void sort(C<T> & container, uint begin, uint end)
{
if (begin < end)
{
introsortLoop(container, begin, end);
insertionSort(container, begin, end);
}
}
template <typename T, template <typename T> class C>
void insertionSort(C<T> & container)
{
insertionSort(container, 0, container.count());
}
template <typename T, template <typename T> class C>
void insertionSort(C<T> & container, uint begin, uint end)
{
for (uint i = begin + 1; i != end; ++i)
{
T value = container[i];
uint j = i;
while (j != begin && container[j-1] > value)
{
container[j] = container[j-1];
--j;
}
if (i != j)
{
container[j] = value;
}
}
}
template <typename T, template <typename T> class C>
void introsortLoop(C<T> & container, uint begin, uint end)
{
while (end-begin > 16)
{
uint p = partition(container, begin, end, medianof3(container, begin, begin+((end-begin)/2)+1, end-1));
introsortLoop(container, p, end);
end = p;
}
}
template <typename T, template <typename T> class C>
uint partition(C<T> & a, uint begin, uint end, const T & x)
{
int i = begin, j = end;
while (true)
{
while (a[i] < x) ++i;
--j;
while (x < a[j]) --j;
if (i >= j)
return i;
swap(a[i], a[j]);
i++;
}
}
template <typename T, template <typename T> class C>
const T & medianof3(C<T> & a, uint lo, uint mid, uint hi)
{
if (a[mid] < a[lo])
{
if (a[hi] < a[mid])
{
return a[mid];
}
else
{
return (a[hi] < a[lo]) ? a[hi] : a[lo];
}
}
else
{
if (a[hi] < a[mid])
{
return (a[hi] < a[lo]) ? a[lo] : a[hi];
}
else
{
return a[mid];
}
}
}
} // nv namespace
#endif // NV_CORE_ALGORITHMS_H

525
src/nvcore/Array.h Normal file
View File

@ -0,0 +1,525 @@
// This code is in the public domain -- Ignacio Casta<74>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 foreach macros that I use are very non-standard and somewhat confusing. It would be nice to have
standard foreach as in Qt.
*/
#include "nvcore.h"
#include "Memory.h"
#include "Debug.h"
#include "Stream.h"
#include "Utils.h" // swap
#include <string.h> // memmove
#include <new> // for placement new
#if NV_CC_GNUC // If typeof is available:
#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))
/*
#define NV_FOREACH(i, container) \
for(typename typeof(container)::PseudoIndex i((container).start()); !(container).isDone(i); (container).advance(i))
*/
#else // If typeof not available:
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
#endif
namespace nv
{
/// 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];
}
}
/**
* Replacement for std::vector that is easier to debug and provides
* some nice foreach enumerators.
*/
template<typename T>
class NVCORE_CLASS Array {
public:
/// Ctor.
Array() : m_buffer(NULL), m_size(0), m_buffer_size(0)
{
}
/// Copy ctor.
Array( const Array & a ) : m_buffer(NULL), m_size(0), m_buffer_size(0)
{
copy(a.m_buffer, a.m_size);
}
/// Ctor that initializes the vector with the given elements.
Array( const T * ptr, int num ) : m_buffer(NULL), m_size(0), m_buffer_size(0)
{
copy(ptr, num);
}
/// Allocate array.
explicit Array(uint capacity) : m_buffer(NULL), m_size(0), m_buffer_size(0)
{
allocate(capacity);
}
/// Dtor.
~Array()
{
clear();
allocate(0);
}
/// Const element access.
const T & operator[]( uint index ) const
{
nvDebugCheck(index < m_size);
return m_buffer[index];
}
const T & at( uint index ) const
{
nvDebugCheck(index < m_size);
return m_buffer[index];
}
/// Element access.
T & operator[] ( uint index )
{
nvDebugCheck(index < m_size);
return m_buffer[index];
}
T & at( uint index )
{
nvDebugCheck(index < m_size);
return m_buffer[index];
}
/// Get vector size.
uint size() const { return m_size; }
/// Get vector size.
uint count() const { return m_size; }
/// Get const vector pointer.
const T * buffer() const { return m_buffer; }
/// Get vector pointer.
T * mutableBuffer() { return m_buffer; }
/// Is vector empty.
bool isEmpty() const { return m_size == 0; }
/// Is a null vector.
bool isNull() const { return m_buffer == NULL; }
/// Push an element at the end of the vector.
void push_back( const T & val )
{
uint new_size = m_size + 1;
if (new_size > m_buffer_size)
{
const T copy(val); // create a copy in case value is inside of this array.
resize(new_size);
m_buffer[new_size-1] = copy;
}
else
{
m_size = new_size;
new(m_buffer+new_size-1) T(val);
}
}
void pushBack( const T & val )
{
push_back(val);
}
void append( const T & val )
{
push_back(val);
}
/// Qt like push operator.
Array<T> & operator<< ( T & t )
{
push_back(t);
return *this;
}
/// Pop and return element at the end of the vector.
void pop_back()
{
nvDebugCheck( m_size > 0 );
resize( m_size - 1 );
}
void popBack()
{
pop_back();
}
/// Get back element.
const T & back() const
{
nvDebugCheck( m_size > 0 );
return m_buffer[m_size-1];
}
/// Get back element.
T & back()
{
nvDebugCheck( m_size > 0 );
return m_buffer[m_size-1];
}
/// Get front element.
const T & front() const
{
nvDebugCheck( m_size > 0 );
return m_buffer[0];
}
/// Get front element.
T & front()
{
nvDebugCheck( m_size > 0 );
return m_buffer[0];
}
/// Return true if element found.
bool find(const T & element, uint * index)
{
return find(element, 0, m_size, index);
}
/// Return true if element found within the given range.
bool find(const T & element, uint first, uint count, uint * index)
{
for (uint i = first; i < first+count; i++) {
if (m_buffer[i] == element) {
if (index != NULL) *index = i;
return true;
}
}
return false;
}
/// Check if the given element is contained in the array.
bool contains(const T & e) const
{
return find(e, NULL);
}
/// Remove the element at the given index. This is an expensive operation!
void removeAt( uint index )
{
nvCheck(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.
void remove(const T & element)
{
for (uint i = 0; i < m_size; i++) {
removeAt(i);
break;
}
}
/// Insert the given element at the given index shifting all the elements up.
void insertAt( uint index, const T & val = T() )
{
nvCheck( index >= 0 && index <= m_size );
resize( 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.
void append(const Array<T> & other)
{
append(other.m_buffer, other.m_size);
}
/// Append the given data to our vector.
void append(const T other[], uint count)
{
if( count > 0 ) {
const uint old_size = m_size;
resize(m_size + count);
// Must use operator=() to copy elements, in case of side effects (e.g. ref-counting).
for( uint i = 0; i < count; i++ ) {
m_buffer[old_size + i] = other[i];
}
}
}
/// Remove the given element by replacing it with the last one.
void replaceWithLast(uint index)
{
nvDebugCheck( index < m_size );
swap(m_buffer[index], back());
//m_buffer[index] = back();
(m_buffer+m_size-1)->~T();
m_size--;
}
/// Resize the vector preserving existing elements.
void resize(uint new_size)
{
uint i;
uint old_size = m_size;
m_size = new_size;
// Destruct old elements (if we're shrinking).
for( i = new_size; i < old_size; i++ ) {
(m_buffer+i)->~T(); // Explicit call to the destructor
}
if( m_size == 0 ) {
//Allocate(0); // Don't shrink automatically.
}
else if( m_size <= m_buffer_size/* && m_size > m_buffer_size >> 1*/) {
// don't compact yet.
nvDebugCheck(m_buffer != NULL);
}
else {
uint new_buffer_size;
if( m_buffer_size == 0 ) {
// first allocation
new_buffer_size = m_size;
}
else {
// growing
new_buffer_size = m_size + (m_size >> 2);
}
allocate( new_buffer_size );
}
// Call default constructors
for( i = old_size; i < new_size; i++ ) {
new(m_buffer+i) T; // placement new
}
}
/// Resize the vector preserving existing elements and initializing the
/// new ones with the given value.
void resize( uint new_size, const T &elem )
{
uint i;
uint old_size = m_size;
m_size = new_size;
// Destruct old elements (if we're shrinking).
for( i = new_size; i < old_size; i++ ) {
(m_buffer+i)->~T(); // Explicit call to the destructor
}
if( m_size == 0 ) {
//Allocate(0); // Don't shrink automatically.
}
else if( m_size <= m_buffer_size && m_size > m_buffer_size >> 1 ) {
// don't compact yet.
}
else {
uint new_buffer_size;
if( m_buffer_size == 0 ) {
// first allocation
new_buffer_size = m_size;
}
else {
// growing
new_buffer_size = m_size + (m_size >> 2);
}
allocate( new_buffer_size );
}
// Call copy constructors
for( i = old_size; i < new_size; i++ ) {
new(m_buffer+i) T( elem ); // placement new
}
}
/// Clear the buffer.
void clear()
{
resize(0);
}
/// Shrink the allocated vector.
void shrink()
{
if (m_size < m_buffer_size) {
allocate(m_size);
}
}
/// Preallocate space.
void reserve(uint desired_size)
{
if (desired_size > m_buffer_size) {
allocate( desired_size );
}
}
/// Copy elements to this array. Resizes it if needed.
void copy(const T * ptr, uint num)
{
resize( num );
for (uint i = 0; i < m_size; i++) {
m_buffer[i] = ptr[i];
}
}
/// Assignment operator.
Array<T> & operator=( const Array<T> & a )
{
copy(a.m_buffer, a.m_size);
return *this;
}
/// Array serialization.
friend Stream & operator<< ( Stream & s, Array<T> & 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;
}
// Array enumerator.
typedef uint PseudoIndex;
PseudoIndex start() const { return 0; }
bool isDone(const PseudoIndex & i) const { nvDebugCheck(i <= this->m_size); return i == this->m_size; };
void advance(PseudoIndex & i) const { nvDebugCheck(i <= this->m_size); i++; }
#if NV_CC_MSVC
T & operator[]( const PseudoIndexWrapper & i ) {
return m_buffer[i(this)];
}
const T & operator[]( const PseudoIndexWrapper & i ) const {
return m_buffer[i(this)];
}
#endif
/// Swap the members of this vector and the given vector.
friend void swap(Array<T> & a, Array<T> & b)
{
swap(a.m_buffer, b.m_buffer);
swap(a.m_size, b.m_size);
swap(a.m_buffer_size, b.m_buffer_size);
}
private:
/// Change buffer size.
void allocate( uint rsize )
{
m_buffer_size = rsize;
// free the buffer.
if (m_buffer_size == 0) {
if (m_buffer) {
mem::free( m_buffer );
m_buffer = NULL;
}
}
// realloc the buffer
else {
if (m_buffer) m_buffer = (T *) mem::realloc( m_buffer, sizeof(T) * m_buffer_size );
else m_buffer = (T *) mem::malloc( sizeof(T) * m_buffer_size );
}
}
private:
T * m_buffer;
uint m_size;
uint m_buffer_size;
};
} // nv namespace
#endif // NV_CORE_ARRAY_H

View File

@ -2,8 +2,8 @@ PROJECT(nvcore)
SET(CORE_SRCS SET(CORE_SRCS
nvcore.h nvcore.h
Algorithms.h Utils.h
Containers.h Array.h HashMap.h
Debug.h Debug.cpp Debug.h Debug.cpp
DefsGnucDarwin.h DefsGnucDarwin.h
DefsGnucLinux.h DefsGnucLinux.h
@ -13,7 +13,7 @@ SET(CORE_SRCS
Library.h Library.cpp Library.h Library.cpp
Memory.h Memory.cpp Memory.h Memory.cpp
Ptr.h Ptr.h
RefCounted.h RefCounted.cpp RefCounted.h
StrLib.h StrLib.cpp StrLib.h StrLib.cpp
Stream.h Stream.h
StdStream.h StdStream.h
@ -27,7 +27,7 @@ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
ADD_DEFINITIONS(-DNVCORE_EXPORTS) ADD_DEFINITIONS(-DNVCORE_EXPORTS)
IF(UNIX) IF(UNIX)
SET(LIBS ${LIBS} ${CMAKE_DL_LIBS}) SET(LIBS ${LIBS} ${CMAKE_DL_LIBS})
ENDIF(UNIX) ENDIF(UNIX)
IF(NVCORE_SHARED) IF(NVCORE_SHARED)

File diff suppressed because it is too large Load Diff

View File

@ -1,51 +1,51 @@
// This code is in the public domain -- castanyo@yahoo.es // This code is in the public domain -- Ignacio Casta<74>o <castano@gmail.com>
#include "Debug.h" #include "Debug.h"
#include "StrLib.h" #include "StrLib.h"
// Extern // Extern
#if NV_OS_WIN32 //&& NV_CC_MSVC #if NV_OS_WIN32 //&& NV_CC_MSVC
# define WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN
# define VC_EXTRALEAN # define VC_EXTRALEAN
# include <windows.h> # include <windows.h>
# include <direct.h> # include <direct.h>
# if NV_CC_MSVC # if NV_CC_MSVC
# include <crtdbg.h> # include <crtdbg.h>
# if _MSC_VER < 1300 # if _MSC_VER < 1300
# define DECLSPEC_DEPRECATED # define DECLSPEC_DEPRECATED
// VC6: change this path to your Platform SDK headers // VC6: change this path to your Platform SDK headers
# include <dbghelp.h> // must be XP version of file # include <dbghelp.h> // must be XP version of file
// include "M:\\dev7\\vs\\devtools\\common\\win32sdk\\include\\dbghelp.h" // include "M:\\dev7\\vs\\devtools\\common\\win32sdk\\include\\dbghelp.h"
# else # else
// VC7: ships with updated headers // VC7: ships with updated headers
# include <dbghelp.h> # include <dbghelp.h>
# endif # endif
# endif # endif
#endif #endif
#if !NV_OS_WIN32 && defined(HAVE_SIGNAL_H) #if !NV_OS_WIN32 && defined(HAVE_SIGNAL_H)
# include <signal.h> # include <signal.h>
#endif #endif
#if NV_OS_LINUX && defined(HAVE_EXECINFO_H) #if NV_OS_LINUX && defined(HAVE_EXECINFO_H)
# include <execinfo.h> // backtrace # include <execinfo.h> // backtrace
# if NV_CC_GNUC // defined(HAVE_CXXABI_H) # if NV_CC_GNUC // defined(HAVE_CXXABI_H)
# include <cxxabi.h> # include <cxxabi.h>
# endif # endif
#endif #endif
#if NV_OS_DARWIN || NV_OS_FREEBSD #if NV_OS_DARWIN || NV_OS_FREEBSD
# include <unistd.h> // getpid # include <unistd.h> // getpid
# include <sys/types.h> # include <sys/types.h>
# include <sys/sysctl.h> // sysctl # include <sys/sysctl.h> // sysctl
# include <sys/ucontext.h> # include <sys/ucontext.h>
# undef HAVE_EXECINFO_H # undef HAVE_EXECINFO_H
# if defined(HAVE_EXECINFO_H) // only after OSX 10.5 # if defined(HAVE_EXECINFO_H) // only after OSX 10.5
# include <execinfo.h> // backtrace # include <execinfo.h> // backtrace
# if NV_CC_GNUC // defined(HAVE_CXXABI_H) # if NV_CC_GNUC // defined(HAVE_CXXABI_H)
# include <cxxabi.h> # include <cxxabi.h>
# endif # endif
# endif # endif
#endif #endif
#include <stdexcept> // std::runtime_error #include <stdexcept> // std::runtime_error
@ -56,149 +56,149 @@ using namespace nv;
namespace namespace
{ {
static MessageHandler * s_message_handler = NULL; static MessageHandler * s_message_handler = NULL;
static AssertHandler * s_assert_handler = NULL; static AssertHandler * s_assert_handler = NULL;
static bool s_sig_handler_enabled = false; static bool s_sig_handler_enabled = false;
#if NV_OS_WIN32 && NV_CC_MSVC #if NV_OS_WIN32 && NV_CC_MSVC
// Old exception filter. // Old exception filter.
static LPTOP_LEVEL_EXCEPTION_FILTER s_old_exception_filter = NULL; static LPTOP_LEVEL_EXCEPTION_FILTER s_old_exception_filter = NULL;
#elif !NV_OS_WIN32 && defined(HAVE_SIGNAL_H) #elif !NV_OS_WIN32 && defined(HAVE_SIGNAL_H)
// Old signal handlers. // Old signal handlers.
struct sigaction s_old_sigsegv; struct sigaction s_old_sigsegv;
struct sigaction s_old_sigtrap; struct sigaction s_old_sigtrap;
struct sigaction s_old_sigfpe; struct sigaction s_old_sigfpe;
struct sigaction s_old_sigbus; struct sigaction s_old_sigbus;
#endif #endif
#if NV_OS_WIN32 && NV_CC_MSVC #if NV_OS_WIN32 && NV_CC_MSVC
// TODO write minidump // TODO write minidump
static LONG WINAPI nvTopLevelFilter( struct _EXCEPTION_POINTERS * pExceptionInfo) static LONG WINAPI nvTopLevelFilter( struct _EXCEPTION_POINTERS * pExceptionInfo)
{ {
NV_UNUSED(pExceptionInfo); NV_UNUSED(pExceptionInfo);
/* BOOL (WINAPI * Dump) (HANDLE, DWORD, HANDLE, MINIDUMP_TYPE, PMINIDUMP_EXCEPTION_INFORMATION, PMINIDUMP_USER_STREAM_INFORMATION, PMINIDUMP_CALLBACK_INFORMATION ); /* BOOL (WINAPI * Dump) (HANDLE, DWORD, HANDLE, MINIDUMP_TYPE, PMINIDUMP_EXCEPTION_INFORMATION, PMINIDUMP_USER_STREAM_INFORMATION, PMINIDUMP_CALLBACK_INFORMATION );
AutoString dbghelp_path(512); AutoString dbghelp_path(512);
getcwd(dbghelp_path, 512); getcwd(dbghelp_path, 512);
dbghelp_path.Append("\\DbgHelp.dll"); dbghelp_path.Append("\\DbgHelp.dll");
nvTranslatePath(dbghelp_path); nvTranslatePath(dbghelp_path);
PiLibrary DbgHelp_lib(dbghelp_path, true); PiLibrary DbgHelp_lib(dbghelp_path, true);
if( !DbgHelp_lib.IsValid() ) { if( !DbgHelp_lib.IsValid() ) {
nvDebug("*** 'DbgHelp.dll' not found.\n"); nvDebug("*** 'DbgHelp.dll' not found.\n");
return EXCEPTION_CONTINUE_SEARCH; return EXCEPTION_CONTINUE_SEARCH;
} }
if( !DbgHelp_lib.BindSymbol( (void **)&Dump, "MiniDumpWriteDump" ) ) { if( !DbgHelp_lib.BindSymbol( (void **)&Dump, "MiniDumpWriteDump" ) ) {
nvDebug("*** 'DbgHelp.dll' too old.\n"); nvDebug("*** 'DbgHelp.dll' too old.\n");
return EXCEPTION_CONTINUE_SEARCH; return EXCEPTION_CONTINUE_SEARCH;
} }
// create the file // create the file
HANDLE hFile = ::CreateFile( "nv.dmp", GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); HANDLE hFile = ::CreateFile( "nv.dmp", GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
if( hFile == INVALID_HANDLE_VALUE ) { if( hFile == INVALID_HANDLE_VALUE ) {
nvDebug("*** Failed to create dump file.\n"); nvDebug("*** Failed to create dump file.\n");
return EXCEPTION_CONTINUE_SEARCH; return EXCEPTION_CONTINUE_SEARCH;
} }
_MINIDUMP_EXCEPTION_INFORMATION ExInfo; _MINIDUMP_EXCEPTION_INFORMATION ExInfo;
ExInfo.ThreadId = ::GetCurrentThreadId(); ExInfo.ThreadId = ::GetCurrentThreadId();
ExInfo.ExceptionPointers = pExceptionInfo; ExInfo.ExceptionPointers = pExceptionInfo;
ExInfo.ClientPointers = NULL; ExInfo.ClientPointers = NULL;
// write the dump // write the dump
bool ok = Dump( GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpNormal, &ExInfo, NULL, NULL )!=0; bool ok = Dump( GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpNormal, &ExInfo, NULL, NULL )!=0;
::CloseHandle(hFile); ::CloseHandle(hFile);
if( !ok ) { if( !ok ) {
nvDebug("*** Failed to save dump file.\n"); nvDebug("*** Failed to save dump file.\n");
return EXCEPTION_CONTINUE_SEARCH; return EXCEPTION_CONTINUE_SEARCH;
} }
nvDebug("--- Dump file saved.\n"); nvDebug("--- Dump file saved.\n");
*/ */
return EXCEPTION_CONTINUE_SEARCH; return EXCEPTION_CONTINUE_SEARCH;
} }
#elif !NV_OS_WIN32 && defined(HAVE_SIGNAL_H) // NV_OS_LINUX || NV_OS_DARWIN #elif !NV_OS_WIN32 && defined(HAVE_SIGNAL_H) // NV_OS_LINUX || NV_OS_DARWIN
#if defined(HAVE_EXECINFO_H) // NV_OS_LINUX #if defined(HAVE_EXECINFO_H) // NV_OS_LINUX
static bool nvHasStackTrace() { static bool nvHasStackTrace() {
#if NV_OS_DARWIN #if NV_OS_DARWIN
return backtrace != NULL; return backtrace != NULL;
#else #else
return true; return true;
#endif #endif
} }
static void nvPrintStackTrace(void * trace[], int size, int start=0) { static void nvPrintStackTrace(void * trace[], int size, int start=0) {
char ** string_array = backtrace_symbols(trace, size); char ** string_array = backtrace_symbols(trace, size);
nvDebug( "\nDumping stacktrace:\n" ); nvDebug( "\nDumping stacktrace:\n" );
for(int i = start; i < size-1; i++ ) { for(int i = start; i < size-1; i++ ) {
# if NV_CC_GNUC // defined(HAVE_CXXABI_H) # if NV_CC_GNUC // defined(HAVE_CXXABI_H)
char * begin = strchr(string_array[i], '('); char * begin = strchr(string_array[i], '(');
char * end = strchr(string_array[i], '+'); char * end = strchr(string_array[i], '+');
if( begin != 0 && begin < end ) { if( begin != 0 && begin < end ) {
int stat; int stat;
*end = '\0'; *end = '\0';
*begin = '\0'; *begin = '\0';
char * module = string_array[i]; char * module = string_array[i];
char * name = abi::__cxa_demangle(begin+1, 0, 0, &stat); char * name = abi::__cxa_demangle(begin+1, 0, 0, &stat);
if( name == NULL || begin[1] != '_' || begin[2] != 'Z' ) { if( name == NULL || begin[1] != '_' || begin[2] != 'Z' ) {
nvDebug( " In: [%s] '%s'\n", module, begin+1 ); nvDebug( " In: [%s] '%s'\n", module, begin+1 );
} }
else { else {
nvDebug( " In: [%s] '%s'\n", module, name ); nvDebug( " In: [%s] '%s'\n", module, name );
} }
free(name); free(name);
} }
else { else {
nvDebug( " In: '%s'\n", string_array[i] ); nvDebug( " In: '%s'\n", string_array[i] );
} }
# else # else
nvDebug( " In: '%s'\n", string_array[i] ); nvDebug( " In: '%s'\n", string_array[i] );
# endif # endif
} }
nvDebug("\n"); nvDebug("\n");
free(string_array); free(string_array);
} }
#endif // defined(HAVE_EXECINFO_H) #endif // defined(HAVE_EXECINFO_H)
static void * callerAddress(void * secret) static void * callerAddress(void * secret)
{ {
# if NV_OS_DARWIN #if NV_OS_DARWIN
# if defined(_STRUCT_MCONTEXT) # if defined(_STRUCT_MCONTEXT)
# if NV_CPU_PPC # if NV_CPU_PPC
ucontext_t * ucp = (ucontext_t *)secret; ucontext_t * ucp = (ucontext_t *)secret;
return (void *) ucp->uc_mcontext->__ss.__srr0; return (void *) ucp->uc_mcontext->__ss.__srr0;
# elif NV_CPU_X86 # elif NV_CPU_X86
ucontext_t * ucp = (ucontext_t *)secret; ucontext_t * ucp = (ucontext_t *)secret;
return (void *) ucp->uc_mcontext->__ss.__eip; return (void *) ucp->uc_mcontext->__ss.__eip;
# endif # endif
# else # else
# if NV_CPU_PPC # if NV_CPU_PPC
ucontext_t * ucp = (ucontext_t *)secret; ucontext_t * ucp = (ucontext_t *)secret;
return (void *) ucp->uc_mcontext->ss.srr0; return (void *) ucp->uc_mcontext->ss.srr0;
# elif NV_CPU_X86 # elif NV_CPU_X86
ucontext_t * ucp = (ucontext_t *)secret; ucontext_t * ucp = (ucontext_t *)secret;
return (void *) ucp->uc_mcontext->ss.eip; return (void *) ucp->uc_mcontext->ss.eip;
# endif # endif
# endif # endif
# elif NV_OS_FREEBSD # elif NV_OS_FREEBSD
# if NV_CPU_X86_64 # if NV_CPU_X86_64
ucontext_t * ucp = (ucontext_t *)secret; ucontext_t * ucp = (ucontext_t *)secret;
@ -207,253 +207,249 @@ namespace
ucontext_t * ucp = (ucontext_t *)secret; ucontext_t * ucp = (ucontext_t *)secret;
return (void *)ucp->uc_mcontext.mc_eip; return (void *)ucp->uc_mcontext.mc_eip;
# endif # endif
# else #else
# if NV_CPU_X86_64 # if NV_CPU_X86_64
// #define REG_RIP REG_INDEX(rip) // seems to be 16 // #define REG_RIP REG_INDEX(rip) // seems to be 16
ucontext_t * ucp = (ucontext_t *)secret; ucontext_t * ucp = (ucontext_t *)secret;
return (void *)ucp->uc_mcontext.gregs[REG_RIP]; return (void *)ucp->uc_mcontext.gregs[REG_RIP];
# elif NV_CPU_X86 # elif NV_CPU_X86
ucontext_t * ucp = (ucontext_t *)secret; ucontext_t * ucp = (ucontext_t *)secret;
return (void *)ucp->uc_mcontext.gregs[14/*REG_EIP*/]; return (void *)ucp->uc_mcontext.gregs[14/*REG_EIP*/];
# elif NV_CPU_PPC # elif NV_CPU_PPC
ucontext_t * ucp = (ucontext_t *)secret; ucontext_t * ucp = (ucontext_t *)secret;
return (void *) ucp->uc_mcontext.regs->nip; return (void *) ucp->uc_mcontext.regs->nip;
# endif # endif
# endif #endif
// How to obtain the instruction pointers in different platforms, from mlton's source code. // How to obtain the instruction pointers in different platforms, from mlton's source code.
// http://mlton.org/ // http://mlton.org/
// OpenBSD && NetBSD // OpenBSD && NetBSD
// ucp->sc_eip // ucp->sc_eip
// FreeBSD: // FreeBSD:
// ucp->uc_mcontext.mc_eip // ucp->uc_mcontext.mc_eip
// HPUX: // HPUX:
// ucp->uc_link // ucp->uc_link
// Solaris: // Solaris:
// ucp->uc_mcontext.gregs[REG_PC] // ucp->uc_mcontext.gregs[REG_PC]
// Linux hppa: // Linux hppa:
// uc->uc_mcontext.sc_iaoq[0] & ~0x3UL // uc->uc_mcontext.sc_iaoq[0] & ~0x3UL
// Linux sparc: // Linux sparc:
// ((struct sigcontext*) secret)->sigc_regs.tpc // ((struct sigcontext*) secret)->sigc_regs.tpc
// Linux sparc64: // Linux sparc64:
// ((struct sigcontext*) secret)->si_regs.pc // ((struct sigcontext*) secret)->si_regs.pc
// potentially correct for other archs: // potentially correct for other archs:
// Linux alpha: ucp->m_context.sc_pc // Linux alpha: ucp->m_context.sc_pc
// Linux arm: ucp->m_context.ctx.arm_pc // Linux arm: ucp->m_context.ctx.arm_pc
// Linux ia64: ucp->m_context.sc_ip & ~0x3UL // Linux ia64: ucp->m_context.sc_ip & ~0x3UL
// Linux mips: ucp->m_context.sc_pc // Linux mips: ucp->m_context.sc_pc
// Linux s390: ucp->m_context.sregs->regs.psw.addr // Linux s390: ucp->m_context.sregs->regs.psw.addr
} }
static void nvSigHandler(int sig, siginfo_t *info, void *secret) static void nvSigHandler(int sig, siginfo_t *info, void *secret)
{ {
void * pnt = callerAddress(secret); void * pnt = callerAddress(secret);
// Do something useful with siginfo_t // Do something useful with siginfo_t
if (sig == SIGSEGV) { if (sig == SIGSEGV) {
if (pnt != NULL) nvDebug("Got signal %d, faulty address is %p, from %p\n", sig, info->si_addr, pnt); 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 nvDebug("Got signal %d, faulty address is %p\n", sig, info->si_addr);
} }
else if(sig == SIGTRAP) { else if(sig == SIGTRAP) {
nvDebug("Breakpoint hit.\n"); nvDebug("Breakpoint hit.\n");
} }
else { else {
nvDebug("Got signal %d\n", sig); nvDebug("Got signal %d\n", sig);
} }
# if defined(HAVE_EXECINFO_H) #if defined(HAVE_EXECINFO_H)
if (nvHasStackTrace()) // in case of weak linking if (nvHasStackTrace()) // in case of weak linking
{ {
void * trace[64]; void * trace[64];
int size = backtrace(trace, 64); int size = backtrace(trace, 64);
if (pnt != NULL) { if (pnt != NULL) {
// Overwrite sigaction with caller's address. // Overwrite sigaction with caller's address.
trace[1] = pnt; trace[1] = pnt;
} }
nvPrintStackTrace(trace, size, 1); nvPrintStackTrace(trace, size, 1);
} }
# endif // defined(HAVE_EXECINFO_H) #endif // defined(HAVE_EXECINFO_H)
exit(0); exit(0);
} }
#endif // defined(HAVE_SIGNAL_H) #endif // defined(HAVE_SIGNAL_H)
#if NV_OS_WIN32 //&& NV_CC_MSVC #if NV_OS_WIN32 //&& NV_CC_MSVC
/** Win32 asset handler. */ /** Win32 asset handler. */
struct Win32AssertHandler : public AssertHandler struct Win32AssertHandler : public AssertHandler
{ {
// Code from Daniel Vogel. // Code from Daniel Vogel.
static bool isDebuggerPresent() static bool isDebuggerPresent()
{ {
bool result = false; bool result = false;
HINSTANCE kern_lib = LoadLibraryExA( "kernel32.dll", NULL, 0 ); HINSTANCE kern_lib = LoadLibraryExA( "kernel32.dll", NULL, 0 );
if( kern_lib ) { if( kern_lib ) {
FARPROC lIsDebuggerPresent = GetProcAddress( kern_lib, "IsDebuggerPresent" ); FARPROC lIsDebuggerPresent = GetProcAddress( kern_lib, "IsDebuggerPresent" );
if( lIsDebuggerPresent && lIsDebuggerPresent() ) { if( lIsDebuggerPresent && lIsDebuggerPresent() ) {
result = true; result = true;
} }
FreeLibrary( kern_lib ); FreeLibrary( kern_lib );
} }
return result; return result;
} }
// Flush the message queue. This is necessary for the message box to show up. // Flush the message queue. This is necessary for the message box to show up.
static void flushMessageQueue() static void flushMessageQueue()
{ {
MSG msg; MSG msg;
while( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) { while( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) {
if( msg.message == WM_QUIT ) break; if( msg.message == WM_QUIT ) break;
TranslateMessage( &msg ); TranslateMessage( &msg );
DispatchMessage( &msg ); DispatchMessage( &msg );
} }
} }
// Assert handler method. // Assert handler method.
virtual int assert( const char * exp, const char * file, int line, const char * func/*=NULL*/ ) virtual int assertion( const char * exp, const char * file, int line, const char * func/*=NULL*/ )
{ {
int ret = NV_ABORT_EXIT; int ret = NV_ABORT_EXIT;
StringBuilder error_string; StringBuilder error_string;
if( func != NULL ) { 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 ); 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 ); nvDebug( error_string );
} }
else { else {
error_string.format( "*** Assertion failed: %s\n On file: %s\n On line: %d\n ", exp, file, line ); error_string.format( "*** Assertion failed: %s\n On file: %s\n On line: %d\n ", exp, file, line );
nvDebug( error_string ); nvDebug( error_string );
} }
#if _DEBUG if (isDebuggerPresent()) {
return NV_ABORT_DEBUG;
if( isDebuggerPresent() ) { }
return NV_ABORT_DEBUG;
} flushMessageQueue();
int action = MessageBoxA(NULL, error_string, "Assertion failed", MB_ABORTRETRYIGNORE|MB_ICONERROR);
flushMessageQueue(); switch( action ) {
int action = MessageBoxA(NULL, error_string, "Assertion failed", MB_ABORTRETRYIGNORE|MB_ICONERROR); case IDRETRY:
switch( action ) { ret = NV_ABORT_DEBUG;
case IDRETRY: break;
ret = NV_ABORT_DEBUG; case IDIGNORE:
break; ret = NV_ABORT_IGNORE;
case IDIGNORE: break;
ret = NV_ABORT_IGNORE; case IDABORT:
break; default:
case IDABORT: ret = NV_ABORT_EXIT;
default: break;
ret = NV_ABORT_EXIT; }
break; /*if( _CrtDbgReport( _CRT_ASSERT, file, line, module, exp ) == 1 ) {
} return NV_ABORT_DEBUG;
/*if( _CrtDbgReport( _CRT_ASSERT, file, line, module, exp ) == 1 ) { }*/
return NV_ABORT_DEBUG;
}*/ if( ret == NV_ABORT_EXIT ) {
// Exit cleanly.
#endif throw std::runtime_error("Assertion failed");
}
if( ret == NV_ABORT_EXIT ) {
// Exit cleanly. return ret;
throw std::runtime_error("Assertion failed"); }
} };
return ret;
}
};
#else #else
/** Unix asset handler. */
struct UnixAssertHandler : public AssertHandler
{
bool isDebuggerPresent()
{
# if 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
}
// Assert handler method.
virtual int assert(const char * exp, const char * file, int line, const char * func)
{
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( isDebuggerPresent() ) {
return NV_ABORT_DEBUG;
}
# endif
# if defined(HAVE_EXECINFO_H) /** Unix asset handler. */
if (nvHasStackTrace()) struct UnixAssertHandler : public AssertHandler
{ {
void * trace[64]; bool isDebuggerPresent()
int size = backtrace(trace, 64); {
nvPrintStackTrace(trace, size, 2); #if NV_OS_DARWIN
} int mib[4];
# endif 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
}
// Assert handler method.
virtual int assertion(const char * exp, const char * file, int line, const char * func)
{
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 (isDebuggerPresent()) {
return NV_ABORT_DEBUG;
}
#endif
#if defined(HAVE_EXECINFO_H)
if (nvHasStackTrace())
{
void * trace[64];
int size = backtrace(trace, 64);
nvPrintStackTrace(trace, size, 2);
}
#endif
// Exit cleanly.
throw std::runtime_error("Assertion failed");
}
};
// Exit cleanly.
throw std::runtime_error("Assertion failed");
}
};
#endif #endif
} // namespace } // namespace
/// Handle assertion through the asset handler. /// Handle assertion through the asset handler.
int nvAbort(const char * exp, const char * file, int line, const char * func) int nvAbort(const char * exp, const char * file, int line, const char * func/*=NULL*/)
{ {
#if NV_OS_WIN32 //&& NV_CC_MSVC #if NV_OS_WIN32 //&& NV_CC_MSVC
static Win32AssertHandler s_default_assert_handler; static Win32AssertHandler s_default_assert_handler;
#else #else
static UnixAssertHandler s_default_assert_handler; static UnixAssertHandler s_default_assert_handler;
#endif #endif
if( s_assert_handler != NULL ) { if (s_assert_handler != NULL) {
return s_assert_handler->assert( exp, file, line, func ); return s_assert_handler->assertion( exp, file, line, func );
} }
else { else {
return s_default_assert_handler.assert( exp, file, line, func ); return s_default_assert_handler.assertion( exp, file, line, func );
} }
} }
/// Shows a message through the message handler. /// Shows a message through the message handler.
void NV_CDECL nvDebug(const char *msg, ...) void NV_CDECL nvDebugPrint(const char *msg, ...)
{ {
va_list arg; va_list arg;
va_start(arg,msg); va_start(arg,msg);
if( s_message_handler != NULL ) { if (s_message_handler != NULL) {
s_message_handler->log( msg, arg ); s_message_handler->log( msg, arg );
} }
va_end(arg); va_end(arg);
} }
@ -461,12 +457,12 @@ void NV_CDECL nvDebug(const char *msg, ...)
void debug::dumpInfo() void debug::dumpInfo()
{ {
#if !NV_OS_WIN32 && defined(HAVE_SIGNAL_H) && defined(HAVE_EXECINFO_H) #if !NV_OS_WIN32 && defined(HAVE_SIGNAL_H) && defined(HAVE_EXECINFO_H)
if (nvHasStackTrace()) if (nvHasStackTrace())
{ {
void * trace[64]; void * trace[64];
int size = backtrace(trace, 64); int size = backtrace(trace, 64);
nvPrintStackTrace(trace, size, 1); nvPrintStackTrace(trace, size, 1);
} }
#endif #endif
} }
@ -474,72 +470,72 @@ void debug::dumpInfo()
/// Set the debug message handler. /// Set the debug message handler.
void debug::setMessageHandler(MessageHandler * message_handler) void debug::setMessageHandler(MessageHandler * message_handler)
{ {
s_message_handler = message_handler; s_message_handler = message_handler;
} }
/// Reset the debug message handler. /// Reset the debug message handler.
void debug::resetMessageHandler() void debug::resetMessageHandler()
{ {
s_message_handler = NULL; s_message_handler = NULL;
} }
/// Set the assert handler. /// Set the assert handler.
void debug::setAssertHandler(AssertHandler * assert_handler) void debug::setAssertHandler(AssertHandler * assert_handler)
{ {
s_assert_handler = assert_handler; s_assert_handler = assert_handler;
} }
/// Reset the assert handler. /// Reset the assert handler.
void debug::resetAssertHandler() void debug::resetAssertHandler()
{ {
s_assert_handler = NULL; s_assert_handler = NULL;
} }
/// Enable signal handler. /// Enable signal handler.
void debug::enableSigHandler() void debug::enableSigHandler()
{ {
nvCheck(s_sig_handler_enabled != true); nvCheck(s_sig_handler_enabled != true);
s_sig_handler_enabled = true; s_sig_handler_enabled = true;
#if NV_OS_WIN32 && NV_CC_MSVC #if NV_OS_WIN32 && NV_CC_MSVC
s_old_exception_filter = ::SetUnhandledExceptionFilter( nvTopLevelFilter ); s_old_exception_filter = ::SetUnhandledExceptionFilter( nvTopLevelFilter );
#elif !NV_OS_WIN32 && defined(HAVE_SIGNAL_H) #elif !NV_OS_WIN32 && defined(HAVE_SIGNAL_H)
// Install our signal handler // Install our signal handler
struct sigaction sa; struct sigaction sa;
sa.sa_sigaction = nvSigHandler; sa.sa_sigaction = nvSigHandler;
sigemptyset (&sa.sa_mask); sigemptyset (&sa.sa_mask);
sa.sa_flags = SA_ONSTACK | SA_RESTART | SA_SIGINFO; 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);
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 #endif
} }
/// Disable signal handler. /// Disable signal handler.
void debug::disableSigHandler() void debug::disableSigHandler()
{ {
nvCheck(s_sig_handler_enabled == true); nvCheck(s_sig_handler_enabled == true);
s_sig_handler_enabled = false; s_sig_handler_enabled = false;
#if NV_OS_WIN32 && NV_CC_MSVC #if NV_OS_WIN32 && NV_CC_MSVC
::SetUnhandledExceptionFilter( s_old_exception_filter ); ::SetUnhandledExceptionFilter( s_old_exception_filter );
s_old_exception_filter = NULL; s_old_exception_filter = NULL;
#elif !NV_OS_WIN32 && defined(HAVE_SIGNAL_H) #elif !NV_OS_WIN32 && defined(HAVE_SIGNAL_H)
sigaction(SIGSEGV, &s_old_sigsegv, NULL); sigaction(SIGSEGV, &s_old_sigsegv, NULL);
sigaction(SIGTRAP, &s_old_sigtrap, NULL); sigaction(SIGTRAP, &s_old_sigtrap, NULL);
sigaction(SIGFPE, &s_old_sigfpe, NULL); sigaction(SIGFPE, &s_old_sigfpe, NULL);
sigaction(SIGBUS, &s_old_sigbus, NULL); sigaction(SIGBUS, &s_old_sigbus, NULL);
#endif #endif
} }

View File

@ -1,131 +1,173 @@
// This code is in the public domain -- castanyo@yahoo.es // This code is in the public domain -- Ignacio Casta<74>o <castano@gmail.com>
#pragma once
#ifndef NV_CORE_DEBUG_H #ifndef NV_CORE_DEBUG_H
#define NV_CORE_DEBUG_H #define NV_CORE_DEBUG_H
#include "nvcore.h" #include "nvcore.h"
#if defined(HAVE_STDARG_H) #if defined(HAVE_STDARG_H)
# include <stdarg.h> // va_list # include <stdarg.h> // va_list
#endif #endif
#define NV_ABORT_DEBUG 1 #define NV_ABORT_DEBUG 1
#define NV_ABORT_IGNORE 2 #define NV_ABORT_IGNORE 2
#define NV_ABORT_EXIT 3 #define NV_ABORT_EXIT 3
#undef assert // avoid conflicts with assert method.
#define nvNoAssert(exp) \ #define nvNoAssert(exp) \
do { \ do { \
(void)sizeof(exp); \ (void)sizeof(exp); \
} while(0) } while(0)
#if NV_NO_ASSERT #if NV_NO_ASSERT
# define nvAssert(exp) nvNoAssert(exp) # define nvAssert(exp) nvNoAssert(exp)
# define nvCheck(exp) nvNoAssert(exp) # define nvCheck(exp) nvNoAssert(exp)
# define nvDebugAssert(exp) nvNoAssert(exp) # define nvDebugAssert(exp) nvNoAssert(exp)
# define nvDebugCheck(exp) nvNoAssert(exp) # define nvDebugCheck(exp) nvNoAssert(exp)
# define nvDebugBreak() nvNoAssert(0) # define nvDebugBreak() nvNoAssert(0)
#else // NV_NO_ASSERT #else // NV_NO_ASSERT
# if NV_CC_MSVC # if NV_CC_MSVC
// @@ Does this work in msvc-6 and earlier? // @@ Does this work in msvc-6 and earlier?
// @@ Do I have to include <intrin.h> ? # define nvDebugBreak() __debugbreak()
# define nvDebugBreak() __debugbreak() //# define nvDebugBreak() __asm { int 3 }
// define nvDebugBreak() __asm int 3 # elif NV_CC_GNUC && NV_CPU_PPC && NV_OS_DARWIN
# elif NV_CC_GNUC && NV_CPU_PPC && NV_OS_DARWIN // @@ Use __builtin_trap() on GCC
# define nvDebugBreak() __asm__ volatile ("trap"); # define nvDebugBreak() __asm__ volatile ("trap");
# elif NV_CC_GNUC && NV_CPU_X86 && NV_OS_DARWIN # elif NV_CC_GNUC && NV_CPU_X86 && NV_OS_DARWIN
# define nvDebugBreak() __asm__ volatile ("int3"); # define nvDebugBreak() __asm__ volatile ("int3");
# elif NV_CC_GNUC && NV_CPU_X86 # elif NV_CC_GNUC && NV_CPU_X86
# define nvDebugBreak() __asm__ ( "int %0" : :"I"(3) ) # define nvDebugBreak() __asm__ ( "int %0" : :"I"(3) )
# else # else
# include <signal.h> # include <signal.h>
# define nvDebugBreak() raise(SIGTRAP); # define nvDebugBreak() raise(SIGTRAP);
// define nvDebugBreak() *((int *)(0)) = 0 // define nvDebugBreak() *((int *)(0)) = 0
# endif # endif
# define nvAssertMacro(exp) \ #define nvDebugBreakOnce() \
do { \ do { \
if(!(exp)) { \ static bool firstTime = true; \
if( nvAbort(#exp, __FILE__, __LINE__, __FUNC__) == NV_ABORT_DEBUG ) { \ if (firstTime) { firstTime = false; nvDebugBreak(); } \
nvDebugBreak(); \ } while(false)
} \
} \
} while(false)
# define nvAssert(exp) nvAssertMacro(exp) # define nvAssertMacro(exp) \
# define nvCheck(exp) nvAssertMacro(exp) do { \
if (!(exp)) { \
if (nvAbort(#exp, __FILE__, __LINE__, __FUNC__) == NV_ABORT_DEBUG) { \
nvDebugBreak(); \
} \
} \
} while(false)
# if defined(_DEBUG) # define nvAssertMacroWithIgnoreAll(exp) \
# define nvDebugAssert(exp) nvAssertMacro(exp) do { \
# define nvDebugCheck(exp) nvAssertMacro(exp) static bool ignoreAll = false; \
# else // _DEBUG if (!ignoreAll && !(exp)) { \
# define nvDebugAssert(exp) nvNoAssert(exp) if (nvAbort(#exp, __FILE__, __LINE__, __FUNC__) == NV_ABORT_DEBUG) { \
# define nvDebugCheck(exp) nvNoAssert(exp) nvDebugBreak(); \
# endif // _DEBUG } else { \
ignoreAll = true; \
} \
} \
} while(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 #endif // NV_NO_ASSERT
// Use nvAssume for very simple expresions only: nvAssume(0), nvAssume(value == true), etc. // Use nvAssume for very simple expresions only: nvAssume(0), nvAssume(value == true), etc.
#if defined(_DEBUG) #if !defined(_DEBUG)
# if NV_CC_MSVC # if NV_CC_MSVC
# define nvAssume(exp) __assume(exp) # define nvAssume(exp) __assume(exp)
# else # else
# define nvAssume(exp) nvCheck(exp) # define nvAssume(exp) nvCheck(exp)
# endif # endif
#else #else
# define nvAssume(exp) nvCheck(exp) # define nvAssume(exp) nvCheck(exp)
#endif #endif
#define nvError(x) nvAbort(x, __FILE__, __LINE__, __FUNC__) #define nvError(x) nvAbort(x, __FILE__, __LINE__, __FUNC__)
#define nvWarning(x) nvDebug("*** Warning %s/%d: %s\n", __FILE__, __LINE__, (x)) #define nvWarning(x) nvDebugPrint("*** Warning %s/%d: %s\n", __FILE__, __LINE__, (x))
#ifndef NV_DEBUG_PRINT
#if PI_CC_MSVC #define NV_DEBUG_PRINT 1 //defined(_DEBUG)
// @@ I'm not sure it's a good idea to use the default static assert.
# define nvStaticCheck(x) _STATIC_ASSERT(x)
#else
# define nvStaticCheck(x) typedef char NV_DO_STRING_JOIN2(__static_assert_,__LINE__)[(x)]
// define nvStaticCheck(x) switch(0) { case 0: case x:; }
#endif #endif
NVCORE_API int nvAbort(const char *exp, const char *file, int line, const char * func = 0); #if NV_DEBUG_PRINT
NVCORE_API void NV_CDECL nvDebug( const char *msg, ... ) __attribute__((format (printf, 1, 2))); #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
#if __cplusplus > 199711L
#define nvStaticCheck(x) static_assert(x)
#else
#define nvStaticCheck(x) typedef char NV_DO_STRING_JOIN2(__static_assert_,__LINE__)[(x)]
#endif
NVCORE_API int nvAbort(const char *exp, const char *file, int line, const char * func = NULL);
NVCORE_API void NV_CDECL nvDebugPrint( const char *msg, ... ) __attribute__((format (printf, 1, 2)));
namespace nv namespace nv
{ {
/** Message handler interface. */ inline bool isValidPtr(const void * ptr) {
struct MessageHandler { #if NV_CPU_X86_64
virtual void log(const char * str, va_list arg) = 0; if (ptr == NULL) return true;
virtual ~MessageHandler() {} if (reinterpret_cast<uint64>(ptr) < 0x10000ULL) return false;
}; if (reinterpret_cast<uint64>(ptr) >= 0x000007FFFFFEFFFFULL) return false;
#else
/** Assert handler interface. */ if (reinterpret_cast<uint>(ptr) == 0xcccccccc) return false;
struct AssertHandler { if (reinterpret_cast<uint>(ptr) == 0xcdcdcdcd) return false;
virtual int assert(const char *exp, const char *file, int line, const char *func = 0) = 0; if (reinterpret_cast<uint>(ptr) == 0xdddddddd) return false;
virtual ~AssertHandler() {} if (reinterpret_cast<uint>(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 = NULL) = 0;
virtual ~AssertHandler() {}
};
namespace debug namespace debug
{ {
NVCORE_API void dumpInfo(); NVCORE_API void dumpInfo();
// These functions are not thread safe. NVCORE_API void setMessageHandler( MessageHandler * messageHandler );
NVCORE_API void setMessageHandler( MessageHandler * messageHandler ); NVCORE_API void resetMessageHandler();
NVCORE_API void resetMessageHandler();
NVCORE_API void setAssertHandler( AssertHandler * assertHanlder );
NVCORE_API void setAssertHandler( AssertHandler * assertHanlder ); NVCORE_API void resetAssertHandler();
NVCORE_API void resetAssertHandler();
NVCORE_API void enableSigHandler();
NVCORE_API void enableSigHandler(); NVCORE_API void disableSigHandler();
NVCORE_API void disableSigHandler(); }
}
} // nv namespace } // nv namespace
#endif // NV_CORE_DEBUG_H #endif // NV_CORE_DEBUG_H

View File

@ -1,3 +1,5 @@
// This code is in the public domain -- Ignacio Casta<74>o <castano@gmail.com>
#ifndef NV_CORE_H #ifndef NV_CORE_H
#error "Do not include this file directly." #error "Do not include this file directly."
#endif #endif
@ -20,13 +22,15 @@
// Set standard function names. // Set standard function names.
#define snprintf _snprintf #define snprintf _snprintf
#if _MSC_VER < 1500 #if _MSC_VER < 1500
# define vsnprintf _vsnprintf # define vsnprintf _vsnprintf
#endif #endif
#define vsscanf _vsscanf #define vsscanf _vsscanf
#define chdir _chdir #define chdir _chdir
#define getcwd _getcwd #define getcwd _getcwd
#define va_copy(a, b) a = b #ifndef va_copy
#define va_copy(a, b) (a) = (b)
#endif
#if !defined restrict #if !defined restrict
#define restrict #define restrict
@ -59,20 +63,20 @@ typedef uint32 uint;
// Unwanted VC++ warnings to disable. // Unwanted VC++ warnings to disable.
/* /*
#pragma warning(disable : 4244) // conversion to float, possible loss of data #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 : 4245) // conversion from 'enum ' to 'unsigned long', signed/unsigned mismatch
#pragma warning(disable : 4100) // unreferenced formal parameter #pragma warning(disable : 4100) // unreferenced formal parameter
#pragma warning(disable : 4514) // unreferenced inline function has been removed #pragma warning(disable : 4514) // unreferenced inline function has been removed
#pragma warning(disable : 4710) // inline function not expanded #pragma warning(disable : 4710) // inline function not expanded
#pragma warning(disable : 4127) // Conditional expression is constant #pragma warning(disable : 4127) // Conditional expression is constant
#pragma warning(disable : 4305) // truncation from 'const double' to 'float' #pragma warning(disable : 4305) // truncation from 'const double' to 'float'
#pragma warning(disable : 4505) // unreferenced local function has been removed #pragma warning(disable : 4505) // unreferenced local function has been removed
#pragma warning(disable : 4702) // unreachable code in inline expanded function #pragma warning(disable : 4702) // unreachable code in inline expanded function
#pragma warning(disable : 4711) // function selected for automatic inlining #pragma warning(disable : 4711) // function selected for automatic inlining
#pragma warning(disable : 4725) // Pentium fdiv bug #pragma warning(disable : 4725) // Pentium fdiv bug
#pragma warning(disable : 4786) // Identifier was truncated and cannot be debugged. #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(disable : 4675) // resolved overload was found by argument-dependent lookup
*/ */

View File

@ -1,13 +1,13 @@
// This code is in the public domain -- castano@gmail.com // This code is in the public domain -- castano@gmail.com
#include "FileSystem.h" #include "FileSystem.h"
#include <nvcore/nvcore.h>
#if NV_OS_WIN32 #if NV_OS_WIN32
#define _CRT_NONSTDC_NO_WARNINGS // _chdir is defined deprecated, but that's a bug, chdir is deprecated, _chdir is *not*. #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 <shlwapi.h> // PathFileExists
#include <windows.h> // GetFileAttributes #include <windows.h> // GetFileAttributes
#include <direct.h> // _mkdir #include <direct.h> // _mkdir
#include <stdio.h> // remove, unlink
#else #else
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
@ -26,7 +26,7 @@ bool FileSystem::exists(const char * path)
#elif NV_OS_WIN32 #elif NV_OS_WIN32
// PathFileExists requires linking to shlwapi.lib // PathFileExists requires linking to shlwapi.lib
//return PathFileExists(path) != 0; //return PathFileExists(path) != 0;
return GetFileAttributes(path) != 0xFFFFFFFF; return GetFileAttributesA(path) != 0xFFFFFFFF;
#else #else
if (FILE * fp = fopen(path, "r")) if (FILE * fp = fopen(path, "r"))
{ {
@ -53,4 +53,10 @@ bool FileSystem::changeDirectory(const char * path)
#else #else
return chdir(path) != -1; return chdir(path) != -1;
#endif #endif
}
bool FileSystem::removeFile(const char * path)
{
// @@ Use unlink or remove?
return remove(path) == 0;
} }

View File

@ -1,5 +1,6 @@
// This code is in the public domain -- castano@gmail.com // This code is in the public domain -- castano@gmail.com
#pragma once
#ifndef NV_CORE_FILESYSTEM_H #ifndef NV_CORE_FILESYSTEM_H
#define NV_CORE_FILESYSTEM_H #define NV_CORE_FILESYSTEM_H
@ -8,14 +9,14 @@
namespace nv namespace nv
{ {
namespace FileSystem 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);
NVCORE_API bool exists(const char * path); } // FileSystem namespace
NVCORE_API bool createDirectory(const char * path);
NVCORE_API bool changeDirectory(const char * path);
} // FileSystem namespace
} // nv namespace } // nv namespace

563
src/nvcore/HashMap.h Normal file
View File

@ -0,0 +1,563 @@
// This code is in the public domain -- Ignacio Casta<74>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.
*/
#include "nvcore.h"
#include "Memory.h"
#include "Debug.h"
#include "Stream.h"
#include "Utils.h" // hash
#include "Array.h" // foreach/pseudoindex
#include <new> // for placement new
namespace nv
{
/** 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(); }
/// Set a new or existing value under the key, to the value.
void 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.
void 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.
bool 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) {
assert(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.
void 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();
}
}
mem::free(table);
table = NULL;
entry_count = 0;
size_mask = -1;
}
}
/// Returns true if the hash is empty.
bool 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.
*/
bool get(const T& key, U* value = NULL) const
{
int index = findIndex(key);
if (index >= 0)
{
if (value) {
*value = entry(index).value; // take care with side-effects!
}
return true;
}
return false;
}
/// Determine if the given key is contained in the hash.
bool contains(const T & key) const
{
return get(key);
}
/// Number of entries in the hash.
int size() const
{
return entry_count;
}
/// Number of entries in the hash.
int count() const
{
return size();
}
/**
* Resize the hash table to fit one more entry. Often this
* doesn't involve any action.
*/
void 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.
void 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.
*/
void setCapacity(int new_size)
{
int new_raw_size = (new_size * 3) / 2;
if (new_raw_size < size()) { return; }
setRawCapacity(new_raw_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_CC_GNUC
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;
}
#elif NV_CC_MSVC
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;
}
#endif
private:
static const uint TOMBSTONE_HASH = (uint) -1;
uint 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.
int 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.
int 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.
Entry & entry(int index)
{
nvDebugCheck(table != NULL);
nvDebugCheck(index >= 0 && index <= size_mask);
return table[index];
}
const Entry & 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).
*/
void setRawCapacity(int new_size)
{
if (new_size <= 0) {
// Special case.
clear();
return;
}
// Force new_size to be a power of two.
new_size = nextPowerOfTwo(new_size);
HashMap<T, U, H, E> new_hash;
new_hash.table = (Entry *) mem::malloc(sizeof(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.
mem::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.
void findNext(PseudoIndex & i) const {
while (i <= size_mask) {
const Entry & e = entry(i);
if (e.isEmpty() == false && e.isTombstone() == false) {
break;
}
i++;
}
}
int entry_count;
int size_mask;
Entry * table;
};
} // nv namespace
#endif // NV_CORE_HASHMAP_H

View File

@ -1,5 +1,6 @@
// This code is in the public domain -- castano@gmail.com // This code is in the public domain -- castano@gmail.com
#pragma once
#ifndef NV_CORE_LIBRARY_H #ifndef NV_CORE_LIBRARY_H
#define NV_CORE_LIBRARY_H #define NV_CORE_LIBRARY_H

View File

@ -1,3 +1,4 @@
// This code is in the public domain -- Ignacio Casta<74>o <castano@gmail.com>
#include "Memory.h" #include "Memory.h"
#include "Debug.h" #include "Debug.h"
@ -13,24 +14,24 @@ using namespace nv;
void * nv::mem::malloc(size_t size) void * nv::mem::malloc(size_t size)
{ {
return ::malloc(size); return ::malloc(size);
} }
void * nv::mem::malloc(size_t size, const char * file, int line) void * nv::mem::malloc(size_t size, const char * file, int line)
{ {
NV_UNUSED(file); NV_UNUSED(file);
NV_UNUSED(line); NV_UNUSED(line);
return ::malloc(size); return ::malloc(size);
} }
void nv::mem::free(const void * ptr) void nv::mem::free(const void * ptr)
{ {
::free(const_cast<void *>(ptr)); ::free(const_cast<void *>(ptr));
} }
void * nv::mem::realloc(void * ptr, size_t size) void * nv::mem::realloc(void * ptr, size_t size)
{ {
nvDebugCheck(ptr != NULL || size != 0); // undefined realloc behavior. nvDebugCheck(ptr != NULL || size != 0); // undefined realloc behavior.
return ::realloc(ptr, size); return ::realloc(ptr, size);
} }

View File

@ -1,5 +1,6 @@
// This code is in the public domain -- castanyo@yahoo.es // This code is in the public domain -- Ignacio Castaño <castano@gmail.com>
#pragma once
#ifndef NV_CORE_MEMORY_H #ifndef NV_CORE_MEMORY_H
#define NV_CORE_MEMORY_H #define NV_CORE_MEMORY_H

View File

@ -1,4 +1,4 @@
// This code is in the public domain -- castanyo@yahoo.es // This code is in the public domain -- Ignacio Casta<74>o <castano@gmail.com>
#ifndef NV_CORE_PTR_H #ifndef NV_CORE_PTR_H
#define NV_CORE_PTR_H #define NV_CORE_PTR_H
@ -6,266 +6,316 @@
#include "nvcore.h" #include "nvcore.h"
#include "Debug.h" #include "Debug.h"
#include <stdio.h> // NULL
namespace nv 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> /** Simple auto pointer template class.
AutoPtr(Q * p) : m_ptr(static_cast<T *>(p)) { } *
* This is very similar to the standard auto_ptr class, but with some
/** Dtor. Deletes owned pointer. */ * additional limitations to make its use less error prone:
~AutoPtr() { * - Copy constructor and assignment operator are disabled.
delete m_ptr; * - reset method is removed.
m_ptr = NULL; *
} * 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:
/** Delete owned pointer and assign new one. */ /// Ctor.
void operator=( T * p ) { AutoPtr(T * p = NULL) : m_ptr(p) { }
if (p != m_ptr)
{
delete m_ptr;
m_ptr = p;
}
}
template <class Q> template <class Q>
void operator=( Q * p ) { AutoPtr(Q * p) : m_ptr(static_cast<T *>(p)) { }
if (p != m_ptr)
{
delete m_ptr;
m_ptr = static_cast<T *>(p);
}
}
/** Member access. */ /// Dtor. Deletes owned pointer.
T * operator -> () const { ~AutoPtr() {
nvDebugCheck(m_ptr != NULL); delete m_ptr;
return m_ptr; m_ptr = NULL;
} }
/** Get reference. */ /// Delete owned pointer and assign new one.
T & operator*() const { void operator=( T * p ) {
nvDebugCheck(m_ptr != NULL); if (p != m_ptr)
return *m_ptr; {
} delete m_ptr;
m_ptr = p;
}
}
/** Get pointer. */ template <class Q>
T * ptr() const { return m_ptr; } void operator=( Q * p ) {
if (p != m_ptr)
/** Relinquish ownership of the underlying pointer and returns that pointer. */ {
T * release() { delete m_ptr;
T * tmp = m_ptr; m_ptr = static_cast<T *>(p);
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. */ /// Member access.
friend bool operator != (const AutoPtr<T> & ap, const T * const p) { T * operator -> () const {
return (ap.ptr() != p); nvDebugCheck(m_ptr != NULL);
} return m_ptr;
}
/** Const pointer equal comparation. */ /// Get reference.
friend bool operator == (const T * const p, const AutoPtr<T> & ap) { T & operator*() const {
return (ap.ptr() == p); nvDebugCheck(m_ptr != NULL);
} return *m_ptr;
}
/** Const pointer nequal comparation. */ /// Get pointer.
friend bool operator != (const T * const p, const AutoPtr<T> & ap) { T * ptr() const { return m_ptr; }
return (ap.ptr() != p);
}
private: /// Relinquish ownership of the underlying pointer and returns that pointer.
T * m_ptr; 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. /// Smart pointer template class.
template <class BaseClass> template <class BaseClass>
class SmartPtr { class SmartPtr {
public: public:
// BaseClass must implement addRef() and release(). // BaseClass must implement addRef() and release().
typedef SmartPtr<BaseClass> ThisType; typedef SmartPtr<BaseClass> ThisType;
/// Default ctor. /// Default ctor.
SmartPtr() : m_ptr(NULL) SmartPtr() : m_ptr(NULL)
{ {
} }
/** Other type assignment. */ /// Other type assignment.
template <class OtherBase> template <class OtherBase>
SmartPtr( const SmartPtr<OtherBase> & tc ) SmartPtr( const SmartPtr<OtherBase> & tc )
{ {
m_ptr = static_cast<BaseClass *>( tc.ptr() ); m_ptr = static_cast<BaseClass *>( tc.ptr() );
if( m_ptr ) { if (m_ptr) {
m_ptr->addRef(); m_ptr->addRef();
} }
} }
/** Copy ctor. */ /// Copy ctor.
SmartPtr( const ThisType & bc ) SmartPtr( const ThisType & bc )
{ {
m_ptr = bc.ptr(); m_ptr = bc.ptr();
if( m_ptr ) { if (m_ptr) {
m_ptr->addRef(); m_ptr->addRef();
} }
} }
/** Copy cast ctor. SmartPtr(NULL) is valid. */ /// Copy cast ctor. SmartPtr(NULL) is valid.
explicit SmartPtr( BaseClass * bc ) explicit SmartPtr( BaseClass * bc )
{ {
m_ptr = bc; m_ptr = bc;
if( m_ptr ) { if (m_ptr) {
m_ptr->addRef(); m_ptr->addRef();
} }
} }
/** Dtor. */ /// Dtor.
~SmartPtr() ~SmartPtr()
{ {
set(NULL); set(NULL);
} }
/** @name Accessors: */ /// -> operator.
//@{ BaseClass * operator -> () const
/** -> operator. */ {
BaseClass * operator -> () const nvCheck( m_ptr != NULL );
{ return m_ptr;
nvCheck( m_ptr != NULL ); }
return m_ptr;
}
/** * operator. */ /// * operator.
BaseClass & operator*() const BaseClass & operator*() const
{ {
nvCheck( m_ptr != NULL ); nvCheck( m_ptr != NULL );
return *m_ptr; return *m_ptr;
} }
/** Get pointer. */ /// Get pointer.
BaseClass * ptr() const BaseClass * ptr() const
{ {
return m_ptr; 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 );
}
/** @name Mutators: */ /// Other type equal comparation.
//@{ template <class OtherBase>
/** Other type assignment. */ bool operator == ( const SmartPtr<OtherBase> & other ) const
template <class OtherBase> {
void operator = ( const SmartPtr<OtherBase> & tc ) return m_ptr == other.ptr();
{ }
set( static_cast<BaseClass *>(tc.ptr()) );
}
/** This type assignment. */ /// This type equal comparation.
void operator = ( const ThisType & bc ) bool operator == ( const ThisType & bc ) const
{ {
set( bc.ptr() ); return m_ptr == bc.ptr();
} }
/** Pointer assignment. */ /// Const pointer equal comparation.
void operator = ( BaseClass * bc ) bool operator == ( const BaseClass * const bc ) const
{ {
set( bc ); 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;
};
/** @name Comparators: */ /// Smart pointer template class.
//@{ template <class T>
/** Other type equal comparation. */ class WeakPtr {
template <class OtherBase> public:
bool operator == ( const SmartPtr<OtherBase> & other ) const
{
return m_ptr == other.ptr();
}
/** This type equal comparation. */ WeakPtr() {}
bool operator == ( const ThisType & bc ) const
{
return m_ptr == bc.ptr();
}
/** Const pointer equal comparation. */ WeakPtr(T * p) { operator=(p); }
bool operator == ( const BaseClass * const bc ) const WeakPtr(const SmartPtr<T> & p) { operator=(p.ptr()); }
{
return m_ptr == bc;
}
/** Other type not equal comparation. */ // Default constructor and assignment from weak_ptr<T> are OK.
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. */ void operator=(T * p)
bool operator != (const BaseClass * const bc) const {
{ if (p) {
return m_ptr != bc; m_proxy = p->getWeakProxy();
} assert(m_proxy != NULL);
assert(m_proxy->ptr() == p);
}
else {
m_proxy = NULL;
}
}
/** This type lower than comparation. */ void operator=(const SmartPtr<T> & ptr) { operator=(ptr.ptr()); }
bool operator < (const ThisType & p) const
{
return m_ptr < p.ptr();
}
//@}
private: bool operator==(const SmartPtr<T> & p) const { return ptr() == p.ptr(); }
bool operator!=(const SmartPtr<T> & p) const { return ptr() != p.ptr(); }
/** Set this pointer. */ bool operator==(const WeakPtr<T> & p) const { return ptr() == p.ptr(); }
void set( BaseClass * p ) bool operator!=(const WeakPtr<T> & p) const { return ptr() != p.ptr(); }
{
if( m_ptr != p ) {
if( m_ptr ) m_ptr->release();
if( p ) p->addRef();
m_ptr = p;
}
}
private: bool operator==(T * p) const { return ptr() == p; }
bool operator!=(T * p) const { return ptr() != p; }
BaseClass * m_ptr; T * operator->() const
{
T * p = ptr();
assert(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 } // nv namespace

View File

@ -1,9 +0,0 @@
// This code is in the public domain -- castanyo@yahoo.es
#include "RefCounted.h"
using namespace nv;
int nv::RefCounted::s_total_ref_count = 0;
int nv::RefCounted::s_total_obj_count = 0;

View File

@ -1,4 +1,4 @@
// This code is in the public domain -- castanyo@yahoo.es // This code is in the public domain -- Ignacio Casta<74>o <castano@gmail.com>
#ifndef NV_CORE_REFCOUNTED_H #ifndef NV_CORE_REFCOUNTED_H
#define NV_CORE_REFCOUNTED_H #define NV_CORE_REFCOUNTED_H
@ -6,107 +6,142 @@
#include "nvcore.h" #include "nvcore.h"
#include "Debug.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 namespace nv
{ {
/// Weak proxy.
class WeakProxy
{
NV_FORBID_COPY(WeakProxy);
public:
/// Ctor.
WeakProxy(void * ptr) : m_count(0), m_ptr(ptr) { }
/// Reference counted base class to be used with SmartPtr and WeakPtr. /// Dtor.
class RefCounted ~WeakProxy()
{ {
NV_FORBID_COPY(RefCounted); nvCheck( m_count == 0 );
public: }
/// Ctor. /// Increase reference count.
RefCounted() : m_count(0)/*, m_weak_proxy(NULL)*/ uint addRef() const
{ {
s_total_obj_count++; m_count++;
} return m_count;
}
/// Virtual dtor. /// Decrease reference count and remove when 0.
virtual ~RefCounted() uint release() const
{ {
nvCheck( m_count == 0 ); nvCheck( m_count > 0 );
nvCheck( s_total_obj_count > 0 );
s_total_obj_count--; 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;
};
/// Increase reference count. /// Reference counted base class to be used with SmartPtr and WeakPtr.
uint addRef() const class RefCounted
{ {
s_total_ref_count++; NV_FORBID_COPY(RefCounted);
m_count++; public:
return m_count;
} /// Ctor.
RefCounted() : m_count(0), m_weak_proxy(NULL)
{
}
/// Virtual dtor.
virtual ~RefCounted()
{
nvCheck( m_count == 0 );
releaseWeakProxy();
}
/// Decrease reference count and remove when 0. /// Increase reference count.
uint release() const uint addRef() const
{ {
nvCheck( m_count > 0 ); m_count++;
return m_count;
s_total_ref_count--; }
m_count--;
if( m_count == 0 ) {
// releaseWeakProxy();
delete this;
return 0;
}
return m_count;
}
/*
/// Get weak proxy.
WeakProxy * getWeakProxy() const
{
if (m_weak_proxy == NULL) {
m_weak_proxy = new WeakProxy;
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;
}
}
*/
/** @name Debug methods: */
//@{
/// Get reference count.
int refCount() const
{
return m_count;
}
/// Get total number of objects.
static int totalObjectCount()
{
return s_total_obj_count;
}
/// Get total number of references.
static int totalReferenceCount()
{
return s_total_ref_count;
}
//@}
private: /// Decrease reference count and remove when 0.
uint release() const
{
nvCheck( m_count > 0 );
NVCORE_API static int s_total_ref_count; m_count--;
NVCORE_API static int s_total_obj_count; if( m_count == 0 ) {
delete this;
return 0;
}
return m_count;
}
mutable int m_count; /// Get weak proxy.
// mutable WeakProxy * 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 } // nv namespace

View File

@ -1,9 +1,11 @@
// This code is in the public domain -- castano@gmail.com // This code is in the public domain -- Ignacio Casta<74>o <castano@gmail.com>
#pragma once
#ifndef NV_CORE_STDSTREAM_H #ifndef NV_CORE_STDSTREAM_H
#define NV_CORE_STDSTREAM_H #define NV_CORE_STDSTREAM_H
#include <nvcore/Stream.h> #include "nvcore.h"
#include "Stream.h"
#include <stdio.h> // fopen #include <stdio.h> // fopen
#include <string.h> // memcpy #include <string.h> // memcpy
@ -12,358 +14,358 @@
namespace nv namespace nv
{ {
// Portable version of fopen. // Portable version of fopen.
inline FILE * fileOpen(const char * fileName, const char * mode) inline FILE * fileOpen(const char * fileName, const char * mode)
{ {
nvCheck(fileName != NULL); nvCheck(fileName != NULL);
#if NV_CC_MSVC && _MSC_VER >= 1400 #if NV_CC_MSVC && _MSC_VER >= 1400
FILE * fp; FILE * fp;
if (fopen_s(&fp, fileName, mode) == 0) { if (fopen_s(&fp, fileName, mode) == 0) {
return fp; return fp;
} }
return NULL; return NULL;
#else #else
return fopen(fileName, mode); return fopen(fileName, mode);
#endif #endif
} }
/// Base stdio stream. /// Base stdio stream.
class NVCORE_CLASS StdStream : public Stream class NVCORE_CLASS StdStream : public Stream
{ {
NV_FORBID_COPY(StdStream); NV_FORBID_COPY(StdStream);
public: public:
/// Ctor. /// Ctor.
StdStream( FILE * fp, bool autoclose=true ) : StdStream( FILE * fp, bool autoclose=true ) :
m_fp(fp), m_autoclose(autoclose) { } m_fp(fp), m_autoclose(autoclose) { }
/// Dtor. /// Dtor.
virtual ~StdStream() virtual ~StdStream()
{ {
if( m_fp != NULL && m_autoclose ) { if( m_fp != NULL && m_autoclose ) {
fclose( m_fp ); fclose( m_fp );
} }
} }
/** @name Stream implementation. */ /** @name Stream implementation. */
//@{ //@{
virtual void seek( uint pos ) virtual void seek( uint pos )
{ {
nvDebugCheck(m_fp != NULL); nvDebugCheck(m_fp != NULL);
nvDebugCheck(pos < size()); nvDebugCheck(pos < size());
fseek(m_fp, pos, SEEK_SET); fseek(m_fp, pos, SEEK_SET);
} }
virtual uint tell() const
{
nvDebugCheck(m_fp != NULL);
return ftell(m_fp);
}
virtual uint size() const
{
nvDebugCheck(m_fp != NULL);
uint pos = ftell(m_fp);
fseek(m_fp, 0, SEEK_END);
uint end = ftell(m_fp);
fseek(m_fp, pos, SEEK_SET);
return end;
}
virtual bool isError() const
{
return m_fp == NULL || ferror( m_fp ) != 0;
}
virtual void clearError() virtual uint tell() const
{ {
nvDebugCheck(m_fp != NULL); nvDebugCheck(m_fp != NULL);
clearerr(m_fp); return ftell(m_fp);
} }
virtual bool isAtEnd() const
{
nvDebugCheck(m_fp != NULL);
return feof( m_fp ) != 0;
}
/// Always true.
virtual bool isSeekable() const { return true; }
//@}
protected: virtual uint size() const
{
nvDebugCheck(m_fp != NULL);
uint pos = ftell(m_fp);
fseek(m_fp, 0, SEEK_END);
uint end = ftell(m_fp);
fseek(m_fp, pos, SEEK_SET);
return end;
}
FILE * m_fp; virtual bool isError() const
bool m_autoclose; {
return m_fp == NULL || ferror( m_fp ) != 0;
}
}; virtual void clearError()
{
nvDebugCheck(m_fp != NULL);
clearerr(m_fp);
}
virtual bool isAtEnd() const
{
nvDebugCheck(m_fp != NULL);
return feof( m_fp ) != 0;
}
/// Always true.
virtual bool isSeekable() const { return true; }
//@}
protected:
FILE * m_fp;
bool m_autoclose;
};
/// Standard output stream. /// Standard output stream.
class NVCORE_CLASS StdOutputStream : public StdStream class NVCORE_CLASS StdOutputStream : public StdStream
{ {
NV_FORBID_COPY(StdOutputStream); NV_FORBID_COPY(StdOutputStream);
public: public:
/// Construct stream by file name. /// Construct stream by file name.
StdOutputStream( const char * name ) : StdOutputStream( const char * name ) :
StdStream(fileOpen(name, "wb")) { } StdStream(fileOpen(name, "wb")) { }
/// Construct stream by file handle. /// Construct stream by file handle.
StdOutputStream( FILE * fp, bool autoclose=true ) : StdStream(fp, autoclose) StdOutputStream( FILE * fp, bool autoclose=true ) : StdStream(fp, autoclose)
{ {
} }
/** @name Stream implementation. */ /** @name Stream implementation. */
//@{ //@{
/// Write data. /// Write data.
virtual uint serialize( void * data, uint len ) virtual uint serialize( void * data, uint len )
{ {
nvDebugCheck(data != NULL); nvDebugCheck(data != NULL);
nvDebugCheck(m_fp != NULL); nvDebugCheck(m_fp != NULL);
return (uint)fwrite(data, 1, len, m_fp); return (uint)fwrite(data, 1, len, m_fp);
} }
virtual bool isLoading() const
{
return false;
}
virtual bool isSaving() const
{
return true;
}
//@}
}; virtual bool isLoading() const
{
return false;
}
virtual bool isSaving() const
{
return true;
}
//@}
};
/// Standard input stream. /// Standard input stream.
class NVCORE_CLASS StdInputStream : public StdStream class NVCORE_CLASS StdInputStream : public StdStream
{ {
NV_FORBID_COPY(StdInputStream); NV_FORBID_COPY(StdInputStream);
public: public:
/// Construct stream by file name. /// Construct stream by file name.
StdInputStream( const char * name ) : StdInputStream( const char * name ) :
StdStream(fileOpen(name, "rb")) { } StdStream(fileOpen(name, "rb")) { }
/// Construct stream by file handle. /// Construct stream by file handle.
StdInputStream( FILE * fp, bool autoclose=true ) : StdStream(fp, autoclose) StdInputStream( FILE * fp, bool autoclose=true ) : StdStream(fp, autoclose)
{ {
} }
/** @name Stream implementation. */ /** @name Stream implementation. */
//@{ //@{
/// Read data. /// Read data.
virtual uint serialize( void * data, uint len ) virtual uint serialize( void * data, uint len )
{ {
nvDebugCheck(data != NULL); nvDebugCheck(data != NULL);
nvDebugCheck(m_fp != NULL); nvDebugCheck(m_fp != NULL);
return (uint)fread(data, 1, len, m_fp); return (uint)fread(data, 1, len, m_fp);
} }
virtual bool isLoading() const virtual bool isLoading() const
{ {
return true; return true;
} }
virtual bool isSaving() const virtual bool isSaving() const
{ {
return false; return false;
} }
//@} //@}
}; };
/// Memory input stream. /// Memory input stream.
class NVCORE_CLASS MemoryInputStream : public Stream class NVCORE_CLASS MemoryInputStream : public Stream
{ {
NV_FORBID_COPY(MemoryInputStream); NV_FORBID_COPY(MemoryInputStream);
public: public:
/// Ctor. /// Ctor.
MemoryInputStream( const uint8 * mem, uint size ) : MemoryInputStream( const uint8 * mem, uint size ) :
m_mem(mem), m_ptr(mem), m_size(size) { } m_mem(mem), m_ptr(mem), m_size(size) { }
/** @name Stream implementation. */ /** @name Stream implementation. */
//@{ //@{
/// Read data. /// Read data.
virtual uint serialize( void * data, uint len ) virtual uint serialize( void * data, uint len )
{ {
nvDebugCheck(data != NULL); nvDebugCheck(data != NULL);
nvDebugCheck(!isError()); nvDebugCheck(!isError());
uint left = m_size - tell(); uint left = m_size - tell();
if (len > left) len = left; 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 memcpy( data, m_ptr, len );
{ m_ptr += len;
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;
}
//@}
return len;
private: }
const uint8 * m_mem; virtual void seek( uint pos )
const uint8 * m_ptr; {
uint m_size; 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;
}
//@}
/// Protected input stream. private:
class NVCORE_CLASS ProtectedStream : public Stream
{
NV_FORBID_COPY(ProtectedStream);
public:
/// Ctor. const uint8 * m_mem;
ProtectedStream( Stream & s ) : m_s(&s), m_autodelete(false) const uint8 * m_ptr;
{ uint m_size;
}
/// 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. */ /// Protected input stream.
//@{ class NVCORE_CLASS ProtectedStream : public Stream
/// Read data. {
virtual uint serialize( void * data, uint len ) NV_FORBID_COPY(ProtectedStream);
{ public:
nvDebugCheck(data != NULL);
len = m_s->serialize( data, len );
if( m_s->isError() ) {
throw std::exception();
}
return len; /// Ctor.
} ProtectedStream( Stream & s ) : m_s(&s), m_autodelete(false)
{
virtual void seek( uint pos ) }
{
m_s->seek( pos );
if( m_s->isError() ) {
throw std::exception();
}
}
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() /// Ctor.
{ ProtectedStream( Stream * s, bool autodelete = true ) :
m_s->clearError(); m_s(s), m_autodelete(autodelete)
} {
nvDebugCheck(m_s != NULL);
}
virtual bool isAtEnd() const /// Dtor.
{ virtual ~ProtectedStream()
return m_s->isAtEnd(); {
} if( m_autodelete ) {
delete m_s;
virtual bool isSeekable() const }
{ }
return m_s->isSeekable();
}
virtual bool isLoading() const
{
return m_s->isLoading();
}
virtual bool isSaving() const
{
return m_s->isSaving();
}
//@}
/** @name Stream implementation. */
private: //@{
/// Read data.
Stream * const m_s; virtual uint serialize( void * data, uint len )
bool const m_autodelete; {
nvDebugCheck(data != NULL);
len = m_s->serialize( data, len );
}; if( m_s->isError() ) {
throw std::exception();
}
return len;
}
virtual void seek( uint pos )
{
m_s->seek( pos );
if( m_s->isError() ) {
throw std::exception();
}
}
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 } // nv namespace

View File

@ -1,9 +1,9 @@
// This code is in the public domain -- castanyo@yahoo.es // This code is in the public domain -- Ignacio Casta<74>o <castano@gmail.com>
#include "StrLib.h" #include "StrLib.h"
#include <math.h> // log #include <math.h> // log
#include <stdio.h> // vsnprintf #include <stdio.h> // vsnprintf
#if NV_CC_MSVC #if NV_CC_MSVC
#include <stdarg.h> // vsnprintf #include <stdarg.h> // vsnprintf
@ -19,110 +19,110 @@ using namespace nv;
namespace namespace
{ {
static char * strAlloc(uint size) static char * strAlloc(uint size)
{ {
return static_cast<char *>(mem::malloc(size)); return static_cast<char *>(mem::malloc(size));
} }
static char * strReAlloc(char * str, uint size) static char * strReAlloc(char * str, uint size)
{ {
return static_cast<char *>(mem::realloc(str, size)); return static_cast<char *>(mem::realloc(str, size));
} }
static void strFree(const char * str) static void strFree(const char * str)
{ {
return mem::free(const_cast<char *>(str)); return mem::free(const_cast<char *>(str));
} }
/*static char * strDup( const char * str ) /*static char * strDup( const char * str )
{ {
nvDebugCheck( str != NULL ); nvDebugCheck( str != NULL );
uint len = uint(strlen( str ) + 1); uint len = uint(strlen( str ) + 1);
char * dup = strAlloc( len ); char * dup = strAlloc( len );
memcpy( dup, str, len ); memcpy( dup, str, len );
return dup; return dup;
}*/ }*/
// helper function for integer to string conversion. // helper function for integer to string conversion.
static char * i2a( uint i, char *a, uint r ) static char * i2a( uint i, char *a, uint r )
{ {
if( i / r > 0 ) { if( i / r > 0 ) {
a = i2a( i / r, a, r ); a = i2a( i / r, a, r );
} }
*a = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[i % r]; *a = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[i % r];
return a + 1; return a + 1;
} }
// Locale independent functions. // Locale independent functions.
static inline char toUpper( char c ) { static inline char toUpper( char c ) {
return (c<'a' || c>'z') ? (c) : (c+'A'-'a'); return (c<'a' || c>'z') ? (c) : (c+'A'-'a');
} }
static inline char toLower( char c ) { static inline char toLower( char c ) {
return (c<'A' || c>'Z') ? (c) : (c+'a'-'A'); return (c<'A' || c>'Z') ? (c) : (c+'a'-'A');
} }
static inline bool isAlpha( char c ) { static inline bool isAlpha( char c ) {
return (c>='a' && c<='z') || (c>='A' && c<='Z'); return (c>='a' && c<='z') || (c>='A' && c<='Z');
} }
static inline bool isDigit( char c ) { static inline bool isDigit( char c ) {
return c>='0' && c<='9'; return c>='0' && c<='9';
} }
static inline bool isAlnum( char c ) { static inline bool isAlnum( char c ) {
return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9'); return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9');
} }
} }
int nv::strCmp(const char * s1, const char * s2) int nv::strCmp(const char * s1, const char * s2)
{ {
nvDebugCheck(s1 != NULL); nvDebugCheck(s1 != NULL);
nvDebugCheck(s2 != NULL); nvDebugCheck(s2 != NULL);
return strcmp(s1, s2); return strcmp(s1, s2);
} }
int nv::strCaseCmp(const char * s1, const char * s2) int nv::strCaseCmp(const char * s1, const char * s2)
{ {
nvDebugCheck(s1 != NULL); nvDebugCheck(s1 != NULL);
nvDebugCheck(s1 != NULL); nvDebugCheck(s1 != NULL);
#if NV_CC_MSVC #if NV_CC_MSVC
return _stricmp(s1, s2); return _stricmp(s1, s2);
#else #else
return strcasecmp(s1, s2); return strcasecmp(s1, s2);
#endif #endif
} }
void nv::strCpy(char * dst, int size, const char * src) void nv::strCpy(char * dst, int size, const char * src)
{ {
nvDebugCheck(dst != NULL); nvDebugCheck(dst != NULL);
nvDebugCheck(src != NULL); nvDebugCheck(src != NULL);
#if NV_CC_MSVC && _MSC_VER >= 1400 #if NV_CC_MSVC && _MSC_VER >= 1400
strcpy_s(dst, size, src); strcpy_s(dst, size, src);
#else #else
NV_UNUSED(size); NV_UNUSED(size);
strcpy(dst, src); strcpy(dst, src);
#endif #endif
} }
void nv::strCpy(char * dst, int size, const char * src, int len) void nv::strCpy(char * dst, int size, const char * src, int len)
{ {
nvDebugCheck(dst != NULL); nvDebugCheck(dst != NULL);
nvDebugCheck(src != NULL); nvDebugCheck(src != NULL);
#if NV_CC_MSVC && _MSC_VER >= 1400 #if NV_CC_MSVC && _MSC_VER >= 1400
strncpy_s(dst, size, src, len); strncpy_s(dst, size, src, len);
#else #else
NV_UNUSED(size); NV_UNUSED(size);
strncpy(dst, src, len); strncpy(dst, src, len);
#endif #endif
} }
void nv::strCat(char * dst, int size, const char * src) void nv::strCat(char * dst, int size, const char * src)
{ {
nvDebugCheck(dst != NULL); nvDebugCheck(dst != NULL);
nvDebugCheck(src != NULL); nvDebugCheck(src != NULL);
#if NV_CC_MSVC && _MSC_VER >= 1400 #if NV_CC_MSVC && _MSC_VER >= 1400
strcat_s(dst, size, src); strcat_s(dst, size, src);
#else #else
NV_UNUSED(size); NV_UNUSED(size);
strcat(dst, src); strcat(dst, src);
#endif #endif
} }
@ -130,8 +130,8 @@ void nv::strCat(char * dst, int size, const char * src)
/** Pattern matching routine. I don't remember where did I get this. */ /** Pattern matching routine. I don't remember where did I get this. */
bool nv::strMatch(const char * str, const char * pat) bool nv::strMatch(const char * str, const char * pat)
{ {
nvDebugCheck(str != NULL); nvDebugCheck(str != NULL);
nvDebugCheck(pat != NULL); nvDebugCheck(pat != NULL);
char c2; char c2;
@ -197,311 +197,319 @@ StringBuilder::StringBuilder() : m_size(0), m_str(NULL)
/** Preallocate space. */ /** Preallocate space. */
StringBuilder::StringBuilder( int size_hint ) : m_size(size_hint) StringBuilder::StringBuilder( int size_hint ) : m_size(size_hint)
{ {
nvDebugCheck(m_size > 0); nvDebugCheck(m_size > 0);
m_str = strAlloc(m_size); m_str = strAlloc(m_size);
*m_str = '\0'; *m_str = '\0';
} }
/** Copy ctor. */ /** Copy ctor. */
StringBuilder::StringBuilder( const StringBuilder & s ) : m_size(0), m_str(NULL) StringBuilder::StringBuilder( const StringBuilder & s ) : m_size(0), m_str(NULL)
{ {
copy(s); copy(s);
} }
/** Copy string. */ /** Copy string. */
StringBuilder::StringBuilder( const char * s ) : m_size(0), m_str(NULL) StringBuilder::StringBuilder( const char * s, int extra_size_hint/*=0*/ ) : m_size(0), m_str(NULL)
{ {
copy(s); copy(s, extra_size_hint);
} }
/** Delete the string. */ /** Delete the string. */
StringBuilder::~StringBuilder() StringBuilder::~StringBuilder()
{ {
m_size = 0; m_size = 0;
strFree(m_str); strFree(m_str);
m_str = NULL; m_str = NULL;
} }
/** Format a string safely. */ /** Format a string safely. */
StringBuilder & StringBuilder::format( const char * fmt, ... ) StringBuilder & StringBuilder::format( const char * fmt, ... )
{ {
nvDebugCheck(fmt != NULL); nvDebugCheck(fmt != NULL);
va_list arg; va_list arg;
va_start( arg, fmt ); va_start( arg, fmt );
format( fmt, arg ); format( fmt, arg );
va_end( arg ); va_end( arg );
return *this; return *this;
} }
/** Format a string safely. */ /** Format a string safely. */
StringBuilder & StringBuilder::format( const char * fmt, va_list arg ) StringBuilder & StringBuilder::format( const char * fmt, va_list arg )
{ {
nvDebugCheck(fmt != NULL); nvDebugCheck(fmt != NULL);
if( m_size == 0 ) { if( m_size == 0 ) {
m_size = 64; m_size = 64;
m_str = strAlloc( m_size ); m_str = strAlloc( m_size );
} }
va_list tmp; va_list tmp;
va_copy(tmp, arg); va_copy(tmp, arg);
#if NV_CC_MSVC && _MSC_VER >= 1400 #if NV_CC_MSVC && _MSC_VER >= 1400
int n = vsnprintf_s(m_str, m_size, _TRUNCATE, fmt, tmp); int n = vsnprintf_s(m_str, m_size, _TRUNCATE, fmt, tmp);
#else #else
int n = vsnprintf(m_str, m_size, fmt, tmp); int n = vsnprintf(m_str, m_size, fmt, tmp);
#endif #endif
va_end(tmp); va_end(tmp);
while( n < 0 || n >= int(m_size) ) { while( n < 0 || n >= int(m_size) ) {
if( n > -1 ) { if( n > -1 ) {
m_size = n + 1; m_size = n + 1;
} }
else { else {
m_size *= 2; m_size *= 2;
} }
m_str = strReAlloc(m_str, m_size); m_str = strReAlloc(m_str, m_size);
va_copy(tmp, arg); va_copy(tmp, arg);
#if NV_CC_MSVC && _MSC_VER >= 1400 #if NV_CC_MSVC && _MSC_VER >= 1400
n = vsnprintf_s(m_str, m_size, _TRUNCATE, fmt, tmp); n = vsnprintf_s(m_str, m_size, _TRUNCATE, fmt, tmp);
#else #else
n = vsnprintf(m_str, m_size, fmt, tmp); n = vsnprintf(m_str, m_size, fmt, tmp);
#endif #endif
va_end(tmp); va_end(tmp);
} }
nvDebugCheck(n < int(m_size));
// Make sure it's null terminated.
nvDebugCheck(m_str[n] == '\0');
//str[n] = '\0';
return *this; nvDebugCheck(n < int(m_size));
// Make sure it's null terminated.
nvDebugCheck(m_str[n] == '\0');
//str[n] = '\0';
return *this;
} }
/** Append a string. */ /** Append a string. */
StringBuilder & StringBuilder::append( const char * s ) StringBuilder & StringBuilder::append( const char * s )
{ {
nvDebugCheck(s != NULL); nvDebugCheck(s != NULL);
const uint slen = uint(strlen( s )); const uint slen = uint(strlen( s ));
if( m_str == NULL ) { if( m_str == NULL ) {
m_size = slen + 1; m_size = slen + 1;
m_str = strAlloc(m_size); m_str = strAlloc(m_size);
strCpy( m_str, m_size, s ); strCpy( m_str, m_size, s );
} }
else { else {
const uint len = uint(strlen( m_str ));
if( m_size < len + slen + 1 ) { const uint len = uint(strlen( m_str ));
m_size = len + slen + 1;
m_str = strReAlloc(m_str, m_size);
}
strCat( m_str, m_size, s );
}
return *this; if( m_size < len + slen + 1 ) {
m_size = len + slen + 1;
m_str = strReAlloc(m_str, m_size);
}
strCat( m_str, m_size, s );
}
return *this;
} }
/** Append a formatted string. */ /** Append a formatted string. */
StringBuilder & StringBuilder::appendFormat( const char * format, ... ) StringBuilder & StringBuilder::appendFormat( const char * format, ... )
{ {
nvDebugCheck( format != NULL ); nvDebugCheck( format != NULL );
va_list arg; va_list arg;
va_start( arg, format ); va_start( arg, format );
appendFormat( format, arg ); appendFormat( format, arg );
va_end( arg ); va_end( arg );
return *this; return *this;
} }
/** Append a formatted string. */ /** Append a formatted string. */
StringBuilder & StringBuilder::appendFormat( const char * format, va_list arg ) StringBuilder & StringBuilder::appendFormat( const char * format, va_list arg )
{ {
nvDebugCheck( format != NULL ); nvDebugCheck( format != NULL );
va_list tmp;
va_copy(tmp, arg);
StringBuilder tmp_str; va_list tmp;
tmp_str.format( format, tmp ); va_copy(tmp, arg);
append( tmp_str );
va_end(tmp);
return *this; StringBuilder tmp_str;
tmp_str.format( format, tmp );
append( tmp_str );
va_end(tmp);
return *this;
} }
/** Convert number to string in the given base. */ /** Convert number to string in the given base. */
StringBuilder & StringBuilder::number( int i, int base ) StringBuilder & StringBuilder::number( int i, int base )
{ {
nvCheck( base >= 2 ); nvCheck( base >= 2 );
nvCheck( base <= 36 ); nvCheck( base <= 36 );
// @@ This needs to be done correctly. // @@ This needs to be done correctly.
// length = floor(log(i, base)); // length = floor(log(i, base));
uint len = uint(log(float(i)) / log(float(base)) + 2); // one more if negative uint len = uint(log(float(i)) / log(float(base)) + 2); // one more if negative
reserve(len); reserve(len);
if( i < 0 ) { if( i < 0 ) {
*m_str = '-'; *m_str = '-';
*i2a(uint(-i), m_str+1, base) = 0; *i2a(uint(-i), m_str+1, base) = 0;
} }
else { else {
*i2a(i, m_str, base) = 0; *i2a(i, m_str, base) = 0;
} }
return *this; return *this;
} }
/** Convert number to string in the given base. */ /** Convert number to string in the given base. */
StringBuilder & StringBuilder::number( uint i, int base ) StringBuilder & StringBuilder::number( uint i, int base )
{ {
nvCheck( base >= 2 ); nvCheck( base >= 2 );
nvCheck( base <= 36 ); nvCheck( base <= 36 );
// @@ This needs to be done correctly. // @@ This needs to be done correctly.
// length = floor(log(i, base)); // length = floor(log(i, base));
uint len = uint(log(float(i)) / log(float(base)) - 0.5f + 1); uint len = uint(log(float(i)) / log(float(base)) - 0.5f + 1);
reserve(len); reserve(len);
*i2a(i, m_str, base) = 0; *i2a(i, m_str, base) = 0;
return *this; return *this;
} }
/** Resize the string preserving the contents. */ /** Resize the string preserving the contents. */
StringBuilder & StringBuilder::reserve( uint size_hint ) StringBuilder & StringBuilder::reserve( uint size_hint )
{ {
nvCheck(size_hint != 0); nvCheck(size_hint != 0);
if( size_hint > m_size ) { if( size_hint > m_size ) {
m_str = strReAlloc(m_str, size_hint); m_str = strReAlloc(m_str, size_hint);
m_size = size_hint; m_size = size_hint;
} }
return *this; return *this;
} }
/** Copy a string safely. */ /** Copy a string safely. */
StringBuilder & StringBuilder::copy( const char * s ) StringBuilder & StringBuilder::copy( const char * s, int extra_size/*=0*/ )
{ {
nvCheck( s != NULL ); nvCheck( s != NULL );
uint str_size = uint(strlen( s )) + 1; uint str_size = uint(strlen( s )) + 1;
reserve(str_size); reserve(str_size + extra_size);
strCpy( m_str, str_size, s ); strCpy( m_str, str_size, s );
return *this; return *this;
} }
/** Copy an StringBuilder. */ /** Copy an StringBuilder. */
StringBuilder & StringBuilder::copy( const StringBuilder & s ) StringBuilder & StringBuilder::copy( const StringBuilder & s )
{ {
if( s.m_str == NULL ) { if( s.m_str == NULL ) {
nvCheck( s.m_size == 0 ); nvCheck( s.m_size == 0 );
m_size = 0; m_size = 0;
strFree( m_str ); strFree( m_str );
m_str = NULL; m_str = NULL;
} }
else { else {
reserve( s.m_size ); reserve( s.m_size );
strCpy( m_str, s.m_size, s.m_str ); strCpy( m_str, s.m_size, s.m_str );
} }
return *this; return *this;
} }
/** Reset the string. */ /** Reset the string. */
void StringBuilder::reset() void StringBuilder::reset()
{ {
m_size = 0; m_size = 0;
strFree( m_str ); strFree( m_str );
m_str = NULL; m_str = NULL;
} }
/** Release the allocated string. */
char * StringBuilder::release()
{
char * str = m_str;
m_size = 0;
m_str = NULL;
return str;
}
/// Get the file name from a path. /// Get the file name from a path.
const char * Path::fileName() const const char * Path::fileName() const
{ {
return fileName(m_str); return fileName(m_str);
} }
/// Get the extension from a file path. /// Get the extension from a file path.
const char * Path::extension() const const char * Path::extension() const
{ {
return extension(m_str); return extension(m_str);
} }
/// Toggles path separators (ie. \\ into /). /// Toggles path separators (ie. \\ into /).
void Path::translatePath() void Path::translatePath()
{ {
nvCheck( m_str != NULL ); nvCheck( m_str != NULL );
for(int i = 0; ; i++) { for(int i = 0; ; i++) {
if( m_str[i] == '\0' ) break; if( m_str[i] == '\0' ) break;
#if NV_PATH_SEPARATOR == '/' #if NV_PATH_SEPARATOR == '/'
if( m_str[i] == '\\' ) m_str[i] = NV_PATH_SEPARATOR; if( m_str[i] == '\\' ) m_str[i] = NV_PATH_SEPARATOR;
#else #else
if( m_str[i] == '/' ) m_str[i] = NV_PATH_SEPARATOR; if( m_str[i] == '/' ) m_str[i] = NV_PATH_SEPARATOR;
#endif #endif
} }
} }
/** /**
* Strip the file name from a path. * Strip the file name from a path.
* @warning path cannot end with '/' o '\\', can't it? * @warning path cannot end with '/' o '\\', can't it?
*/ */
void Path::stripFileName() void Path::stripFileName()
{ {
nvCheck( m_str != NULL ); nvCheck( m_str != NULL );
int length = (int)strlen(m_str) - 1; int length = (int)strlen(m_str) - 1;
while (length > 0 && m_str[length] != '/' && m_str[length] != '\\'){ while (length > 0 && m_str[length] != '/' && m_str[length] != '\\'){
length--; length--;
} }
if( length ) { if( length ) {
m_str[length+1] = 0; m_str[length+1] = 0;
} }
else { else {
m_str[0] = 0; m_str[0] = 0;
} }
} }
/// Strip the extension from a path name. /// Strip the extension from a path name.
void Path::stripExtension() void Path::stripExtension()
{ {
nvCheck( m_str != NULL ); nvCheck( m_str != NULL );
int length = (int)strlen(m_str) - 1; int length = (int)strlen(m_str) - 1;
while( length > 0 && m_str[length] != '.' ) { while( length > 0 && m_str[length] != '.' ) {
length--; length--;
if( m_str[length] == NV_PATH_SEPARATOR ) { if( m_str[length] == NV_PATH_SEPARATOR ) {
return; // no extension return; // no extension
} }
} }
if( length ) { if( length ) {
m_str[length] = 0; m_str[length] = 0;
} }
} }
@ -509,39 +517,39 @@ void Path::stripExtension()
// static // static
char Path::separator() char Path::separator()
{ {
return NV_PATH_SEPARATOR; return NV_PATH_SEPARATOR;
} }
// static // static
const char * Path::fileName(const char * str) const char * Path::fileName(const char * str)
{ {
nvCheck( str != NULL ); nvCheck( str != NULL );
int length = (int)strlen(str) - 1; int length = (int)strlen(str) - 1;
while( length >= 0 && str[length] != separator() ) { while (length >= 0 && str[length] != '\\' && str[length] != '/') {
length--; length--;
} }
return &str[length+1]; return &str[length+1];
} }
// static // static
const char * Path::extension(const char * str) const char * Path::extension(const char * str)
{ {
nvCheck( str != NULL ); nvCheck( str != NULL );
int length, l; int length, l;
l = length = (int)strlen( str ); l = length = (int)strlen( str );
while( length > 0 && str[length] != '.' ) { while (length > 0 && str[length] != '.') {
length--; length--;
if( str[length] == separator() ) { if (str[length] != '\\' || str[length] != '/') {
return &str[l]; // no extension return &str[l]; // no extension
} }
} }
if( length == 0 ) { if (length == 0) {
return &str[l]; return &str[l];
} }
return &str[length]; return &str[length];
} }
@ -549,36 +557,36 @@ const char * Path::extension(const char * str)
/// Clone this string /// Clone this string
String String::clone() const String String::clone() const
{ {
String str(data); String str(data);
return str; return str;
} }
void String::setString(const char * str) void String::setString(const char * str)
{ {
if (str == NULL) { if (str == NULL) {
data = NULL; data = NULL;
} }
else { else {
allocString( str ); allocString( str );
addRef(); addRef();
} }
} }
void String::setString(const char * str, int length) void String::setString(const char * str, int length)
{ {
nvDebugCheck(str != NULL); nvDebugCheck(str != NULL);
allocString(str, length); allocString(str, length);
addRef(); addRef();
} }
void String::setString(const StringBuilder & str) void String::setString(const StringBuilder & str)
{ {
if (str.str() == NULL) { if (str.str() == NULL) {
data = NULL; data = NULL;
} }
else { else {
allocString(str); allocString(str);
addRef(); addRef();
} }
} }

View File

@ -1,10 +1,12 @@
// This code is in the public domain -- castanyo@yahoo.es // This code is in the public domain -- Ignacio Casta<74>o <castano@gmail.com>
#pragma once
#ifndef NV_CORE_STRING_H #ifndef NV_CORE_STRING_H
#define NV_CORE_STRING_H #define NV_CORE_STRING_H
#include "nvcore.h" #include "nvcore.h"
#include "Containers.h" // swap #include "Memory.h"
#include "Utils.h" // swap, hash
#include <string.h> // strlen, strcmp, etc. #include <string.h> // strlen, strcmp, etc.
@ -12,343 +14,352 @@
namespace nv namespace nv
{ {
uint strHash(const char * str, uint h) NV_PURE; uint strHash(const char * str, uint h) NV_PURE;
/// String hash based on Bernstein's hash. /// String hash based on Bernstein's hash.
inline uint strHash(const char * data, uint h = 5381) inline uint strHash(const char * data, uint h = 5381)
{ {
uint i = 0; uint i = 0;
while(data[i] != 0) { while(data[i] != 0) {
h = (33 * h) ^ uint(data[i]); h = (33 * h) ^ uint(data[i]);
i++; i++;
} }
return h; return h;
} }
template <> struct hash<const char *> {
uint operator()(const char * str) const { return strHash(str); }
};
NVCORE_API int strCaseCmp(const char * s1, const char * s2) NV_PURE;
NVCORE_API int strCmp(const char * s1, const char * s2) NV_PURE;
NVCORE_API void strCpy(char * dst, int size, const char * src);
NVCORE_API void strCpy(char * dst, int size, const char * src, int len);
NVCORE_API void strCat(char * dst, int size, const char * src);
NVCORE_API bool strMatch(const char * str, const char * pat) NV_PURE; template <> struct Hash<const char *> {
uint operator()(const char * str) const { return strHash(str); }
};
/// String builder.
class NVCORE_CLASS StringBuilder
{
public:
StringBuilder();
explicit StringBuilder( int size_hint );
StringBuilder( const char * str );
StringBuilder( const StringBuilder & );
~StringBuilder();
StringBuilder & format( const char * format, ... ) __attribute__((format (printf, 2, 3)));
StringBuilder & format( const char * format, va_list arg );
StringBuilder & append( const char * str );
StringBuilder & appendFormat( const char * format, ... ) __attribute__((format (printf, 2, 3)));
StringBuilder & appendFormat( const char * format, va_list arg );
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 StringBuilder & str );
StringBuilder & toLower();
StringBuilder & toUpper();
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; }
/// 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 {
if (s.isNull()) return isNull();
else if (isNull()) return false;
else return strcmp(s.m_str, m_str) != 0;
}
/// Return the exact length.
uint length() const { return isNull() ? 0 : uint(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) {
nv::swap(a.m_size, b.m_size);
nv::swap(a.m_str, b.m_str);
}
protected:
/// Size of the string container.
uint m_size;
/// String.
char * m_str;
};
/// Path string. @@ This should be called PathBuilder. NVCORE_API int strCaseCmp(const char * s1, const char * s2) NV_PURE;
class NVCORE_CLASS Path : public StringBuilder NVCORE_API int strCmp(const char * s1, const char * s2) NV_PURE;
{
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();
void stripFileName();
void stripExtension();
// statics template <> struct Equal<const char *> {
NVCORE_API static char separator(); bool operator()(const char * a, const char * b) const { return strCmp(a, b) == 0; }
NVCORE_API static const char * fileName(const char *); };
NVCORE_API static const char * extension(const char *);
};
/// String class.
class NVCORE_CLASS String
{
public:
/// Constructs a null string. @sa isNull()
String()
{
data = NULL;
}
/// Constructs a shared copy of str. NVCORE_API void strCpy(char * dst, int size, const char * src);
String(const String & str) NVCORE_API void strCpy(char * dst, int size, const char * src, int len);
{ NVCORE_API void strCat(char * dst, int size, const char * src);
data = str.data;
if (data != NULL) addRef();
}
/// Constructs a shared string from a standard string. NVCORE_API bool strMatch(const char * str, const char * pat) NV_PURE;
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 builder.
String(const StringBuilder & str) class NVCORE_CLASS StringBuilder
{ {
setString(str); public:
}
/// Dtor. StringBuilder();
~String() explicit StringBuilder( int size_hint );
{ StringBuilder( const char * str, int extra_size_hint = 0);
release(); StringBuilder( const StringBuilder & );
}
String clone() const; ~StringBuilder();
/// 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. StringBuilder & format( const char * format, ... ) __attribute__((format (printf, 2, 3)));
const String & operator=( const StringBuilder & str ) StringBuilder & format( const char * format, va_list arg );
{
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. StringBuilder & append( const char * str );
bool operator==( const String & str ) const StringBuilder & appendFormat( const char * format, ... ) __attribute__((format (printf, 2, 3)));
{ StringBuilder & appendFormat( const char * format, va_list arg );
if( str.data == data ) {
return true;
}
if ((data == NULL) != (str.data == NULL)) {
return false;
}
return strcmp(data, str.data) == 0;
}
/// Equal operator. StringBuilder & number( int i, int base = 10 );
bool operator==( const char * str ) const StringBuilder & number( uint i, int base = 10 );
{
nvCheck(str != NULL); // Use isNull!
if (data == NULL) {
return false;
}
return strcmp(data, str) == 0;
}
/// Not equal operator. StringBuilder & reserve( uint size_hint );
bool operator!=( const String & str ) const StringBuilder & copy( const char * str, int extra_size/*=0*/ );
{ StringBuilder & copy( const StringBuilder & str );
if( str.data == data ) {
return false;
}
if ((data == NULL) != (str.data == NULL)) {
return true;
}
return strcmp(data, str.data) != 0;
}
/// Not equal operator.
bool operator!=( const char * str ) const
{
nvCheck(str != NULL); // Use isNull!
if (data == NULL) {
return false;
}
return strcmp(data, str) != 0;
}
/// 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 uint(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: StringBuilder & toLower();
StringBuilder & toUpper();
// Add reference count. void reset();
void addRef() bool isNull() const { return m_size == 0; }
{
if (data != NULL)
{
setRefCount(getRefCount() + 1);
}
}
// Decrease reference count.
void release()
{
if (data != NULL)
{
const uint16 count = getRefCount();
setRefCount(count - 1);
if (count - 1 == 0) {
mem::free(data - 2);
data = NULL;
}
}
}
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, (int)strlen(str));
}
void allocString(const char * str, int len) // const char * accessors
{ operator const char * () const { return m_str; }
const char * ptr = static_cast<const char *>(mem::malloc(2 + len + 1)); operator char * () { return m_str; }
const char * str() const { return m_str; }
setData( ptr ); char * str() { return m_str; }
setRefCount( 0 );
// Copy string.
strCpy(const_cast<char *>(data), len+1, str, len);
// Add terminating character. char * release();
const_cast<char *>(data)[len] = '\0';
}
void setString(const char * str);
void setString(const char * str, int length);
void setString(const StringBuilder & str);
/// Swap strings.
friend void swap(String & a, String & b) {
swap(a.data, b.data);
}
private:
const char * data; /// 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 {
if (s.isNull()) return isNull();
else if (isNull()) return false;
else return strcmp(s.m_str, m_str) != 0;
}
/// Return the exact length.
uint length() const { return isNull() ? 0 : uint(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) {
nv::swap(a.m_size, b.m_size);
nv::swap(a.m_str, b.m_str);
}
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, int extra_size_hint = 0) : StringBuilder(str, extra_size_hint) {}
Path(const Path & path) : StringBuilder(path) {}
const char * fileName() const;
const char * extension() const;
void translatePath();
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 *);
};
/// 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
{
if( str.data == data ) {
return true;
}
if ((data == NULL) != (str.data == NULL)) {
return false;
}
return strcmp(data, str.data) == 0;
}
/// Equal operator.
bool operator==( const char * str ) const
{
nvCheck(str != NULL); // Use isNull!
if (data == NULL) {
return false;
}
return strcmp(data, str) == 0;
}
/// Not equal operator.
bool operator!=( const String & str ) const
{
if( str.data == data ) {
return false;
}
if ((data == NULL) != (str.data == NULL)) {
return true;
}
return strcmp(data, str.data) != 0;
}
/// Not equal operator.
bool operator!=( const char * str ) const
{
nvCheck(str != NULL); // Use isNull!
if (data == NULL) {
return false;
}
return strcmp(data, str) != 0;
}
/// 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 uint(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()
{
if (data != NULL)
{
setRefCount(getRefCount() + 1);
}
}
// Decrease reference count.
void release()
{
if (data != NULL)
{
const uint16 count = getRefCount();
setRefCount(count - 1);
if (count - 1 == 0) {
mem::free(data - 2);
data = NULL;
}
}
}
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, (int)strlen(str));
}
void allocString(const char * str, int len)
{
const char * ptr = static_cast<const char *>(mem::malloc(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 setString(const char * str);
void setString(const char * str, int length);
void setString(const StringBuilder & str);
/// Swap strings.
friend void swap(String & a, String & b) {
swap(a.data, b.data);
}
private:
const char * data;
};
} // nv namespace } // nv namespace

View File

@ -1,5 +1,6 @@
// This code is in the public domain -- castano@gmail.com // This code is in the public domain -- Ignacio Casta<74>o <castano@gmail.com>
#pragma once
#ifndef NV_CORE_STREAM_H #ifndef NV_CORE_STREAM_H
#define NV_CORE_STREAM_H #define NV_CORE_STREAM_H
@ -9,151 +10,151 @@
namespace nv namespace nv
{ {
/// Base stream class. /// Base stream class.
class NVCORE_CLASS Stream { class NVCORE_CLASS Stream {
public: public:
enum ByteOrder { enum ByteOrder {
LittleEndian = false, LittleEndian = false,
BigEndian = true, BigEndian = true,
}; };
/// Get the byte order of the system. /// Get the byte order of the system.
static ByteOrder getSystemByteOrder() { static ByteOrder getSystemByteOrder() {
#if NV_LITTLE_ENDIAN #if NV_LITTLE_ENDIAN
return LittleEndian; return LittleEndian;
#else #else
return BigEndian; return BigEndian;
#endif #endif
} }
/// Ctor. /// Ctor.
Stream() : m_byteOrder(LittleEndian) { } Stream() : m_byteOrder(LittleEndian) { }
/// Virtual destructor. /// Virtual destructor.
virtual ~Stream() {} virtual ~Stream() {}
/// Set byte order. /// Set byte order.
void setByteOrder(ByteOrder bo) { m_byteOrder = bo; } void setByteOrder(ByteOrder bo) { m_byteOrder = bo; }
/// Get byte order.
ByteOrder byteOrder() const { return m_byteOrder; }
/// Get byte order.
/// Serialize the given data. ByteOrder byteOrder() const { return m_byteOrder; }
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;
// friends
friend Stream & operator<<( Stream & s, bool & c ) {
#if NV_OS_DARWIN
nvStaticCheck(sizeof(bool) == 4);
uint8 b = c ? 1 : 0;
s.serialize( &b, 1 );
c = (b == 1);
#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: /// Serialize the given data.
virtual uint serialize( void * data, uint len ) = 0;
ByteOrder m_byteOrder; /// 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;
// friends
friend Stream & operator<<( Stream & s, bool & c ) {
#if NV_OS_DARWIN
nvStaticCheck(sizeof(bool) == 4);
uint8 b = c ? 1 : 0;
s.serialize( &b, 1 );
c = (b == 1);
#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 } // nv namespace

View File

@ -1,4 +1,4 @@
// This code is in the public domain -- castano@gmail.com // This code is in the public domain -- Ignacio Casta<74>o <castano@gmail.com>
#include "TextReader.h" #include "TextReader.h"

View File

@ -1,37 +1,39 @@
// This code is in the public domain -- castano@gmail.com // This code is in the public domain -- Ignacio Casta<74>o <castano@gmail.com>
#ifndef NV_CORE_TEXTREADER_H #pragma once
#define NV_CORE_TEXTREADER_H #ifndef NVCORE_TEXTREADER_H
#define NVCORE_TEXTREADER_H
#include "Containers.h" #include "nvcore.h"
#include "Stream.h" #include "Stream.h"
#include "Array.h"
namespace nv namespace nv
{ {
/// Text reader. /// Text reader.
class NVCORE_CLASS TextReader { class NVCORE_CLASS TextReader {
public: public:
/// Ctor.
TextReader(Stream * stream) : m_stream(stream), m_text(512) {
nvCheck(stream != NULL);
nvCheck(stream->isLoading());
}
char peek();
char read();
const char *readToEnd();
// Returns a temporary string. /// Ctor.
const char * readLine(); TextReader(Stream * stream) : m_stream(stream), m_text(512) {
nvCheck(stream != NULL);
nvCheck(stream->isLoading());
}
private: char peek();
Stream * m_stream; char read();
Array<char> m_text;
}; const char *readToEnd();
// Returns a temporary string.
const char * readLine();
private:
Stream * m_stream;
Array<char> m_text;
};
} // nv namespace } // nv namespace
#endif // NV_CORE_TEXTREADER_H #endif // NVCORE_TEXTREADER_H

View File

@ -1,4 +1,4 @@
// This code is in the public domain -- castano@gmail.com // This code is in the public domain -- Ignacio Casta<74>o <castano@gmail.com>
#include "TextWriter.h" #include "TextWriter.h"
@ -7,39 +7,39 @@ using namespace nv;
/// Constructor /// Constructor
TextWriter::TextWriter(Stream * s) : TextWriter::TextWriter(Stream * s) :
s(s), s(s),
str(1024) str(1024)
{ {
nvCheck(s != NULL); nvCheck(s != NULL);
nvCheck(s->isSaving()); nvCheck(s->isSaving());
} }
void TextWriter::writeString(const char * str) void TextWriter::writeString(const char * str)
{ {
nvDebugCheck(s != NULL); nvDebugCheck(s != NULL);
s->serialize(const_cast<char *>(str), (int)strlen(str)); s->serialize(const_cast<char *>(str), (int)strlen(str));
} }
void TextWriter::writeString(const char * str, uint len) void TextWriter::writeString(const char * str, uint len)
{ {
nvDebugCheck(s != NULL); nvDebugCheck(s != NULL);
s->serialize(const_cast<char *>(str), len); s->serialize(const_cast<char *>(str), len);
} }
void TextWriter::write(const char * format, ...) void TextWriter::write(const char * format, ...)
{ {
va_list arg; va_list arg;
va_start(arg,format); va_start(arg,format);
str.format(format, arg); str.format(format, arg);
writeString(str.str(), str.length()); writeString(str.str(), str.length());
va_end(arg); va_end(arg);
} }
void TextWriter::write(const char * format, va_list arg) void TextWriter::write(const char * format, va_list arg)
{ {
va_list tmp; va_list tmp;
va_copy(tmp, arg); va_copy(tmp, arg);
str.format(format, arg); str.format(format, arg);
writeString(str.str(), str.length()); writeString(str.str(), str.length());
va_end(tmp); va_end(tmp);
} }

View File

@ -1,64 +1,62 @@
// This code is in the public domain -- castano@gmail.com // This code is in the public domain -- Ignacio Casta<74>o <castano@gmail.com>
#ifndef NV_CORE_TEXTWRITER_H #pragma once
#define NV_CORE_TEXTWRITER_H #ifndef NVCORE_TEXTWRITER_H
#define NVCORE_TEXTWRITER_H
#include "StrLib.h" #include "nvcore.h"
#include "Stream.h" #include "Stream.h"
#include "StrLib.h"
namespace nv namespace nv
{ {
/// Text writer. /// Text writer.
class NVCORE_CLASS TextWriter class NVCORE_CLASS TextWriter
{ {
public: public:
TextWriter(Stream * s); TextWriter(Stream * s);
void writeString(const char * str); void writeString(const char * str);
void writeString(const char * str, uint len); void writeString(const char * str, uint len);
void write(const char * format, ...) __attribute__((format (printf, 2, 3))); void write(const char * format, ...) __attribute__((format (printf, 2, 3)));
void write(const char * format, va_list arg); void write(const char * format, va_list arg);
private: private:
Stream * s; Stream * s;
// Temporary string. // Temporary string.
StringBuilder str; StringBuilder str;
}; };
inline TextWriter & operator<<( TextWriter & tw, int i) inline TextWriter & operator<<( TextWriter & tw, int i)
{ {
tw.write("%d", i); tw.write("%d", i);
return tw; return tw;
} }
inline TextWriter & operator<<( TextWriter & tw, uint i) inline TextWriter & operator<<( TextWriter & tw, uint i)
{ {
tw.write("%u", i); tw.write("%u", i);
return tw; return tw;
} }
inline TextWriter & operator<<( TextWriter & tw, float f) inline TextWriter & operator<<( TextWriter & tw, float f)
{ {
tw.write("%f", f); tw.write("%f", f);
return tw; return tw;
} }
inline TextWriter & operator<<( TextWriter & tw, const char * str) inline TextWriter & operator<<( TextWriter & tw, const char * str)
{ {
tw.writeString(str); tw.writeString(str);
return tw; return tw;
} }
} // nv namespace } // nv namespace
#endif // NVCORE_TEXTWRITER_H #endif // NVCORE_TEXTWRITER_H

View File

@ -1,5 +1,6 @@
// This code is in the public domain -- castano@gmail.com // This code is in the public domain -- castano@gmail.com
#pragma once
#ifndef NV_CORE_TIMER_H #ifndef NV_CORE_TIMER_H
#define NV_CORE_TIMER_H #define NV_CORE_TIMER_H
@ -12,16 +13,16 @@
class NVCORE_CLASS Timer class NVCORE_CLASS Timer
{ {
public: public:
Timer() {} Timer() {}
void start() { m_start = clock(); } void start() { m_start = clock(); }
void stop() { m_stop = clock(); } void stop() { m_stop = clock(); }
float elapsed() const { return float(m_stop - m_start) / CLOCKS_PER_SEC; }
float elapsed() const { return float(m_stop - m_start) / CLOCKS_PER_SEC; }
private: private:
clock_t m_start; clock_t m_start;
clock_t m_stop; clock_t m_stop;
}; };
#else #else
@ -34,22 +35,22 @@ private:
class NVCORE_CLASS Timer class NVCORE_CLASS Timer
{ {
public: public:
Timer() { Timer() {
// get the tick frequency from the OS // get the tick frequency from the OS
QueryPerformanceFrequency((LARGE_INTEGER*) &m_frequency); QueryPerformanceFrequency((LARGE_INTEGER*) &m_frequency);
} }
void start() { QueryPerformanceCounter((LARGE_INTEGER*) &m_start); } void start() { QueryPerformanceCounter((LARGE_INTEGER*) &m_start); }
void stop() { QueryPerformanceCounter((LARGE_INTEGER*) &m_stop); } void stop() { QueryPerformanceCounter((LARGE_INTEGER*) &m_stop); }
int elapsed() const {
return (int)1000 * ((double)m_stop.QuadPart - (double)m_start.QuadPart) / (double)m_frequency.QuadPart;
}
int elapsed() const {
return (int)1000 * ((double)m_stop.QuadPart - (double)m_start.QuadPart) / (double)m_frequency.QuadPart;
}
private: private:
LARGE_INTEGER m_frequency; LARGE_INTEGER m_frequency;
LARGE_INTEGER m_start; LARGE_INTEGER m_start;
LARGE_INTEGER m_stop; LARGE_INTEGER m_stop;
}; };

134
src/nvcore/Utils.h Normal file
View File

@ -0,0 +1,134 @@
// This code is in the public domain -- Ignacio Casta<74>o <castano@gmail.com>
#pragma once
#ifndef NV_CORE_UTILS_H
#define NV_CORE_UTILS_H
#include "nvcore.h"
#include "Debug.h" // nvDebugCheck
namespace nv
{
/// 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.
template <typename T>
inline const T & max(const T & a, const T & b)
{
//return std::max(a, b);
if( a < b ) {
return b;
}
return a;
}
/// Return the maximum of the three arguments.
template <typename T>
inline const T & max(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)
{
//return std::min(a, b);
if( b < a ) {
return b;
}
return a;
}
/// Return the maximum of the three arguments.
template <typename T>
inline const T & min(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)
{
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 uint nextPowerOfTwo( uint 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
}
/// Return true if @a n is a power of two.
inline bool isPowerOfTwo( uint n )
{
return (n & (n-1)) == 0;
}
// Some hash functors:
template <typename Key> struct Hash
{
inline uint sdbm_hash(const void * data_in, uint size, uint h = 5381) const
{
const uint8 * data = (const uint8 *) data_in;
uint i = 0;
while (i < size) {
h = (h << 16) + (h << 6) - h + (uint) data[i++];
}
return h;
}
uint operator()(const Key & k) const {
return sdbm_hash(&k, sizeof(Key));
}
};
template <> struct Hash<int>
{
uint operator()(int x) const { return x; }
};
template <> struct Hash<uint>
{
uint operator()(uint x) const { return x; }
};
template <typename Key> struct Equal
{
bool operator()(const Key & k0, const Key & k1) const {
return k0 == k1;
}
};
} // nv namespace
#endif // NV_CORE_UTILS_H

View File

@ -1,5 +1,6 @@
// This code is in the public domain -- castanyo@yahoo.es // This code is in the public domain -- Ignacio Casta<74>o <castano@gmail.com>
#pragma once
#ifndef NV_CORE_H #ifndef NV_CORE_H
#define NV_CORE_H #define NV_CORE_H
@ -33,7 +34,7 @@
// NV_OS_UNIX // NV_OS_UNIX
// NV_OS_DARWIN // NV_OS_DARWIN
#define NV_OS_STRING POSH_OS_STRING #define NV_OS_STRING POSH_OS_STRING
#if defined POSH_OS_LINUX #if defined POSH_OS_LINUX
# define NV_OS_LINUX 1 # define NV_OS_LINUX 1
@ -59,22 +60,21 @@
# error "Unsupported OS" # error "Unsupported OS"
#endif #endif
// CPUs: // CPUs:
// NV_CPU_X86 // NV_CPU_X86
// NV_CPU_X86_64 // NV_CPU_X86_64
// NV_CPU_PPC // NV_CPU_PPC
#define NV_CPU_STRING POSH_CPU_STRING #define NV_CPU_STRING POSH_CPU_STRING
#if defined POSH_CPU_X86_64 #if defined POSH_CPU_X86_64
# define NV_CPU_X86_64 1 # define NV_CPU_X86_64 1
#elif defined POSH_CPU_X86 #elif defined POSH_CPU_X86
# define NV_CPU_X86 1 # define NV_CPU_X86 1
#elif defined POSH_CPU_PPC #elif defined POSH_CPU_PPC
# define NV_CPU_PPC 1 # define NV_CPU_PPC 1
#else #else
# error "Unsupported CPU" # error "Unsupported CPU"
#endif #endif
@ -86,26 +86,26 @@
// @@ NV_CC_MSVC8 // @@ NV_CC_MSVC8
#if defined POSH_COMPILER_GCC #if defined POSH_COMPILER_GCC
# define NV_CC_GNUC 1 # define NV_CC_GNUC 1
# define NV_CC_STRING "gcc" # define NV_CC_STRING "gcc"
#elif defined POSH_COMPILER_MSVC #elif defined POSH_COMPILER_MSVC
# define NV_CC_MSVC 1 # define NV_CC_MSVC 1
# define NV_CC_STRING "msvc" # define NV_CC_STRING "msvc"
#else #else
# error "Unsupported compiler" # error "Unsupported compiler"
#endif #endif
// Endiannes: // Endiannes:
#define NV_LITTLE_ENDIAN POSH_LITTLE_ENDIAN #define NV_LITTLE_ENDIAN POSH_LITTLE_ENDIAN
#define NV_BIG_ENDIAN POSH_BIG_ENDIAN #define NV_BIG_ENDIAN POSH_BIG_ENDIAN
#define NV_ENDIAN_STRING POSH_ENDIAN_STRING #define NV_ENDIAN_STRING POSH_ENDIAN_STRING
// Version string: // Version string:
#define NV_VERSION_STRING \ #define NV_VERSION_STRING \
NV_OS_STRING "/" NV_CC_STRING "/" NV_CPU_STRING"/" \ NV_OS_STRING "/" NV_CC_STRING "/" NV_CPU_STRING"/" \
NV_ENDIAN_STRING"-endian - " __DATE__ "-" __TIME__ NV_ENDIAN_STRING"-endian - " __DATE__ "-" __TIME__
/// Disable copy constructor and assignment operator. /// Disable copy constructor and assignment operator.
@ -120,9 +120,9 @@
/// See Prohibiting Heap-Based Objects in More Effective C++. /// See Prohibiting Heap-Based Objects in More Effective C++.
/// @hideinitializer /// @hideinitializer
#define NV_FORBID_HEAPALLOC() \ #define NV_FORBID_HEAPALLOC() \
private: \ private: \
static void *operator new(size_t size); \ static void *operator new(size_t size); \
static void *operator new[](size_t size); static void *operator new[](size_t size);
// String concatenation macros. // String concatenation macros.
#define NV_STRING_JOIN2(arg1, arg2) NV_DO_STRING_JOIN2(arg1, arg2) #define NV_STRING_JOIN2(arg1, arg2) NV_DO_STRING_JOIN2(arg1, arg2)
@ -139,12 +139,12 @@
// Startup initialization macro. // Startup initialization macro.
#define NV_AT_STARTUP(some_code) \ #define NV_AT_STARTUP(some_code) \
namespace { \ namespace { \
static struct NV_STRING_JOIN2(AtStartup_, __LINE__) { \ static struct NV_STRING_JOIN2(AtStartup_, __LINE__) { \
NV_STRING_JOIN2(AtStartup_, __LINE__)() { some_code; } \ NV_STRING_JOIN2(AtStartup_, __LINE__)() { some_code; } \
} \ } \
NV_STRING_JOIN3(AtStartup_, __LINE__, Instance); \ NV_STRING_JOIN3(AtStartup_, __LINE__, Instance); \
}; };
/// Indicate the compiler that the parameter is not used to suppress compier warnings. /// Indicate the compiler that the parameter is not used to suppress compier warnings.
/// @hideinitializer /// @hideinitializer
@ -160,23 +160,23 @@
// Platform includes // Platform includes
#if NV_CC_MSVC #if NV_CC_MSVC
# if NV_OS_WIN32 # if NV_OS_WIN32
# include "DefsVcWin32.h" # include "DefsVcWin32.h"
# else # else
# error "MSVC: Platform not supported" # error "MSVC: Platform not supported"
# endif # endif
#elif NV_CC_GNUC #elif NV_CC_GNUC
# if NV_OS_LINUX # if NV_OS_LINUX
# include "DefsGnucLinux.h" # include "DefsGnucLinux.h"
# elif NV_OS_DARWIN || NV_OS_FREEBSD # elif NV_OS_DARWIN || NV_OS_FREEBSD
# include "DefsGnucDarwin.h" # include "DefsGnucDarwin.h"
# elif NV_OS_MINGW # elif NV_OS_MINGW
# include "DefsGnucWin32.h" # include "DefsGnucWin32.h"
# elif NV_OS_CYGWIN # elif NV_OS_CYGWIN
# error "GCC: Cygwin not supported" # error "GCC: Cygwin not supported"
# else # else
# error "GCC: Platform not supported" # error "GCC: Platform not supported"
# endif # endif
#endif #endif
#endif // NV_CORE_H #endif // NV_CORE_H