yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
3ed776159
master
1#include "slang-string.h" 2 3#include "slang-char-util.h" 4#include "slang-text-io.h" 5 6namespace Slang 7{ 8// HACK! 9// JS: Many of the inlined functions of CharUtil just access a global map. That referencing this 10// global is *NOT* enough to link correctly with CharUtil on linux for a shared library. The 11// following call exists to try and force linkage of CharUtil for anything that uses core 12static const auto s_charUtilLink = CharUtil ::_ensureLink (); 13 14 15// StringRepresentation 16 17void StringRepresentation ::setContents (const UnownedStringSlice & slice ) 18{ 19const auto sliceLength = slice .getLength (); 20SLANG_ASSERT (sliceLength <=capacity ); 21 22char * chars = getData (); 23 24// Use move (rather than memcpy), because the slice *could* be contained in the 25// StringRepresentation 26 ::memmove (chars ,slice .begin (),sliceLength * sizeof (char )); 27// Zero terminate. 28chars [sliceLength ]= 0 ; 29// Set the length 30length = sliceLength ; 31} 32 33 34/* static */ StringRepresentation * StringRepresentation ::create (const UnownedStringSlice & slice ) 35{ 36const auto sliceLength = slice .getLength (); 37 38if (sliceLength ) 39 { 40StringRepresentation * rep = StringRepresentation ::createWithLength (sliceLength ); 41 42char * chars = rep -> getData (); 43 ::memcpy (chars ,slice .begin (),sizeof (char )* sliceLength ); 44chars [sliceLength ]= 0 ; 45 46return rep ; 47 } 48else 49 { 50return nullptr ; 51 } 52} 53 54/* static */ StringRepresentation * StringRepresentation ::createWithReference ( 55const UnownedStringSlice & slice ) 56{ 57const auto sliceLength = slice .getLength (); 58 59if (sliceLength ) 60 { 61StringRepresentation * rep = StringRepresentation ::createWithLength (sliceLength ); 62rep -> addReference (); 63 64char * chars = rep -> getData (); 65 ::memcpy (chars ,slice .begin (),sizeof (char )* sliceLength ); 66chars [sliceLength ]= 0 ; 67 68return rep ; 69 } 70else 71 { 72return nullptr ; 73 } 74} 75 76// OSString 77 78OSString ::OSString () 79 :m_begin (nullptr ),m_end (nullptr ) 80{ 81} 82 83OSString ::OSString (wchar_t * begin ,wchar_t * end ) 84 :m_begin (begin ),m_end (end ) 85{ 86} 87 88void OSString ::_releaseBuffer () 89{ 90if (m_begin ) 91 { 92delete []m_begin ; 93 } 94} 95 96void OSString ::set (const wchar_t * begin ,const wchar_t * end ) 97{ 98if (m_begin ) 99 { 100delete []m_begin ; 101m_begin = nullptr ; 102m_end = nullptr ; 103 } 104const size_t len = end - begin ; 105if (len > 0 ) 106 { 107// TODO(JS): The allocation is only done this way to be compatible with the buffer being 108// detached from an array This is unfortunate, because it means that the allocation stores 109// the size (and alignment fix), which is a shame because we know the size 110m_begin = new wchar_t [len + 1 ]; 111memcpy (m_begin ,begin ,len * sizeof (wchar_t )); 112// Zero terminate 113m_begin [len ]= 0 ; 114m_end = m_begin + len ; 115 } 116} 117 118static const wchar_t kEmptyOSString []= {0 }; 119 120wchar_t const * OSString ::begin ()const 121{ 122return m_begin ?m_begin :kEmptyOSString ; 123} 124 125wchar_t const * OSString ::end ()const 126{ 127return m_end ?m_end :kEmptyOSString ; 128} 129 130// UnownedStringSlice 131 132bool UnownedStringSlice ::startsWith (UnownedStringSlice const & other )const 133{ 134UInt thisSize = getLength (); 135UInt otherSize = other .getLength (); 136 137if (otherSize > thisSize ) 138return false; 139 140return head (otherSize )== other ; 141} 142 143bool UnownedStringSlice ::startsWith (char const * str )const 144{ 145return startsWith (UnownedTerminatedStringSlice (str )); 146} 147 148bool UnownedStringSlice ::startsWithCaseInsensitive (UnownedStringSlice const & other )const 149{ 150UInt thisSize = getLength (); 151UInt otherSize = other .getLength (); 152 153if (otherSize > thisSize ) 154return false; 155 156return head (otherSize ).caseInsensitiveEquals (other ); 157} 158 159 160bool UnownedStringSlice ::endsWith (UnownedStringSlice const & other )const 161{ 162UInt thisSize = getLength (); 163UInt otherSize = other .getLength (); 164 165if (otherSize > thisSize ) 166return false; 167 168return UnownedStringSlice (end ()- otherSize ,end ())== other ; 169} 170 171bool UnownedStringSlice ::endsWithCaseInsensitive (UnownedStringSlice const & other )const 172{ 173UInt thisSize = getLength (); 174UInt otherSize = other .getLength (); 175 176if (otherSize > thisSize ) 177return false; 178 179return UnownedStringSlice (end ()- otherSize ,end ()).caseInsensitiveEquals (other ); 180} 181 182bool UnownedStringSlice ::endsWith (char const * str )const 183{ 184return endsWith (UnownedTerminatedStringSlice (str )); 185} 186 187bool UnownedStringSlice ::endsWithCaseInsensitive (char const * str )const 188{ 189return endsWithCaseInsensitive (UnownedTerminatedStringSlice (str )); 190} 191 192UnownedStringSlice UnownedStringSlice ::trim ()const 193{ 194const char * start = m_begin ; 195const char * end = m_end ; 196 197while (start < end && CharUtil ::isHorizontalWhitespace (* start )) 198start ++ ; 199while (end > start && CharUtil ::isHorizontalWhitespace (end [-1 ])) 200end -- ; 201return UnownedStringSlice (start ,end ); 202} 203 204UnownedStringSlice UnownedStringSlice ::trimStart ()const 205{ 206const char * start = m_begin ; 207 208while (start < m_end && CharUtil ::isHorizontalWhitespace (* start )) 209start ++ ; 210return UnownedStringSlice (start ,m_end ); 211} 212 213UnownedStringSlice UnownedStringSlice ::trim (char c )const 214{ 215const char * start = m_begin ; 216const char * end = m_end ; 217 218while (start < end && * start == c ) 219start ++ ; 220while (end > start && end [-1 ]== c ) 221end -- ; 222return UnownedStringSlice (start ,end ); 223} 224 225// StringSlice 226 227StringSlice ::StringSlice () 228 :representation (0 ),beginIndex (0 ),endIndex (0 ) 229{ 230} 231 232StringSlice ::StringSlice (String const & str ) 233 :representation (str .m_buffer ),beginIndex (0 ),endIndex (str .getLength ()) 234{ 235} 236 237StringSlice ::StringSlice (String const & str ,UInt beginIndex ,UInt endIndex ) 238 :representation (str .m_buffer ),beginIndex (beginIndex ),endIndex (endIndex ) 239{ 240} 241 242 243// 244 245_EndLine EndLine ; 246 247String operator+ (const char * op1 ,const String & op2 ) 248{ 249String result (op1 ); 250result .append (op2 ); 251return result ; 252} 253 254String operator+ (const String & op1 ,const char * op2 ) 255{ 256String result (op1 ); 257result .append (op2 ); 258return result ; 259} 260 261String operator+ (const String & op1 ,const String & op2 ) 262{ 263String result (op1 ); 264result .append (op2 ); 265return result ; 266} 267 268int stringToInt (const String & str ,int radix ) 269{ 270if (str .startsWith ("0x" )) 271return (int )strtoll (str .getBuffer (),NULL ,16 ); 272else 273return (int )strtoll (str .getBuffer (),NULL ,radix ); 274} 275unsigned int stringToUInt (const String & str ,int radix ) 276{ 277if (str .startsWith ("0x" )) 278return (unsigned int )strtoull (str .getBuffer (),NULL ,16 ); 279else 280return (unsigned int )strtoull (str .getBuffer (),NULL ,radix ); 281} 282double stringToDouble (const String & str ) 283{ 284return (double )strtod (str .getBuffer (),NULL ); 285} 286float stringToFloat (const String & str ) 287{ 288return strtof (str .getBuffer (),NULL ); 289} 290 291#if 0 292String String ::ReplaceAll (String src ,String dst )const 293 { 294String rs = * this ; 295int index = 0 ; 296int srcLen = src .length ; 297int len = rs .length ; 298while ((index = rs .IndexOf (src ,index ))!= -1 ) 299 { 300rs = rs .SubString (0 ,index )+ dst + rs .SubString (index + srcLen ,len - index - srcLen ); 301len = rs .length ; 302 } 303return rs ; 304 } 305#endif 306 307String String ::fromWString (const wchar_t * wstr ) 308{ 309List < char > buf ; 310#ifdef _WIN32 311Slang ::CharEncoding ::UTF16 -> decode ( 312 (const Byte * )wstr , 313 (int )(wcslen (wstr )* sizeof (wchar_t )), 314buf ); 315#else 316Slang ::CharEncoding ::UTF32 -> decode ( 317 (const Byte * )wstr , 318 (int )(wcslen (wstr )* sizeof (wchar_t )), 319buf ); 320#endif 321return String (buf .begin (),buf .end ()); 322} 323 324String String ::fromWString (const wchar_t * wstr ,const wchar_t * wend ) 325{ 326List < char > buf ; 327#ifdef _WIN32 328Slang ::CharEncoding ::UTF16 -> decode ( 329 (const Byte * )wstr , 330 (int )((wend - wstr )* sizeof (wchar_t )), 331buf ); 332#else 333Slang ::CharEncoding ::UTF32 -> decode ( 334 (const Byte * )wstr , 335 (int )((wend - wstr )* sizeof (wchar_t )), 336buf ); 337#endif 338return String (buf .begin (),buf .end ()); 339} 340 341String String ::fromWChar (const wchar_t ch ) 342{ 343List < char > buf ; 344#ifdef _WIN32 345Slang ::CharEncoding ::UTF16 -> decode ((const Byte * )& ch , (int )(sizeof (wchar_t )),buf ); 346#else 347Slang ::CharEncoding ::UTF32 -> decode ((const Byte * )& ch , (int )(sizeof (wchar_t )),buf ); 348#endif 349return String (buf .begin (),buf .end ()); 350} 351 352/* static */ String String ::fromUnicodePoint (Char32 codePoint ) 353{ 354char buf [6 ]; 355int len = Slang ::encodeUnicodePointToUTF8 (codePoint ,buf ); 356return String (buf ,buf + len ); 357} 358 359OSString String ::toWString (Index * outLength )const 360{ 361if (!m_buffer ) 362 { 363return OSString (); 364 } 365else 366 { 367List < Byte > buf ; 368switch (sizeof (wchar_t )) 369 { 370case 2 : 371Slang ::CharEncoding ::UTF16 -> encode (getUnownedSlice (),buf ); 372break ; 373 374case 4 : 375Slang ::CharEncoding ::UTF32 -> encode (getUnownedSlice (),buf ); 376break ; 377 378default : 379break ; 380 } 381 382auto length = Index (buf .getCount () /sizeof (wchar_t )); 383if (outLength ) 384* outLength = length ; 385 386for (size_t ii = 0 ;ii < sizeof (wchar_t );++ ii ) 387buf .add (0 ); 388 389wchar_t * beginData = (wchar_t * )buf .getBuffer (); 390wchar_t * endData = beginData + length ; 391 392OSString ret ; 393ret .set (beginData ,endData ); 394return ret ; 395 } 396} 397 398// 399 400void String ::ensureUniqueStorageWithCapacity (Index requiredCapacity ) 401{ 402if (m_buffer && m_buffer -> isUniquelyReferenced ()&& m_buffer -> capacity >=requiredCapacity ) 403return ; 404 405Index newCapacity = m_buffer ?2 * m_buffer -> capacity :16 ; 406if (newCapacity < requiredCapacity ) 407 { 408newCapacity = requiredCapacity ; 409 } 410 411Index length = getLength (); 412StringRepresentation * newRepresentation = 413StringRepresentation ::createWithCapacityAndLength (newCapacity ,length ); 414 415if (m_buffer ) 416 { 417memcpy (newRepresentation -> getData (),m_buffer -> getData (),length + 1 ); 418 } 419 420m_buffer = newRepresentation ; 421} 422 423char * String ::prepareForAppend (Index count ) 424{ 425auto oldLength = getLength (); 426auto newLength = oldLength + count ; 427ensureUniqueStorageWithCapacity (newLength ); 428return getData ()+ oldLength ; 429} 430void String ::appendInPlace (const char * chars ,Index count ) 431{ 432SLANG_UNUSED (chars ); 433 434if (count > 0 ) 435 { 436SLANG_ASSERT (m_buffer && m_buffer -> isUniquelyReferenced ()); 437 438auto oldLength = getLength (); 439auto newLength = oldLength + count ; 440 441char * dst = m_buffer -> getData (); 442 443// Make sure the input buffer is the same one returned from prepareForAppend 444SLANG_ASSERT (chars == dst + oldLength ); 445// It has to fit within the capacity 446SLANG_ASSERT (newLength <=m_buffer -> capacity ); 447 448// We just need to modify the length 449m_buffer -> length = newLength ; 450 451// And mark with a terminating 0 452dst [newLength ]= 0 ; 453 } 454} 455 456void String ::reduceLength (Index newLength ) 457{ 458Index oldLength = getLength (); 459SLANG_ASSERT (newLength <=oldLength ); 460if (oldLength == newLength ) 461 { 462return ; 463 } 464 465// It must have a buffer, because only 0 length allows for nullptr 466// and being 0 sized is already covered 467SLANG_ASSERT (m_buffer ); 468 469if (m_buffer -> isUniquelyReferenced ()) 470 { 471m_buffer -> length = newLength ; 472m_buffer -> getData ()[newLength ]= 0 ; 473 } 474else 475 { 476// If 0 length is wanted we can just free 477if (newLength == 0 ) 478 { 479m_buffer .setNull (); 480 } 481else 482 { 483// We need to make a new copy, that we will shrink 484 485// We'll just go with capacity enough for the new length 486const Index newCapacity = newLength ; 487StringRepresentation * newRepresentation = 488StringRepresentation ::createWithCapacityAndLength (newCapacity ,newLength ); 489 490// Copy 491char * dst = newRepresentation -> getData (); 492memcpy (dst ,m_buffer -> getData (),sizeof (char )* newLength ); 493// Zero terminate 494dst [newLength ]= 0 ; 495 496// Set the new rep 497m_buffer = newRepresentation ; 498 } 499 } 500} 501 502void String ::append (char const * str ,size_t len ) 503{ 504append (str ,str + len ); 505} 506 507void String ::append (const char * textBegin ,char const * textEnd ) 508{ 509auto oldLength = getLength (); 510auto textLength = textEnd - textBegin ; 511if (textLength <=0 ) 512return ; 513 514auto newLength = oldLength + textLength ; 515 516ensureUniqueStorageWithCapacity (newLength ); 517 518memcpy (getData ()+ oldLength ,textBegin ,textLength ); 519getData ()[newLength ]= 0 ; 520m_buffer -> length = newLength ; 521} 522 523void String ::append (char const * str ) 524{ 525if (str ) 526 { 527append (str ,str + strlen (str )); 528 } 529} 530 531void String ::appendRepeatedChar (char chr ,Index count ) 532{ 533SLANG_ASSERT (count >=0 ); 534if (count > 0 ) 535 { 536char * chars = prepareForAppend (count ); 537// Set all space to repeated chr. 538 ::memset (chars ,chr ,sizeof (char )* count ); 539appendInPlace (chars ,count ); 540 } 541} 542 543void String ::appendChar (char c ) 544{ 545const auto oldLength = getLength (); 546const auto newLength = oldLength + 1 ; 547 548ensureUniqueStorageWithCapacity (newLength ); 549 550// Since there must be space for at least one character, m_buffer cannot be nullptr 551SLANG_ASSERT (m_buffer ); 552char * data = m_buffer -> getData (); 553data [oldLength ]= c ; 554data [newLength ]= 0 ; 555 556m_buffer -> length = newLength ; 557} 558 559void String ::append (char chr ) 560{ 561appendChar (chr ); 562} 563 564void String ::append (String const & str ) 565{ 566if (!m_buffer ) 567 { 568m_buffer = str .m_buffer ; 569return ; 570 } 571 572append (str .begin (),str .end ()); 573} 574 575void String ::append (StringSlice const & slice ) 576{ 577append (slice .begin (),slice .end ()); 578} 579 580void String ::append (UnownedStringSlice const & slice ) 581{ 582append (slice .begin (),slice .end ()); 583} 584 585void String ::append (int32_t value ,int radix ) 586{ 587enum 588 { 589kCount = 33 590 }; 591char * data = prepareForAppend (kCount ); 592const auto count = intToAscii (data ,value ,radix ); 593m_buffer -> length += count ; 594} 595 596void String ::append (uint32_t value ,int radix ) 597{ 598enum 599 { 600kCount = 33 601 }; 602char * data = prepareForAppend (kCount ); 603const auto count = intToAscii (data ,value ,radix ); 604m_buffer -> length += count ; 605} 606 607void String ::append (int64_t value ,int radix ) 608{ 609enum 610 { 611kCount = 65 612 }; 613char * data = prepareForAppend (kCount ); 614auto count = intToAscii (data ,value ,radix ); 615m_buffer -> length += count ; 616} 617 618void String ::append (uint64_t value ,int radix ) 619{ 620enum 621 { 622kCount = 65 623 }; 624char * data = prepareForAppend (kCount ); 625auto count = intToAscii (data ,value ,radix ); 626m_buffer -> length += count ; 627} 628 629void String ::append (float val ,const char * format ) 630{ 631enum 632 { 633kCount = 128 634 }; 635char * data = prepareForAppend (kCount ); 636sprintf_s (data ,kCount ,format ,val ); 637m_buffer -> length += strnlen_s (data ,kCount ); 638} 639 640void String ::append (double val ,const char * format ) 641{ 642enum 643 { 644kCount = 128 645 }; 646char * data = prepareForAppend (kCount ); 647sprintf_s (data ,kCount ,format ,val ); 648m_buffer -> length += strnlen_s (data ,kCount ); 649} 650 651void String ::append (StableHashCode32 value ) 652{ 653const Index digits = 8 ; 654// + null terminator 655char * data = prepareForAppend (digits + 1 ); 656auto count = intToAscii (data ,value .hash ,16 ,digits ); 657m_buffer -> length += count ; 658} 659 660void String ::append (StableHashCode64 value ) 661{ 662const Index digits = 16 ; 663// + null terminator 664char * data = prepareForAppend (digits + 1 ); 665auto count = intToAscii (data ,value .hash ,16 ,digits ); 666m_buffer -> length += count ; 667} 668 669// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! UnownedStringSlice !!!!!!!!!!!!!!!!!!!!!!!!!!!!! 670 671Index UnownedStringSlice ::indexOf (char c )const 672{ 673const Index size = Index (m_end - m_begin ); 674for (Index i = 0 ;i < size ;++ i ) 675 { 676if (m_begin [i ]== c ) 677 { 678return i ; 679 } 680 } 681return -1 ; 682} 683 684Index UnownedStringSlice ::indexOf (const UnownedStringSlice & in )const 685{ 686const Index len = getLength (); 687const Index inLen = in .getLength (); 688if (inLen > len ) 689 { 690return -1 ; 691 } 692 693const char * inChars = in .m_begin ; 694switch (inLen ) 695 { 696case 0 : 697return 0 ; 698case 1 : 699return indexOf (inChars [0 ]); 700default : 701break ; 702 } 703 704const char * chars = m_begin ; 705const char firstChar = inChars [0 ]; 706 707for (Int i = 0 ;i <=len - inLen ;++ i ) 708 { 709if (chars [i ]== firstChar && in == UnownedStringSlice (chars + i ,inLen )) 710 { 711return i ; 712 } 713 } 714 715return -1 ; 716} 717 718UnownedStringSlice UnownedStringSlice ::subString (Index idx ,Index len )const 719{ 720const Index totalLen = getLength (); 721SLANG_ASSERT (idx >=0 && len >=0 && idx <=totalLen ); 722 723// If too large, we truncate 724len = (idx + len > totalLen ) ? (totalLen - idx ) :len ; 725 726// Return the substring 727return UnownedStringSlice (m_begin + idx ,m_begin + idx + len ); 728} 729 730int compare (UnownedStringSlice const & lhs ,UnownedStringSlice const & rhs ) 731{ 732auto lhsSize = lhs .getLength (); 733auto rhsSize = rhs .getLength (); 734 735auto lhsData = lhs .begin (); 736auto rhsData = rhs .begin (); 737 738auto sharedPrefixSize = std::min (lhsSize ,rhsSize ); 739int sharedPrefixCmp = memcmp (lhsData ,rhsData ,sharedPrefixSize ); 740if (sharedPrefixCmp != 0 ) 741return sharedPrefixCmp ; 742 743return int (lhsSize - rhsSize ); 744} 745 746bool UnownedStringSlice ::operator== (ThisType const & other )const 747{ 748// Note that memcmp is undefined when passed in null ptrs, so if we want to handle 749// we need to cover that case. 750// Can only be nullptr if size is 0. 751auto thisSize = getLength (); 752auto otherSize = other .getLength (); 753 754if (thisSize != otherSize ) 755 { 756return false; 757 } 758 759const char * const thisChars = begin (); 760const char * const otherChars = other .begin (); 761if (thisChars == otherChars || thisSize == 0 ) 762 { 763return true; 764 } 765SLANG_ASSERT (thisChars && otherChars ); 766return memcmp (thisChars ,otherChars ,thisSize )== 0 ; 767} 768 769bool UnownedStringSlice ::caseInsensitiveEquals (const ThisType & rhs )const 770{ 771const auto length = getLength (); 772if (length != rhs .getLength ()) 773 { 774return false; 775 } 776 777const char * a = m_begin ; 778const char * b = rhs .m_begin ; 779 780// Assuming this is a faster test 781if (memcmp (a ,b ,length )!= 0 ) 782 { 783// They aren't identical so compare character by character 784for (Index i = 0 ;i < length ;++ i ) 785 { 786if (CharUtil ::toLower (a [i ])!= CharUtil ::toLower (b [i ])) 787 { 788return false; 789 } 790 } 791 } 792 793return true; 794} 795}// namespace Slang 796 797std::ostream & operator<<(std::ostream & stream ,const Slang ::String & s ) 798{ 799stream <<s .getBuffer (); 800return stream ; 801}