MCPcopy Index your code
hub / github.com/RustPython/RustPython / scanstring

Function scanstring

crates/stdlib/src/json/machinery.rs:143–255  ·  view source on GitHub ↗

Scan a JSON string starting right after the opening quote. # Arguments `s` - The string slice starting at the first character after the opening `"` `char_offset` - The character index where this slice starts (for error messages) `strict` - Whether to reject control characters # Returns `Ok((result, chars_consumed, bytes_consumed))` - The decoded string and how much was consumed `Err(DecodeError)

(
    s: &'a Wtf8,
    char_offset: usize,
    strict: bool,
)

Source from the content-addressed store, hash-verified

141/// * `Ok((result, chars_consumed, bytes_consumed))` - The decoded string and how much was consumed
142/// * `Err(DecodeError)` - If the string is malformed
143pub fn scanstring<'a>(
144 s: &'a Wtf8,
145 char_offset: usize,
146 strict: bool,
147) -> Result<(Wtf8Buf, usize, usize), DecodeError> {
148 flame_guard!("machinery::scanstring");
149 let unterminated_err = || DecodeError::new("Unterminated string starting at", char_offset - 1);
150
151 let bytes = s.as_bytes();
152
153 // Fast path: use memchr to find " or \ quickly
154 if let Some(pos) = memchr2(b'"', b'\\', bytes)
155 && bytes[pos] == b'"'
156 {
157 let content_bytes = &bytes[..pos];
158
159 // In strict mode, check for control characters (0x00-0x1F)
160 let has_control_char = strict && content_bytes.iter().any(|&b| b < 0x20);
161
162 if !has_control_char {
163 flame_guard!("machinery::scanstring::fast_path");
164 let result_slice = &s[..pos];
165 let char_count = result_slice.code_points().count();
166 let mut out = Wtf8Buf::with_capacity(pos);
167 out.push_wtf8(result_slice);
168 // +1 for the closing quote
169 return Ok((out, char_count + 1, pos + 1));
170 }
171 }
172
173 // Slow path: chunk-based parsing for strings with escapes or control chars
174 flame_guard!("machinery::scanstring::slow_path");
175 let mut chunks: Vec<StrOrChar<'a>> = Vec::new();
176 let mut output_len = 0usize;
177 let mut push_chunk = |chunk: StrOrChar<'a>| {
178 output_len += chunk.len();
179 chunks.push(chunk);
180 };
181
182 let mut chars = s.code_point_indices().enumerate().peekable();
183 let mut chunk_start: usize = 0;
184
185 while let Some((char_i, (byte_i, c))) = chars.next() {
186 match c.to_char_lossy() {
187 '"' => {
188 push_chunk(StrOrChar::Str(&s[chunk_start..byte_i]));
189 flame_guard!("machinery::scanstring::assemble_chunks");
190 let mut out = Wtf8Buf::with_capacity(output_len);
191 for x in chunks {
192 match x {
193 StrOrChar::Str(s) => out.push_wtf8(s),
194 StrOrChar::Char(c) => out.push(c),
195 }
196 }
197 // +1 for the closing quote
198 return Ok((out, char_i + 1, byte_i + 1));
199 }
200 '\\' => {

Callers 4

test_scanstringMethod · 0.50
assertScanMethod · 0.50
test_bad_escapesMethod · 0.50
JSONObjectFunction · 0.50

Calls 15

newFunction · 0.85
decode_unicodeFunction · 0.85
CharClass · 0.85
code_pointsMethod · 0.80
push_wtf8Method · 0.80
code_point_indicesMethod · 0.80
ok_or_elseMethod · 0.80
to_lead_surrogateMethod · 0.80
to_trail_surrogateMethod · 0.80
StrClass · 0.50
ErrClass · 0.50
as_bytesMethod · 0.45

Tested by 3

test_scanstringMethod · 0.40
assertScanMethod · 0.40
test_bad_escapesMethod · 0.40