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