etqw-sdk/source/idlib/text/WStr.cpp

1082 lines
20 KiB
C++
Raw Permalink Normal View History

2008-05-29 00:00:00 +00:00
// Copyright (C) 2007 Id Software, Inc.
//
#include "../precompiled.h"
#pragma hdrstop
#include <wctype.h>
#if defined( MACOS_X )
#pragma GCC visibility push(hidden)
#endif
wideStringDataAllocator_t* idWStr::stringDataAllocator;
bool idWStr::stringAllocatorIsShared;
struct ShutdownStringAllocator {
~ShutdownStringAllocator() {
idWStr::ShutdownMemory();
}
};
#if defined( MACOS_X )
#pragma GCC visibility pop
#endif
struct strColor_t;
#if defined( MACOS_X )
#pragma GCC visibility push(hidden)
#endif
static ShutdownStringAllocator shutdownStringAllocator;
#if defined( MACOS_X )
#pragma GCC visibility pop
#endif
idWStr::hmsFormat_t idWStr::defaultHMSFormat;
extern dword g_dword_color_table[COLOR_BITS+1];
/*
============
idWStr::ColorForIndex
============
*/
const idVec4& idWStr::ColorForIndex( int i ) {
return idStr::ColorForIndex( i );
}
/*
============
idWStr::ColorForChar
============
*/
const idVec4& idWStr::ColorForChar( wchar_t c ) {
return idStr::ColorForIndex( ColorIndex( c ) );
}
/*
============
idWStr::StrForColorIndex
============
*/
const char* idWStr::StrForColorIndex( int i ) {
return idStr::StrForColorIndex( i );
}
/*
============
idWStr::DColorForIndex
============
*/
dword& idWStr::DColorForIndex( int i ) {
return g_dword_color_table[ i & W_COLOR_BITS ];
}
/*
============
idWStr::ColorForChar
============
*/
dword& idWStr::DColorForChar( wchar_t c ) {
return g_dword_color_table[ ColorIndex( c ) ];
}
/*
============
idWStr::ReAllocate
============
*/
void idWStr::ReAllocate( int amount, bool keepold ) {
wchar_t *newbuffer;
int newsize;
int mod;
//assert( data );
assert( amount > 0 );
mod = amount % STR_ALLOC_GRAN;
if ( !mod ) {
newsize = amount;
} else {
newsize = amount + STR_ALLOC_GRAN - mod;
}
alloced = newsize;
newbuffer = stringDataAllocator->Alloc( alloced );
if ( keepold && data ) {
if ( len ) {
wcsncpy( newbuffer, data, len );
newbuffer[ len ] = L'\0';
} else {
newbuffer[0] = L'\0';
}
}
if ( data && data != baseBuffer ) {
stringDataAllocator->Free( data );
}
data = newbuffer;
}
/*
============
idWStr::FreeData
============
*/
void idWStr::FreeData( void ) {
if ( data && data != baseBuffer ) {
stringDataAllocator->Free( data );
data = baseBuffer;
}
}
/*
============
idWStr::operator=
============
*/
void idWStr::operator=( const wchar_t *text ) {
int l;
int diff;
int i;
if ( text == NULL ) {
// safe behaviour if NULL
EnsureAlloced( 1, false );
data[ 0 ] = L'\0';
len = 0;
return;
}
if ( text == data ) {
return; // copying same thing
}
// check if we're aliasing
if ( text >= data && text <= data + len ) {
diff = text - data;
assert( idWStr::Length( text ) < len );
for ( i = 0; text[ i ]; i++ ) {
data[ i ] = text[ i ];
}
data[ i ] = L'\0';
len -= diff;
return;
}
l = Length( text );
EnsureAlloced( l + 1, false );
wcscpy( data, text );
len = l;
}
/*
============
idWStr::FindChar
returns INVALID_POSITION if not found otherwise the index of the char
============
*/
int idWStr::FindChar( const wchar_t *str, const wchar_t c, int start, int end ) {
int i;
if ( end == INVALID_POSITION ) {
end = Length( str ) - 1;
}
for ( i = start; i <= end; i++ ) {
if ( str[i] == c ) {
return i;
}
}
return INVALID_POSITION;
}
/*
============
idWStr::FindText
returns INVALID_POSITION if not found otherwise the index of the text
============
*/
int idWStr::FindText( const wchar_t *str, const wchar_t *text, bool casesensitive, int start, int end ) {
int l, i, j;
if ( end == INVALID_POSITION ) {
end = Length( str );
}
l = end - Length( text );
for ( i = start; i <= l; i++ ) {
if ( casesensitive ) {
for ( j = 0; text[j]; j++ ) {
if ( str[i+j] != text[j] ) {
break;
}
}
} else {
for ( j = 0; text[j]; j++ ) {
if ( towupper( str[i+j] ) != towupper( text[j] ) ) {
break;
}
}
}
if ( !text[j] ) {
return i;
}
}
return INVALID_POSITION;
}
/*
============
idWStr::ReplaceChar
============
*/
void idWStr::ReplaceChar( wchar_t oldChar, wchar_t newChar ) {
int i;
for ( i = 0; i < len; i++ ) {
if ( data[ i ] != oldChar ) {
continue;
}
data[ i ] = newChar;
}
}
/*
============
idWStr::Replace
============
*/
void idWStr::Replace( const wchar_t *old, const wchar_t *nw ) {
if ( Length( old ) == 0 ) {
return;
}
int oldLen, newLen, i, count;
oldLen = Length( old );
newLen = Length( nw );
// Work out how big the new string will be
count = 0;
for ( i = 0; i < len; i++ ) {
if( !Cmpn( &data[i], old, oldLen ) ) {
count++;
i += oldLen - 1;
}
}
if ( count ) {
idWStr oldString( data );
int j;
EnsureAlloced( len + ( ( newLen - oldLen ) * count ) + 2, false );
// Replace the old data with the new data
for ( i = 0, j = 0; i < oldString.Length(); i++ ) {
if( !Cmpn( &oldString[i], old, oldLen ) ) {
wmemcpy( data + j, nw, newLen );
i += oldLen - 1;
j += newLen;
} else {
data[j] = oldString[i];
j++;
}
}
data[j] = L'\0';
len = Length( data );
}
}
/*
============
idWStr::ReplaceFirst
============
*/
void idWStr::ReplaceFirst( const wchar_t *old, const wchar_t *nw ) {
if( Length( old ) == 0 ) {
return;
}
int oldLen, newLen, i;
bool present;
oldLen = Length( old );
newLen = Length( nw );
// Work out how big the new string will be
present = false;
for ( i = 0; i < len; i++ ) {
if ( !Cmpn( &data[i], old, oldLen ) ) {
present = true;
i += oldLen - 1;
break;
}
}
if ( present ) {
idWStr oldString( data );
int j;
EnsureAlloced( len + ( newLen - oldLen ) + 2, false );
// Replace the old data with the new data
for ( i = 0, j = 0; i < oldString.Length(); i++ ) {
if ( !Cmpn( &oldString[i], old, oldLen ) ) {
wmemcpy( data + j, nw, newLen );
i += oldLen;
j += newLen;
break;
} else {
data[j] = oldString[i];
j++;
}
}
::wmemcpy( data + j, &oldString[i], oldString.Length() - i );
data[j + oldString.Length() - i] = L'\0';
len = Length( data );
}
}
/*
============
idWStr::Mid
============
*/
const wchar_t *idWStr::Mid( int start, int len, idWStr& result ) const {
int i;
assert( &result != this );
result.Empty();
i = Length();
if ( i == 0 || len <= 0 || start >= i ) {
return NULL;
}
if ( start + len >= i ) {
len = i - start;
}
result.Append( &data[ start ], len );
return result.c_str();
}
/*
============
idWStr::Mid
============
*/
idWStr idWStr::Mid( int start, int len ) const {
int i;
idWStr result;
i = Length();
if ( i == 0 || len <= 0 || start >= i ) {
return result;
}
if ( start + len >= i ) {
len = i - start;
}
result.Append( &data[ start ], len );
return result;
}
/*
=====================================================================
char * methods to replace library functions
=====================================================================
*/
/*
================
idWStr::StripFilename
================
*/
wchar_t* idWStr::StripFilename( wchar_t* string ) {
int pos;
pos = idWStr::Length( string ) - 1;
while( ( pos > 0 ) && ( string[ pos ] != L'/' ) && ( string[ pos ] != L'\\' ) ) {
pos--;
}
if ( pos < 0 ) {
pos = 0;
}
string[ pos ] = L'\0';
return string;
}
/*
================
idWStr::Cmp
================
*/
int idWStr::Cmp( const wchar_t *s1, const wchar_t *s2 ) {
int c1, c2, d;
do {
c1 = *s1++;
c2 = *s2++;
d = c1 - c2;
if ( d ) {
return ( INTSIGNBITNOTSET( d ) << 1 ) - 1;
}
} while( c1 );
return 0; // strings are equal
}
/*
================
idWStr::Cmpn
================
*/
int idWStr::Cmpn( const wchar_t *s1, const wchar_t *s2, int n ) {
int c1, c2, d;
assert( n >= 0 );
do {
c1 = *s1++;
c2 = *s2++;
if ( !n-- ) {
return 0; // strings are equal until end point
}
d = c1 - c2;
if ( d ) {
return ( INTSIGNBITNOTSET( d ) << 1 ) - 1;
}
} while( c1 );
return 0; // strings are equal
}
/*
================
idWStr::Icmp
================
*/
int idWStr::Icmp( const wchar_t *s1, const wchar_t *s2 ) {
int c1, c2, d;
do {
c1 = *s1++;
c2 = *s2++;
d = c1 - c2;
while( d ) {
if ( c1 <= 'Z' && c1 >= 'A' ) {
d += ('a' - 'A');
if ( !d ) {
break;
}
}
if ( c2 <= 'Z' && c2 >= 'A' ) {
d -= ('a' - 'A');
if ( !d ) {
break;
}
}
return ( INTSIGNBITNOTSET( d ) << 1 ) - 1;
}
} while( c1 );
return 0; // strings are equal
}
/*
================
idWStr::Icmpn
================
*/
int idWStr::Icmpn( const wchar_t *s1, const wchar_t *s2, int n ) {
int c1, c2, d;
assert( n >= 0 );
do {
c1 = *s1++;
c2 = *s2++;
if ( !n-- ) {
return 0; // strings are equal until end point
}
d = c1 - c2;
while( d ) {
if ( c1 <= 'Z' && c1 >= 'A' ) {
d += ('a' - 'A');
if ( !d ) {
break;
}
}
if ( c2 <= 'Z' && c2 >= 'A' ) {
d -= ('a' - 'A');
if ( !d ) {
break;
}
}
return ( INTSIGNBITNOTSET( d ) << 1 ) - 1;
}
} while( c1 );
return 0; // strings are equal
}
/*
================
idWStr::Icmp
================
*/
int idWStr::IcmpNoColor( const wchar_t *s1, const wchar_t *s2 ) {
int c1, c2, d;
do {
while ( IsColor( s1 ) ) {
s1 += 2;
}
while ( IsColor( s2 ) ) {
s2 += 2;
}
c1 = *s1++;
c2 = *s2++;
d = c1 - c2;
while( d ) {
if ( c1 <= 'Z' && c1 >= 'A' ) {
d += ('a' - 'A');
if ( !d ) {
break;
}
}
if ( c2 <= 'Z' && c2 >= 'A' ) {
d -= ('a' - 'A');
if ( !d ) {
break;
}
}
return ( INTSIGNBITNOTSET( d ) << 1 ) - 1;
}
} while( c1 );
return 0; // strings are equal
}
/*
=============
idWStr::Copynz
Safe strncpy that ensures a trailing zero
=============
*/
void idWStr::Copynz( wchar_t *dest, const wchar_t *src, int destsize ) {
if ( src == NULL ) {
idLib::common->Warning( "idWStr::Copynz: NULL src" );
return;
}
if ( destsize < 1 ) {
idLib::common->Warning( "idWStr::Copynz: destsize < 1" );
return;
}
::wcsncpy( dest, src, destsize-1 );
dest[destsize-1] = L'\0';
}
/*
================
idWStr::Append
never goes past bounds or leaves without a terminating 0
================
*/
void idWStr::Append( wchar_t *dest, int size, const wchar_t *src ) {
int l1;
l1 = Length( dest );
if ( l1 >= size ) {
idLib::common->Error( "idWStr::Append: already overflowed" );
}
Copynz( dest + l1, src, size - l1 );
}
/*
================
idWStr::LengthWithoutColors
================
*/
int idWStr::LengthWithoutColors( const wchar_t *s ) {
int len;
const wchar_t *p;
if ( !s ) {
return 0;
}
len = 0;
p = s;
while( *p != L'\0' ) {
if ( IsColor( p ) ) {
p += 2;
continue;
}
p++;
len++;
}
return len;
}
/*
================
idWStr::RemoveColors
================
*/
wchar_t *idWStr::RemoveColors( wchar_t *string ) {
wchar_t *d;
wchar_t *s;
int c;
s = string;
d = string;
while( (c = *s) != 0 ) {
if ( IsColor( s ) ) {
s++;
}
else {
*d++ = c;
}
s++;
}
*d = L'\0';
return string;
}
/*
================
idWStr::snPrintf
================
*/
int idWStr::snPrintf( wchar_t *dest, int size, const wchar_t *fmt, ...) {
int ret;
va_list argptr;
#ifdef _WIN32
#undef _vsnwprintf
va_start( argptr, fmt );
ret = _vsnwprintf( dest, size-1, fmt, argptr );
va_end( argptr );
#define _vsnprintf use_idStr_vsnPrintf
#else
// there is a vswprintf( idWStr ), so no #undef
va_start( argptr, fmt );
ret = vswprintf( dest, size, fmt, argptr );
va_end( argptr );
#endif
dest[size-1] = L'\0';
#ifdef _DEBUG
// never pass a %s formatting, always use %hs or %ls (%s works only on windows)
if ( FindText( fmt, L"%s" ) != INVALID_POSITION ) {
common->Error( "idWStr::snPrintf: attempted to pass a non-portable format string" );
}
#endif
if ( ret < 0 || ret >= size ) {
return -1;
}
return ret;
}
/*
============
idWStr::vsnPrintf
see idStr::vsnPrintf
same _WIN32 vs rest of the world for _vsnwprintf/vswprintf
you can call vsnPrintf( buffer, size ) with wchar_t buffer[size]
always has a terminating null character after call
will return -1 on error or overflow
============
*/
int idWStr::vsnPrintf( wchar_t *dest, int size, const wchar_t *fmt, va_list argptr ) {
int ret;
#ifdef _WIN32
#undef _vsnwprintf
ret = _vsnwprintf( dest, size-1, fmt, argptr );
#define _vsnwprintf use_idWStr_vsnPrintf
#else
// there is a vswprintf( idWStr ), so no #undef
ret = vswprintf( dest, size, fmt, argptr );
#endif
dest[size-1] = L'\0';
#ifdef _DEBUG
// never pass a %s formatting, always use %hs or %ls (%s works only on windows)
if ( FindText( fmt, L"%s" ) != INVALID_POSITION ) {
common->Error( "idWStr::vsnPrintf: attempted to pass a non-portable format string" );
}
#endif
if ( ret < 0 || ret >= size ) {
return -1;
}
return ret;
}
/*
===============
idWStr::Test
FIXME: not working correctly on !_WIN32. investigate
===============
*/
void idWStr::Test( void ) {
wchar_t buffer[10];
int ret;
idWStr test;
idLib::common->Printf( "idWStr::Test\n" );
idWStr::Copynz( buffer, L"012345678", 10 );
assert( buffer[9] == L'\0' );
ret = test.snPrintf( buffer, 10, L"%ls", L"876543210" );
assert( buffer[9] == L'\0' );
#ifdef _WIN32
OutputDebugStringW( va( L"%d %ls\n", ret, buffer ) );
#else
printf( "%d %ls\n", ret, buffer );
#endif
ret = test.snPrintf( buffer, 10, L"%ls", L"0123456789" );
assert( buffer[9] == '\0' );
#ifdef _WIN32
OutputDebugStringW( va( L"%d %ls\n", ret, buffer ) );
#else
printf( "%d %ls\n", ret, buffer );
#endif
ret = test.snPrintf( buffer, 10, L"%hs", "towide" );
#ifdef _WIN32
OutputDebugStringW( va( L"%d %ls\n", ret, buffer ) );
#else
printf( "%d %ls\n", ret, buffer );
#endif
}
/*
============
swprintf
Sets the value of the string using a printf interface.
============
*/
int swprintf( idWStr &string, const wchar_t *fmt, ... ) {
static const int BUFFER_SIZE = 32000;
int l;
va_list argptr;
wchar_t buffer[BUFFER_SIZE];
va_start( argptr, fmt );
l = idWStr::vsnPrintf( buffer, BUFFER_SIZE, fmt, argptr );
va_end( argptr );
string = buffer;
return l;
}
/*
============
vswprintf
Sets the value of the string using a vprintf interface.
============
*/
int vswprintf( idWStr &string, const wchar_t *fmt, va_list argptr ) {
static const int BUFFER_SIZE = 32000;
int l;
wchar_t buffer[BUFFER_SIZE];
l = idWStr::vsnPrintf( buffer, BUFFER_SIZE, fmt, argptr );
string = buffer;
return l;
}
/*
============
va
does a varargs printf into a temp buffer
NOTE: not thread safe
============
*/
#define VA_BUF_LEN 16384
wchar_t *va( const wchar_t *fmt, ... ) {
va_list argptr;
static int index = 0;
static wchar_t string[4][VA_BUF_LEN]; // in case called by nested functions
wchar_t *buf;
buf = string[index];
index = (index + 1) & 3;
va_start( argptr, fmt );
idWStr::vsnPrintf( buf, VA_BUF_LEN, fmt, argptr );
va_end( argptr );
return buf;
}
/*
================
idWStr::InitMemory
================
*/
void idWStr::InitMemory( void ) {
if( !stringDataAllocator ) {
stringDataAllocator = new wideStringDataAllocator_t;
stringDataAllocator->Init();
stringAllocatorIsShared = false;
}
}
/*
================
idWStr::ShutdownMemory
================
*/
void idWStr::ShutdownMemory( void ) {
if( stringDataAllocator && !stringAllocatorIsShared ) {
stringDataAllocator->Shutdown();
delete stringDataAllocator;
stringDataAllocator = NULL;
}
}
/*
================
idWStr::PurgeMemory
================
*/
void idWStr::PurgeMemory( void ) {
stringDataAllocator->FreeEmptyBaseBlocks();
}
/*
================
idWStr::ShowMemoryUsage_f
================
*/
void idWStr::ShowMemoryUsage_f( const idCmdArgs &args ) {
idLib::common->Printf( "%6d KB wide string memory (%d KB free in %d blocks, %d empty base blocks)\n",
stringDataAllocator->GetBaseBlockMemory() >> 10, stringDataAllocator->GetFreeBlockMemory() >> 10,
stringDataAllocator->GetNumFreeBlocks(), stringDataAllocator->GetNumEmptyBaseBlocks() );
}
/*
============
idWStr::SetStringAllocator
============
*/
void idWStr::SetStringAllocator( wideStringDataAllocator_t* allocator ) {
if( !stringAllocatorIsShared ) {
delete stringDataAllocator;
}
stringDataAllocator = allocator;
stringAllocatorIsShared = true;
}
/*
============
idWStr::GetStringAllocator
============
*/
wideStringDataAllocator_t* idWStr::GetStringAllocator( void ) {
return stringDataAllocator;
}
/*
============
idWStr::EraseRange
============
*/
void idWStr::EraseRange( int start, int len ) {
if( IsEmpty() || len == 0 ) {
return;
}
if( start < 0 ) {
start = 0;
}
if( start >= this->len ) {
return;
}
int totalLength = Length();
if( len == INVALID_POSITION ) {
len = totalLength - start;
}
if( len == totalLength ) {
// erase the whole thing
Empty();
return;
}
if ( totalLength - start - len ) {
::wmemmove( &data[ start ], &data[ start + len ], totalLength - start - len );
}
data[ totalLength - len ] = L'\0';
this->len -= len;
}
/*
============
idWStr::EraseChar
============
*/
void idWStr::EraseChar( const wchar_t c, int start ) {
if( start < 0 ) {
start = 0;
}
int totalLength = Length();
while( start < totalLength - 1 ) {
int offset = start + 1;
while( data[ start ] == c && offset < totalLength ) {
idSwap( data[ start ], data[ offset ] );
offset++;
}
start++;
}
start = totalLength - 1;
while( start > 0 && data[ start ] == c ) {
data[ start ] = L'\0';
start--;
}
len = start + 1;
}
/*
============
idWStr::Append
============
*/
void idWStr::Append( int count, const wchar_t c ) {
EnsureAlloced( len + count + 1 );
int start = len;
int end = len + count;
while( start < end ) {
data[ start ] = c;
start++;
}
data[ start ] = L'\0';
len += count;
}
/*
============
idWStr::MS2HMS
============
*/
const wchar_t* idWStr::MS2HMS( double ms, const hmsFormat_t& formatSpec ) {
if ( ms < 0.0 ) {
ms = 0.0;
}
int sec = idMath::Ftoi( MS2SEC( ms ) );
if( sec == 0 && formatSpec.showZeroSeconds == false ) {
return L"";
}
int min = sec / 60;
int hour = min / 60;
sec -= min * 60;
min -= hour * 60;
// don't show minutes if they're zeroed
if( min == 0 && hour == 0 && formatSpec.showZeroMinutes == false && formatSpec.showZeroHours == false ) {
return va( L"%02i", sec );
}
// don't show hours if they're zeroed
if( hour == 0 && formatSpec.showZeroHours == false ) {
return va( L"%02i:%02i", min, sec );
}
return va( L"%02i:%02i:%02i", hour, min, sec );
}
/*
============
idWStr::CollapseColors
============
*/
idWStr& idWStr::CollapseColors( void ) {
int colorBegin = -1;
int lastColor = -1;
for( int i = 0; i < len; i++ ) {
while( idWStr::IsColor( &data[ i ] ) && i < len ) {
if( colorBegin == -1 ) {
colorBegin = i;
}
lastColor = i;
i += 2;
}
if( colorBegin != -1 && lastColor != colorBegin ) {
EraseRange( colorBegin, lastColor - colorBegin );
i -= lastColor - colorBegin;
}
colorBegin = -1;
lastColor = -1;
}
return *this;
}
/*
============
idWStr::StripFileExtension
============
*/
idWStr &idWStr::StripFileExtension( void ) {
int i;
for ( i = len-1; i >= 0; i-- ) {
if ( data[i] == L'/' || data[i] == L'\\' ) {
break;
}
if ( data[i] == L'.' ) {
data[i] = L'\0';
len = i;
break;
}
}
return *this;
}