str.cpp
Engine/source/core/util/str.cpp
Classes:
A delete policy for the AutoPtr class.
Struct with String::StringData's field so we can initialize this without a constructor.
Type for the intern string table.
Namespaces:
Public Variables
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, StringChar c)
operator+(StringChar c, const String & a)
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)
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)
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* ) ∅ 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