str.cpp

Engine/source/core/util/str.cpp

More...

Classes:

class

A delete policy for the AutoPtr class.

class

Struct with String::StringData's field so we can initialize this without a constructor.

class

Type for the intern string table.

Namespaces:

namespace
namespace

Public Functions

DefineConsoleFunction(dumpStringMemStats , void , () , "()" "@brief Dumps information about <a href="/coding/class/classstring/">String</a> memory <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">usage\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Debugging\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Strings\n</a>" )
const char *
StrFind(const char * hay, char needle, S32 pos, U32 mode)

Search for a character.

const char *
StrFind(const char * hay, const char * needle, S32 pos, U32 mode)

Search for a StringData.

Detailed Description

Public Variables

StringInternTable * sInternTable 
KillInternTable sKillInternTable 

Public Functions

DefineConsoleFunction(dumpStringMemStats , void , () , "()" "@brief Dumps information about <a href="/coding/class/classstring/">String</a> memory <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">usage\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Debugging\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Strings\n</a>" )

operator+(const String & a, const String & b)

operator+(const String & a, const StringChar * b)

operator+(const String & a, StringChar c)

operator+(const StringChar * a, const String & b)

operator+(StringChar c, const String & a)

StrFind(const char * hay, char needle, S32 pos, U32 mode)

Search for a character.

Search for the position of the needle in the haystack. Default mode is StrCase | StrLeft, mode also accepts StrNoCase and StrRight. If pos is non-zero, then in mode StrLeft the search starts at (hay + pos) and in mode StrRight the search starts at (hay + pos - 1)

return:

Returns a pointer to the location of the character in the haystack or 0

StrFind(const char * hay, const char * needle, S32 pos, U32 mode)

Search for a StringData.

Search for the position of the needle in the haystack. Default mode is StrCase | StrLeft, mode also accepts StrNoCase and StrRight. If pos is non-zero, then in mode StrLeft the search starts at (hay + pos) and in mode StrRight the search starts at (hay + pos - 1)

return:

Returns a pointer to the StringData in the haystack or 0

   1
   2//-----------------------------------------------------------------------------
   3// Copyright (c) 2012 GarageGames, LLC
   4//
   5// Permission is hereby granted, free of charge, to any person obtaining a copy
   6// of this software and associated documentation files (the "Software"), to
   7// deal in the Software without restriction, including without limitation the
   8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
   9// sell copies of the Software, and to permit persons to whom the Software is
  10// furnished to do so, subject to the following conditions:
  11//
  12// The above copyright notice and this permission notice shall be included in
  13// all copies or substantial portions of the Software.
  14//
  15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  21// IN THE SOFTWARE.
  22//-----------------------------------------------------------------------------
  23
  24#include <stdarg.h>
  25#include <stdio.h>
  26
  27#include "platform/platform.h"
  28
  29// Sigh... guess what compiler needs this...
  30namespace DictHash { U32 hash( String::StringData* ); }
  31namespace KeyCmp
  32{
  33   template< typename Key > bool equals( const Key&, const Key& );
  34   template<> bool equals<>( String::StringData* const&, String::StringData* const& );
  35}
  36
  37#include "core/util/str.h"
  38#include "core/util/tDictionary.h"
  39#include "core/strings/stringFunctions.h"
  40#include "core/strings/unicode.h"
  41#include "core/util/hashFunction.h"
  42#include "core/util/autoPtr.h"
  43#include "core/util/tVector.h"
  44#include "core/dataChunker.h"
  45#include "console/console.h"
  46#include "console/engineAPI.h"
  47
  48#include "math/mMathFn.h"
  49
  50#include "platform/platform.h"
  51#include "platform/profiler.h"
  52#include "platform/platformIntrinsics.h"
  53#include "platform/threads/mutex.h"
  54
  55#ifndef TORQUE_DISABLE_MEMORY_MANAGER
  56#  undef new
  57#else
  58#  define _new new
  59#endif
  60
  61const String::SizeType String::NPos = U32(~0);
  62const String String::EmptyString;
  63
  64/// A delete policy for the AutoPtr class
  65struct DeleteString
  66{
  67   template<class T>
  68   static void destroy(T *ptr) { dFree(ptr); }
  69};
  70
  71
  72//-----------------------------------------------------------------------------
  73
  74/// Search for a character.
  75/// Search for the position of the needle in the haystack.
  76/// Default mode is StrCase | StrLeft, mode also accepts StrNoCase and StrRight.
  77/// If pos is non-zero, then in mode StrLeft the search starts at (hay + pos) and
  78/// in mode StrRight the search starts at (hay + pos - 1)
  79/// @return Returns a pointer to the location of the character in the haystack or 0
  80static const char* StrFind(const char* hay, char needle, S32 pos, U32 mode)
  81{
  82   if (mode & String::Right)
  83   {
  84      // Go to the end first, then search backwards
  85      const char  *he = hay;
  86
  87      if (pos)
  88      {
  89         he += pos - 1;
  90      }
  91      else
  92      {
  93         while (*he)
  94            he++;
  95      }
  96
  97      if (mode & String::NoCase)
  98      {
  99         needle = dTolower(needle);
 100
 101         for (; he >= hay; he--)
 102         {
 103            if (dTolower(*he) == needle)
 104               return he;
 105         }
 106      }
 107      else
 108      {
 109         for (; he >= hay; he--)
 110         {
 111            if (*he == needle)
 112               return he;
 113         }
 114      }
 115      return 0;
 116   }
 117   else
 118   {
 119      if (mode & String::NoCase)
 120      {
 121         needle = dTolower(needle);
 122         for (hay += pos; *hay && dTolower(*hay) != needle;)
 123            hay++;
 124      }
 125      else
 126      {
 127         for (hay += pos; *hay && *hay != needle;)
 128            hay++;
 129      }
 130
 131      return *hay ? hay : 0;
 132   }
 133}
 134
 135/// Search for a StringData.
 136/// Search for the position of the needle in the haystack.
 137/// Default mode is StrCase | StrLeft, mode also accepts StrNoCase and StrRight.
 138/// If pos is non-zero, then in mode StrLeft the search starts at (hay + pos) and
 139/// in mode StrRight the search starts at (hay + pos - 1)
 140/// @return Returns a pointer to the StringData in the haystack or 0
 141static const char* StrFind(const char* hay, const char* needle, S32 pos, U32 mode)
 142{
 143   if (mode & String::Right)
 144   {
 145      const char  *he = hay;
 146
 147      if (pos)
 148      {
 149         he += pos - 1;
 150      }
 151      else
 152      {
 153         while (*he)
 154            he++;
 155      }
 156
 157      if (mode & String::NoCase)
 158      {
 159         AutoPtr<char,DeleteString> ln(dStrlwr(dStrdup(needle)));
 160         for (; he >= hay; he--)
 161         {
 162            if (dTolower(*he) == *ln)
 163            {
 164               U32 i = 0;
 165               while (ln[i] && ln[i] == dTolower(he[i]))
 166                  i++;
 167               if (!ln[i])
 168                  return he;
 169               if (!hay[i])
 170                  return 0;
 171            }
 172         }
 173      }
 174      else
 175      {
 176         for (; he >= hay; he--)
 177         {
 178            if (*he == *needle)
 179            {
 180               U32 i = 0;
 181               while (needle[i] && needle[i] == he[i])
 182                  i++;
 183               if (!needle[i])
 184                  return he;
 185               if (!hay[i])
 186                  return 0;
 187            }
 188         }
 189      }
 190      return 0;
 191   }
 192   else
 193   {
 194      if (mode & String::NoCase)
 195      {
 196         AutoPtr<char,DeleteString> ln(dStrlwr(dStrdup(needle)));
 197         for (hay += pos; *hay; hay++)
 198         {
 199            if (dTolower(*hay) == *ln)
 200            {
 201               U32 i = 0;
 202               while (ln[i] && ln[i] == dTolower(hay[i]))
 203                  i++;
 204               if (!ln[i])
 205                  return hay;
 206               if (!hay[i])
 207                  return 0;
 208            }
 209         }
 210      }
 211      else
 212      {
 213         for (hay += pos; *hay; hay++)
 214         {
 215            if (*hay == *needle)
 216            {
 217               U32 i = 0;
 218               while (needle[i] && needle[i] == hay[i])
 219                  i++;
 220               if (!needle[i])
 221                  return hay;
 222               if (!hay[i])
 223                  return 0;
 224            }
 225         }
 226      }
 227   }
 228
 229   return 0;
 230}
 231
 232//-----------------------------------------------------------------------------
 233
 234/// Struct with String::StringData's field so we can initialize
 235/// this without a constructor.
 236struct StringDataImpl
 237{
 238#ifdef TORQUE_DEBUG
 239      StringChar*       mString;       ///< so we can inspect data in a debugger
 240#endif
 241
 242      U32               mRefCount;     ///< String reference count; string is not refcounted if this is U32_MAX (necessary for thread-safety of interned strings and the empty string).
 243      U32               mLength;       ///< String length in bytes excluding null.
 244      mutable U32       mNumChars;     ///< Character count; varies from byte count for strings with multi-bytes characters.
 245      mutable U32       mHashCase;     ///< case-sensitive hash
 246      mutable U32       mHashNoCase;   ///< case-insensitive hash
 247      mutable UTF16*    mUTF16;
 248      bool              mIsInterned;   ///< If true, this string is interned in the string table.
 249      StringChar        mData[1];      ///< Start of string data
 250};
 251
 252///
 253class String::StringData : protected StringDataImpl
 254{
 255   public:
 256
 257      ///
 258      StringData( const StringChar* data, bool interned = false )
 259      {
 260         mRefCount = 1;
 261         mNumChars = U32_MAX;
 262         mHashCase = U32_MAX;
 263         mHashNoCase = U32_MAX;
 264         mUTF16 = NULL;
 265         mIsInterned = interned;
 266         
 267         // mLength is initialized by operator new()
 268
 269         if( data )
 270         {
 271            dMemcpy( mData, data, sizeof( StringChar ) * mLength );
 272            mData[ mLength ] = '\0';
 273         }
 274         
 275#ifdef TORQUE_DEBUG
 276         mString = &mData[0];
 277#endif
 278         if( mIsInterned )
 279            mRefCount = U32_MAX;
 280      }
 281
 282      ~StringData()
 283      {
 284         if( mUTF16 )
 285            delete [] mUTF16;
 286      }
 287
 288      void* operator new(size_t size, U32 len);
 289      void* operator new( size_t size, U32 len, DataChunker& chunker );
 290      void operator delete(void *);
 291
 292      bool isShared() const
 293      {
 294         return ( mRefCount > 1 );
 295      }
 296
 297      void addRef()
 298      {
 299         if( mRefCount != U32_MAX )
 300            mRefCount ++;
 301      }
 302
 303      void release()
 304      {
 305         if( mRefCount != U32_MAX )
 306         {
 307            -- mRefCount;
 308            if( !mRefCount )
 309               delete this;
 310         }
 311      }
 312
 313      U32 getLength() const
 314      {
 315         return mLength;
 316      }
 317
 318      U32 getDataSize() const
 319      {
 320         return ( mLength + 1 );
 321      }
 322
 323      U32 getDataSizeUTF16() const
 324      {
 325         return ( mLength * sizeof( UTF16 ) );
 326      }
 327
 328      UTF8 operator []( U32 index ) const
 329      {
 330         AssertFatal( index < mLength, "String::StringData::operator []() - index out of range" );
 331         return mData[ index ];
 332      }
 333
 334      UTF8* utf8()
 335      {
 336         return mData;
 337      }
 338
 339      const UTF8* utf8() const
 340      {
 341         return mData;
 342      }
 343
 344      UTF16* utf16() const
 345      {
 346         if( !mUTF16 )
 347         {
 348            // Do this atomically to protect interned strings.
 349            
 350            UTF16* utf16 = createUTF16string( mData );
 351            if( !dCompareAndSwap( mUTF16,( UTF16* ) NULL, utf16 ) )
 352               delete [] utf16;
 353         }
 354         return mUTF16;
 355      }
 356
 357      U32 getHashCase() const
 358      {
 359         return mHashCase;
 360      }
 361
 362      U32 getOrCreateHashCase() const
 363      {
 364         if( mHashCase == U32_MAX )
 365         {
 366            PROFILE_SCOPE(StringData_getOrCreateHashCase);
 367            mHashCase = Torque::hash((const U8 *)(mData), mLength, 0);
 368         }
 369         return mHashCase;
 370      }
 371
 372      U32 getHashNoCase() const
 373      {
 374         return mHashNoCase;
 375      }
 376
 377      U32 getOrCreateHashNoCase() const
 378      {
 379         if( mHashNoCase == U32_MAX)
 380         {
 381            PROFILE_SCOPE(StringData_getOrCreateHashNoCase);
 382            UTF8 *lower = new UTF8[ mLength + 1 ];
 383            dStrncpy( lower, utf8(), mLength );
 384            lower[ mLength ] = 0;
 385            dStrlwr( lower );
 386            mHashNoCase = Torque::hash( (const U8*)lower, mLength, 0 );
 387            delete [] lower;
 388         }
 389
 390         return mHashNoCase;
 391      }
 392
 393      U32 getNumChars() const
 394      {
 395         if( mNumChars == U32_MAX )
 396            mNumChars = dStrlen( utf16() );
 397         
 398         return mNumChars;
 399      }
 400
 401      bool isInterned() const
 402      {
 403         return mIsInterned;
 404      }
 405
 406      static StringData* Empty()
 407      {
 408         static UTF16 emptyUTF16[ 1 ] = { 0 };
 409         static StringDataImpl empty =
 410         {
 411            #ifdef TORQUE_DEBUG
 412            "",            // mString
 413            #endif
 414            
 415            U32_MAX,       // mRefCount
 416            0,             // mLength
 417            0,             // mNumChars
 418            0,             // mHashCase
 419            0,             // mHashNoCase
 420            emptyUTF16,    // mUTF16
 421            true,          // mIsInterned
 422            { 0 }          // mData
 423         };
 424                        
 425         return ( StringData* ) &empty;
 426      }
 427};
 428
 429//-----------------------------------------------------------------------------
 430
 431namespace DictHash
 432{
 433   inline U32 hash( String::StringData* data )
 434   {
 435      return data->getOrCreateHashCase();
 436   }
 437}
 438namespace KeyCmp
 439{
 440   template<>
 441   inline bool equals<>( String::StringData* const& d1, String::StringData* const& d2 )
 442   {
 443      return ( dStrcmp( d1->utf8(), d2->utf8() ) == 0 );
 444   }
 445}
 446
 447/// Type for the intern string table.  We don't want String instances directly
 448/// on the table so that destructors don't run when the table is destroyed.  This
 449/// is because we really shouldn't depend on dtor ordering within this file and thus
 450/// we can't tell whether the intern string memory is freed before or after the
 451/// table is destroyed.
 452struct StringInternTable : public HashTable< String::StringData*, String::StringData* >
 453{
 454   Mutex mMutex;
 455   DataChunker mChunker;
 456};
 457
 458static StringInternTable* sInternTable;
 459
 460struct KillInternTable
 461{
 462   ~KillInternTable()
 463   {
 464      if( sInternTable )
 465         delete sInternTable;
 466   }
 467};
 468static KillInternTable sKillInternTable;
 469
 470//-----------------------------------------------------------------------------
 471
 472#ifdef TORQUE_DEBUG
 473
 474/// Tracks the number of bytes allocated for strings.
 475/// @bug This currently does not include UTF16 allocations.
 476static U32 sgStringMemBytes;
 477
 478/// Tracks the number of Strings which are currently instantiated.
 479static U32 sgStringInstances;
 480
 481
 482
 483#endif
 484DefineConsoleFunction( dumpStringMemStats, void, (), , "()"
 485            "@brief Dumps information about String memory usage\n\n"
 486            "@ingroup Debugging\n"
 487            "@ingroup Strings\n")
 488{
 489#ifdef TORQUE_DEBUG
 490   Con::printf( "String Data: %i instances, %i bytes", sgStringInstances, sgStringMemBytes );
 491#endif
 492}
 493
 494//-----------------------------------------------------------------------------
 495
 496void* String::StringData::operator new( size_t size, U32 len )
 497{
 498   AssertFatal( len != 0, "String::StringData::operator new() - string must not be empty" );
 499   StringData *str = static_cast<StringData*>( dMalloc( size + len * sizeof(StringChar) ) );
 500
 501   str->mLength      = len;
 502
 503#ifdef TORQUE_DEBUG
 504   dFetchAndAdd( sgStringMemBytes, size + len * sizeof(StringChar) );
 505   dFetchAndAdd( sgStringInstances, 1 );
 506#endif
 507
 508   return str;
 509}
 510
 511void String::StringData::operator delete(void *ptr)
 512{
 513   StringData* sub = static_cast<StringData *>(ptr);
 514   AssertFatal( sub->mRefCount == 0, "StringData::delete() - invalid refcount" );
 515
 516#ifdef TORQUE_DEBUG
 517   dFetchAndAdd( sgStringMemBytes, U32( -( S32( sizeof( StringData ) + sub->mLength * sizeof(StringChar) ) ) ) );
 518   dFetchAndAdd( sgStringInstances, U32( -1 ) );
 519#endif
 520
 521   dFree( ptr );
 522}
 523
 524void* String::StringData::operator new( size_t size, U32 len, DataChunker& chunker )
 525{
 526   AssertFatal( len != 0, "String::StringData::operator new() - string must not be empty" );
 527   StringData *str = static_cast<StringData*>( chunker.alloc( size + len * sizeof(StringChar) ) );
 528
 529   str->mLength      = len;
 530
 531#ifdef TORQUE_DEBUG
 532   dFetchAndAdd( sgStringMemBytes, size + len * sizeof(StringChar) );
 533   dFetchAndAdd( sgStringInstances, 1 );
 534#endif
 535
 536   return str;
 537}
 538
 539//-----------------------------------------------------------------------------
 540
 541String::String()
 542{
 543   PROFILE_SCOPE(String_default_constructor);
 544   _string = StringData::Empty();
 545}
 546
 547String::String(const String &str)
 548{
 549   PROFILE_SCOPE(String_String_constructor);
 550   _string = str._string;
 551   _string->addRef();
 552}
 553
 554String::String(const StringChar *str)
 555{
 556   PROFILE_SCOPE(String_char_constructor);
 557   if( str && *str )
 558   {
 559      U32 len = dStrlen(str);
 560      _string = new ( len ) StringData( str );
 561   }
 562   else
 563      _string = StringData::Empty();
 564}
 565
 566String::String(const StringChar *str, SizeType len)
 567{
 568   PROFILE_SCOPE(String_char_len_constructor);
 569   if (str && *str && len!=0)
 570   {
 571      _string = new ( len ) StringData( str );
 572   }
 573   else
 574      _string = StringData::Empty();
 575}
 576
 577String::String(const UTF16 *str)
 578{
 579   PROFILE_SCOPE(String_UTF16_constructor);
 580
 581   if( str && str[ 0 ] )
 582   {
 583      UTF8* utf8 = createUTF8string( str );
 584      U32 len = dStrlen( utf8 );
 585      _string = new ( len ) StringData( utf8 );
 586      delete [] utf8;
 587   }
 588   else
 589      _string = StringData::Empty();
 590}
 591
 592String::~String()
 593{
 594   _string->release();
 595}
 596
 597//-----------------------------------------------------------------------------
 598
 599String String::intern() const
 600{
 601   if( isInterned() )
 602      return *this;
 603      
 604   // Create the intern table, if we haven't already.
 605   
 606   if( !sInternTable )
 607      sInternTable = new StringInternTable;
 608      
 609   // Lock the string table.
 610         
 611   MutexHandle mutex;
 612   mutex.lock( &sInternTable->mMutex );
 613   
 614   // Lookup.
 615   
 616   StringInternTable::Iterator iter = sInternTable->find( _string );
 617   if( iter != sInternTable->end() )
 618      return ( *iter ).value;
 619      
 620   // Create new.
 621   
 622   StringData* data = new ( length(), sInternTable->mChunker ) StringData( c_str(), true );
 623   iter = sInternTable->insertUnique( data, data );
 624   
 625   return ( *iter ).value;
 626}
 627
 628//-----------------------------------------------------------------------------
 629
 630const StringChar* String::c_str() const
 631{
 632   return _string->utf8();
 633}
 634
 635const UTF16 *String::utf16() const
 636{
 637   return _string->utf16();
 638}
 639
 640String::SizeType String::length() const
 641{
 642   return _string->getLength();
 643}
 644
 645String::SizeType String::size() const
 646{
 647   return _string->getDataSize();
 648}
 649
 650String::SizeType String::numChars() const
 651{
 652   return _string->getNumChars();
 653}
 654
 655bool String::isEmpty() const
 656{
 657   return ( _string == StringData::Empty() );
 658}
 659
 660bool String::isEmpty(const char* str)
 661{
 662   return str == 0 || str[0] == '\0';
 663}
 664
 665bool String::isShared() const
 666{
 667   return _string->isShared();
 668}
 669
 670bool String::isSame( const String& str ) const
 671{
 672   return ( _string == str._string );
 673}
 674
 675bool String::isInterned() const
 676{
 677   return ( _string->isInterned() );
 678}
 679
 680U32 String::getHashCaseSensitive() const
 681{
 682   return _string->getOrCreateHashCase();
 683}
 684
 685U32 String::getHashCaseInsensitive() const
 686{
 687   return _string->getOrCreateHashNoCase();
 688}
 689
 690//-----------------------------------------------------------------------------
 691
 692String::SizeType String::find(const String &str, SizeType pos, U32 mode) const
 693{
 694   return find(str._string->utf8(), pos, mode);
 695}
 696
 697String& String::insert(SizeType pos, const String &str)
 698{
 699   return insert(pos, str._string->utf8());
 700}
 701
 702String& String::replace(SizeType pos, SizeType len, const String &str)
 703{
 704   return replace(pos, len, str._string->utf8());
 705}
 706
 707//-----------------------------------------------------------------------------
 708
 709String& String::operator=(StringChar c)
 710{
 711   _string->release();
 712
 713   _string = new ( 2 ) StringData( 0 );
 714   _string->utf8()[ 0 ] = c;
 715   _string->utf8()[ 1 ] = '\0';
 716
 717   return *this;
 718}
 719
 720String& String::operator+=(StringChar c)
 721{
 722   // Append the given string into a new string
 723   U32 len = _string->getLength();
 724   StringData* sub = new ( len + 1 ) StringData( NULL );
 725
 726   copy( sub->utf8(), _string->utf8(), len );
 727   sub->utf8()[len] = c;
 728   sub->utf8()[len+1] = 0;
 729
 730   _string->release();
 731   _string = sub;
 732
 733   return *this;
 734}
 735
 736//-----------------------------------------------------------------------------
 737
 738String& String::operator=(const StringChar *str)
 739{
 740   // Protect against self assignment which is not only a
 741   // waste of time, but can also lead to the string being
 742   // freed before it can be reassigned.
 743   if ( _string->utf8() == str )
 744      return *this;
 745
 746   _string->release();
 747
 748   if (str && *str)
 749   {
 750      U32 len = dStrlen(str);
 751      _string = new ( len ) StringData( str );
 752   }
 753   else
 754      _string = StringData::Empty();
 755
 756   return *this;
 757}
 758
 759String& String::operator=(const String &src)
 760{
 761   // Inc src first to avoid assignment to self problems.
 762   src._string->addRef();
 763
 764   _string->release();
 765   _string = src._string;
 766
 767   return *this;
 768}
 769
 770String& String::operator+=(const StringChar *src)
 771{
 772   if( src == NULL || !*src )
 773      return *this;
 774
 775   // Append the given string into a new string
 776   U32 lena = _string->getLength();
 777   U32 lenb = dStrlen(src);
 778   U32 newlen = lena + lenb;
 779
 780   StringData* sub;
 781   if( !newlen )
 782      sub = StringData::Empty();
 783   else
 784   {
 785      sub = new ( newlen ) StringData( NULL );
 786
 787      copy(sub->utf8(),_string->utf8(),lena);
 788      copy(sub->utf8() + lena,src,lenb + 1);
 789   }
 790
 791   _string->release();
 792   _string = sub;
 793
 794   return *this;
 795}
 796
 797String& String::operator+=(const String &src)
 798{
 799   if( src.isEmpty() )
 800      return *this;
 801
 802   // Append the given string into a new string
 803   U32 lena = _string->getLength();
 804   U32 lenb = src._string->getLength();
 805   U32 newlen = lena + lenb;
 806
 807   StringData* sub;
 808   if( !newlen )
 809      sub = StringData::Empty();
 810   else
 811   {
 812      sub = new ( newlen ) StringData( NULL );
 813
 814      copy(sub->utf8(),_string->utf8(),lena);
 815      copy(sub->utf8() + lena,src._string->utf8(),lenb + 1);
 816   }
 817
 818   _string->release();
 819   _string = sub;
 820
 821   return *this;
 822}
 823
 824//-----------------------------------------------------------------------------
 825
 826String operator+(const String &a, const String &b)
 827{
 828   PROFILE_SCOPE( String_String_plus_String );
 829   
 830   if( a.isEmpty() )
 831      return b;
 832   else if( b.isEmpty() )
 833      return a;
 834
 835   U32 lena = a.length();
 836   U32 lenb = b.length();
 837
 838   String::StringData *sub = new ( lena + lenb ) String::StringData( NULL );
 839
 840   String::copy(sub->utf8(),a._string->utf8(),lena);
 841   String::copy(sub->utf8() + lena,b._string->utf8(),lenb + 1);
 842
 843   return String(sub);
 844}
 845
 846String operator+(const String &a, StringChar c)
 847{
 848   //PROFILE_SCOPE( String_String_plus_Char );
 849
 850   U32 lena = a.length();
 851   String::StringData *sub = new ( lena + 1 ) String::StringData( NULL );
 852
 853   String::copy(sub->utf8(),a._string->utf8(),lena);
 854
 855   sub->utf8()[lena] = c;
 856   sub->utf8()[lena+1] = 0;
 857
 858   return String(sub);
 859}
 860
 861String operator+(StringChar c, const String &a)
 862{
 863   //PROFILE_SCOPE( String_Char_plus_String );
 864
 865   U32 lena = a.length();
 866   String::StringData *sub = new ( lena + 1 ) String::StringData( NULL );
 867
 868   String::copy(sub->utf8() + 1,a._string->utf8(),lena + 1);
 869   sub->utf8()[0] = c;
 870
 871   return String(sub);
 872}
 873
 874String operator+(const String &a, const StringChar *b)
 875{
 876   //PROFILE_SCOPE( String_String_plus_CString );
 877
 878   AssertFatal(b,"String:: Invalid null ptr argument");
 879
 880   if( a.isEmpty() )
 881      return String( b );
 882
 883   U32 lena = a.length();
 884   U32 lenb = dStrlen(b);
 885
 886   if( !lenb )
 887      return a;
 888
 889   String::StringData *sub = new ( lena + lenb ) String::StringData( NULL );
 890
 891   String::copy(sub->utf8(),a._string->utf8(),lena);
 892   String::copy(sub->utf8() + lena,b,lenb + 1);
 893
 894   return String(sub);
 895}
 896
 897String operator+(const StringChar *a, const String &b)
 898{
 899   //PROFILE_SCOPE( String_CString_plus_String );
 900   AssertFatal(a,"String:: Invalid null ptr argument");
 901
 902   if( b.isEmpty() )
 903      return String( a );
 904
 905   U32 lena = dStrlen(a);
 906   if( !lena )
 907      return b;
 908
 909   U32 lenb = b.length();
 910
 911   String::StringData* sub = new ( lena + lenb ) String::StringData( NULL );
 912
 913   String::copy(sub->utf8(),a,lena);
 914   String::copy(sub->utf8() + lena,b._string->utf8(),lenb + 1);
 915
 916   return String(sub);
 917}
 918
 919bool String::operator==(const String &str) const
 920{
 921   //PROFILE_SCOPE( String_op_equal );
 922
 923   if( str._string == _string )
 924      return true;
 925   else if( str._string->isInterned() && _string->isInterned() )
 926      return false;
 927   else if( str.length() != length() )
 928      return false;
 929   else if( str._string->getHashCase() != U32_MAX
 930            && _string->getHashCase() != U32_MAX
 931            && str._string->getHashCase() != _string->getHashCase() )
 932      return false;
 933   else
 934      return ( dMemcmp( str._string->utf8(), _string->utf8(), _string->getLength() ) == 0 );
 935}
 936
 937bool String::operator==( StringChar c ) const
 938{
 939   if( !_string || _string->getLength() != 1 )
 940      return false;
 941   else
 942      return ( _string->utf8()[ 0 ] == c );
 943}
 944
 945bool String::operator<(const String &str) const
 946{
 947   return ( dStrnatcmp( _string->utf8(), str._string->utf8() ) < 0 );
 948}
 949
 950bool String::operator>(const String &str) const
 951{
 952   return ( dStrnatcmp( _string->utf8(), str._string->utf8() ) > 0 );
 953}
 954
 955bool String::operator<=(const String &str) const
 956{
 957   return ( dStrnatcmp( _string->utf8(), str._string->utf8() ) <= 0 );
 958}
 959
 960bool String::operator>=(const String &str) const
 961{
 962   return ( dStrnatcmp( _string->utf8(), str._string->utf8() ) >= 0 );
 963}
 964
 965//-----------------------------------------------------------------------------
 966// Base functions for string comparison
 967
 968S32 String::compare(const StringChar *str, SizeType len, U32 mode) const
 969{
 970   PROFILE_SCOPE( String_compare );
 971   
 972   AssertFatal(str,"String:: Invalid null ptr argument");
 973
 974   const StringChar  *p1 = _string->utf8();
 975   const StringChar  *p2 = str;
 976
 977   if (p1 == p2)
 978      return 0;
 979
 980   if( mode & String::Right )
 981   {
 982      U32 n = len;
 983      if( n > length() )
 984         n = length();
 985
 986      p1 += length() - n;
 987      p2 += dStrlen( str ) - n;
 988   }
 989
 990   if (mode & String::NoCase)
 991   {
 992      if (len)
 993      {
 994         for (;--len; p1++,p2++)
 995         {
 996            if (dTolower(*p1) != dTolower(*p2) || !*p1)
 997               break;
 998         }
 999      }
1000      else
1001      {
1002         while (dTolower(*p1) == dTolower(*p2) && *p1)
1003         {
1004            p1++;
1005            p2++;
1006         }
1007      }
1008
1009      return dTolower(*p1) - dTolower(*p2);
1010   }
1011
1012   if (len)
1013      return dMemcmp(p1,p2,len);
1014
1015   while (*p1 == *p2 && *p1)
1016   {
1017      p1++;
1018      p2++;
1019   }
1020
1021   return *p1 - *p2;
1022}
1023
1024S32 String::compare(const String &str, SizeType len, U32 mode) const
1025{
1026   if ( str._string == _string )
1027      return 0;
1028
1029   return compare( str.c_str(), len, mode );
1030}
1031
1032bool String::equal(const String &str, U32 mode) const
1033{
1034   if( !mode )
1035      return ( *this == str );
1036   else
1037   {
1038      if( _string == str._string )
1039         return true;
1040      else if( _string->isInterned() && str._string->isInterned() )
1041         return false;
1042      else if( length() != str.length() )
1043         return false;
1044      else if( _string->getHashNoCase() != U32_MAX
1045               && str._string->getHashNoCase() != U32_MAX
1046               && _string->getHashNoCase() != str._string->getHashNoCase() )
1047         return false;
1048      else
1049         return ( compare( str.c_str(), length(), mode ) == 0 );
1050   }
1051}
1052
1053//-----------------------------------------------------------------------------
1054
1055String::SizeType String::find(StringChar c, SizeType pos, U32 mode) const
1056{
1057   const StringChar* ptr = StrFind(_string->utf8(),c,pos,mode);
1058
1059   return ptr? SizeType(ptr - _string->utf8()): NPos;
1060}
1061
1062String::SizeType String::find(const StringChar *str, SizeType pos, U32 mode)  const
1063{
1064   AssertFatal(str,"String:: Invalid null ptr argument");
1065
1066   const StringChar* ptr = StrFind(_string->utf8(),str,pos,mode);
1067
1068   return ptr? SizeType(ptr - _string->utf8()): NPos;
1069}
1070
1071
1072//-----------------------------------------------------------------------------
1073
1074String& String::insert(SizeType pos, const StringChar *str)
1075{
1076   AssertFatal(str,"String:: Invalid null ptr argument");
1077
1078   return insert(pos,str,dStrlen(str));
1079}
1080
1081///@todo review for error checking
1082String& String::insert(SizeType pos, const StringChar *str, SizeType len)
1083{
1084   if( !len )
1085      return *this;
1086
1087   AssertFatal( str, "String:: Invalid null ptr argument" );
1088         
1089   SizeType lena = length();
1090   AssertFatal((pos <= lena),"Calling String::insert with position greater than length");
1091   U32 newlen = lena + len;
1092
1093   StringData *sub;
1094   if( !newlen )
1095      sub = StringData::Empty();
1096   else
1097   {
1098      sub = new ( newlen ) StringData( NULL );
1099
1100      String::copy(sub->utf8(),_string->utf8(),pos);
1101      String::copy(sub->utf8() + pos,str,len);
1102      String::copy(sub->utf8() + pos + len,_string->utf8() + pos,lena - pos + 1);
1103   }
1104
1105   _string->release();
1106   _string = sub;
1107
1108   return *this;
1109}
1110
1111String& String::erase(SizeType pos, SizeType len)
1112{
1113   AssertFatal( len != 0, "String::erase() - Calling String::erase with 0 length" );
1114   AssertFatal( ( pos + len ) <= length(), "String::erase() - Invalid string region" );
1115
1116   if( !len )
1117      return *this;
1118
1119   SizeType slen = length();
1120   U32 newlen = slen - len;
1121
1122   StringData *sub;
1123   if( !newlen )
1124      sub = StringData::Empty();
1125   else
1126   {
1127      sub = new ( newlen ) StringData( NULL );
1128
1129      if (pos > 0)
1130         String::copy(sub->utf8(),_string->utf8(),pos);
1131
1132      String::copy(sub->utf8() + pos, _string->utf8() + pos + len, slen - (pos + len) + 1);
1133   }
1134
1135   _string->release();
1136   _string = sub;
1137
1138   return *this;
1139}
1140
1141///@todo review for error checking
1142String& String::replace(SizeType pos, SizeType len, const StringChar *str)
1143{
1144   AssertFatal( str, "String::replace() - Invalid null ptr argument" );
1145   AssertFatal( len != 0, "String::replace() - Zero length" );
1146   AssertFatal( ( pos + len ) <= length(), "String::replace() - Invalid string region" );
1147
1148   SizeType slen = length();
1149   SizeType rlen = dStrlen(str);
1150
1151   U32 newlen = slen - len + rlen;
1152   StringData *sub;
1153   if( !newlen )
1154      sub = StringData::Empty();
1155   else
1156   {
1157      sub = new ( newlen ) StringData( NULL );
1158
1159      String::copy(sub->utf8(),_string->utf8(), pos);
1160      String::copy(sub->utf8() + pos,str,rlen);
1161      String::copy(sub->utf8() + pos + rlen,_string->utf8() + pos + len,slen - pos - len + 1);
1162   }
1163
1164   _string->release();
1165   _string = sub;
1166
1167   return *this;
1168}
1169
1170String& String::replace( StringChar c1, StringChar c2 )
1171{
1172   if( isEmpty() )
1173      return *this;
1174      
1175   // Create the new string lazily so that we don't needlessly
1176   // dup strings when there is nothing to replace.
1177
1178   StringData* sub = NULL;   
1179   bool foundReplacement = false;
1180
1181   StringChar* c = _string->utf8();
1182   while( *c )
1183   {
1184      if( *c == c1 )
1185      {
1186         if( !foundReplacement )
1187         {
1188            sub = new ( length() ) StringData( _string->utf8() );
1189            c = &sub->utf8()[ c - _string->utf8() ];
1190            foundReplacement = true;
1191         }
1192         
1193         *c = c2;
1194      }
1195
1196      c++;
1197   }
1198 
1199   if( foundReplacement )
1200   {
1201      _string->release();
1202      _string = sub;
1203   }
1204
1205   return *this;
1206}
1207
1208String &String::replace(const String &s1, const String &s2)
1209{
1210   // Find number of occurrences of s1 and
1211   // Calculate length of the new string...
1212
1213   const U32 &s1len = s1.length();
1214   const U32 &s2len = s2.length();
1215
1216   U32 pos = 0;
1217   Vector<U32> indices;
1218   StringChar *walk = _string->utf8();
1219
1220   while ( walk )
1221   {
1222      // Casting away the const... was there a better way?
1223      walk = (StringChar*)StrFind( _string->utf8(), s1.c_str(), pos, Case</a>|<a href="/coding/class/classstring/#classstring_1ad23530ba3c445d964722346a3c80771da84aa4530ee3fa8bfa8e0c2305744bff8">Left );
1224      if ( walk )
1225      {
1226         pos = SizeType(walk - _string->utf8());
1227         indices.push_back( pos );
1228         pos += s1len;
1229      }
1230   }
1231
1232   // Early-out, no StringDatas found.
1233   if ( indices.size() == 0 )
1234      return *this;
1235
1236   U32 newSize = size() - ( indices.size() * s1len ) + ( indices.size() * s2len );
1237   StringData *sub;
1238   if( newSize == 1 )
1239      sub = StringData::Empty();
1240   else
1241   {
1242      sub = new (newSize - 1 ) StringData( NULL );
1243
1244      // Now assemble the new string from the pieces of the old...
1245
1246      // Index into the old string
1247      pos = 0;
1248      // Index into the new string
1249      U32 newPos = 0;
1250      // Used to store a character count to be memcpy'd
1251      U32 copyCharCount = 0;
1252
1253      for ( U32 i = 0; i < indices.size(); i++ )
1254      {
1255         const U32 &index = indices[i];
1256
1257         // Number of chars (if any) before the next indexed StringData
1258         copyCharCount = index - pos;
1259
1260         // Copy chars before the StringData if we have any.
1261         if ( copyCharCount > 0 )
1262         {
1263            dMemcpy( sub->utf8() + newPos, _string->utf8() + pos, copyCharCount * sizeof(StringChar) );
1264            newPos += copyCharCount;
1265         }
1266
1267         // Copy over the replacement string.
1268         if ( s2len > 0 )
1269            dMemcpy( sub->utf8() + newPos, s2._string->utf8(), s2len * sizeof(StringChar) );
1270
1271         newPos += s2len;
1272         pos = index + s1len;
1273      }
1274
1275      // There could be characters left in the original string after the last
1276      // StringData occurrence, which we need to copy now - outside the loop.
1277      copyCharCount = length() - indices.last() - s1len;
1278      if ( copyCharCount != 0 )
1279         dMemcpy( sub->utf8() + newPos, _string->utf8() + pos, copyCharCount * sizeof(StringChar) );
1280
1281      // Null terminate it!
1282      sub->utf8()[newSize-1] = 0;
1283   }
1284
1285   _string->release();
1286   _string = sub;
1287
1288   return *this;
1289}
1290
1291//-----------------------------------------------------------------------------
1292
1293String String::substr(SizeType pos, SizeType len) const
1294{
1295   //PROFILE_SCOPE( String_substr );
1296   
1297   AssertFatal( pos <= length(), "String::substr - Invalid position!" );
1298
1299   if ( len == -1 )
1300      len = length() - pos;
1301
1302   AssertFatal( len + pos <= length(), "String::substr - Invalid length!" );
1303
1304   StringData* sub;
1305   if( !len )
1306      sub = StringData::Empty();
1307   else
1308      sub = new ( len ) StringData( _string->utf8() + pos );
1309
1310   return sub;
1311}
1312
1313//-----------------------------------------------------------------------------
1314
1315String String::trim() const
1316{
1317   if( isEmpty() )
1318      return *this;
1319   
1320   const StringChar* start = _string->utf8();
1321   while( *start && dIsspace( *start ) )
1322      start ++;
1323   
1324   const StringChar* end = _string->utf8() + length() - 1;
1325   while( end > start && dIsspace( *end ) )
1326      end --;
1327   end ++;
1328   
1329   const U32 len = end - start;
1330   if( len == length() )
1331      return *this;
1332   
1333   StringData* sub;
1334   if( !len )
1335      sub = StringData::Empty();
1336   else
1337      sub = new ( len ) StringData( start );
1338
1339   return sub;
1340}
1341
1342//-----------------------------------------------------------------------------
1343
1344String String::expandEscapes() const
1345{
1346   char* tmp = ( char* ) dMalloc( length() * 2 + 1 ); // worst-case situation.
1347   expandEscape( tmp, c_str() );
1348   String str( tmp );
1349   dFree( tmp );
1350   return str;
1351}
1352
1353//-----------------------------------------------------------------------------
1354
1355String String::collapseEscapes() const
1356{
1357   char* tmp = dStrdup( c_str() );
1358   collapseEscape( tmp );
1359   String str( tmp );
1360   dFree( tmp );
1361   return str;
1362}
1363
1364//-----------------------------------------------------------------------------
1365
1366void String::split( const char* delimiter, Vector< String>& outElements ) const
1367{
1368   const char* ptr = _string->utf8();
1369   
1370   const char* start = ptr;
1371   while( *ptr )
1372   {
1373      // Search for start of delimiter.
1374      
1375      if( *ptr != delimiter[ 0 ] )
1376         ptr ++;
1377      else
1378      {
1379         // Skip delimiter.
1380         
1381         const char* end = ptr;
1382         const char* del = delimiter;
1383         while( *del && *del == *ptr )
1384         {
1385            ptr ++;
1386            del ++;
1387         }
1388         
1389         // If we didn't match all of delimiter,
1390         // continue with search.
1391         
1392         if( *del != '\0' )
1393            continue;
1394            
1395         // Extract component.
1396         
1397         outElements.push_back( String( start, end - start ) );
1398         start = ptr;
1399      }
1400   }
1401   
1402   // Add rest of string if there is any.
1403   
1404   if( start != ptr )
1405      outElements.push_back( start );
1406}
1407
1408//-----------------------------------------------------------------------------
1409
1410bool String::startsWith( const char* text ) const
1411{
1412   return dStrStartsWith( _string->utf8(), text );
1413}
1414
1415//-----------------------------------------------------------------------------
1416
1417bool String::endsWith( const char* text ) const
1418{
1419   return dStrEndsWith( _string->utf8(), text );
1420}
1421
1422//-----------------------------------------------------------------------------
1423
1424void String::copy(StringChar* dst, const StringChar *src, U32 len)
1425{
1426   dMemcpy(dst, src, len * sizeof(StringChar));
1427}
1428
1429//-----------------------------------------------------------------------------
1430
1431#if defined(TORQUE_OS_WIN) || defined(TORQUE_OS_XBOX) || defined(TORQUE_OS_XENON)
1432// This standard function is not defined when compiling with VC7...
1433#define vsnprintf _vsnprintf
1434#endif
1435
1436String::StrFormat::~StrFormat()
1437{
1438   if( _dynamicBuffer )
1439      dFree( _dynamicBuffer );
1440}
1441
1442S32 String::StrFormat::format( const char *format, va_list args )
1443{
1444   _len=0;
1445   return formatAppend(format,args);
1446}
1447
1448S32 String::StrFormat::formatAppend( const char *format, va_list args )
1449{
1450   // Format into the fixed buffer first.
1451   S32 startLen = _len;
1452   if (_dynamicBuffer == NULL)
1453   {
1454      _len += vsnprintf(_fixedBuffer + _len, sizeof(_fixedBuffer) - _len, format, args);
1455      if (_len >= 0 && _len < sizeof(_fixedBuffer))
1456         return _len;
1457
1458      // Start off the dynamic buffer at twice fixed buffer size
1459      _len = startLen;
1460      _dynamicSize = sizeof(_fixedBuffer) * 2;
1461      _dynamicBuffer = (char*)dMalloc(_dynamicSize);
1462      dMemcpy(_dynamicBuffer, _fixedBuffer, _len + 1);
1463   }
1464
1465   // Format into the dynamic buffer, if the buffer is not large enough, then
1466   // keep doubling it's size until it is.  The buffer is not reallocated
1467   // using reallocate() to avoid unnecessary buffer copying.
1468   _len += vsnprintf(_dynamicBuffer + _len, _dynamicSize - _len, format, *(va_list*)args);
1469   while (_len < 0 || _len >= _dynamicSize)
1470   {
1471      _len = startLen;
1472      _dynamicBuffer = (char*)dRealloc(_dynamicBuffer, _dynamicSize *= 2);
1473      _len += vsnprintf(_dynamicBuffer + _len, _dynamicSize - _len, format, *(va_list*)args);
1474   }
1475
1476   return _len;
1477}
1478
1479S32 String::StrFormat::append(const char * str, S32 len)
1480{
1481   if (_dynamicBuffer == NULL)
1482   {
1483      if (_len+len >= 0 && _len+len < sizeof(_fixedBuffer))
1484      {
1485         dMemcpy(_fixedBuffer + _len, str, len);
1486         _len += len;
1487         _fixedBuffer[_len] = '\0';
1488         return _len;
1489      }
1490
1491      _dynamicSize = sizeof(_fixedBuffer) * 2;
1492      _dynamicBuffer = (char*)dMalloc(_dynamicSize);
1493      dMemcpy(_dynamicBuffer, _fixedBuffer, _len + 1);
1494   }
1495
1496   S32 newSize = _dynamicSize;
1497   while (newSize < _len+len)
1498      newSize *= 2;
1499   if (newSize != _dynamicSize)
1500      _dynamicBuffer = (char*) dRealloc(_dynamicBuffer, newSize);
1501   _dynamicSize = newSize;
1502   dMemcpy(_dynamicBuffer + _len, str, len);
1503   _len += len;
1504   _dynamicBuffer[_len] = '\0';
1505   return _len;
1506}
1507
1508S32 String::StrFormat::append(const char * str)
1509{
1510   return append(str, dStrlen(str));
1511}
1512
1513char* String::StrFormat::copy( char *buffer ) const
1514{
1515   dMemcpy(buffer, _dynamicBuffer? _dynamicBuffer: _fixedBuffer, _len+1);
1516   return buffer;
1517}
1518
1519//-----------------------------------------------------------------------------
1520
1521String String::ToString( bool value )
1522{
1523   static String sTrue = "true";
1524   static String sFalse = "false";
1525   
1526   if( value )
1527      return sTrue;
1528   return sFalse;
1529}
1530
1531String String::ToString(const char *str, ...)
1532{
1533   AssertFatal(str,"String:: Invalid null ptr argument");
1534
1535   // Use the format object
1536   va_list args;
1537   va_start(args, str);
1538   String ret = VToString(str, args);
1539   va_end(args);
1540   return ret;
1541}
1542
1543String String::VToString(const char* str, va_list args)
1544{
1545   StrFormat format(str,args);
1546
1547   // Copy it into a string
1548   U32         len = format.length();
1549   StringData* sub;
1550   if( !len )
1551      sub = StringData::Empty();
1552   else
1553   {
1554      sub = new ( len ) StringData( NULL );
1555
1556      format.copy( sub->utf8() );
1557      sub->utf8()[ len ] = 0;
1558   }
1559
1560   return sub;
1561}
1562
1563String   String::SpanToString(const char *start, const char *end)
1564{
1565   if ( end == start )
1566      return String();
1567   
1568   AssertFatal( end > start, "Invalid arguments to String::SpanToString - end is before start" );
1569
1570   U32         len = U32(end - start);
1571   StringData* sub = new ( len ) StringData( start );
1572
1573   return sub;
1574}
1575
1576String String::ToLower(const String &string)
1577{
1578   if ( string.isEmpty() )
1579      return String();
1580
1581   StringData* sub = new ( string.length() ) StringData( string );
1582   dStrlwr( sub->utf8() );
1583
1584   return sub;
1585}
1586
1587String String::ToUpper(const String &string)
1588{
1589   if ( string.isEmpty() )
1590      return String();
1591
1592   StringData* sub = new ( string.length() ) StringData( string );
1593   dStrupr( sub->utf8() );
1594
1595   return sub;
1596}
1597
1598String String::GetTrailingNumber(const char* str, S32& number)
1599{
1600   // Check for trivial strings
1601   if (!str || !str[0])
1602      return String::EmptyString;
1603
1604   // Find the number at the end of the string
1605   String base(str);
1606   const char* p = base.c_str() + base.length() - 1;
1607
1608   // Ignore trailing whitespace
1609   while ((p != base.c_str()) && dIsspace(*p))
1610      p--;
1611
1612   // Need at least one digit!
1613   if (!isdigit(*p))
1614      return base;
1615
1616   // Back up to the first non-digit character
1617   while ((p != base.c_str()) && isdigit(*p))
1618      p--;
1619
1620   // Convert number => allow negative numbers, treat '_' as '-' for Maya
1621   if ((*p == '-') || (*p == '_'))
1622      number = -dAtoi(p + 1);
1623   else
1624      number = ((p == base.c_str()) ? dAtoi(p) : dAtoi(++p));
1625
1626   // Remove space between the name and the number
1627   while ((p > base.c_str()) && dIsspace(*(p-1)))
1628      p--;
1629
1630   return base.substr(0, p - base.c_str());
1631}
1632
1633String String::GetFirstNumber(const char* str, U32& startPos, U32& endPos)
1634{
1635   // Check for trivial strings
1636   if (!str || !str[0])
1637      return String::EmptyString;
1638
1639   // Find the number at the end of the string
1640   String base(str);
1641   const char* p = base.c_str();
1642   const char* end = base.c_str() + base.length() - 1;
1643   bool dec = false;
1644   startPos = 0;
1645
1646   //Check if we are just a digit
1647   if(p == end && isdigit(*p))
1648      return base;
1649
1650   //Look for the first digit
1651   while ((p != end) && (dIsspace(*p) || !isdigit(*p)))
1652   {
1653      p++;
1654      startPos++;
1655   }
1656
1657   //Handle if we are at the end and found nothing
1658   if(p == end && !isdigit(*p))
1659      return "";
1660
1661   //update our end position at least to the start of our number
1662   endPos = startPos;
1663
1664   //Backup our ptr
1665   const char* backup = p;
1666
1667   //Check for any negative or decimal values
1668   if(startPos > 0)
1669   {
1670      p--;
1671      startPos--;
1672      if(*p == '.')
1673      {
1674         dec = true;
1675
1676         //ignore any duplicate periods
1677         while ((p != base.c_str()) && (*p == '.'))
1678         {
1679            p--;
1680            startPos--;
1681         }
1682
1683         //Found a decimal lets still check for negative sign
1684         if(startPos > 0)
1685         {
1686            p--;
1687            startPos--;
1688            if((*p != '-') && (*p != '_'))
1689            {
1690               startPos++;
1691               p++;
1692            }
1693         }
1694      }
1695      else if((*p != '-') && (*p != '_'))
1696      {
1697         //go back to where we where cause no decimal or negative sign found
1698         startPos++;
1699         p++;
1700      }
1701   }
1702
1703   //Restore where we were
1704   p = backup;
1705
1706   //look for the end of the digits
1707   bool justFoundDec = false;
1708   while (p != end)
1709   {
1710      if(*p == '.')
1711      {
1712         if(dec && !justFoundDec)
1713            break;
1714         else
1715         {
1716            dec = true;
1717            justFoundDec = true;
1718         }
1719      }
1720      else if(!isdigit(*p))
1721         break;
1722      else if(justFoundDec)
1723         justFoundDec = false;
1724
1725      p++;
1726      endPos++;
1727   }
1728
1729   U32 len = (!isdigit(*p)) ? endPos - startPos : (endPos + 1) - startPos;
1730   return base.substr(startPos, len);
1731}
1732