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

DefineEngineFunction(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

DefineEngineFunction(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 ( String::compare( 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
 484DefineEngineFunction( 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   if (_string && _string != StringData::Empty())
 595      _string->release();
 596}
 597
 598//-----------------------------------------------------------------------------
 599
 600String String::intern() const
 601{
 602   if( isInterned() )
 603      return *this;
 604      
 605   // Create the intern table, if we haven't already.
 606   
 607   if( !sInternTable )
 608      sInternTable = new StringInternTable;
 609      
 610   // Lock the string table.
 611         
 612   MutexHandle mutex;
 613   mutex.lock( &sInternTable->mMutex );
 614   
 615   // Lookup.
 616   
 617   StringInternTable::Iterator iter = sInternTable->find( _string );
 618   if( iter != sInternTable->end() )
 619      return ( *iter ).value;
 620      
 621   // Create new.
 622   
 623   StringData* data = new ( length(), sInternTable->mChunker ) StringData( c_str(), true );
 624   iter = sInternTable->insertUnique( data, data );
 625   
 626   return ( *iter ).value;
 627}
 628
 629//-----------------------------------------------------------------------------
 630
 631const StringChar* String::c_str() const
 632{
 633   return _string->utf8();
 634}
 635
 636const UTF16 *String::utf16() const
 637{
 638   return _string->utf16();
 639}
 640
 641String::SizeType String::length() const
 642{
 643   return _string->getLength();
 644}
 645
 646String::SizeType String::size() const
 647{
 648   return _string->getDataSize();
 649}
 650
 651String::SizeType String::numChars() const
 652{
 653   return _string->getNumChars();
 654}
 655
 656bool String::isEmpty() const
 657{
 658   return ( _string == StringData::Empty() );
 659}
 660
 661bool String::isEmpty(const char* str)
 662{
 663   return str == 0 || str[0] == '\0';
 664}
 665
 666bool String::isShared() const
 667{
 668   return _string->isShared();
 669}
 670
 671bool String::isSame( const String& str ) const
 672{
 673   return ( _string == str._string );
 674}
 675
 676bool String::isInterned() const
 677{
 678   return ( _string->isInterned() );
 679}
 680
 681U32 String::getHashCaseSensitive() const
 682{
 683   return _string->getOrCreateHashCase();
 684}
 685
 686U32 String::getHashCaseInsensitive() const
 687{
 688   return _string->getOrCreateHashNoCase();
 689}
 690
 691//-----------------------------------------------------------------------------
 692
 693String::SizeType String::find(const String &str, SizeType pos, U32 mode) const
 694{
 695   return find(str._string->utf8(), pos, mode);
 696}
 697
 698String& String::insert(SizeType pos, const String &str)
 699{
 700   return insert(pos, str._string->utf8());
 701}
 702
 703String& String::replace(SizeType pos, SizeType len, const String &str)
 704{
 705   return replace(pos, len, str._string->utf8());
 706}
 707
 708//-----------------------------------------------------------------------------
 709
 710String& String::operator=(StringChar c)
 711{
 712   _string->release();
 713
 714   _string = new ( 2 ) StringData( 0 );
 715   _string->utf8()[ 0 ] = c;
 716   _string->utf8()[ 1 ] = '\0';
 717
 718   return *this;
 719}
 720
 721String& String::operator+=(StringChar c)
 722{
 723   // Append the given string into a new string
 724   U32 len = _string->getLength();
 725   StringData* sub = new ( len + 1 ) StringData( NULL );
 726
 727   copy( sub->utf8(), _string->utf8(), len );
 728   sub->utf8()[len] = c;
 729   sub->utf8()[len+1] = 0;
 730
 731   _string->release();
 732   _string = sub;
 733
 734   return *this;
 735}
 736
 737//-----------------------------------------------------------------------------
 738
 739String& String::operator=(const StringChar *str)
 740{
 741   // Protect against self assignment which is not only a
 742   // waste of time, but can also lead to the string being
 743   // freed before it can be reassigned.
 744   if ( _string->utf8() == str )
 745      return *this;
 746
 747   _string->release();
 748
 749   if (str && *str)
 750   {
 751      U32 len = dStrlen(str);
 752      _string = new ( len ) StringData( str );
 753   }
 754   else
 755      _string = StringData::Empty();
 756
 757   return *this;
 758}
 759
 760String& String::operator=(const String &src)
 761{
 762   // Inc src first to avoid assignment to self problems.
 763   src._string->addRef();
 764
 765   _string->release();
 766   _string = src._string;
 767
 768   return *this;
 769}
 770
 771String& String::operator+=(const StringChar *src)
 772{
 773   if( src == NULL || !*src )
 774      return *this;
 775
 776   // Append the given string into a new string
 777   U32 lena = _string->getLength();
 778   U32 lenb = dStrlen(src);
 779   U32 newlen = lena + lenb;
 780
 781   StringData* sub;
 782   if( !newlen )
 783      sub = StringData::Empty();
 784   else
 785   {
 786      sub = new ( newlen ) StringData( NULL );
 787
 788      copy(sub->utf8(),_string->utf8(),lena);
 789      copy(sub->utf8() + lena,src,lenb + 1);
 790   }
 791
 792   _string->release();
 793   _string = sub;
 794
 795   return *this;
 796}
 797
 798String& String::operator+=(const String &src)
 799{
 800   if( src.isEmpty() )
 801      return *this;
 802
 803   // Append the given string into a new string
 804   U32 lena = _string->getLength();
 805   U32 lenb = src._string->getLength();
 806   U32 newlen = lena + lenb;
 807
 808   StringData* sub;
 809   if( !newlen )
 810      sub = StringData::Empty();
 811   else
 812   {
 813      sub = new ( newlen ) StringData( NULL );
 814
 815      copy(sub->utf8(),_string->utf8(),lena);
 816      copy(sub->utf8() + lena,src._string->utf8(),lenb + 1);
 817   }
 818
 819   _string->release();
 820   _string = sub;
 821
 822   return *this;
 823}
 824
 825//-----------------------------------------------------------------------------
 826
 827String operator+(const String &a, const String &b)
 828{
 829   PROFILE_SCOPE( String_String_plus_String );
 830   
 831   if( a.isEmpty() )
 832      return b;
 833   else if( b.isEmpty() )
 834      return a;
 835
 836   U32 lena = a.length();
 837   U32 lenb = b.length();
 838
 839   String::StringData *sub = new ( lena + lenb ) String::StringData( NULL );
 840
 841   String::copy(sub->utf8(),a._string->utf8(),lena);
 842   String::copy(sub->utf8() + lena,b._string->utf8(),lenb + 1);
 843
 844   return String(sub);
 845}
 846
 847String operator+(const String &a, StringChar c)
 848{
 849   //PROFILE_SCOPE( String_String_plus_Char );
 850
 851   U32 lena = a.length();
 852   String::StringData *sub = new ( lena + 1 ) String::StringData( NULL );
 853
 854   String::copy(sub->utf8(),a._string->utf8(),lena);
 855
 856   sub->utf8()[lena] = c;
 857   sub->utf8()[lena+1] = 0;
 858
 859   return String(sub);
 860}
 861
 862String operator+(StringChar c, const String &a)
 863{
 864   //PROFILE_SCOPE( String_Char_plus_String );
 865
 866   U32 lena = a.length();
 867   String::StringData *sub = new ( lena + 1 ) String::StringData( NULL );
 868
 869   String::copy(sub->utf8() + 1,a._string->utf8(),lena + 1);
 870   sub->utf8()[0] = c;
 871
 872   return String(sub);
 873}
 874
 875String operator+(const String &a, const StringChar *b)
 876{
 877   //PROFILE_SCOPE( String_String_plus_CString );
 878
 879   AssertFatal(b,"String:: Invalid null ptr argument");
 880
 881   if( a.isEmpty() )
 882      return String( b );
 883
 884   U32 lena = a.length();
 885   U32 lenb = dStrlen(b);
 886
 887   if( !lenb )
 888      return a;
 889
 890   String::StringData *sub = new ( lena + lenb ) String::StringData( NULL );
 891
 892   String::copy(sub->utf8(),a._string->utf8(),lena);
 893   String::copy(sub->utf8() + lena,b,lenb + 1);
 894
 895   return String(sub);
 896}
 897
 898String operator+(const StringChar *a, const String &b)
 899{
 900   //PROFILE_SCOPE( String_CString_plus_String );
 901   AssertFatal(a,"String:: Invalid null ptr argument");
 902
 903   if( b.isEmpty() )
 904      return String( a );
 905
 906   U32 lena = dStrlen(a);
 907   if( !lena )
 908      return b;
 909
 910   U32 lenb = b.length();
 911
 912   String::StringData* sub = new ( lena + lenb ) String::StringData( NULL );
 913
 914   String::copy(sub->utf8(),a,lena);
 915   String::copy(sub->utf8() + lena,b._string->utf8(),lenb + 1);
 916
 917   return String(sub);
 918}
 919
 920bool String::operator==(const String &str) const
 921{
 922   //PROFILE_SCOPE( String_op_equal );
 923
 924   if( str._string == _string )
 925      return true;
 926   else if( str._string->isInterned() && _string->isInterned() )
 927      return false;
 928   else if( str.length() != length() )
 929      return false;
 930   else if( str._string->getHashCase() != U32_MAX
 931            && _string->getHashCase() != U32_MAX
 932            && str._string->getHashCase() != _string->getHashCase() )
 933      return false;
 934   else
 935      return ( dMemcmp( str._string->utf8(), _string->utf8(), _string->getLength() ) == 0 );
 936}
 937
 938bool String::operator==( StringChar c ) const
 939{
 940   if( !_string || _string->getLength() != 1 )
 941      return false;
 942   else
 943      return ( _string->utf8()[ 0 ] == c );
 944}
 945
 946bool String::operator<(const String &str) const
 947{
 948   return ( dStrnatcmp( _string->utf8(), str._string->utf8() ) < 0 );
 949}
 950
 951bool String::operator>(const String &str) const
 952{
 953   return ( dStrnatcmp( _string->utf8(), str._string->utf8() ) > 0 );
 954}
 955
 956bool String::operator<=(const String &str) const
 957{
 958   return ( dStrnatcmp( _string->utf8(), str._string->utf8() ) <= 0 );
 959}
 960
 961bool String::operator>=(const String &str) const
 962{
 963   return ( dStrnatcmp( _string->utf8(), str._string->utf8() ) >= 0 );
 964}
 965
 966//-----------------------------------------------------------------------------
 967// Base functions for string comparison
 968
 969S32 String::compare(const StringChar *str, SizeType len, U32 mode) const
 970{
 971   PROFILE_SCOPE( String_compare );
 972   
 973   AssertFatal(str,"String:: Invalid null ptr argument");
 974
 975   const StringChar  *p1 = _string->utf8();
 976   const StringChar  *p2 = str;
 977
 978   if (p1 == p2)
 979      return 0;
 980
 981   if( mode & String::Right )
 982   {
 983      U32 n = len;
 984      if( n > length() )
 985         n = length();
 986
 987      p1 += length() - n;
 988      p2 += dStrlen( str ) - n;
 989   }
 990
 991   if (mode & String::NoCase)
 992   {
 993      if (len)
 994      {
 995         for (;--len; p1++,p2++)
 996         {
 997            if (dTolower(*p1) != dTolower(*p2) || !*p1)
 998               break;
 999         }
1000      }
1001      else
1002      {
1003         while (dTolower(*p1) == dTolower(*p2) && *p1)
1004         {
1005            p1++;
1006            p2++;
1007         }
1008      }
1009
1010      return dTolower(*p1) - dTolower(*p2);
1011   }
1012
1013   if (len)
1014      return dMemcmp(p1,p2,len);
1015
1016   while (*p1 == *p2 && *p1)
1017   {
1018      p1++;
1019      p2++;
1020   }
1021
1022   return *p1 - *p2;
1023}
1024
1025S32 String::compare(const String &str, SizeType len, U32 mode) const
1026{
1027   if ( str._string == _string )
1028      return 0;
1029
1030   return compare( str.c_str(), len, mode );
1031}
1032
1033S32 String::compare(const char *str1, const char *str2)
1034{
1035   return strcmp(str1, str2);
1036}
1037
1038S32 String::compare(const UTF16 *str1, const UTF16 *str2)
1039{
1040#if defined(TORQUE_OS_WIN) || defined(TORQUE_OS_XBOX) || defined(TORQUE_OS_XENON)
1041   return wcscmp(reinterpret_cast<const wchar_t *>(str1), reinterpret_cast<const wchar_t *>(str2));
1042#else
1043   S32 ret;
1044   const UTF16 *a, *b;
1045   a = str1;
1046   b = str2;
1047
1048   while (((ret = *a - *b) == 0) && *a && *b)
1049      a++, b++;
1050
1051   return ret;
1052#endif
1053}
1054
1055bool String::equal(const String &str, U32 mode) const
1056{
1057   if( !mode )
1058      return ( *this == str );
1059   else
1060   {
1061      if( _string == str._string )
1062         return true;
1063      else if( _string->isInterned() && str._string->isInterned() )
1064         return false;
1065      else if( length() != str.length() )
1066         return false;
1067      else if( _string->getHashNoCase() != U32_MAX
1068               && str._string->getHashNoCase() != U32_MAX
1069               && _string->getHashNoCase() != str._string->getHashNoCase() )
1070         return false;
1071      else
1072         return ( compare( str.c_str(), length(), mode ) == 0 );
1073   }
1074}
1075
1076//-----------------------------------------------------------------------------
1077
1078String::SizeType String::find(StringChar c, SizeType pos, U32 mode) const
1079{
1080   const StringChar* ptr = StrFind(_string->utf8(),c,pos,mode);
1081
1082   return ptr? SizeType(ptr - _string->utf8()): NPos;
1083}
1084
1085String::SizeType String::find(const StringChar *str, SizeType pos, U32 mode)  const
1086{
1087   AssertFatal(str,"String:: Invalid null ptr argument");
1088
1089   const StringChar* ptr = StrFind(_string->utf8(),str,pos,mode);
1090
1091   return ptr? SizeType(ptr - _string->utf8()): NPos;
1092}
1093
1094
1095//-----------------------------------------------------------------------------
1096
1097String& String::insert(SizeType pos, const StringChar *str)
1098{
1099   AssertFatal(str,"String:: Invalid null ptr argument");
1100
1101   return insert(pos,str,dStrlen(str));
1102}
1103
1104///@todo review for error checking
1105String& String::insert(SizeType pos, const StringChar *str, SizeType len)
1106{
1107   if( !len )
1108      return *this;
1109
1110   AssertFatal( str, "String:: Invalid null ptr argument" );
1111         
1112   SizeType lena = length();
1113   AssertFatal((pos <= lena),"Calling String::insert with position greater than length");
1114   U32 newlen = lena + len;
1115
1116   StringData *sub;
1117   if( !newlen )
1118      sub = StringData::Empty();
1119   else
1120   {
1121      sub = new ( newlen ) StringData( NULL );
1122
1123      String::copy(sub->utf8(),_string->utf8(),pos);
1124      String::copy(sub->utf8() + pos,str,len);
1125      String::copy(sub->utf8() + pos + len,_string->utf8() + pos,lena - pos + 1);
1126   }
1127
1128   _string->release();
1129   _string = sub;
1130
1131   return *this;
1132}
1133
1134String& String::erase(SizeType pos, SizeType len)
1135{
1136   AssertFatal( len != 0, "String::erase() - Calling String::erase with 0 length" );
1137   AssertFatal( ( pos + len ) <= length(), "String::erase() - Invalid string region" );
1138
1139   if( !len )
1140      return *this;
1141
1142   SizeType slen = length();
1143   U32 newlen = slen - len;
1144
1145   StringData *sub;
1146   if( !newlen )
1147      sub = StringData::Empty();
1148   else
1149   {
1150      sub = new ( newlen ) StringData( NULL );
1151
1152      if (pos > 0)
1153         String::copy(sub->utf8(),_string->utf8(),pos);
1154
1155      String::copy(sub->utf8() + pos, _string->utf8() + pos + len, slen - (pos + len) + 1);
1156   }
1157
1158   _string->release();
1159   _string = sub;
1160
1161   return *this;
1162}
1163
1164///@todo review for error checking
1165String& String::replace(SizeType pos, SizeType len, const StringChar *str)
1166{
1167   AssertFatal( str, "String::replace() - Invalid null ptr argument" );
1168   AssertFatal( len != 0, "String::replace() - Zero length" );
1169   AssertFatal( ( pos + len ) <= length(), "String::replace() - Invalid string region" );
1170
1171   SizeType slen = length();
1172   SizeType rlen = dStrlen(str);
1173
1174   U32 newlen = slen - len + rlen;
1175   StringData *sub;
1176   if( !newlen )
1177      sub = StringData::Empty();
1178   else
1179   {
1180      sub = new ( newlen ) StringData( NULL );
1181
1182      String::copy(sub->utf8(),_string->utf8(), pos);
1183      String::copy(sub->utf8() + pos,str,rlen);
1184      String::copy(sub->utf8() + pos + rlen,_string->utf8() + pos + len,slen - pos - len + 1);
1185   }
1186
1187   _string->release();
1188   _string = sub;
1189
1190   return *this;
1191}
1192
1193String& String::replace( StringChar c1, StringChar c2 )
1194{
1195   if( isEmpty() )
1196      return *this;
1197      
1198   // Create the new string lazily so that we don't needlessly
1199   // dup strings when there is nothing to replace.
1200
1201   StringData* sub = NULL;   
1202   bool foundReplacement = false;
1203
1204   StringChar* c = _string->utf8();
1205   while( *c )
1206   {
1207      if( *c == c1 )
1208      {
1209         if( !foundReplacement )
1210         {
1211            sub = new ( length() ) StringData( _string->utf8() );
1212            c = &sub->utf8()[ c - _string->utf8() ];
1213            foundReplacement = true;
1214         }
1215         
1216         *c = c2;
1217      }
1218
1219      c++;
1220   }
1221 
1222   if( foundReplacement )
1223   {
1224      _string->release();
1225      _string = sub;
1226   }
1227
1228   return *this;
1229}
1230
1231String &String::replace(const String &s1, const String &s2)
1232{
1233   // Find number of occurrences of s1 and
1234   // Calculate length of the new string...
1235
1236   const U32 &s1len = s1.length();
1237   const U32 &s2len = s2.length();
1238
1239   U32 pos = 0;
1240   Vector<U32> indices;
1241   StringChar *walk = _string->utf8();
1242
1243   while ( walk )
1244   {
1245      // Casting away the const... was there a better way?
1246      walk = (StringChar*)StrFind( _string->utf8(), s1.c_str(), pos, Case</a>|<a href="/coding/class/classstring/#classstring_1ad23530ba3c445d964722346a3c80771da84aa4530ee3fa8bfa8e0c2305744bff8">Left );
1247      if ( walk )
1248      {
1249         pos = SizeType(walk - _string->utf8());
1250         indices.push_back( pos );
1251         pos += s1len;
1252      }
1253   }
1254
1255   // Early-out, no StringDatas found.
1256   if ( indices.size() == 0 )
1257      return *this;
1258
1259   U32 newSize = size() - ( indices.size() * s1len ) + ( indices.size() * s2len );
1260   StringData *sub;
1261   if( newSize == 1 )
1262      sub = StringData::Empty();
1263   else
1264   {
1265      sub = new (newSize - 1 ) StringData( NULL );
1266
1267      // Now assemble the new string from the pieces of the old...
1268
1269      // Index into the old string
1270      pos = 0;
1271      // Index into the new string
1272      U32 newPos = 0;
1273      // Used to store a character count to be memcpy'd
1274      U32 copyCharCount = 0;
1275
1276      for ( U32 i = 0; i < indices.size(); i++ )
1277      {
1278         const U32 &index = indices[i];
1279
1280         // Number of chars (if any) before the next indexed StringData
1281         copyCharCount = index - pos;
1282
1283         // Copy chars before the StringData if we have any.
1284         if ( copyCharCount > 0 )
1285         {
1286            dMemcpy( sub->utf8() + newPos, _string->utf8() + pos, copyCharCount * sizeof(StringChar) );
1287            newPos += copyCharCount;
1288         }
1289
1290         // Copy over the replacement string.
1291         if ( s2len > 0 )
1292            dMemcpy( sub->utf8() + newPos, s2._string->utf8(), s2len * sizeof(StringChar) );
1293
1294         newPos += s2len;
1295         pos = index + s1len;
1296      }
1297
1298      // There could be characters left in the original string after the last
1299      // StringData occurrence, which we need to copy now - outside the loop.
1300      copyCharCount = length() - indices.last() - s1len;
1301      if ( copyCharCount != 0 )
1302         dMemcpy( sub->utf8() + newPos, _string->utf8() + pos, copyCharCount * sizeof(StringChar) );
1303
1304      // Null terminate it!
1305      sub->utf8()[newSize-1] = 0;
1306   }
1307
1308   _string->release();
1309   _string = sub;
1310
1311   return *this;
1312}
1313
1314//-----------------------------------------------------------------------------
1315
1316String String::substr(SizeType pos, SizeType len) const
1317{
1318   //PROFILE_SCOPE( String_substr );
1319   
1320   AssertFatal( pos <= length(), "String::substr - Invalid position!" );
1321
1322   if ( len == -1 )
1323      len = length() - pos;
1324
1325   AssertFatal( len + pos <= length(), "String::substr - Invalid length!" );
1326
1327   StringData* sub;
1328   if( !len )
1329      sub = StringData::Empty();
1330   else
1331      sub = new ( len ) StringData( _string->utf8() + pos );
1332
1333   return sub;
1334}
1335
1336//-----------------------------------------------------------------------------
1337
1338String String::trim() const
1339{
1340   if( isEmpty() )
1341      return *this;
1342   
1343   const StringChar* start = _string->utf8();
1344   while( *start && dIsspace( *start ) )
1345      start ++;
1346   
1347   const StringChar* end = _string->utf8() + length() - 1;
1348   while( end > start && dIsspace( *end ) )
1349      end --;
1350   end ++;
1351   
1352   const U32 len = end - start;
1353   if( len == length() )
1354      return *this;
1355   
1356   StringData* sub;
1357   if( !len )
1358      sub = StringData::Empty();
1359   else
1360      sub = new ( len ) StringData( start );
1361
1362   return sub;
1363}
1364
1365//-----------------------------------------------------------------------------
1366
1367String String::expandEscapes() const
1368{
1369   char* tmp = ( char* ) dMalloc( length() * 2 + 1 ); // worst-case situation.
1370   expandEscape( tmp, c_str() );
1371   String str( tmp );
1372   dFree( tmp );
1373   return str;
1374}
1375
1376//-----------------------------------------------------------------------------
1377
1378String String::collapseEscapes() const
1379{
1380   char* tmp = dStrdup( c_str() );
1381   collapseEscape( tmp );
1382   String str( tmp );
1383   dFree( tmp );
1384   return str;
1385}
1386
1387//-----------------------------------------------------------------------------
1388
1389void String::split( const char* delimiter, Vector< String>& outElements ) const
1390{
1391   const char* ptr = _string->utf8();
1392   
1393   const char* start = ptr;
1394   while( *ptr )
1395   {
1396      // Search for start of delimiter.
1397      
1398      if( *ptr != delimiter[ 0 ] )
1399         ptr ++;
1400      else
1401      {
1402         // Skip delimiter.
1403         
1404         const char* end = ptr;
1405         const char* del = delimiter;
1406         while( *del && *del == *ptr )
1407         {
1408            ptr ++;
1409            del ++;
1410         }
1411         
1412         // If we didn't match all of delimiter,
1413         // continue with search.
1414         
1415         if( *del != '\0' )
1416            continue;
1417            
1418         // Extract component.
1419         
1420         outElements.push_back( String( start, end - start ) );
1421         start = ptr;
1422      }
1423   }
1424   
1425   // Add rest of string if there is any.
1426   
1427   if( start != ptr )
1428      outElements.push_back( start );
1429}
1430
1431//-----------------------------------------------------------------------------
1432
1433bool String::startsWith( const char* text ) const
1434{
1435   return dStrStartsWith( _string->utf8(), text );
1436}
1437
1438//-----------------------------------------------------------------------------
1439
1440bool String::endsWith( const char* text ) const
1441{
1442   return dStrEndsWith( _string->utf8(), text );
1443}
1444
1445//-----------------------------------------------------------------------------
1446
1447void String::copy(StringChar* dst, const StringChar *src, U32 len)
1448{
1449   dMemcpy(dst, src, len * sizeof(StringChar));
1450}
1451
1452//-----------------------------------------------------------------------------
1453
1454#if defined(TORQUE_OS_WIN)
1455// This standard function is not defined when compiling with VC7...
1456#define vsnprintf _vsnprintf
1457#endif
1458
1459String::StrFormat::~StrFormat()
1460{
1461   if( _dynamicBuffer )
1462      dFree( _dynamicBuffer );
1463}
1464
1465S32 String::StrFormat::format( const char *format, va_list args )
1466{
1467   _len=0;
1468   return formatAppend(format,args);
1469}
1470
1471S32 String::StrFormat::formatAppend( const char *format, va_list args )
1472{
1473   // Format into the fixed buffer first.
1474   S32 startLen = _len;
1475   if (_dynamicBuffer == NULL)
1476   {
1477      _len += vsnprintf(_fixedBuffer + _len, sizeof(_fixedBuffer) - _len, format, args);
1478      if (_len >= 0 && _len < sizeof(_fixedBuffer))
1479         return _len;
1480
1481      // Start off the dynamic buffer at twice fixed buffer size
1482      _len = startLen;
1483      _dynamicSize = sizeof(_fixedBuffer) * 2;
1484      _dynamicBuffer = (char*)dMalloc(_dynamicSize);
1485      dMemcpy(_dynamicBuffer, _fixedBuffer, _len + 1);
1486   }
1487
1488   // Format into the dynamic buffer, if the buffer is not large enough, then
1489   // keep doubling it's size until it is.  The buffer is not reallocated
1490   // using reallocate() to avoid unnecessary buffer copying.
1491   _len += vsnprintf(_dynamicBuffer + _len, _dynamicSize - _len, format, *(va_list*)args);
1492   while (_len < 0 || _len >= _dynamicSize)
1493   {
1494      _len = startLen;
1495      _dynamicBuffer = (char*)dRealloc(_dynamicBuffer, _dynamicSize *= 2);
1496      _len += vsnprintf(_dynamicBuffer + _len, _dynamicSize - _len, format, *(va_list*)args);
1497   }
1498
1499   return _len;
1500}
1501
1502S32 String::StrFormat::append(const char * str, S32 len)
1503{
1504   if (_dynamicBuffer == NULL)
1505   {
1506      if (_len+len >= 0 && _len+len < sizeof(_fixedBuffer))
1507      {
1508         dMemcpy(_fixedBuffer + _len, str, len);
1509         _len += len;
1510         _fixedBuffer[_len] = '\0';
1511         return _len;
1512      }
1513
1514      _dynamicSize = sizeof(_fixedBuffer) * 2;
1515      _dynamicBuffer = (char*)dMalloc(_dynamicSize);
1516      dMemcpy(_dynamicBuffer, _fixedBuffer, _len + 1);
1517   }
1518
1519   S32 newSize = _dynamicSize;
1520   while (newSize < _len+len)
1521      newSize *= 2;
1522   if (newSize != _dynamicSize)
1523      _dynamicBuffer = (char*) dRealloc(_dynamicBuffer, newSize);
1524   _dynamicSize = newSize;
1525   dMemcpy(_dynamicBuffer + _len, str, len);
1526   _len += len;
1527   _dynamicBuffer[_len] = '\0';
1528   return _len;
1529}
1530
1531S32 String::StrFormat::append(const char * str)
1532{
1533   return append(str, dStrlen(str));
1534}
1535
1536char* String::StrFormat::copy( char *buffer ) const
1537{
1538   dMemcpy(buffer, _dynamicBuffer? _dynamicBuffer: _fixedBuffer, _len+1);
1539   return buffer;
1540}
1541
1542//-----------------------------------------------------------------------------
1543
1544String String::ToString( bool value )
1545{
1546   static String sTrue = "true";
1547   static String sFalse = "false";
1548   
1549   if( value )
1550      return sTrue;
1551   return sFalse;
1552}
1553
1554String String::ToString(const char *str, ...)
1555{
1556   AssertFatal(str,"String:: Invalid null ptr argument");
1557
1558   // Use the format object
1559   va_list args;
1560   va_start(args, str);
1561   String ret = VToString(str, args);
1562   va_end(args);
1563   return ret;
1564}
1565
1566String String::VToString(const char* str, va_list args)
1567{
1568   StrFormat format(str,args);
1569
1570   // Copy it into a string
1571   U32         len = format.length();
1572   StringData* sub;
1573   if( !len )
1574      sub = StringData::Empty();
1575   else
1576   {
1577      sub = new ( len ) StringData( NULL );
1578
1579      format.copy( sub->utf8() );
1580      sub->utf8()[ len ] = 0;
1581   }
1582
1583   return sub;
1584}
1585
1586String   String::SpanToString(const char *start, const char *end)
1587{
1588   if ( end == start )
1589      return String();
1590   
1591   AssertFatal( end > start, "Invalid arguments to String::SpanToString - end is before start" );
1592
1593   U32         len = U32(end - start);
1594   StringData* sub = new ( len ) StringData( start );
1595
1596   return sub;
1597}
1598
1599String String::ToLower(const String &string)
1600{
1601   if ( string.isEmpty() )
1602      return String();
1603
1604   StringData* sub = new ( string.length() ) StringData( string );
1605   dStrlwr( sub->utf8() );
1606
1607   return sub;
1608}
1609
1610String String::ToUpper(const String &string)
1611{
1612   if ( string.isEmpty() )
1613      return String();
1614
1615   StringData* sub = new ( string.length() ) StringData( string );
1616   dStrupr( sub->utf8() );
1617
1618   return sub;
1619}
1620
1621String String::GetTrailingNumber(const char* str, S32& number)
1622{
1623   // Check for trivial strings
1624   if (!str || !str[0])
1625      return String::EmptyString;
1626
1627   // Find the number at the end of the string
1628   String base(str);
1629   const char* p = base.c_str() + base.length() - 1;
1630
1631   // Ignore trailing whitespace
1632   while ((p != base.c_str()) && dIsspace(*p))
1633      p--;
1634
1635   // Need at least one digit!
1636   if (!isdigit(*p))
1637      return base;
1638
1639   // Back up to the first non-digit character
1640   while ((p != base.c_str()) && isdigit(*p))
1641      p--;
1642
1643   // Convert number => allow negative numbers, treat '_' as '-' for Maya
1644   if ((*p == '-') || (*p == '_'))
1645      number = -dAtoi(p + 1);
1646   else
1647      number = (isdigit(*p) ? dAtoi(p) : dAtoi(++p));
1648
1649   // Remove space between the name and the number
1650   while ((p > base.c_str()) && dIsspace(*(p-1)))
1651      p--;
1652
1653   return base.substr(0, p - base.c_str());
1654}
1655
1656String String::GetFirstNumber(const char* str, U32& startPos, U32& endPos)
1657{
1658   // Check for trivial strings
1659   if (!str || !str[0])
1660      return String::EmptyString;
1661
1662   // Find the number at the end of the string
1663   String base(str);
1664   const char* p = base.c_str();
1665   const char* end = base.c_str() + base.length() - 1;
1666   bool dec = false;
1667   startPos = 0;
1668
1669   //Check if we are just a digit
1670   if(p == end && isdigit(*p))
1671      return base;
1672
1673   //Look for the first digit
1674   while ((p != end) && (dIsspace(*p) || !isdigit(*p)))
1675   {
1676      p++;
1677      startPos++;
1678   }
1679
1680   //Handle if we are at the end and found nothing
1681   if(p == end && !isdigit(*p))
1682      return "";
1683
1684   //update our end position at least to the start of our number
1685   endPos = startPos;
1686
1687   //Backup our ptr
1688   const char* backup = p;
1689
1690   //Check for any negative or decimal values
1691   if(startPos > 0)
1692   {
1693      p--;
1694      startPos--;
1695      if(*p == '.')
1696      {
1697         dec = true;
1698
1699         //ignore any duplicate periods
1700         while ((p != base.c_str()) && (*p == '.'))
1701         {
1702            p--;
1703            startPos--;
1704         }
1705
1706         //Found a decimal lets still check for negative sign
1707         if(startPos > 0)
1708         {
1709            p--;
1710            startPos--;
1711            if((*p != '-') && (*p != '_'))
1712            {
1713               startPos++;
1714               p++;
1715            }
1716         }
1717      }
1718      else if((*p != '-') && (*p != '_'))
1719      {
1720         //go back to where we where cause no decimal or negative sign found
1721         startPos++;
1722         p++;
1723      }
1724   }
1725
1726   //Restore where we were
1727   p = backup;
1728
1729   //look for the end of the digits
1730   bool justFoundDec = false;
1731   while (p != end)
1732   {
1733      if(*p == '.')
1734      {
1735         if(dec && !justFoundDec)
1736            break;
1737         else
1738         {
1739            dec = true;
1740            justFoundDec = true;
1741         }
1742      }
1743      else if(!isdigit(*p))
1744         break;
1745      else if(justFoundDec)
1746         justFoundDec = false;
1747
1748      p++;
1749      endPos++;
1750   }
1751
1752   U32 len = (!isdigit(*p)) ? endPos - startPos : (endPos + 1) - startPos;
1753   return base.substr(startPos, len);
1754}
1755