1use super::{Budgeted, CancellationToken, JsonReadError, JsonReader, JsonToken, MemoryBudget};
10use crate::memory::{Produced, ProductionControl, ProductionString};
11
12pub fn decode_json_string(
14 encoded: impl AsRef<[u8]>,
15 memory: &MemoryBudget,
16 cancellation: &CancellationToken,
17) -> Result<Budgeted<String>, JsonReadError> {
18 decode_json_string_with_control(
19 encoded,
20 &ProductionControl::new(memory, cancellation, cancellation),
21 )?
22 .into_budgeted()
23 .map_err(|_| unreachable!("controlled JSON string"))
24}
25
26pub fn decode_json_string_with_control(
28 encoded: impl AsRef<[u8]>,
29 control: &ProductionControl<'_>,
30) -> Result<Produced<String>, JsonReadError> {
31 control.check()?;
32 let input = std::str::from_utf8(encoded.as_ref()).map_err(|_| JsonReadError::InvalidJson)?;
33 let mut reader = JsonReader::with_control(input, control);
34 let event = reader.next_event()?.ok_or(JsonReadError::InvalidJson)?;
35 let JsonToken::String(token) = event.token else {
36 return Err(JsonReadError::InvalidJson);
37 };
38 if reader.next_event()?.is_some() {
39 return Err(JsonReadError::InvalidJson);
40 }
41 let text = std::str::from_utf8(token).map_err(|_| JsonReadError::InvalidJson)?;
42 let mut output = ProductionString::new(*control);
43 let mut position = 1;
44 while position < text.len() - 1 {
45 control.check()?;
46 let start = position;
47 while position < text.len() - 1 && text.as_bytes()[position] != b'\\' {
48 position += 1;
49 if position.is_multiple_of(4096) {
50 control.check()?;
51 }
52 }
53 output.push_str(&text[start..position])?;
54 if position == text.len() - 1 {
55 break;
56 }
57 position += 1;
58 let escape = text.as_bytes()[position];
59 position += 1;
60 let character = match escape {
61 b'"' => '"',
62 b'\\' => '\\',
63 b'/' => '/',
64 b'b' => '\u{0008}',
65 b'f' => '\u{000c}',
66 b'n' => '\n',
67 b'r' => '\r',
68 b't' => '\t',
69 b'u' => {
70 let mut code = hex_unit(text, &mut position);
71 if (0xd800..=0xdbff).contains(&code) {
72 position += 2;
73 let low = hex_unit(text, &mut position);
74 code = 0x10000 + ((code - 0xd800) << 10) + (low - 0xdc00);
75 }
76 char::from_u32(code).expect("validated JSON Unicode escape")
77 }
78 _ => unreachable!("validated JSON escape"),
79 };
80 output.push(character)?;
81 }
82 output.finish().map_err(Into::into)
83}
84
85fn hex_unit(text: &str, position: &mut usize) -> u32 {
86 let value = u32::from_str_radix(&text[*position..*position + 4], 16)
87 .expect("validated JSON hexadecimal escape");
88 *position += 4;
89 value
90}