Merge changes from The Witness.

This commit is contained in:
castano
2012-07-20 16:19:03 +00:00
parent fea97461c5
commit 3b4fcd0369
36 changed files with 1073 additions and 178 deletions

View File

@ -30,6 +30,7 @@ namespace nv
template<typename T>
class NVCORE_CLASS Array {
public:
typedef uint size_type;
// Default constructor.
NV_FORCEINLINE Array() : m_buffer(NULL), m_capacity(0), m_size(0) {}
@ -86,12 +87,18 @@ namespace nv
/// Get vector size.
NV_FORCEINLINE uint count() const { return m_size; }
/// Get vector capacity.
NV_FORCEINLINE uint capacity() const { return m_capacity; }
/// Get const vector pointer.
NV_FORCEINLINE const T * buffer() const { return m_buffer; }
/// Get vector pointer.
NV_FORCEINLINE T * buffer() { return m_buffer; }
NV_FORCEINLINE T * begin() { return m_buffer; }
NV_FORCEINLINE T * end() { return m_buffer + m_size; }
/// Is vector empty.
NV_FORCEINLINE bool isEmpty() const { return m_size == 0; }
@ -120,6 +127,7 @@ namespace nv
void replaceWithLast(uint index);
void resize(uint new_size);
void resize(uint new_size, const T & elem);
void fill(const T & elem);
void clear();
void shrink();
void reserve(uint desired_size);

View File

@ -249,10 +249,19 @@ namespace nv
construct_range(m_buffer, new_size, old_size, elem);
}
// Fill array with the given value.
template <typename T>
void Array<T>::fill(const T & elem)
{
fill(m_buffer, m_size, elem)
}
// Clear the buffer.
template <typename T>
NV_FORCEINLINE void Array<T>::clear()
{
nvDebugCheck(isValidPtr(m_buffer));
// Destruct old elements
destroy_range(m_buffer, 0, m_size);

View File

@ -1,8 +1,11 @@
// This code is in the public domain -- Ignacio Casta<74>o <castano@gmail.com>
#include "Debug.h"
#include "Array.inl"
#include "StrLib.h" // StringBuilder
#include "StdStream.h" // fileOpen
// Extern
#if NV_OS_WIN32 //&& NV_CC_MSVC
# define WIN32_LEAN_AND_MEAN
@ -58,6 +61,9 @@
# endif
#endif
#define USE_SEPARATE_THREAD 1
using namespace nv;
namespace
@ -67,6 +73,7 @@ namespace
static AssertHandler * s_assert_handler = NULL;
static bool s_sig_handler_enabled = false;
static bool s_interactive = true;
#if NV_OS_WIN32 && NV_CC_MSVC
@ -86,12 +93,82 @@ namespace
#if NV_OS_WIN32 && NV_CC_MSVC
// We should try to simplify the top level filter as much as possible.
// http://www.nynaeve.net/?p=128
#if USE_SEPARATE_THREAD
// The critical section enforcing the requirement that only one exception be
// handled by a handler at a time.
static CRITICAL_SECTION s_handler_critical_section;
// Semaphores used to move exception handling between the exception thread
// and the handler thread. handler_start_semaphore_ is signalled by the
// exception thread to wake up the handler thread when an exception occurs.
// handler_finish_semaphore_ is signalled by the handler thread to wake up
// the exception thread when handling is complete.
static HANDLE s_handler_start_semaphore = NULL;
static HANDLE s_handler_finish_semaphore = NULL;
// The exception handler thread.
static HANDLE s_handler_thread = NULL;
static DWORD s_requesting_thread_id = 0;
static EXCEPTION_POINTERS * s_exception_info = NULL;
#endif // USE_SEPARATE_THREAD
struct MinidumpCallbackContext {
ULONG64 memory_base;
ULONG memory_size;
bool finished;
};
// static
static BOOL CALLBACK miniDumpWriteDumpCallback(PVOID context, const PMINIDUMP_CALLBACK_INPUT callback_input, PMINIDUMP_CALLBACK_OUTPUT callback_output)
{
switch (callback_input->CallbackType)
{
case MemoryCallback: {
MinidumpCallbackContext* callback_context = reinterpret_cast<MinidumpCallbackContext*>(context);
if (callback_context->finished)
return FALSE;
// Include the specified memory region.
callback_output->MemoryBase = callback_context->memory_base;
callback_output->MemorySize = callback_context->memory_size;
callback_context->finished = true;
return TRUE;
}
// Include all modules.
case IncludeModuleCallback:
case ModuleCallback:
return TRUE;
// Include all threads.
case IncludeThreadCallback:
case ThreadCallback:
return TRUE;
// Stop receiving cancel callbacks.
case CancelCallback:
callback_output->CheckCancel = FALSE;
callback_output->Cancel = FALSE;
return TRUE;
}
// Ignore other callback types.
return FALSE;
}
static bool writeMiniDump(EXCEPTION_POINTERS * pExceptionInfo)
{
// create the file
HANDLE hFile = CreateFileA("crash.dmp", GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
nvDebug("*** Failed to create dump file.\n");
//nvDebug("*** Failed to create dump file.\n");
return false;
}
@ -100,20 +177,77 @@ namespace
ExInfo.ExceptionPointers = pExceptionInfo;
ExInfo.ClientPointers = NULL;
MINIDUMP_CALLBACK_INFORMATION callback;
MINIDUMP_CALLBACK_INFORMATION * callback_pointer = NULL;
MinidumpCallbackContext context;
// Find a memory region of 256 bytes centered on the
// faulting instruction pointer.
const ULONG64 instruction_pointer =
#if defined(_M_IX86)
pExceptionInfo->ContextRecord->Eip;
#elif defined(_M_AMD64)
pExceptionInfo->ContextRecord->Rip;
#else
#error Unsupported platform
#endif
MEMORY_BASIC_INFORMATION info;
if (VirtualQuery(reinterpret_cast<LPCVOID>(instruction_pointer), &info, sizeof(MEMORY_BASIC_INFORMATION)) != 0 && info.State == MEM_COMMIT)
{
// Attempt to get 128 bytes before and after the instruction
// pointer, but settle for whatever's available up to the
// boundaries of the memory region.
const ULONG64 kIPMemorySize = 256;
context.memory_base = max(reinterpret_cast<ULONG64>(info.BaseAddress), instruction_pointer - (kIPMemorySize / 2));
ULONG64 end_of_range = min(instruction_pointer + (kIPMemorySize / 2), reinterpret_cast<ULONG64>(info.BaseAddress) + info.RegionSize);
context.memory_size = static_cast<ULONG>(end_of_range - context.memory_base);
context.finished = false;
callback.CallbackRoutine = miniDumpWriteDumpCallback;
callback.CallbackParam = reinterpret_cast<void*>(&context);
callback_pointer = &callback;
}
MINIDUMP_TYPE miniDumpType = (MINIDUMP_TYPE)(MiniDumpNormal|MiniDumpWithHandleData|MiniDumpWithThreadInfo);
// write the dump
BOOL ok = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpNormal, &ExInfo, NULL, NULL) != 0;
BOOL ok = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, miniDumpType, &ExInfo, NULL, callback_pointer) != 0;
CloseHandle(hFile);
if (ok == FALSE) {
nvDebug("*** Failed to save dump file.\n");
//nvDebug("*** Failed to save dump file.\n");
return false;
}
nvDebug("\nDump file saved.\n");
//nvDebug("\nDump file saved.\n");
return true;
}
#if USE_SEPARATE_THREAD
static DWORD WINAPI ExceptionHandlerThreadMain(void* lpParameter) {
nvDebugCheck(s_handler_start_semaphore != NULL);
nvDebugCheck(s_handler_finish_semaphore != NULL);
while (true) {
if (WaitForSingleObject(s_handler_start_semaphore, INFINITE) == WAIT_OBJECT_0) {
writeMiniDump(s_exception_info);
// Allow the requesting thread to proceed.
ReleaseSemaphore(s_handler_finish_semaphore, 1, NULL);
}
}
// This statement is not reached when the thread is unconditionally
// terminated by the ExceptionHandler destructor.
return 0;
}
#endif // USE_SEPARATE_THREAD
static bool hasStackTrace() {
return true;
}
@ -195,12 +329,12 @@ namespace
}
#pragma warning(pop)
static NV_NOINLINE void printStackTrace(void * trace[], int size, int start=0)
static NV_NOINLINE void writeStackTrace(void * trace[], int size, int start, Array<const char *> & lines)
{
StringBuilder builder(512);
HANDLE hProcess = GetCurrentProcess();
nvDebug( "\nDumping stacktrace:\n" );
// Resolve PC to function names
for (int i = start; i < size; i++)
{
@ -243,7 +377,7 @@ namespace
DWORD dwDisplacement;
if (!SymGetLineFromAddr64(hProcess, ip, &dwDisplacement, &theLine))
{
nvDebug("unknown(%08X) : %s\n", (uint32)ip, pFunc);
builder.format("unknown(%08X) : %s\n", (uint32)ip, pFunc);
}
else
{
@ -256,26 +390,128 @@ namespace
int line = theLine.LineNumber;
nvDebug("%s(%d) : %s\n", pFile, line, pFunc);
builder.format("%s(%d) : %s\n", pFile, line, pFunc);
}
lines.append(builder.release());
}
}
}
// Write mini dump and print stack trace.
static LONG WINAPI topLevelFilter(EXCEPTION_POINTERS * pExceptionInfo)
static LONG WINAPI handleException(EXCEPTION_POINTERS * pExceptionInfo)
{
#if USE_SEPARATE_THREAD
EnterCriticalSection(&s_handler_critical_section);
s_requesting_thread_id = GetCurrentThreadId();
s_exception_info = pExceptionInfo;
// This causes the handler thread to call writeMiniDump.
ReleaseSemaphore(s_handler_start_semaphore, 1, NULL);
// Wait until WriteMinidumpWithException is done and collect its return value.
WaitForSingleObject(s_handler_finish_semaphore, INFINITE);
//bool status = s_handler_return_value;
// Clean up.
s_requesting_thread_id = 0;
s_exception_info = NULL;
LeaveCriticalSection(&s_handler_critical_section);
#else
// First of all, write mini dump.
writeMiniDump(pExceptionInfo);
#endif
nvDebug("\nDump file saved.\n");
// Try to attach to debugger.
if (s_interactive && debug::attachToDebugger()) {
nvDebugBreak();
return EXCEPTION_CONTINUE_EXECUTION;
}
// If that fails, then try to pretty print a stack trace and terminate.
void * trace[64];
int size = backtraceWithSymbols(pExceptionInfo->ContextRecord, trace, 64);
printStackTrace(trace, size, 0);
writeMiniDump(pExceptionInfo);
// @@ Use win32's CreateFile?
FILE * fp = fileOpen("crash.txt", "wb");
if (fp != NULL) {
Array<const char *> lines;
writeStackTrace(trace, size, 0, lines);
return EXCEPTION_CONTINUE_SEARCH;
for (uint i = 0; i < lines.count(); i++) {
fputs(lines[i], fp);
delete lines[i];
}
// @@ Add more info to crash.txt?
fclose(fp);
}
return EXCEPTION_EXECUTE_HANDLER; // Terminate app.
}
/*static void handlePureVirtualCall() {
// This is an pure virtual function call, not an exception. It's safe to
// play with sprintf here.
AutoExceptionHandler auto_exception_handler;
ExceptionHandler* current_handler = auto_exception_handler.get_handler();
MDRawAssertionInfo assertion;
memset(&assertion, 0, sizeof(assertion));
assertion.type = MD_ASSERTION_INFO_TYPE_PURE_VIRTUAL_CALL;
// Make up an exception record for the current thread and CPU context
// to make it possible for the crash processor to classify these
// as do regular crashes, and to make it humane for developers to
// analyze them.
EXCEPTION_RECORD exception_record = {};
CONTEXT exception_context = {};
EXCEPTION_POINTERS exception_ptrs = { &exception_record, &exception_context };
::RtlCaptureContext(&exception_context);
exception_record.ExceptionCode = STATUS_NONCONTINUABLE_EXCEPTION;
// We store pointers to the the expression and function strings,
// and the line as exception parameters to make them easy to
// access by the developer on the far side.
exception_record.NumberParameters = 3;
exception_record.ExceptionInformation[0] = reinterpret_cast<ULONG_PTR>(&assertion.expression);
exception_record.ExceptionInformation[1] = reinterpret_cast<ULONG_PTR>(&assertion.file);
exception_record.ExceptionInformation[2] = assertion.line;
bool success = false;
// In case of out-of-process dump generation, directly call
// WriteMinidumpWithException since there is no separate thread running.
success = current_handler->WriteMinidumpOnHandlerThread(&exception_ptrs, &assertion);
if (!success) {
if (current_handler->previous_pch_) {
// The handler didn't fully handle the exception. Give it to the
// previous purecall handler.
current_handler->previous_pch_();
else {
// If there's no previous handler, return and let _purecall handle it.
// This will just put up an assertion dialog.
return;
}
}
// The handler either took care of the invalid parameter problem itself,
// or passed it on to another handler. "Swallow" it by exiting, paralleling
// the behavior of "swallowing" exceptions.
exit(0);
}*/
#elif !NV_OS_WIN32 && defined(HAVE_SIGNAL_H) // NV_OS_LINUX || NV_OS_DARWIN
#if defined(HAVE_EXECINFO_H)
@ -491,29 +727,34 @@ namespace
}
nvDebug( error_string.str() );
// Print stack trace:
debug::dumpInfo();
if (debug::isDebuggerPresent()) {
return NV_ABORT_DEBUG;
}
flushMessageQueue();
int action = MessageBoxA(NULL, error_string.str(), "Assertion failed", MB_ABORTRETRYIGNORE|MB_ICONERROR);
switch( action ) {
case IDRETRY:
ret = NV_ABORT_DEBUG;
break;
case IDIGNORE:
ret = NV_ABORT_IGNORE;
break;
case IDABORT:
default:
ret = NV_ABORT_EXIT;
break;
if (s_interactive) {
flushMessageQueue();
int action = MessageBoxA(NULL, error_string.str(), "Assertion failed", MB_ABORTRETRYIGNORE|MB_ICONERROR);
switch( action ) {
case IDRETRY:
ret = NV_ABORT_DEBUG;
break;
case IDIGNORE:
ret = NV_ABORT_IGNORE;
break;
case IDABORT:
default:
ret = NV_ABORT_EXIT;
break;
}
/*if( _CrtDbgReport( _CRT_ASSERT, file, line, module, exp ) == 1 ) {
return NV_ABORT_DEBUG;
}*/
}
/*if( _CrtDbgReport( _CRT_ASSERT, file, line, module, exp ) == 1 ) {
return NV_ABORT_DEBUG;
}*/
if( ret == NV_ABORT_EXIT ) {
if (ret == NV_ABORT_EXIT) {
// Exit cleanly.
throw "Assertion failed";
}
@ -633,7 +874,16 @@ void debug::dumpInfo()
{
void * trace[64];
int size = backtrace(trace, 64);
printStackTrace(trace, size, 1);
nvDebug( "\nDumping stacktrace:\n" );
Array<const char *> lines;
writeStackTrace(trace, size, 1, lines);
for (uint i = 0; i < lines.count(); i++) {
nvDebug(lines[i]);
delete lines[i];
}
}
#endif
}
@ -664,15 +914,87 @@ void debug::resetAssertHandler()
}
/// Enable signal handler.
void debug::enableSigHandler()
#if USE_SEPARATE_THREAD
static void initHandlerThread()
{
static const int kExceptionHandlerThreadInitialStackSize = 64 * 1024;
// Set synchronization primitives and the handler thread. Each
// ExceptionHandler object gets its own handler thread because that's the
// only way to reliably guarantee sufficient stack space in an exception,
// and it allows an easy way to get a snapshot of the requesting thread's
// context outside of an exception.
InitializeCriticalSection(&s_handler_critical_section);
s_handler_start_semaphore = CreateSemaphore(NULL, 0, 1, NULL);
nvDebugCheck(s_handler_start_semaphore != NULL);
s_handler_finish_semaphore = CreateSemaphore(NULL, 0, 1, NULL);
nvDebugCheck(s_handler_finish_semaphore != NULL);
// Don't attempt to create the thread if we could not create the semaphores.
if (s_handler_finish_semaphore != NULL && s_handler_start_semaphore != NULL) {
DWORD thread_id;
s_handler_thread = CreateThread(NULL, // lpThreadAttributes
kExceptionHandlerThreadInitialStackSize,
ExceptionHandlerThreadMain,
NULL, // lpParameter
0, // dwCreationFlags
&thread_id);
nvDebugCheck(s_handler_thread != NULL);
}
/* @@ We should avoid loading modules in the exception handler!
dbghelp_module_ = LoadLibrary(L"dbghelp.dll");
if (dbghelp_module_) {
minidump_write_dump_ = reinterpret_cast<MiniDumpWriteDump_type>(GetProcAddress(dbghelp_module_, "MiniDumpWriteDump"));
}
*/
}
static void shutHandlerThread() {
// @@ Free stuff. Terminate thread.
}
#endif
// Enable signal handler.
void debug::enableSigHandler(bool interactive)
{
nvCheck(s_sig_handler_enabled != true);
s_sig_handler_enabled = true;
s_interactive = interactive;
#if NV_OS_WIN32 && NV_CC_MSVC
if (interactive) {
// Do not display message boxes on error.
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms680621(v=vs.85).aspx
SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOGPFAULTERRORBOX|SEM_NOOPENFILEERRORBOX);
// CRT reports errors to debug output only.
// http://msdn.microsoft.com/en-us/library/1y71x448(v=vs.80).aspx
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG);
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG);
_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG);
}
#if USE_SEPARATE_THREAD
initHandlerThread();
#endif
s_old_exception_filter = ::SetUnhandledExceptionFilter( handleException );
/*
#if _MSC_VER >= 1400 // MSVC 2005/8
_set_invalid_parameter_handler(handleInvalidParameter);
#endif // _MSC_VER >= 1400
_set_purecall_handler(handlePureVirtualCall);
*/
s_old_exception_filter = ::SetUnhandledExceptionFilter( topLevelFilter );
// SYMOPT_DEFERRED_LOADS make us not take a ton of time unless we actual log traces
SymSetOptions(SYMOPT_DEFERRED_LOADS|SYMOPT_FAIL_CRITICAL_ERRORS|SYMOPT_LOAD_LINES|SYMOPT_UNDNAME);
@ -797,8 +1119,6 @@ bool debug::attachToDebugger()
::Sleep(200);
}
}
nvDebugBreak();
#endif // NV_OS_WIN32
return true;

View File

@ -76,6 +76,23 @@
} \
NV_MULTI_LINE_MACRO_END
// Interesting assert macro from Insomniac:
// http://www.gdcvault.com/play/1015319/Developing-Imperfect-Software-How-to
// Used as follows:
// if (nvCheck(i < count)) {
// normal path
// } else {
// fixup code.
// }
// This style of macro could be combined with __builtin_expect to let the compiler know failure is unlikely.
#define nvCheckMacro(exp) \
(\
(exp) ? true : ( \
(nvAbort(#exp, __FILE__, __LINE__, __FUNC__) == NV_ABORT_DEBUG) ? (nvDebugBreak(), true) : ( false ) \
) \
)
#define nvAssert(exp) nvAssertMacro(exp)
#define nvCheck(exp) nvAssertMacro(exp)
@ -90,7 +107,7 @@
#endif // NV_NO_ASSERT
// Use nvAssume for very simple expresions only: nvAssume(0), nvAssume(value == true), etc.
#if !defined(_DEBUG)
/*#if !defined(_DEBUG)
# if NV_CC_MSVC
# define nvAssume(exp) __assume(exp)
# else
@ -98,6 +115,20 @@
# endif
#else
# define nvAssume(exp) nvCheck(exp)
#endif*/
#if defined(_DEBUG)
# if NV_CC_MSVC
# define nvUnreachable() nvAssert(0 && "unreachable"); __assume(0)
# else
# define nvUnreachable() nvAssert(0 && "unreachable"); __builtin_unreachable()
# endif
#else
# if NV_CC_MSVC
# define nvUnreachable() __assume(0)
# else
# define nvUnreachable() __builtin_unreachable()
# endif
#endif
@ -138,13 +169,13 @@ namespace nv
return true;
}
/// Message handler interface.
// Message handler interface.
struct MessageHandler {
virtual void log(const char * str, va_list arg) = 0;
virtual ~MessageHandler() {}
};
/// Assert handler interface.
// Assert handler interface.
struct AssertHandler {
virtual int assertion(const char *exp, const char *file, int line, const char *func = NULL) = 0;
virtual ~AssertHandler() {}
@ -161,7 +192,7 @@ namespace nv
NVCORE_API void setAssertHandler( AssertHandler * assertHanlder );
NVCORE_API void resetAssertHandler();
NVCORE_API void enableSigHandler();
NVCORE_API void enableSigHandler(bool interactive);
NVCORE_API void disableSigHandler();
NVCORE_API bool isDebuggerPresent();

View File

@ -25,7 +25,7 @@
#endif
#define NV_FASTCALL __attribute__((fastcall))
#define NV_FORCEINLINE inline __attribute__((always_inline))
#define NV_FORCEINLINE __attribute__((always_inline))
#define NV_DEPRECATED __attribute__((deprecated))
#if __GNUC__ > 2

View File

@ -25,7 +25,7 @@
#endif
#define NV_FASTCALL __attribute__((fastcall))
#define NV_FORCEINLINE inline __attribute__((always_inline))
#define NV_FORCEINLINE __attribute__((always_inline))
#define NV_DEPRECATED __attribute__((deprecated))

View File

@ -44,6 +44,8 @@
#define NV_NOINLINE __declspec(noinline)
#define NV_FORCEINLINE __forceinline
#define NV_THREAD_LOCAL __declspec(thread)
/*
// Type definitions
typedef unsigned char uint8;

View File

@ -28,7 +28,7 @@ bool FileSystem::exists(const char * path)
#elif NV_OS_WIN32 || NV_OS_XBOX
// PathFileExists requires linking to shlwapi.lib
//return PathFileExists(path) != 0;
return GetFileAttributesA(path) != 0xFFFFFFFF;
return GetFileAttributesA(path) != INVALID_FILE_ATTRIBUTES;
#else
if (FILE * fp = fopen(path, "r"))
{

View File

@ -31,25 +31,24 @@ namespace nv
}
// Some hash functors:
template <typename T>
inline uint hash(const T & t, uint h = 5381)
{
return sdbmHash(&t, sizeof(T), h);
}
template <>
inline uint hash(const float & f, uint h)
{
return sdbmFloatHash(&f, 1, h);
}
// Functors for hash table:
template <typename Key> struct Hash
{
uint operator()(const Key & k) const {
return sdbmHash(&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 <> struct Hash<float>
{
uint operator()(float f) const {
return sdbmFloatHash(&f, 1);
return hash(k);
}
};
@ -60,6 +59,25 @@ namespace nv
}
};
// @@ Move to Utils.h?
template <typename T1, typename T2>
struct Pair {
T1 first;
T2 second;
};
template <typename T1, typename T2>
bool operator==(const Pair<T1,T2> & p0, const Pair<T1,T2> & p1) {
return p0.first == p1.first && p0.second == p1.second;
}
template <typename T1, typename T2>
uint hash(const Pair<T1,T2> & p, uint h = 5381) {
return hash(p.second, hash(p.first));
}
} // nv namespace
#endif // NV_CORE_HASH_H

View File

@ -101,17 +101,18 @@ bool nv::strEqual(const char * s1, const char * s2)
return strCmp(s1, s2) == 0;
}
bool nv::strBeginsWith(const char * dst, const char * prefix)
bool nv::strBeginsWith(const char * str, const char * prefix)
{
//return strstr(dst, prefix) == dst;
return strncmp(dst, prefix, strlen(prefix)) == 0;
//return strstr(str, prefix) == dst;
return strncmp(str, prefix, strlen(prefix)) == 0;
}
// @@ Not tested.
bool nv::strEndsWith(const char * dst, const char * suffix)
bool nv::strEndsWith(const char * str, const char * suffix)
{
const size_t len = strlen(suffix);
return strncmp(dst + strlen(dst) - len, suffix, len) == 0;
uint ml = strLen(str);
uint sl = strLen(suffix);
if (ml < sl) return false;
return strncmp(str + ml - sl, suffix, sl) == 0;
}
@ -379,6 +380,28 @@ StringBuilder & StringBuilder::appendFormatList( const char * fmt, va_list arg )
return *this;
}
// Append n spaces.
StringBuilder & StringBuilder::appendSpace(uint n)
{
if (m_str == NULL) {
m_size = n + 1;
m_str = strAlloc(m_size);
memset(m_str, ' ', m_size);
m_str[n] = '\0';
}
else {
const uint len = strLen(m_str);
if (m_size < len + n + 1) {
m_size = len + n + 1;
m_str = strReAlloc(m_str, m_size);
}
memset(m_str + len, ' ', n);
m_str[len+n] = '\0';
}
return *this;
}
/** Convert number to string in the given base. */
StringBuilder & StringBuilder::number( int i, int base )

View File

@ -77,6 +77,8 @@ namespace nv
StringBuilder & appendFormat( const char * format, ... ) __attribute__((format (printf, 2, 3)));
StringBuilder & appendFormatList( const char * format, va_list arg );
StringBuilder & appendSpace(uint n);
StringBuilder & number( int i, int base = 10 );
StringBuilder & number( uint i, int base = 10 );

View File

@ -119,7 +119,7 @@ namespace nv
/// Return the maximum of the three arguments.
template <typename T>
inline const T & max(const T & a, const T & b, const T & c)
inline const T & max3(const T & a, const T & b, const T & c)
{
return max(a, max(b, c));
}
@ -133,7 +133,7 @@ namespace nv
/// Return the maximum of the three arguments.
template <typename T>
inline const T & min(const T & a, const T & b, const T & c)
inline const T & min3(const T & a, const T & b, const T & c)
{
return min(a, min(b, c));
}
@ -210,6 +210,7 @@ namespace nv
template <typename T>
void destroy_range(T * restrict ptr, uint new_size, uint old_size) {
for (uint i = new_size; i < old_size; i++) {
nvDebugCheck(ptr != NULL && isValidPtr(ptr));
(ptr+i)->~T(); // Explicit call to the destructor
}
}