yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
812e47898
master
1#include "slang-string-escape-util.h" 2 3#include "slang-char-util.h" 4#include "slang-com-helper.h" 5#include "slang-memory-arena.h" 6#include "slang-text-io.h" 7 8namespace Slang 9{ 10 11// !!!!!!!!!!!!!!!!!!!!!!!!!! SpaceStringEscapeHandler !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 12 13class SpaceStringEscapeHandler :public StringEscapeHandler 14{ 15public : 16typedef StringEscapeHandler Super ; 17 18virtual bool isQuotingNeeded (const UnownedStringSlice & slice )SLANG_OVERRIDE 19 { 20return isEscapingNeeded (slice ); 21 } 22 23virtual bool isEscapingNeeded (const UnownedStringSlice & slice )SLANG_OVERRIDE ; 24virtual bool isUnescapingNeeeded (const UnownedStringSlice & slice )SLANG_OVERRIDE ; 25 26virtual SlangResult appendEscaped (const UnownedStringSlice & slice ,StringBuilder & out ) 27SLANG_OVERRIDE ; 28virtual SlangResult appendUnescaped (const UnownedStringSlice & slice ,StringBuilder & out ) 29SLANG_OVERRIDE ; 30virtual SlangResult lexQuoted (const char * cursor ,const char ** outCursor )SLANG_OVERRIDE ; 31 32SpaceStringEscapeHandler () 33 :Super ('"' ) 34 { 35 } 36}; 37 38bool SpaceStringEscapeHandler ::isEscapingNeeded (const UnownedStringSlice & slice ) 39{ 40return slice .indexOf (' ' ) >=0 ; 41} 42 43bool SpaceStringEscapeHandler ::isUnescapingNeeeded (const UnownedStringSlice & slice ) 44{ 45SLANG_UNUSED (slice ); 46// As it stands we never have to unescape 47return false; 48} 49 50SlangResult SpaceStringEscapeHandler ::appendUnescaped ( 51const UnownedStringSlice & slice , 52StringBuilder & out ) 53{ 54if (slice .indexOf ('"' ) >=0 ) 55 { 56return SLANG_FAIL ; 57 } 58 59out .append (slice ); 60return SLANG_OK ; 61} 62 63SlangResult SpaceStringEscapeHandler ::appendEscaped ( 64const UnownedStringSlice & slice , 65StringBuilder & out ) 66{ 67if (slice .indexOf ('"' ) >=0 ) 68 { 69return SLANG_FAIL ; 70 } 71out .append (slice ); 72return SLANG_OK ; 73} 74 75/* static */ SlangResult SpaceStringEscapeHandler ::lexQuoted ( 76const char * cursor , 77const char ** outCursor ) 78{ 79* outCursor = cursor ; 80 81if (* cursor != m_quoteChar ) 82 { 83return SLANG_FAIL ; 84 } 85cursor ++ ; 86 87for (;;) 88 { 89const char c = * cursor ; 90if (c == m_quoteChar ) 91 { 92* outCursor = cursor + 1 ; 93return SLANG_OK ; 94 } 95switch (c ) 96 { 97case 0 : 98case '\n' : 99case '\r' : 100 { 101// Didn't hit closing quote! 102return SLANG_FAIL ; 103 } 104default : 105 { 106++ cursor ; 107break ; 108 } 109 } 110 } 111} 112 113 114// !!!!!!!!!!!!!!!!!!!!!!!!!! CppStringEscapeHandler !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 115 116class CppStringEscapeHandler :public StringEscapeHandler 117{ 118public : 119typedef StringEscapeHandler Super ; 120 121virtual bool isQuotingNeeded (const UnownedStringSlice & slice )SLANG_OVERRIDE 122 { 123SLANG_UNUSED (slice ); 124return true; 125 } 126virtual bool isEscapingNeeded (const UnownedStringSlice & slice )SLANG_OVERRIDE ; 127virtual bool isUnescapingNeeeded (const UnownedStringSlice & slice )SLANG_OVERRIDE ; 128virtual SlangResult appendEscaped (const UnownedStringSlice & slice ,StringBuilder & out ) 129SLANG_OVERRIDE ; 130virtual SlangResult appendUnescaped (const UnownedStringSlice & slice ,StringBuilder & out ) 131SLANG_OVERRIDE ; 132virtual SlangResult lexQuoted (const char * cursor ,const char ** outCursor )SLANG_OVERRIDE ; 133 134CppStringEscapeHandler () 135 :Super ('"' ) 136 { 137 } 138}; 139 140static char _getCppEscapedChar (char c ) 141{ 142switch (c ) 143 { 144case '\b' : 145return 'b' ; 146case '\f' : 147return 'f' ; 148case '\n' : 149return 'n' ; 150case '\r' : 151return 'r' ; 152case '\a' : 153return 'a' ; 154case '\t' : 155return 't' ; 156case '\v' : 157return 'v' ; 158case '\'' : 159return '\'' ; 160case '\"' : 161return '"' ; 162case '\\' : 163return '\\' ; 164default : 165return 0 ; 166 } 167} 168 169static char _getCppUnescapedChar (char c ) 170{ 171switch (c ) 172 { 173case 'b' : 174return '\b' ; 175case 'f' : 176return '\f' ; 177case 'n' : 178return '\n' ; 179case 'r' : 180return '\r' ; 181case 'a' : 182return '\a' ; 183case 't' : 184return '\t' ; 185case 'v' : 186return '\v' ; 187case '\'' : 188return '\'' ; 189case '\"' : 190return '"' ; 191case '\\' : 192return '\\' ; 193default : 194return 0 ; 195 } 196} 197 198bool CppStringEscapeHandler ::isUnescapingNeeeded (const UnownedStringSlice & slice ) 199{ 200return slice .indexOf ('\\' ) >=0 ; 201} 202 203/* static */ bool CppStringEscapeHandler ::isEscapingNeeded (const UnownedStringSlice & slice ) 204{ 205const char * cur = slice .begin (); 206const char * const end = slice .end (); 207 208for (;cur < end ;++ cur ) 209 { 210const char c = * cur ; 211 212switch (c ) 213 { 214case '\'' : 215case '\"' : 216case '\\' : 217 { 218// Strictly speaking ' shouldn't need a quote if in a C style string. 219return true; 220 } 221default : 222 { 223if (c < ' ' || c >=0x7e ) 224 { 225return true; 226 } 227break ; 228 } 229 } 230 } 231return false; 232} 233 234SlangResult CppStringEscapeHandler ::appendEscaped ( 235const UnownedStringSlice & slice , 236StringBuilder & out ) 237{ 238const char * start = slice .begin (); 239const char * cur = start ; 240const char * const end = slice .end (); 241 242// TODO(JS): A cleverer implementation might support U and u prefixing for unicode characters. 243// For now we just stick with hex if it's not 'regular' ascii. 244 245for (;cur < end ;++ cur ) 246 { 247const char c = * cur ; 248const char escapedChar = _getCppEscapedChar (c ); 249 250if (escapedChar ) 251 { 252// Flush 253if (start < cur ) 254 { 255out .append (start ,cur ); 256 } 257 258out .appendChar ('\\' ); 259out .appendChar (escapedChar ); 260 261start = cur + 1 ; 262 } 263else if (c < ' ' || c > 126 ) 264 { 265// Flush 266if (start < cur ) 267 { 268out .append (start ,cur ); 269 } 270 271// NOTE! There is a possible flaw around checking 'next' character (used for outputting 272// oct and hex) If a string is constructed appended in parts, the next character is not 273// available so the problem below can still occur. 274 275// Another solution to this problem would be to output "", but that makes some other 276// assumptions For example Slang doesn't support that style. 277 278// C++ greedily consumes hex/octal digits. This is a problem if we have bytes 279// 0, '1' as by default this will output as 280// "\x001" which is the single character byte 1. 281 282// Note this claims \x is followed with up to 3 hex digits 283// https://msdn.microsoft.com/en-us/library/69ze775t.aspx 284// But the following claims otherwise 285// https://en.cppreference.com/w/cpp/language/string_literal 286 287// On testing in Visual Studio hex can indeed be more than 3 digits 288 289// There is a problem outputting values in hex, because C++ allows *any* amount of hex 290// digits. We could work around with \u \U but they are later extensions (C++11) and 291// have other issue 292 293// The solution taken here is to always output as octal, because octal can be at most 3 294// digits. 295 296// Special case handling of 0 297if (c == 0 && !(cur + 1 < end && CharUtil ::isOctalDigit (cur [1 ]))) 298 { 299// We can just output as (octal) "\0" 300out .append ("\\0" ); 301 } 302else 303 { 304// A slightly more sophisticated implementation could output less digits if needed, 305// if not followed by an octal digit, but for now we go simple and output all 3 306// digits 307 308const uint32_t v = uint32_t (c ); 309 310char buf [4 ]; 311buf [0 ]= '\\' ; 312buf [1 ]= ((v >>6 )& 3 )+ '0' ; 313buf [2 ]= ((v >>3 )& 7 )+ '0' ; 314buf [3 ]= ((v >>0 )& 7 )+ '0' ; 315 316out .append (buf ,buf + 4 ); 317 } 318 319start = cur + 1 ; 320 } 321 } 322 323// Flush anything remaining 324if (start < end ) 325 { 326out .append (start ,end ); 327 } 328return SLANG_OK ; 329} 330 331SlangResult CppStringEscapeHandler ::appendUnescaped ( 332const UnownedStringSlice & slice , 333StringBuilder & out ) 334{ 335const char * start = slice .begin (); 336const char * cur = start ; 337const char * const end = slice .end (); 338 339while (cur < end ) 340 { 341const char c = * cur ; 342 343if (c == '\\' ) 344 { 345// Flush 346if (start < cur ) 347 { 348out .append (start ,cur ); 349 } 350 351/// Next 352cur ++ ; 353 354if (cur >=end ) 355 { 356// Missing character following '\' 357return SLANG_FAIL ; 358 } 359 360const char nextC = * cur ++ ; 361 362// Need to handle various escape sequence cases 363switch (nextC ) 364 { 365case '\'' : 366case '\"' : 367case '\\' : 368case '?' : 369case 'a' : 370case 'b' : 371case 'f' : 372case 'n' : 373case 'r' : 374case 't' : 375case 'v' : 376 { 377const char unescapedChar = _getCppUnescapedChar (nextC ); 378if (unescapedChar == 0 ) 379 { 380// Don't know how to unescape that char 381return SLANG_FAIL ; 382 } 383out .appendChar (unescapedChar ); 384 385start = cur ; 386break ; 387 } 388case '0' : 389case '1' : 390case '2' : 391case '3' : 392case '4' : 393case '5' : 394case '6' : 395case '7' : 396 { 397// Rewind back a character, as first digit is the 'nextC' 398-- cur ; 399 400// Don't need to check for enough characters, because there must be 1 - the 401// nextC 402 403// octal escape: up to 3 characters 404int value = 0 ; 405 406const char * octEnd = cur + 3 ; 407octEnd = (octEnd > end ) ?end :octEnd ; 408 409for (;cur < octEnd ;++ cur ) 410 { 411const int digitValue = CharUtil ::getOctalDigitValue (* cur ); 412if (digitValue < 0 ) 413 { 414break ; 415 } 416value = (value <<3 ) |digitValue ; 417 } 418out .appendChar (char (value )); 419 420// Reset start 421start = cur ; 422break ; 423 } 424case 'x' : 425 { 426/// In the C++ standard we consume hex digits until we hit a non hex digit 427uint32_t value = 0 ; 428for (;cur < end && CharUtil ::isHexDigit (* cur );++ cur ) 429 { 430const int digitValue = CharUtil ::getHexDigitValue (* cur ); 431if (digitValue < 0 ) 432 { 433return SLANG_FAIL ; 434 } 435 436value = (value <<4 ) |digitValue ; 437 } 438 439// If it's ascii, just output it 440if (value < 0x80 ) 441 { 442out .appendChar (char (value )); 443 } 444else 445 { 446// It's arguable what is appropriate. We only decode/encode 4, which the 447// current spec has, but 6 are possible, so lets go large. 448const Index maxUtf8EncodeCount = 6 ; 449 450char * chars = out .prepareForAppend (maxUtf8EncodeCount ); 451int numChars = encodeUnicodePointToUTF8 (Char32 (value ),chars ); 452out .appendInPlace (chars ,numChars ); 453 } 454 455// Reset start 456start = cur ; 457break ; 458 } 459case 'u' : 460case 'U' : 461 { 462// u implies 4 hex digits 463// U implies 6. 464 465// Work out how many digits we need 466const Count digitCount = (nextC == 'u' ) ?4 :6 ; 467 468// Do we have enough? 469if (end - cur < digitCount ) 470 { 471return SLANG_FAIL ; 472 } 473 474uint32_t value = 0 ; 475for (Index i = 0 ;i < digitCount ;++ i ) 476 { 477const int digitValue = CharUtil ::getHexDigitValue (cur [i ]); 478if (digitValue < 0 ) 479 { 480return SLANG_FAIL ; 481 } 482value = (value <<4 ) |digitValue ; 483 } 484cur += digitCount ; 485 486// Encode to Utf8 487// If it's ascii, just output it 488if (value < 0x80 ) 489 { 490out .appendChar (char (value )); 491 } 492else 493 { 494// It's arguable what is appropriate. We only decode/encode 4, which the 495// current spec has, but 6 are possible, so lets go large. 496const Index maxUtf8EncodeCount = 6 ; 497 498char * chars = out .prepareForAppend (maxUtf8EncodeCount ); 499int numChars = encodeUnicodePointToUTF8 (Char32 (value ),chars ); 500out .appendInPlace (chars ,numChars ); 501 } 502 503// Reset start 504start = cur ; 505break ; 506 } 507default : 508 { 509return SLANG_FAIL ; 510 } 511 } 512 } 513else 514 { 515// Next char 516++ cur ; 517 } 518 } 519 520if (start < end ) 521 { 522out .append (start ,end ); 523 } 524 525return SLANG_OK ; 526} 527 528SlangResult CppStringEscapeHandler ::lexQuoted (const char * cursor ,const char ** outCursor ) 529{ 530* outCursor = cursor ; 531 532if (* cursor != m_quoteChar ) 533 { 534return SLANG_FAIL ; 535 } 536cursor ++ ; 537 538for (;;) 539 { 540const char c = * cursor ; 541if (c == m_quoteChar ) 542 { 543* outCursor = cursor + 1 ; 544return SLANG_OK ; 545 } 546switch (c ) 547 { 548case 0 : 549case '\n' : 550case '\r' : 551 { 552// Didn't hit closing quote! 553return SLANG_FAIL ; 554 } 555case '\\' : 556 { 557++ cursor ; 558// Need to handle various escape sequence cases 559switch (* cursor ) 560 { 561case '\'' : 562case '\"' : 563case '\\' : 564case '?' : 565case 'a' : 566case 'b' : 567case 'f' : 568case 'n' : 569case 'r' : 570case 't' : 571case 'v' : 572 { 573++ cursor ; 574break ; 575 } 576case '0' : 577case '1' : 578case '2' : 579case '3' : 580case '4' : 581case '5' : 582case '6' : 583case '7' : 584 { 585// octal escape: up to 3 characters 586++ cursor ; 587for (int ii = 0 ;ii < 3 ;++ ii ) 588 { 589const char d = * cursor ; 590if (('0' <=d )&& (d <='7' )) 591 { 592++ cursor ; 593continue ; 594 } 595else 596 { 597break ; 598 } 599 } 600break ; 601 } 602case 'x' : 603 { 604// hexadecimal escape: any number of characters 605++ cursor ; 606for (;CharUtil ::isHexDigit (* cursor );++ cursor ) 607 ; 608 609// TODO: Unicode escape sequences 610break ; 611 } 612 } 613break ; 614 } 615default : 616 { 617++ cursor ; 618break ; 619 } 620 } 621 } 622} 623 624// !!!!!!!!!!!!!!!!!!!!!!!!!! JSONStringEscapeHandler !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 625 626class JSONStringEscapeHandler :public StringEscapeHandler 627{ 628public : 629typedef StringEscapeHandler Super ; 630 631virtual bool isQuotingNeeded (const UnownedStringSlice & slice )SLANG_OVERRIDE 632 { 633SLANG_UNUSED (slice ); 634return true; 635 } 636virtual bool isEscapingNeeded (const UnownedStringSlice & slice )SLANG_OVERRIDE ; 637virtual bool isUnescapingNeeeded (const UnownedStringSlice & slice )SLANG_OVERRIDE ; 638virtual SlangResult appendEscaped (const UnownedStringSlice & slice ,StringBuilder & out ) 639SLANG_OVERRIDE ; 640virtual SlangResult appendUnescaped (const UnownedStringSlice & slice ,StringBuilder & out ) 641SLANG_OVERRIDE ; 642virtual SlangResult lexQuoted (const char * cursor ,const char ** outCursor )SLANG_OVERRIDE ; 643 644JSONStringEscapeHandler () 645 :Super ('"' ) 646 { 647 } 648}; 649 650bool JSONStringEscapeHandler ::isUnescapingNeeeded (const UnownedStringSlice & slice ) 651{ 652return slice .indexOf ('\\' ) >=0 ; 653} 654 655bool JSONStringEscapeHandler ::isEscapingNeeded (const UnownedStringSlice & slice ) 656{ 657const char * cur = slice .begin (); 658const char * const end = slice .end (); 659 660for (;cur < end ;++ cur ) 661 { 662const char c = * cur ; 663 664switch (c ) 665 { 666case '\"' : 667case '\\' : 668case '/' : 669 { 670return true; 671 } 672default : 673 { 674if (c < ' ' || c >=0x7e ) 675 { 676return true; 677 } 678break ; 679 } 680 } 681 } 682return false; 683} 684 685SlangResult JSONStringEscapeHandler ::lexQuoted (const char * cursor ,const char ** outCursor ) 686{ 687// We've skipped the first " 688while (true) 689 { 690const char c = * cursor ++ ; 691 692switch (c ) 693 { 694case 0 : 695return SLANG_FAIL ; 696case '"' : 697 { 698* outCursor = cursor ; 699return SLANG_OK ; 700 } 701case '\\' : 702 { 703const char nextC = * cursor ; 704switch (nextC ) 705 { 706case '"' : 707case '\\' : 708case '/' : 709case 'b' : 710case 'f' : 711case 'n' : 712case 'r' : 713case 't' : 714 { 715++ cursor ; 716break ; 717 } 718case 'u' : 719 { 720cursor ++ ; 721for (Index i = 0 ;i < 4 ;++ i ) 722 { 723if (!CharUtil ::isHexDigit (cursor [i ])) 724 { 725return SLANG_FAIL ; 726 } 727 } 728cursor += 4 ; 729break ; 730 } 731 } 732 } 733// Somewhat surprisingly it appears it's valid to have \r\n inside of quotes. 734default : 735break ; 736 } 737 } 738} 739 740static char _getJSONEscapedChar (char c ) 741{ 742switch (c ) 743 { 744case '\b' : 745return 'b' ; 746case '\f' : 747return 'f' ; 748case '\n' : 749return 'n' ; 750case '\r' : 751return 'r' ; 752case '\t' : 753return 't' ; 754case '\\' : 755return '\\' ; 756case '/' : 757return '/' ; 758case '"' : 759return '"' ; 760default : 761return 0 ; 762 } 763} 764 765static char _getJSONUnescapedChar (char c ) 766{ 767switch (c ) 768 { 769case 'b' : 770return '\b' ; 771case 'f' : 772return '\f' ; 773case 'n' : 774return '\n' ; 775case 'r' : 776return '\r' ; 777case 't' : 778return '\t' ; 779case '\\' : 780return '\\' ; 781case '/' : 782return '/' ; 783case '"' : 784return '"' ; 785default : 786return 0 ; 787 } 788} 789 790static const char s_hex []= "0123456789abcdef" ; 791 792// Outputs ioSlice with the chars remaining after utf8 encoded value 793// Returns ~uint32_t(0) if can't decode 794static uint32_t _getUnicodePointFromUTF8 (UnownedStringSlice & ioSlice ) 795{ 796const Index length = ioSlice .getLength (); 797SLANG_ASSERT (length > 0 ); 798const char * cur = ioSlice .begin (); 799 800uint32_t codePoint = 0 ; 801unsigned int leading = cur [0 ]; 802unsigned int mask = 0x80 ; 803 804Index count = 0 ; 805while (leading & mask ) 806 { 807count ++ ; 808mask >>=1 ; 809 } 810 811if (count > length ) 812 { 813SLANG_ASSERT (!"Can't decode" ); 814ioSlice = UnownedStringSlice (ioSlice .end (),ioSlice .end ()); 815return ~uint32_t (0 ); 816 } 817 818codePoint = (leading & (mask - 1 )); 819for (Index i = 1 ;i <=count - 1 ;i ++ ) 820 { 821codePoint <<=6 ; 822codePoint += (cur [i ]& 0x3F ); 823 } 824 825ioSlice = UnownedStringSlice (cur + count ,ioSlice .end ()); 826return codePoint ; 827} 828 829static void _appendHex16 (uint32_t value ,StringBuilder & out ) 830{ 831// Let's go with hex 832char buf []= "\\u0000" ; 833 834buf [2 ]= s_hex [(value >>12 )& 0xf ]; 835buf [3 ]= s_hex [(value >>8 )& 0xf ]; 836buf [4 ]= s_hex [(value >>4 )& 0xf ]; 837buf [5 ]= s_hex [(value >>0 )& 0xf ]; 838 839out .append (UnownedStringSlice (buf ,6 )); 840} 841 842SlangResult JSONStringEscapeHandler ::appendEscaped ( 843const UnownedStringSlice & slice , 844StringBuilder & out ) 845{ 846const char * start = slice .begin (); 847const char * cur = start ; 848const char * const end = slice .end (); 849 850for (;cur < end ;++ cur ) 851 { 852const char c = * cur ; 853 854const char escapedChar = _getJSONEscapedChar (c ); 855 856if (escapedChar ) 857 { 858// Flush 859if (start < cur ) 860 { 861out .append (start ,cur ); 862 } 863out .appendChar ('\\' ); 864out .appendChar (escapedChar ); 865 866start = cur + 1 ; 867 } 868else if (uint8_t (c )& 0x80 ) 869 { 870// Flush 871if (start < cur ) 872 { 873out .append (start ,cur ); 874 } 875 876// UTF8 877UnownedStringSlice remainingSlice (cur ,end ); 878uint32_t codePoint = _getUnicodePointFromUTF8 (remainingSlice ); 879 880// We only support up to 16 bit unicode values for now... 881SLANG_ASSERT (codePoint < 0x10000 ); 882 883_appendHex16 (codePoint ,out ); 884 885cur = remainingSlice .begin ()- 1 ; 886start = cur + 1 ; 887 } 888else if (uint8_t (c )< ' ' || (c >=0x7e )) 889 { 890if (start < cur ) 891 { 892out .append (start ,cur ); 893 } 894 895_appendHex16 (uint32_t (c ),out ); 896 897start = cur + 1 ; 898 } 899else 900 { 901// Can go out as it is 902 } 903 } 904 905// Flush at the end 906if (start < end ) 907 { 908out .append (start ,end ); 909 } 910return SLANG_OK ; 911} 912 913SlangResult JSONStringEscapeHandler ::appendUnescaped ( 914const UnownedStringSlice & slice , 915StringBuilder & out ) 916{ 917const char * start = slice .begin (); 918const char * cur = start ; 919const char * const end = slice .end (); 920 921for (;cur < end ;++ cur ) 922 { 923const char c = * cur ; 924 925if (c == '\\' ) 926 { 927// Flush 928if (start < cur ) 929 { 930out .append (start ,cur ); 931 } 932 933/// Next 934cur ++ ; 935 936if (cur >=end ) 937 { 938return SLANG_FAIL ; 939 } 940 941// Need to handle various escape sequence cases 942switch (* cur ) 943 { 944case '\"' : 945case '\\' : 946case '/' : 947case 'b' : 948case 'f' : 949case 'n' : 950case 'r' : 951case 't' : 952 { 953const char unescapedChar = _getJSONUnescapedChar (* cur ); 954if (unescapedChar == 0 ) 955 { 956// Don't know how to unescape that char 957return SLANG_FAIL ; 958 } 959out .appendChar (unescapedChar ); 960 961start = cur + 1 ; 962break ; 963 } 964case 'u' : 965 { 966uint32_t value = 0 ; 967cur ++ ; 968 969if (cur + 4 > end ) 970 { 971return SLANG_FAIL ; 972 } 973 974for (Index i = 0 ;i < 4 ;++ i ) 975 { 976const char digitC = cur [i ]; 977 978uint32_t digitValue ; 979if (digitC >='0' && digitC <='9' ) 980 { 981digitValue = digitC - '0' ; 982 } 983else if (digitC >='a' && digitC <='f' ) 984 { 985digitValue = digitC - 'a' + 10 ; 986 } 987else if (digitC >='A' && digitC <='F' ) 988 { 989digitValue = digitC - 'A' + 10 ; 990 } 991else 992 { 993return SLANG_FAIL ; 994 } 995SLANG_ASSERT (digitValue < 0x10 ); 996value = (value <<4 ) |digitValue ; 997 } 998cur += 4 ; 999 1000// NOTE! Strictly speaking we may want to combine 2 UTF16 surrogates to make a 1001// single UTF8 encoded char. 1002 1003// Need to encode in UTF8 to concat 1004 1005char buf [8 ]; 1006int len = encodeUnicodePointToUTF8 (Char32 (value ),buf ); 1007 1008out .append (buf ,buf + len ); 1009 1010start = cur ; 1011cur -- ; 1012break ; 1013 } 1014default : 1015 { 1016// Can't decode 1017return SLANG_FAIL ; 1018 } 1019 } 1020 } 1021 } 1022 1023// Flush 1024if (start < end ) 1025 { 1026out .append (start ,end ); 1027 } 1028 1029return SLANG_OK ; 1030} 1031 1032// !!!!!!!!!!!!!!!!!!!!!!!!!! StringEscapeUtil !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 1033 1034static CppStringEscapeHandler g_cppHandler ; 1035static SpaceStringEscapeHandler g_spaceHandler ; 1036static JSONStringEscapeHandler g_jsonHandler ; 1037 1038StringEscapeUtil ::Handler * StringEscapeUtil ::getHandler (Style style ) 1039{ 1040switch (style ) 1041 { 1042case Style ::Cpp : 1043return & g_cppHandler ; 1044case Style ::Space : 1045return & g_spaceHandler ; 1046case Style ::JSON : 1047return & g_jsonHandler ; 1048// TODO(JS): For now we make Slang language string encoding/decoding the same as C++ 1049// That may not be desirable because C++ has a variety of surprising edge cases (for example 1050// around \x) 1051case Style ::Slang : 1052return & g_cppHandler ; 1053default : 1054return nullptr ; 1055 } 1056} 1057 1058/* static */ SlangResult StringEscapeUtil ::appendQuoted ( 1059Handler * handler , 1060const UnownedStringSlice & slice , 1061StringBuilder & out ) 1062{ 1063const char quoteChar = handler -> getQuoteChar (); 1064out .appendChar (quoteChar ); 1065SlangResult res = handler -> appendEscaped (slice ,out ); 1066out .appendChar (quoteChar ); 1067return res ; 1068} 1069 1070/* static */ SlangResult StringEscapeUtil ::appendUnquoted ( 1071Handler * handler , 1072const UnownedStringSlice & slice , 1073StringBuilder & out ) 1074{ 1075const Index len = slice .getLength (); 1076 1077const char quoteChar = handler -> getQuoteChar (); 1078SLANG_UNUSED (quoteChar ); 1079 1080// Must have quote characters around if 1081SLANG_ASSERT (len >=2 && slice [0 ]== quoteChar && slice [len - 1 ]== quoteChar ); 1082 1083return handler -> appendUnescaped (slice .subString (1 ,len - 2 ),out ); 1084} 1085 1086/* static */ SlangResult StringEscapeUtil ::appendMaybeQuoted ( 1087Handler * handler , 1088const UnownedStringSlice & slice , 1089StringBuilder & out ) 1090{ 1091if (handler -> isQuotingNeeded (slice )) 1092 { 1093return appendQuoted (handler ,slice ,out ); 1094 } 1095else 1096 { 1097out .append (slice ); 1098return SLANG_OK ; 1099 } 1100} 1101 1102/* static */ UnownedStringSlice StringEscapeUtil ::maybeUnquoteCommandLineArg ( 1103UnownedStringSlice slice ) 1104{ 1105// If the slice is quoted, unquote it, else return as is 1106if (slice .startsWith ("\'" )|| slice .startsWith ("\"" )) 1107 { 1108const Index len = slice .getLength (); 1109if (len >=2 && slice [len - 1 ]== slice [0 ]) 1110 { 1111// Unquote it 1112return UnownedStringSlice (slice .begin ()+ 1 ,len - 2 ); 1113 } 1114 } 1115return slice ; 1116} 1117 1118/* static */ bool StringEscapeUtil ::isQuoted (char quoteChar ,UnownedStringSlice & slice ) 1119{ 1120const Index len = slice .getLength (); 1121return len >=2 && slice [0 ]== quoteChar && slice [len - 1 ]== quoteChar ; 1122} 1123 1124/* static */ UnownedStringSlice StringEscapeUtil ::unquote ( 1125char quoteChar , 1126const UnownedStringSlice & slice ) 1127{ 1128const Index len = slice .getLength (); 1129if (len >=2 && slice [0 ]== quoteChar && slice [len - 1 ]== quoteChar ) 1130 { 1131return UnownedStringSlice (slice .begin ()+ 1 ,len - 2 ); 1132 } 1133SLANG_ASSERT (!"Not quoted!" ); 1134return UnownedStringSlice (); 1135} 1136 1137/* static */ SlangResult StringEscapeUtil ::appendMaybeUnquoted ( 1138Handler * handler , 1139const UnownedStringSlice & slice , 1140StringBuilder & out ) 1141{ 1142const char quoteChar = handler -> getQuoteChar (); 1143 1144const Index len = slice .getLength (); 1145 1146if (len >=2 && slice [0 ]== quoteChar && slice [len - 1 ]== quoteChar ) 1147 { 1148return appendUnquoted (handler ,slice ,out ); 1149 } 1150else 1151 { 1152out .append (slice ); 1153return SLANG_OK ; 1154 } 1155} 1156 1157/* static */ SlangResult StringEscapeUtil ::isUnescapeShellLikeNeeded ( 1158Handler * handler , 1159const UnownedStringSlice & slice ) 1160{ 1161return slice .indexOf (handler -> getQuoteChar ()) >=0 ; 1162} 1163 1164/* static */ SlangResult StringEscapeUtil ::unescapeShellLike ( 1165Handler * handler , 1166const UnownedStringSlice & slice , 1167StringBuilder & out ) 1168{ 1169StringBuilder buf ; 1170const char quoteChar = handler -> getQuoteChar (); 1171 1172UnownedStringSlice remaining (slice ); 1173 1174while (remaining .getLength ()) 1175 { 1176const Index index = remaining .indexOf (quoteChar ); 1177 1178if (index < 0 ) 1179 { 1180out .append (remaining ); 1181return SLANG_OK ; 1182 } 1183 1184// Append the bit before 1185out .append (remaining .head (index )); 1186 1187// Okay we need to lex to the end 1188 1189const char * quotedEnd = nullptr ; 1190SLANG_RETURN_ON_FAIL (handler -> lexQuoted (remaining .begin ()+ index ,& quotedEnd )); 1191 1192// Unescape it 1193SLANG_RETURN_ON_FAIL ( 1194appendUnquoted (handler ,UnownedStringSlice (remaining .begin ()+ index ,quotedEnd ),out )); 1195 1196// Fix up remaining 1197remaining = UnownedStringSlice (quotedEnd ,remaining .end ()); 1198 } 1199 1200return SLANG_OK ; 1201} 1202 1203String StringEscapeUtil ::escapeString (UnownedStringSlice input ,StringEscapeUtil ::Style style ) 1204{ 1205StringBuilder sb ; 1206auto handler = StringEscapeUtil ::getHandler (style ); 1207StringEscapeUtil ::appendQuoted (handler ,input ,sb ); 1208return sb .produceString (); 1209} 1210 1211String StringEscapeUtil ::unescapeString (UnownedStringSlice input ,StringEscapeUtil ::Style style ) 1212{ 1213StringBuilder sb ; 1214auto handler = StringEscapeUtil ::getHandler (style ); 1215StringEscapeUtil ::appendUnquoted (handler ,input ,sb ); 1216return sb .produceString (); 1217} 1218}// namespace Slang