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