use crate::parse_error::ParseError;
use crate::shared::{ContentRange, UnexpectedState};
type UnicodeEscapeResult = (Option<([u8; 4], usize)>, usize, Option<u32>);
pub struct EscapeProcessor;
use crate::ujson;
use ujson::EventToken;
impl EscapeProcessor {
pub fn token_to_escape_char(escape_token: &ujson::EventToken) -> Option<u8> {
match escape_token {
EventToken::EscapeQuote => Some(b'"'),
EventToken::EscapeBackslash => Some(b'\\'),
EventToken::EscapeSlash => Some(b'/'),
EventToken::EscapeBackspace => Some(b'b'),
EventToken::EscapeFormFeed => Some(b'f'),
EventToken::EscapeNewline => Some(b'n'),
EventToken::EscapeCarriageReturn => Some(b'r'),
EventToken::EscapeTab => Some(b't'),
_ => None,
}
}
pub fn process_escape_token(escape_token: &ujson::EventToken) -> Result<u8, ParseError> {
let escape_char =
Self::token_to_escape_char(escape_token).ok_or(UnexpectedState::InvalidEscapeToken)?;
Self::process_simple_escape(escape_char)
}
pub fn process_simple_escape(escape_char: u8) -> Result<u8, ParseError> {
match escape_char {
b'n' => Ok(b'\n'),
b't' => Ok(b'\t'),
b'r' => Ok(b'\r'),
b'\\' => Ok(b'\\'),
b'"' => Ok(b'"'),
b'/' => Ok(b'/'),
b'b' => Ok(0x08), b'f' => Ok(0x0C), _ => Err(ParseError::InvalidEscapeSequence),
}
}
pub fn validate_hex_digit(byte: u8) -> Result<u32, ParseError> {
match byte {
b'0'..=b'9' => Ok((byte - b'0') as u32),
b'a'..=b'f' => Ok(byte.wrapping_sub(b'a').wrapping_add(10) as u32),
b'A'..=b'F' => Ok(byte.wrapping_sub(b'A').wrapping_add(10) as u32),
_ => Err(ParseError::InvalidUnicodeHex),
}
}
pub fn is_high_surrogate(codepoint: u32) -> bool {
(0xD800..=0xDBFF).contains(&codepoint)
}
pub fn is_low_surrogate(codepoint: u32) -> bool {
(0xDC00..=0xDFFF).contains(&codepoint)
}
pub fn combine_surrogate_pair(high: u32, low: u32) -> Result<u32, ParseError> {
if !Self::is_high_surrogate(high) || !Self::is_low_surrogate(low) {
return Err(ParseError::InvalidUnicodeCodepoint);
}
let codepoint = 0x10000 + ((high & 0x3FF) << 10) + (low & 0x3FF);
Ok(codepoint)
}
pub fn process_unicode_escape<'a>(
hex_slice: &[u8],
utf8_buffer: &'a mut [u8],
pending_high_surrogate: Option<u32>,
) -> Result<(Option<&'a [u8]>, Option<u32>), ParseError> {
if hex_slice.len() != 4 {
return Err(ParseError::InvalidUnicodeHex);
}
let mut codepoint = 0u32;
for &byte in hex_slice {
let digit = Self::validate_hex_digit(byte)?;
codepoint = (codepoint << 4) | digit;
}
if let Some(high) = pending_high_surrogate {
if Self::is_low_surrogate(codepoint) {
let combined = Self::combine_surrogate_pair(high, codepoint)?;
let ch = char::from_u32(combined).ok_or(ParseError::InvalidUnicodeCodepoint)?;
let utf8_str = ch.encode_utf8(utf8_buffer);
Ok((Some(utf8_str.as_bytes()), None))
} else {
Err(ParseError::InvalidUnicodeCodepoint)
}
} else {
if Self::is_high_surrogate(codepoint) {
Ok((None, Some(codepoint)))
} else if Self::is_low_surrogate(codepoint) {
Err(ParseError::InvalidUnicodeCodepoint)
} else {
let ch = char::from_u32(codepoint).ok_or(ParseError::InvalidUnicodeCodepoint)?;
let utf8_str = ch.encode_utf8(utf8_buffer);
Ok((Some(utf8_str.as_bytes()), None))
}
}
}
}
#[derive(Debug)]
pub struct UnicodeEscapeCollector {
hex_buffer: [u8; 4],
hex_pos: usize,
pending_high_surrogate: Option<u32>,
}
impl UnicodeEscapeCollector {
pub fn new() -> Self {
Self {
hex_buffer: [0u8; 4],
hex_pos: 0,
pending_high_surrogate: None,
}
}
pub fn reset(&mut self) {
self.hex_pos = 0;
}
pub fn reset_all(&mut self) {
self.hex_pos = 0;
self.pending_high_surrogate = None;
}
pub fn add_hex_digit(&mut self, digit: u8) -> Result<bool, ParseError> {
EscapeProcessor::validate_hex_digit(digit)?;
if self.hex_pos >= 4 {
return Err(UnexpectedState::InvalidUnicodeEscape.into());
}
if let Some(slot) = self.hex_buffer.get_mut(self.hex_pos) {
*slot = digit;
} else {
return Err(ParseError::InvalidUnicodeHex);
}
self.hex_pos = self.hex_pos.saturating_add(1);
Ok(self.hex_pos == 4)
}
pub fn process_to_utf8<'a>(
&mut self,
utf8_buffer: &'a mut [u8],
) -> Result<(Option<&'a [u8]>, bool), ParseError> {
if self.hex_pos != 4 {
return Err(UnexpectedState::InvalidUnicodeEscape.into());
}
let (result, new_pending) = EscapeProcessor::process_unicode_escape(
&self.hex_buffer,
utf8_buffer,
self.pending_high_surrogate,
)?;
let surrogate_state_changed = self.pending_high_surrogate != new_pending;
self.pending_high_surrogate = new_pending;
Ok((result, surrogate_state_changed))
}
pub fn has_pending_high_surrogate(&self) -> bool {
self.pending_high_surrogate.is_some()
}
pub fn get_pending_high_surrogate(&self) -> Option<u32> {
self.pending_high_surrogate
}
pub fn set_pending_high_surrogate(&mut self, surrogate: Option<u32>) {
self.pending_high_surrogate = surrogate;
}
}
impl Default for UnicodeEscapeCollector {
fn default() -> Self {
Self::new()
}
}
pub(crate) fn process_unicode_escape_sequence<'input, 'scratch, D>(
current_pos: usize,
pending_high_surrogate: Option<u32>,
source: &'input D,
) -> Result<UnicodeEscapeResult, ParseError>
where
D: ?Sized + crate::shared::DataSource<'input, 'scratch>,
{
let (hex_start, hex_end, escape_start_pos) = ContentRange::unicode_escape_bounds(current_pos);
let hex_slice = source.get_borrowed_slice(hex_start, hex_end)?;
if hex_slice.len() != 4 {
return Err(UnexpectedState::InvalidUnicodeEscape.into());
}
let mut temp_collector = UnicodeEscapeCollector::new();
temp_collector.set_pending_high_surrogate(pending_high_surrogate);
for &hex_digit in hex_slice {
temp_collector.add_hex_digit(hex_digit)?;
}
let had_pending_high_surrogate = temp_collector.has_pending_high_surrogate();
let mut utf8_buf = [0u8; 4];
let (utf8_bytes_opt, _surrogate_state_changed) =
temp_collector.process_to_utf8(&mut utf8_buf)?;
let new_pending_high_surrogate = temp_collector.get_pending_high_surrogate();
let result_by_value = utf8_bytes_opt.map(|bytes| {
let mut value_buf = [0u8; 4];
let len = bytes.len();
value_buf[..len].copy_from_slice(bytes);
(value_buf, len)
});
let final_escape_start_pos = if had_pending_high_surrogate && result_by_value.is_some() {
escape_start_pos.saturating_sub(6)
} else {
escape_start_pos
};
Ok((
result_by_value,
final_escape_start_pos,
new_pending_high_surrogate,
))
}
#[cfg(test)]
mod tests {
use super::*;
use ujson::EventToken;
#[test]
fn test_simple_escapes() {
assert_eq!(EscapeProcessor::process_simple_escape(b'n').unwrap(), b'\n');
assert_eq!(EscapeProcessor::process_simple_escape(b't').unwrap(), b'\t');
assert_eq!(EscapeProcessor::process_simple_escape(b'r').unwrap(), b'\r');
assert_eq!(
EscapeProcessor::process_simple_escape(b'\\').unwrap(),
b'\\'
);
assert_eq!(EscapeProcessor::process_simple_escape(b'"').unwrap(), b'"');
assert_eq!(EscapeProcessor::process_simple_escape(b'/').unwrap(), b'/');
assert_eq!(EscapeProcessor::process_simple_escape(b'b').unwrap(), 0x08);
assert_eq!(EscapeProcessor::process_simple_escape(b'f').unwrap(), 0x0C);
}
#[test]
fn test_invalid_simple_escape() {
assert!(EscapeProcessor::process_simple_escape(b'x').is_err());
assert!(EscapeProcessor::process_simple_escape(b'z').is_err());
assert!(EscapeProcessor::process_simple_escape(b'1').is_err());
}
#[test]
fn test_hex_digit_validation() {
assert_eq!(EscapeProcessor::validate_hex_digit(b'0').unwrap(), 0);
assert_eq!(EscapeProcessor::validate_hex_digit(b'9').unwrap(), 9);
assert_eq!(EscapeProcessor::validate_hex_digit(b'a').unwrap(), 10);
assert_eq!(EscapeProcessor::validate_hex_digit(b'f').unwrap(), 15);
assert_eq!(EscapeProcessor::validate_hex_digit(b'A').unwrap(), 10);
assert_eq!(EscapeProcessor::validate_hex_digit(b'F').unwrap(), 15);
assert!(EscapeProcessor::validate_hex_digit(b'g').is_err());
assert!(EscapeProcessor::validate_hex_digit(b'G').is_err());
assert!(EscapeProcessor::validate_hex_digit(b'z').is_err());
assert!(EscapeProcessor::validate_hex_digit(b' ').is_err());
}
#[test]
fn test_unicode_escape_basic() {
let mut buffer = [0u8; 4];
let (result, pending) =
EscapeProcessor::process_unicode_escape(b"0041", &mut buffer, None).unwrap();
assert_eq!(result.unwrap(), b"A");
assert_eq!(pending, None);
let (result, pending) =
EscapeProcessor::process_unicode_escape(b"0048", &mut buffer, None).unwrap();
assert_eq!(result.unwrap(), b"H");
assert_eq!(pending, None);
}
#[test]
fn test_unicode_escape_multibyte() {
let mut buffer = [0u8; 4];
let (result, pending) =
EscapeProcessor::process_unicode_escape(b"03B1", &mut buffer, None).unwrap();
assert_eq!(result.unwrap(), "α".as_bytes());
assert_eq!(pending, None);
let (result, pending) =
EscapeProcessor::process_unicode_escape(b"1F60", &mut buffer, None).unwrap();
assert!(result.is_some());
assert_eq!(pending, None);
}
#[test]
fn test_unicode_escape_invalid_hex() {
let mut buffer = [0u8; 4];
assert!(EscapeProcessor::process_unicode_escape(b"00GG", &mut buffer, None).is_err());
assert!(EscapeProcessor::process_unicode_escape(b"ZZZZ", &mut buffer, None).is_err());
assert!(EscapeProcessor::process_unicode_escape(b"123", &mut buffer, None).is_err());
assert!(EscapeProcessor::process_unicode_escape(b"12345", &mut buffer, None).is_err());
}
#[test]
fn test_unicode_escape_invalid_codepoint() {
let mut buffer = [0u8; 4];
let (result, pending) =
EscapeProcessor::process_unicode_escape(b"0000", &mut buffer, None).unwrap();
assert_eq!(result.unwrap(), "\0".as_bytes());
assert_eq!(pending, None);
}
#[test]
fn test_token_to_escape_char() {
assert_eq!(
EscapeProcessor::token_to_escape_char(&EventToken::EscapeQuote).unwrap(),
b'"'
);
assert_eq!(
EscapeProcessor::token_to_escape_char(&EventToken::EscapeBackslash).unwrap(),
b'\\'
);
assert_eq!(
EscapeProcessor::token_to_escape_char(&EventToken::EscapeSlash).unwrap(),
b'/'
);
assert_eq!(
EscapeProcessor::token_to_escape_char(&EventToken::EscapeBackspace).unwrap(),
b'b'
);
assert_eq!(
EscapeProcessor::token_to_escape_char(&EventToken::EscapeFormFeed).unwrap(),
b'f'
);
assert_eq!(
EscapeProcessor::token_to_escape_char(&EventToken::EscapeNewline).unwrap(),
b'n'
);
assert_eq!(
EscapeProcessor::token_to_escape_char(&EventToken::EscapeCarriageReturn).unwrap(),
b'r'
);
assert_eq!(
EscapeProcessor::token_to_escape_char(&EventToken::EscapeTab).unwrap(),
b't'
);
assert_eq!(
EscapeProcessor::token_to_escape_char(&EventToken::String),
None
);
}
#[test]
fn test_process_escape_token() {
assert_eq!(
EscapeProcessor::process_escape_token(&EventToken::EscapeQuote).unwrap(),
b'"'
);
assert_eq!(
EscapeProcessor::process_escape_token(&EventToken::EscapeBackslash).unwrap(),
b'\\'
);
assert_eq!(
EscapeProcessor::process_escape_token(&EventToken::EscapeSlash).unwrap(),
b'/'
);
assert_eq!(
EscapeProcessor::process_escape_token(&EventToken::EscapeBackspace).unwrap(),
0x08
);
assert_eq!(
EscapeProcessor::process_escape_token(&EventToken::EscapeFormFeed).unwrap(),
0x0C
);
assert_eq!(
EscapeProcessor::process_escape_token(&EventToken::EscapeNewline).unwrap(),
b'\n'
);
assert_eq!(
EscapeProcessor::process_escape_token(&EventToken::EscapeCarriageReturn).unwrap(),
b'\r'
);
assert_eq!(
EscapeProcessor::process_escape_token(&EventToken::EscapeTab).unwrap(),
b'\t'
);
assert!(EscapeProcessor::process_escape_token(&EventToken::String).is_err());
}
#[test]
fn test_unicode_escape_collector_basic() {
let mut collector = UnicodeEscapeCollector::new();
let mut utf8_buffer = [0u8; 4];
assert!(!collector.add_hex_digit(b'0').unwrap()); assert!(!collector.add_hex_digit(b'0').unwrap()); assert!(!collector.add_hex_digit(b'4').unwrap()); assert!(collector.add_hex_digit(b'1').unwrap());
let (result, _surrogate_state_changed) =
collector.process_to_utf8(&mut utf8_buffer).unwrap();
assert_eq!(result.unwrap(), b"A");
}
#[test]
fn test_unicode_escape_collector_invalid_hex() {
let mut collector = UnicodeEscapeCollector::new();
assert!(!collector.add_hex_digit(b'0').unwrap());
assert!(!collector.add_hex_digit(b'0').unwrap());
assert!(collector.add_hex_digit(b'G').is_err());
}
#[test]
fn test_unicode_escape_collector_reset() {
let mut collector = UnicodeEscapeCollector::new();
assert!(!collector.add_hex_digit(b'0').unwrap());
assert!(!collector.add_hex_digit(b'1').unwrap());
collector.reset();
assert!(!collector.add_hex_digit(b'A').unwrap());
}
#[test]
fn test_unicode_escape_collector_surrogate_support() {
let mut collector = UnicodeEscapeCollector::new();
let mut utf8_buffer = [0u8; 4];
assert!(!collector.add_hex_digit(b'D').unwrap());
assert!(!collector.add_hex_digit(b'8').unwrap());
assert!(!collector.add_hex_digit(b'0').unwrap());
assert!(collector.add_hex_digit(b'1').unwrap());
let (result, state_changed) = collector.process_to_utf8(&mut utf8_buffer).unwrap();
assert_eq!(result, None); assert!(state_changed); assert!(collector.has_pending_high_surrogate());
collector.reset();
assert!(!collector.add_hex_digit(b'D').unwrap());
assert!(!collector.add_hex_digit(b'C').unwrap());
assert!(!collector.add_hex_digit(b'3').unwrap());
assert!(collector.add_hex_digit(b'7').unwrap());
let (result, state_changed) = collector.process_to_utf8(&mut utf8_buffer).unwrap();
assert!(result.is_some()); assert!(state_changed); assert!(!collector.has_pending_high_surrogate());
assert_eq!(result.unwrap(), [0xF0, 0x90, 0x90, 0xB7]);
}
#[test]
fn test_unicode_escape_collector_multibyte() {
let mut collector = UnicodeEscapeCollector::new();
let mut utf8_buffer = [0u8; 4];
assert!(!collector.add_hex_digit(b'0').unwrap());
assert!(!collector.add_hex_digit(b'3').unwrap());
assert!(!collector.add_hex_digit(b'B').unwrap());
assert!(collector.add_hex_digit(b'1').unwrap());
let (result, _surrogate_state_changed) =
collector.process_to_utf8(&mut utf8_buffer).unwrap();
assert_eq!(result.unwrap(), "α".as_bytes());
}
#[test]
fn test_unicode_escape_collector_incomplete_processing() {
let mut collector = UnicodeEscapeCollector::new();
let mut utf8_buffer = [0u8; 4];
assert!(!collector.add_hex_digit(b'0').unwrap());
assert!(!collector.add_hex_digit(b'0').unwrap());
assert!(collector.process_to_utf8(&mut utf8_buffer).is_err());
}
#[test]
fn test_surrogate_pair_detection() {
assert!(EscapeProcessor::is_high_surrogate(0xD800));
assert!(EscapeProcessor::is_high_surrogate(0xD801));
assert!(EscapeProcessor::is_high_surrogate(0xDBFF));
assert!(!EscapeProcessor::is_high_surrogate(0xD7FF));
assert!(!EscapeProcessor::is_high_surrogate(0xDC00));
assert!(EscapeProcessor::is_low_surrogate(0xDC00));
assert!(EscapeProcessor::is_low_surrogate(0xDC37));
assert!(EscapeProcessor::is_low_surrogate(0xDFFF));
assert!(!EscapeProcessor::is_low_surrogate(0xDBFF));
assert!(!EscapeProcessor::is_low_surrogate(0xE000));
}
#[test]
fn test_surrogate_pair_combination() {
let combined = EscapeProcessor::combine_surrogate_pair(0xD801, 0xDC37).unwrap();
assert_eq!(combined, 0x10437);
let combined = EscapeProcessor::combine_surrogate_pair(0xD834, 0xDD1E).unwrap();
assert_eq!(combined, 0x1D11E);
assert!(EscapeProcessor::combine_surrogate_pair(0x0041, 0xDC37).is_err()); assert!(EscapeProcessor::combine_surrogate_pair(0xD801, 0x0041).is_err());
}
#[test]
fn test_unicode_escape_with_surrogate_support() {
let mut buffer = [0u8; 4];
let (result, pending) =
EscapeProcessor::process_unicode_escape(b"0041", &mut buffer, None).unwrap();
assert_eq!(result, Some(b"A".as_slice()));
assert_eq!(pending, None);
let (result, pending) =
EscapeProcessor::process_unicode_escape(b"D801", &mut buffer, None).unwrap();
assert_eq!(result, None);
assert_eq!(pending, Some(0xD801));
let (result, pending) =
EscapeProcessor::process_unicode_escape(b"DC37", &mut buffer, Some(0xD801)).unwrap();
assert!(result.is_some());
assert_eq!(pending, None);
assert_eq!(result.unwrap(), [0xF0, 0x90, 0x90, 0xB7]);
}
#[test]
fn test_unicode_escape_surrogate_error_cases() {
let mut buffer = [0u8; 4];
let result = EscapeProcessor::process_unicode_escape(b"DC37", &mut buffer, None);
assert!(result.is_err());
let result = EscapeProcessor::process_unicode_escape(b"0041", &mut buffer, Some(0xD801));
assert!(result.is_err());
}
}