| 838 | // This function handles the prefix/suffix double quotes, escaping, and optional encoding validation. |
| 839 | template<unsigned parseFlags, typename SEncoding, typename TEncoding, typename InputStream, typename OutputStream> |
| 840 | RAPIDJSON_FORCEINLINE void ParseStringToStream(InputStream& is, OutputStream& os) { |
| 841 | //!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN |
| 842 | #define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 |
| 843 | static const char escape[256] = { |
| 844 | Z16, Z16, 0, 0,'\"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'/', |
| 845 | Z16, Z16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, |
| 846 | 0, 0,'\b', 0, 0, 0,'\f', 0, 0, 0, 0, 0, 0, 0,'\n', 0, |
| 847 | 0, 0,'\r', 0,'\t', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 848 | Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 |
| 849 | }; |
| 850 | #undef Z16 |
| 851 | //!@endcond |
| 852 | |
| 853 | for (;;) { |
| 854 | // Scan and copy string before "\\\"" or < 0x20. This is an optional optimzation. |
| 855 | if (!(parseFlags & kParseValidateEncodingFlag)) |
| 856 | ScanCopyUnescapedString(is, os); |
| 857 | |
| 858 | Ch c = is.Peek(); |
| 859 | if (RAPIDJSON_UNLIKELY(c == '\\')) { // Escape |
| 860 | size_t escapeOffset = is.Tell(); // For invalid escaping, report the inital '\\' as error offset |
| 861 | is.Take(); |
| 862 | Ch e = is.Peek(); |
| 863 | if ((sizeof(Ch) == 1 || unsigned(e) < 256) && RAPIDJSON_LIKELY(escape[static_cast<unsigned char>(e)])) { |
| 864 | is.Take(); |
| 865 | os.Put(static_cast<typename TEncoding::Ch>(escape[static_cast<unsigned char>(e)])); |
| 866 | } |
| 867 | else if (RAPIDJSON_LIKELY(e == 'u')) { // Unicode |
| 868 | is.Take(); |
| 869 | unsigned codepoint = ParseHex4(is, escapeOffset); |
| 870 | RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; |
| 871 | if (RAPIDJSON_UNLIKELY(codepoint >= 0xD800 && codepoint <= 0xDBFF)) { |
| 872 | // Handle UTF-16 surrogate pair |
| 873 | if (RAPIDJSON_UNLIKELY(!Consume(is, '\\') || !Consume(is, 'u'))) |
| 874 | RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); |
| 875 | unsigned codepoint2 = ParseHex4(is, escapeOffset); |
| 876 | RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; |
| 877 | if (RAPIDJSON_UNLIKELY(codepoint2 < 0xDC00 || codepoint2 > 0xDFFF)) |
| 878 | RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); |
| 879 | codepoint = (((codepoint - 0xD800) << 10) | (codepoint2 - 0xDC00)) + 0x10000; |
| 880 | } |
| 881 | TEncoding::Encode(os, codepoint); |
| 882 | } |
| 883 | else |
| 884 | RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, escapeOffset); |
| 885 | } |
| 886 | else if (RAPIDJSON_UNLIKELY(c == '"')) { // Closing double quote |
| 887 | is.Take(); |
| 888 | os.Put('\0'); // null-terminate the string |
| 889 | return; |
| 890 | } |
| 891 | else if (RAPIDJSON_UNLIKELY(static_cast<unsigned>(c) < 0x20)) { // RFC 4627: unescaped = %x20-21 / %x23-5B / %x5D-10FFFF |
| 892 | if (c == '\0') |
| 893 | RAPIDJSON_PARSE_ERROR(kParseErrorStringMissQuotationMark, is.Tell()); |
| 894 | else |
| 895 | RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, is.Tell()); |
| 896 | } |
| 897 | else { |