(chars: &mut T)
| 208 | } |
| 209 | |
| 210 | fn parse_hex_char<'a, T: CharProvider<'a>>(chars: &mut T) -> Result<char, ParseStringErrorKind> { |
| 211 | let mut buf1 = [0u8; 4]; |
| 212 | let hex_value = read_four_hex_digits(chars, &mut buf1)?; |
| 213 | |
| 214 | // check if this is a high surrogate (0xD800-0xDBFF) |
| 215 | let hex_char = if (0xD800..=0xDBFF).contains(&hex_value) { |
| 216 | // high surrogate - must be followed by low surrogate (\uXXXX) |
| 217 | if chars.move_next_char() != Some('\\') { |
| 218 | return Err(ParseStringErrorKind::InvalidUnicodeEscapeSequence(format!( |
| 219 | "{} (unpaired high surrogate)", |
| 220 | hex_buf_to_str(&buf1) |
| 221 | ))); |
| 222 | } |
| 223 | |
| 224 | if chars.move_next_char() != Some('u') { |
| 225 | return Err(ParseStringErrorKind::InvalidUnicodeEscapeSequence(format!( |
| 226 | "{} (unpaired high surrogate)", |
| 227 | hex_buf_to_str(&buf1) |
| 228 | ))); |
| 229 | } |
| 230 | |
| 231 | // parse the second \uXXXX |
| 232 | let mut buf2 = [0u8; 4]; |
| 233 | let hex_value2 = read_four_hex_digits(chars, &mut buf2)?; |
| 234 | |
| 235 | // verify it's a low surrogate (0xDC00-0xDFFF) |
| 236 | if !(0xDC00..=0xDFFF).contains(&hex_value2) { |
| 237 | return Err(ParseStringErrorKind::InvalidUnicodeEscapeSequence(format!( |
| 238 | "{} (high surrogate not followed by low surrogate)", |
| 239 | hex_buf_to_str(&buf1) |
| 240 | ))); |
| 241 | } |
| 242 | |
| 243 | // combine surrogate pair using RFC 8259 formula |
| 244 | let code_point = ((hex_value - 0xD800) * 0x400) + (hex_value2 - 0xDC00) + 0x10000; |
| 245 | |
| 246 | match std::char::from_u32(code_point) { |
| 247 | Some(c) => c, |
| 248 | None => { |
| 249 | return Err(ParseStringErrorKind::InvalidUnicodeEscapeSequence(format!( |
| 250 | "{}\\u{} (invalid surrogate pair)", |
| 251 | hex_buf_to_str(&buf1), |
| 252 | hex_buf_to_str(&buf2) |
| 253 | ))); |
| 254 | } |
| 255 | } |
| 256 | } else if (0xDC00..=0xDFFF).contains(&hex_value) { |
| 257 | // low surrogate without high surrogate |
| 258 | return Err(ParseStringErrorKind::InvalidUnicodeEscapeSequence(format!( |
| 259 | "{} (unpaired low surrogate)", |
| 260 | hex_buf_to_str(&buf1) |
| 261 | ))); |
| 262 | } else { |
| 263 | // normal unicode escape |
| 264 | match std::char::from_u32(hex_value) { |
| 265 | Some(hex_char) => hex_char, |
| 266 | None => { |
| 267 | return Err(ParseStringErrorKind::InvalidUnicodeEscapeSequence(hex_buf_to_str( |
no test coverage detected
searching dependent graphs…