(state: ParserState)
| 855 | } |
| 856 | |
| 857 | function parseString(state: ParserState): BareItem { |
| 858 | if (consume(state) !== '"') { |
| 859 | throw new SyntaxError( |
| 860 | `Invalid structured field: expected '"' at position ${state.pos - 1}`, |
| 861 | ); |
| 862 | } |
| 863 | |
| 864 | const { input } = state; |
| 865 | const startPos = state.pos; |
| 866 | |
| 867 | // Fast path: find first special character (\ or ") |
| 868 | let firstSpecial = startPos; |
| 869 | while (firstSpecial < input.length) { |
| 870 | const c = input[firstSpecial]!; |
| 871 | if (c === '"' || c === "\\") break; |
| 872 | // Validate printable ASCII |
| 873 | const code = c.charCodeAt(0); |
| 874 | if (code < 0x20 || code > 0x7e) { |
| 875 | throw new SyntaxError( |
| 876 | `Invalid structured field: invalid character in string at position ${firstSpecial}`, |
| 877 | ); |
| 878 | } |
| 879 | firstSpecial++; |
| 880 | } |
| 881 | |
| 882 | // If we hit end of string without finding closing quote |
| 883 | if (firstSpecial >= input.length) { |
| 884 | throw new SyntaxError("Invalid structured field: unterminated string"); |
| 885 | } |
| 886 | |
| 887 | // Fast path: no escapes, just a closing quote |
| 888 | if (input[firstSpecial] === '"') { |
| 889 | state.pos = firstSpecial + 1; |
| 890 | return { type: "string", value: input.slice(startPos, firstSpecial) }; |
| 891 | } |
| 892 | |
| 893 | // Slow path: has escapes, need to process character by character |
| 894 | let value = input.slice(startPos, firstSpecial); |
| 895 | state.pos = firstSpecial; |
| 896 | |
| 897 | while (!isEof(state)) { |
| 898 | const c = consume(state); |
| 899 | |
| 900 | if (c === "\\") { |
| 901 | const escaped = consume(state); |
| 902 | if (escaped !== '"' && escaped !== "\\") { |
| 903 | throw new SyntaxError( |
| 904 | `Invalid structured field: invalid escape sequence at position ${ |
| 905 | state.pos - 1 |
| 906 | }`, |
| 907 | ); |
| 908 | } |
| 909 | value += escaped; |
| 910 | } else if (c === '"') { |
| 911 | return { type: "string", value }; |
| 912 | } else { |
| 913 | // Must be printable ASCII (0x20-0x7E) excluding 0x22 (") and 0x5C (\) |
| 914 | const code = c.charCodeAt(0); |
no test coverage detected