Coverage Report

Created: 2026-05-18 06:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/sfv-0.14.0/src/parser.rs
Line
Count
Source
1
use std::{borrow::Cow, string::String as StdString};
2
3
use crate::{
4
    error, utils,
5
    visitor::{
6
        DictionaryVisitor, EntryVisitor, InnerListVisitor, ItemVisitor, ListVisitor,
7
        ParameterVisitor,
8
    },
9
    BareItemFromInput, Date, Decimal, Integer, KeyRef, Num, SFVResult, String, StringRef, TokenRef,
10
    Version,
11
};
12
13
3.93M
fn parse_item<'de>(
14
3.93M
    parser: &mut Parser<'de>,
15
3.93M
    visitor: impl ItemVisitor<'de>,
16
3.93M
) -> Result<(), error::Repr> {
17
    // https://httpwg.org/specs/rfc9651.html#parse-item
18
3.93M
    let param_visitor = visitor.bare_item(parser.parse_bare_item()?)?;
19
3.92M
    parser.parse_parameters(param_visitor)
20
3.93M
}
sfv::parser::parse_item::<indexmap::map::core::entry::Entry<sfv::key::Key, sfv::parsed::ListEntry>>
Line
Count
Source
13
227k
fn parse_item<'de>(
14
227k
    parser: &mut Parser<'de>,
15
227k
    visitor: impl ItemVisitor<'de>,
16
227k
) -> Result<(), error::Repr> {
17
    // https://httpwg.org/specs/rfc9651.html#parse-item
18
227k
    let param_visitor = visitor.bare_item(parser.parse_bare_item()?)?;
19
225k
    parser.parse_parameters(param_visitor)
20
227k
}
Unexecuted instantiation: sfv::parser::parse_item::<&mut alloc::vec::Vec<sfv::parsed::ListEntry>>
Unexecuted instantiation: sfv::parser::parse_item::<&mut sfv::parsed::Item>
sfv::parser::parse_item::<&mut sfv::parsed::InnerList>
Line
Count
Source
13
3.70M
fn parse_item<'de>(
14
3.70M
    parser: &mut Parser<'de>,
15
3.70M
    visitor: impl ItemVisitor<'de>,
16
3.70M
) -> Result<(), error::Repr> {
17
    // https://httpwg.org/specs/rfc9651.html#parse-item
18
3.70M
    let param_visitor = visitor.bare_item(parser.parse_bare_item()?)?;
19
3.70M
    parser.parse_parameters(param_visitor)
20
3.70M
}
21
22
7.73k
fn parse_comma_separated<'de>(
23
7.73k
    parser: &mut Parser<'de>,
24
7.73k
    mut parse_member: impl FnMut(&mut Parser<'de>) -> Result<(), error::Repr>,
25
7.73k
) -> Result<(), error::Repr> {
26
2.46M
    while parser.peek().is_some() {
27
2.46M
        parse_member(parser)?;
28
29
2.46M
        parser.consume_ows_chars();
30
31
2.46M
        if parser.peek().is_none() {
32
3.19k
            return Ok(());
33
2.46M
        }
34
35
2.46M
        let comma_index = parser.index;
36
37
2.46M
        if let Some(c) = parser.peek() {
38
2.46M
            if c != b',' {
39
932
                return Err(error::Repr::TrailingCharactersAfterMember(parser.index));
40
2.46M
            }
41
2.46M
            parser.next();
42
0
        }
43
44
2.46M
        parser.consume_ows_chars();
45
46
2.46M
        if parser.peek().is_none() {
47
            // Report the error at the position of the comma itself, rather
48
            // than at the end of input.
49
112
            return Err(error::Repr::TrailingComma(comma_index));
50
2.46M
        }
51
    }
52
53
27
    Ok(())
54
7.73k
}
Unexecuted instantiation: sfv::parser::parse_comma_separated::<<sfv::parser::Parser>::parse_list_with_visitor<alloc::vec::Vec<sfv::parsed::ListEntry>>::{closure#0}::{closure#0}>
sfv::parser::parse_comma_separated::<<sfv::parser::Parser>::parse_dictionary_with_visitor<indexmap::map::IndexMap<sfv::key::Key, sfv::parsed::ListEntry>>::{closure#0}::{closure#0}>
Line
Count
Source
22
7.73k
fn parse_comma_separated<'de>(
23
7.73k
    parser: &mut Parser<'de>,
24
7.73k
    mut parse_member: impl FnMut(&mut Parser<'de>) -> Result<(), error::Repr>,
25
7.73k
) -> Result<(), error::Repr> {
26
2.46M
    while parser.peek().is_some() {
27
2.46M
        parse_member(parser)?;
28
29
2.46M
        parser.consume_ows_chars();
30
31
2.46M
        if parser.peek().is_none() {
32
3.19k
            return Ok(());
33
2.46M
        }
34
35
2.46M
        let comma_index = parser.index;
36
37
2.46M
        if let Some(c) = parser.peek() {
38
2.46M
            if c != b',' {
39
932
                return Err(error::Repr::TrailingCharactersAfterMember(parser.index));
40
2.46M
            }
41
2.46M
            parser.next();
42
0
        }
43
44
2.46M
        parser.consume_ows_chars();
45
46
2.46M
        if parser.peek().is_none() {
47
            // Report the error at the position of the comma itself, rather
48
            // than at the end of input.
49
112
            return Err(error::Repr::TrailingComma(comma_index));
50
2.46M
        }
51
    }
52
53
27
    Ok(())
54
7.73k
}
55
56
/// Exposes methods for parsing input into a structured field value.
57
#[must_use]
58
pub struct Parser<'de> {
59
    input: &'de [u8],
60
    index: usize,
61
    version: Version,
62
}
63
64
impl<'de> Parser<'de> {
65
    /// Creates a parser from the given input with [`Version::Rfc9651`].
66
7.73k
    pub fn new(input: &'de (impl ?Sized + AsRef<[u8]>)) -> Self {
67
7.73k
        Self {
68
7.73k
            input: input.as_ref(),
69
7.73k
            index: 0,
70
7.73k
            version: Version::Rfc9651,
71
7.73k
        }
72
7.73k
    }
<sfv::parser::Parser>::new::<[u8]>
Line
Count
Source
66
7.73k
    pub fn new(input: &'de (impl ?Sized + AsRef<[u8]>)) -> Self {
67
7.73k
        Self {
68
7.73k
            input: input.as_ref(),
69
7.73k
            index: 0,
70
7.73k
            version: Version::Rfc9651,
71
7.73k
        }
72
7.73k
    }
Unexecuted instantiation: <sfv::parser::Parser>::new::<_>
73
74
    /// Sets the parser's version and returns it.
75
0
    pub fn with_version(mut self, version: Version) -> Self {
76
0
        self.version = version;
77
0
        self
78
0
    }
79
80
    /// Parses a structured field value.
81
    ///
82
    /// # Errors
83
    /// When the parsing process is unsuccessful.
84
    #[cfg(feature = "parsed-types")]
85
7.73k
    pub fn parse<T: crate::FieldType>(self) -> SFVResult<T> {
86
7.73k
        T::parse(self)
87
7.73k
    }
<sfv::parser::Parser>::parse::<indexmap::map::IndexMap<sfv::key::Key, sfv::parsed::ListEntry>>
Line
Count
Source
85
7.73k
    pub fn parse<T: crate::FieldType>(self) -> SFVResult<T> {
86
7.73k
        T::parse(self)
87
7.73k
    }
Unexecuted instantiation: <sfv::parser::Parser>::parse::<_>
88
89
    /// Parses input into a structured field value of `Dictionary` type, using
90
    /// the given visitor.
91
    #[cfg_attr(
92
        feature = "parsed-types",
93
        doc = r#"
94
95
This can also be used to parse a dictionary that is split into multiple lines by merging
96
them into an existing structure:
97
98
```
99
# use sfv::{Dictionary, FieldType, Parser};
100
# fn main() -> Result<(), sfv::Error> {
101
let mut dict: Dictionary = Parser::new("a=1").parse()?;
102
103
Parser::new("b=2").parse_dictionary_with_visitor(&mut dict)?;
104
105
assert_eq!(
106
    dict.serialize().as_deref(),
107
    Some("a=1, b=2"),
108
);
109
# Ok(())
110
# }
111
```
112
"#
113
    )]
114
    ///
115
    /// # Errors
116
    /// When the parsing process is unsuccessful, including any error raised by a visitor.
117
7.73k
    pub fn parse_dictionary_with_visitor(
118
7.73k
        self,
119
7.73k
        visitor: &mut (impl ?Sized + DictionaryVisitor<'de>),
120
7.73k
    ) -> SFVResult<()> {
121
        // https://httpwg.org/specs/rfc9651.html#parse-dictionary
122
7.73k
        self.parse_internal(move |parser| {
123
2.46M
            parse_comma_separated(parser, |parser| {
124
                // Note: It is up to the visitor to properly handle duplicate keys.
125
2.46M
                let entry_visitor = visitor.entry(parser.parse_key()?)?;
126
127
2.46M
                if let Some(b'=') = parser.peek() {
128
344k
                    parser.next();
129
344k
                    parser.parse_list_entry(entry_visitor)
130
                } else {
131
2.12M
                    let param_visitor = entry_visitor.bare_item(BareItemFromInput::from(true))?;
132
2.12M
                    parser.parse_parameters(param_visitor)
133
                }
134
2.46M
            })
135
7.73k
        })
136
7.73k
    }
137
138
    /// Parses input into a structured field value of `List` type, using the
139
    /// given visitor.
140
    #[allow(clippy::needless_raw_string_hashes)] // false positive: https://github.com/rust-lang/rust-clippy/issues/11737
141
    #[cfg_attr(
142
        feature = "parsed-types",
143
        doc = r##"
144
145
This can also be used to parse a list that is split into multiple lines by merging them
146
into an existing structure:
147
```
148
# use sfv::{FieldType, List, Parser};
149
# fn main() -> Result<(), sfv::Error> {
150
let mut list: List = Parser::new("11, (12 13)").parse()?;
151
152
Parser::new(r#""foo",        "bar""#).parse_list_with_visitor(&mut list)?;
153
154
assert_eq!(
155
    list.serialize().as_deref(),
156
    Some(r#"11, (12 13), "foo", "bar""#),
157
);
158
# Ok(())
159
# }
160
```
161
"##
162
    )]
163
    ///
164
    /// # Errors
165
    /// When the parsing process is unsuccessful, including any error raised by a visitor.
166
0
    pub fn parse_list_with_visitor(
167
0
        self,
168
0
        visitor: &mut (impl ?Sized + ListVisitor<'de>),
169
0
    ) -> SFVResult<()> {
170
        // https://httpwg.org/specs/rfc9651.html#parse-list
171
0
        self.parse_internal(|parser| {
172
0
            parse_comma_separated(parser, |parser| parser.parse_list_entry(visitor.entry()?))
173
0
        })
174
0
    }
175
176
    /// Parses input into a structured field value of `Item` type, using the
177
    /// given visitor.
178
    ///
179
    /// # Errors
180
    /// When the parsing process is unsuccessful, including any error raised by a visitor.
181
0
    pub fn parse_item_with_visitor(self, visitor: impl ItemVisitor<'de>) -> SFVResult<()> {
182
0
        self.parse_internal(|parser| parse_item(parser, visitor))
183
0
    }
184
185
369M
    fn peek(&self) -> Option<u8> {
186
369M
        self.input.get(self.index).copied()
187
369M
    }
188
189
171M
    fn next(&mut self) -> Option<u8> {
190
171M
        self.peek().inspect(|_| self.index += 1)
191
171M
    }
192
193
    // Generic parse method for checking input before parsing
194
    // and handling trailing text error
195
7.73k
    fn parse_internal(
196
7.73k
        mut self,
197
7.73k
        f: impl FnOnce(&mut Self) -> Result<(), error::Repr>,
198
7.73k
    ) -> SFVResult<()> {
199
        // https://httpwg.org/specs/rfc9651.html#text-parse
200
201
7.73k
        self.consume_sp_chars();
202
203
7.73k
        f(&mut self)?;
204
205
3.22k
        self.consume_sp_chars();
206
207
3.22k
        if self.peek().is_some() {
208
0
            return Err(error::Repr::TrailingCharactersAfterParsedValue(self.index).into());
209
3.22k
        }
210
211
3.22k
        Ok(())
212
7.73k
    }
Unexecuted instantiation: <sfv::parser::Parser>::parse_internal::<<sfv::parser::Parser>::parse_item_with_visitor<&mut sfv::parsed::Item>::{closure#0}>
Unexecuted instantiation: <sfv::parser::Parser>::parse_internal::<<sfv::parser::Parser>::parse_list_with_visitor<alloc::vec::Vec<sfv::parsed::ListEntry>>::{closure#0}>
<sfv::parser::Parser>::parse_internal::<<sfv::parser::Parser>::parse_dictionary_with_visitor<indexmap::map::IndexMap<sfv::key::Key, sfv::parsed::ListEntry>>::{closure#0}>
Line
Count
Source
195
7.73k
    fn parse_internal(
196
7.73k
        mut self,
197
7.73k
        f: impl FnOnce(&mut Self) -> Result<(), error::Repr>,
198
7.73k
    ) -> SFVResult<()> {
199
        // https://httpwg.org/specs/rfc9651.html#text-parse
200
201
7.73k
        self.consume_sp_chars();
202
203
7.73k
        f(&mut self)?;
204
205
3.22k
        self.consume_sp_chars();
206
207
3.22k
        if self.peek().is_some() {
208
0
            return Err(error::Repr::TrailingCharactersAfterParsedValue(self.index).into());
209
3.22k
        }
210
211
3.22k
        Ok(())
212
7.73k
    }
213
214
344k
    fn parse_list_entry(&mut self, visitor: impl EntryVisitor<'de>) -> Result<(), error::Repr> {
215
        // https://httpwg.org/specs/rfc9651.html#parse-item-or-list
216
        // ListEntry represents a tuple (item_or_inner_list, parameters)
217
218
344k
        match self.peek() {
219
116k
            Some(b'(') => self.parse_inner_list(visitor.inner_list()?),
220
227k
            _ => parse_item(self, visitor),
221
        }
222
344k
    }
<sfv::parser::Parser>::parse_list_entry::<indexmap::map::core::entry::Entry<sfv::key::Key, sfv::parsed::ListEntry>>
Line
Count
Source
214
344k
    fn parse_list_entry(&mut self, visitor: impl EntryVisitor<'de>) -> Result<(), error::Repr> {
215
        // https://httpwg.org/specs/rfc9651.html#parse-item-or-list
216
        // ListEntry represents a tuple (item_or_inner_list, parameters)
217
218
344k
        match self.peek() {
219
116k
            Some(b'(') => self.parse_inner_list(visitor.inner_list()?),
220
227k
            _ => parse_item(self, visitor),
221
        }
222
344k
    }
Unexecuted instantiation: <sfv::parser::Parser>::parse_list_entry::<&mut alloc::vec::Vec<sfv::parsed::ListEntry>>
223
224
116k
    pub(crate) fn parse_inner_list(
225
116k
        &mut self,
226
116k
        mut visitor: impl InnerListVisitor<'de>,
227
116k
    ) -> Result<(), error::Repr> {
228
        // https://httpwg.org/specs/rfc9651.html#parse-innerlist
229
230
116k
        if Some(b'(') != self.peek() {
231
0
            return Err(error::Repr::ExpectedStartOfInnerList(self.index));
232
116k
        }
233
234
116k
        self.next();
235
236
3.81M
        while self.peek().is_some() {
237
3.81M
            self.consume_sp_chars();
238
239
3.81M
            if Some(b')') == self.peek() {
240
115k
                self.next();
241
115k
                let param_visitor = visitor.finish()?;
242
115k
                return self.parse_parameters(param_visitor);
243
3.70M
            }
244
245
3.70M
            parse_item(self, visitor.item()?)?;
246
247
3.70M
            if let Some(c) = self.peek() {
248
3.70M
                if c != b' ' && c != b')' {
249
265
                    return Err(error::Repr::ExpectedInnerListDelimiter(self.index));
250
3.70M
                }
251
391
            }
252
        }
253
254
432
        Err(error::Repr::UnterminatedInnerList(self.index))
255
116k
    }
256
257
4.04M
    pub(crate) fn parse_bare_item(&mut self) -> Result<BareItemFromInput<'de>, error::Repr> {
258
        // https://httpwg.org/specs/rfc9651.html#parse-bare-item
259
260
4.04M
        Ok(match self.peek() {
261
10.7k
            Some(b'?') => BareItemFromInput::Boolean(self.parse_bool()?),
262
99.9k
            Some(b'"') => BareItemFromInput::String(self.parse_string()?),
263
106k
            Some(b':') => BareItemFromInput::ByteSequence(self.parse_byte_sequence()?),
264
4.40k
            Some(b'@') => BareItemFromInput::Date(self.parse_date()?),
265
58.0k
            Some(b'%') => BareItemFromInput::DisplayString(self.parse_display_string()?),
266
3.76M
            Some(c) if utils::is_allowed_start_token_char(c) => {
267
3.25M
                BareItemFromInput::Token(self.parse_token()?)
268
            }
269
508k
            Some(c) if c == b'-' || c.is_ascii_digit() => match self.parse_number()? {
270
159k
                Num::Decimal(val) => BareItemFromInput::Decimal(val),
271
348k
                Num::Integer(val) => BareItemFromInput::Integer(val),
272
            },
273
225
            _ => return Err(error::Repr::ExpectedStartOfBareItem(self.index)),
274
        })
275
4.04M
    }
276
277
10.7k
    pub(crate) fn parse_bool(&mut self) -> Result<bool, error::Repr> {
278
        // https://httpwg.org/specs/rfc9651.html#parse-boolean
279
280
10.7k
        if self.peek() != Some(b'?') {
281
0
            return Err(error::Repr::ExpectedStartOfBoolean(self.index));
282
10.7k
        }
283
284
10.7k
        self.next();
285
286
10.7k
        match self.peek() {
287
            Some(b'0') => {
288
3.24k
                self.next();
289
3.24k
                Ok(false)
290
            }
291
            Some(b'1') => {
292
7.51k
                self.next();
293
7.51k
                Ok(true)
294
            }
295
26
            _ => Err(error::Repr::ExpectedBoolean(self.index)),
296
        }
297
10.7k
    }
298
299
99.9k
    pub(crate) fn parse_string(&mut self) -> Result<Cow<'de, StringRef>, error::Repr> {
300
        // https://httpwg.org/specs/rfc9651.html#parse-string
301
302
99.9k
        if self.peek() != Some(b'"') {
303
0
            return Err(error::Repr::ExpectedStartOfString(self.index));
304
99.9k
        }
305
306
99.9k
        self.next();
307
308
99.9k
        let start = self.index;
309
99.9k
        let mut output = Cow::Borrowed(&[] as &[u8]);
310
311
25.7M
        while let Some(curr_char) = self.peek() {
312
25.7M
            match curr_char {
313
                b'"' => {
314
99.4k
                    self.next();
315
                    // TODO: The UTF-8 validation is redundant with the preceding character checks, but
316
                    // its removal is only possible with unsafe code.
317
99.4k
                    return Ok(match output {
318
94.0k
                        Cow::Borrowed(output) => {
319
94.0k
                            let output = std::str::from_utf8(output).unwrap();
320
94.0k
                            Cow::Borrowed(StringRef::from_str(output).unwrap())
321
                        }
322
5.37k
                        Cow::Owned(output) => {
323
5.37k
                            let output = StdString::from_utf8(output).unwrap();
324
5.37k
                            Cow::Owned(String::from_string(output).unwrap())
325
                        }
326
                    });
327
                }
328
25.6M
                0x00..=0x1f | 0x7f..=0xff => {
329
100
                    return Err(error::Repr::InvalidStringCharacter(self.index));
330
                }
331
                b'\\' => {
332
16.5k
                    self.next();
333
16.5k
                    match self.peek() {
334
16.5k
                        Some(c @ (b'\\' | b'"')) => {
335
16.5k
                            self.next();
336
16.5k
                            output.to_mut().push(c);
337
16.5k
                        }
338
14
                        None => return Err(error::Repr::UnterminatedEscapeSequence(self.index)),
339
28
                        Some(_) => return Err(error::Repr::InvalidEscapeSequence(self.index)),
340
                    }
341
                }
342
                _ => {
343
25.6M
                    self.next();
344
25.6M
                    match output {
345
24.8M
                        Cow::Borrowed(ref mut output) => *output = &self.input[start..self.index],
346
833k
                        Cow::Owned(ref mut output) => output.push(curr_char),
347
                    }
348
                }
349
            }
350
        }
351
344
        Err(error::Repr::UnterminatedString(self.index))
352
99.9k
    }
353
354
7.51M
    fn parse_non_empty_str(
355
7.51M
        &mut self,
356
7.51M
        is_allowed_start_char: impl FnOnce(u8) -> bool,
357
7.51M
        is_allowed_inner_char: impl Fn(u8) -> bool,
358
7.51M
    ) -> Option<&'de str> {
359
7.51M
        let start = self.index;
360
361
7.51M
        match self.peek() {
362
7.51M
            Some(c) if is_allowed_start_char(c) => {
363
7.51M
                self.next();
364
7.51M
            }
365
355
            _ => return None,
366
        }
367
368
        loop {
369
82.3M
            match self.peek() {
370
82.3M
                Some(c) if is_allowed_inner_char(c) => {
371
74.8M
                    self.next();
372
74.8M
                }
373
                // TODO: The UTF-8 validation is redundant with the preceding character checks, but
374
                // its removal is only possible with unsafe code.
375
7.51M
                _ => return Some(std::str::from_utf8(&self.input[start..self.index]).unwrap()),
376
            }
377
        }
378
7.51M
    }
<sfv::parser::Parser>::parse_non_empty_str::<sfv::utils::is_allowed_start_key_char, sfv::utils::is_allowed_inner_key_char>
Line
Count
Source
354
4.25M
    fn parse_non_empty_str(
355
4.25M
        &mut self,
356
4.25M
        is_allowed_start_char: impl FnOnce(u8) -> bool,
357
4.25M
        is_allowed_inner_char: impl Fn(u8) -> bool,
358
4.25M
    ) -> Option<&'de str> {
359
4.25M
        let start = self.index;
360
361
4.25M
        match self.peek() {
362
4.25M
            Some(c) if is_allowed_start_char(c) => {
363
4.25M
                self.next();
364
4.25M
            }
365
355
            _ => return None,
366
        }
367
368
        loop {
369
68.2M
            match self.peek() {
370
68.2M
                Some(c) if is_allowed_inner_char(c) => {
371
64.0M
                    self.next();
372
64.0M
                }
373
                // TODO: The UTF-8 validation is redundant with the preceding character checks, but
374
                // its removal is only possible with unsafe code.
375
4.25M
                _ => return Some(std::str::from_utf8(&self.input[start..self.index]).unwrap()),
376
            }
377
        }
378
4.25M
    }
<sfv::parser::Parser>::parse_non_empty_str::<sfv::utils::is_allowed_start_token_char, sfv::utils::is_allowed_inner_token_char>
Line
Count
Source
354
3.25M
    fn parse_non_empty_str(
355
3.25M
        &mut self,
356
3.25M
        is_allowed_start_char: impl FnOnce(u8) -> bool,
357
3.25M
        is_allowed_inner_char: impl Fn(u8) -> bool,
358
3.25M
    ) -> Option<&'de str> {
359
3.25M
        let start = self.index;
360
361
3.25M
        match self.peek() {
362
3.25M
            Some(c) if is_allowed_start_char(c) => {
363
3.25M
                self.next();
364
3.25M
            }
365
0
            _ => return None,
366
        }
367
368
        loop {
369
14.0M
            match self.peek() {
370
14.0M
                Some(c) if is_allowed_inner_char(c) => {
371
10.8M
                    self.next();
372
10.8M
                }
373
                // TODO: The UTF-8 validation is redundant with the preceding character checks, but
374
                // its removal is only possible with unsafe code.
375
3.25M
                _ => return Some(std::str::from_utf8(&self.input[start..self.index]).unwrap()),
376
            }
377
        }
378
3.25M
    }
379
380
3.25M
    pub(crate) fn parse_token(&mut self) -> Result<&'de TokenRef, error::Repr> {
381
        // https://httpwg.org/specs/9651.html#parse-token
382
383
3.25M
        match self.parse_non_empty_str(
384
3.25M
            utils::is_allowed_start_token_char,
385
3.25M
            utils::is_allowed_inner_token_char,
386
3.25M
        ) {
387
0
            None => Err(error::Repr::ExpectedStartOfToken(self.index)),
388
3.25M
            Some(str) => Ok(TokenRef::from_validated_str(str)),
389
        }
390
3.25M
    }
391
392
106k
    pub(crate) fn parse_byte_sequence(&mut self) -> Result<Vec<u8>, error::Repr> {
393
        // https://httpwg.org/specs/rfc9651.html#parse-binary
394
395
106k
        if self.peek() != Some(b':') {
396
0
            return Err(error::Repr::ExpectedStartOfByteSequence(self.index));
397
106k
        }
398
399
106k
        self.next();
400
106k
        let start = self.index;
401
402
        loop {
403
26.5M
            match self.next() {
404
106k
                Some(b':') => break,
405
26.4M
                Some(_) => {}
406
45
                None => return Err(error::Repr::UnterminatedByteSequence(self.index)),
407
            }
408
        }
409
410
106k
        let colon_index = self.index - 1;
411
412
106k
        match base64::Engine::decode(&utils::BASE64, &self.input[start..colon_index]) {
413
106k
            Ok(content) => Ok(content),
414
661
            Err(err) => {
415
661
                let index = match err {
416
533
                    base64::DecodeError::InvalidByte(offset, _)
417
533
                    | base64::DecodeError::InvalidLastSymbol(offset, _) => start + offset,
418
                    // Report these two at the position of the last base64
419
                    // character, since they correspond to errors in the input
420
                    // as a whole.
421
                    base64::DecodeError::InvalidLength(_) | base64::DecodeError::InvalidPadding => {
422
128
                        colon_index - 1
423
                    }
424
                };
425
426
661
                Err(error::Repr::InvalidByteSequence(index))
427
            }
428
        }
429
106k
    }
430
431
512k
    pub(crate) fn parse_number(&mut self) -> Result<Num, error::Repr> {
432
        // https://httpwg.org/specs/rfc9651.html#parse-number
433
434
1.21M
        fn char_to_i64(c: u8) -> i64 {
435
1.21M
            i64::from(c - b'0')
436
1.21M
        }
437
438
512k
        let sign = if let Some(b'-') = self.peek() {
439
2.30k
            self.next();
440
2.30k
            -1
441
        } else {
442
510k
            1
443
        };
444
445
512k
        let mut magnitude = match self.peek() {
446
512k
            Some(c @ b'0'..=b'9') => {
447
512k
                self.next();
448
512k
                char_to_i64(c)
449
            }
450
59
            _ => return Err(error::Repr::ExpectedDigit(self.index)),
451
        };
452
453
512k
        let mut digits = 1;
454
455
        loop {
456
817k
            match self.peek() {
457
                Some(b'.') => {
458
159k
                    if digits > 12 {
459
3
                        return Err(error::Repr::TooManyDigitsBeforeDecimalPoint(self.index));
460
159k
                    }
461
159k
                    self.next();
462
159k
                    break;
463
                }
464
308k
                Some(c @ b'0'..=b'9') => {
465
304k
                    digits += 1;
466
304k
                    if digits > 15 {
467
15
                        return Err(error::Repr::TooManyDigits(self.index));
468
304k
                    }
469
304k
                    self.next();
470
304k
                    magnitude = magnitude * 10 + char_to_i64(c);
471
                }
472
353k
                _ => return Ok(Num::Integer(Integer::try_from(sign * magnitude).unwrap())),
473
            }
474
        }
475
476
159k
        magnitude *= 1000;
477
159k
        let mut scale = 100;
478
479
557k
        while let Some(c @ b'0'..=b'9') = self.peek() {
480
397k
            if scale == 0 {
481
24
                return Err(error::Repr::TooManyDigitsAfterDecimalPoint(self.index));
482
397k
            }
483
484
397k
            self.next();
485
397k
            magnitude += char_to_i64(c) * scale;
486
397k
            scale /= 10;
487
        }
488
489
159k
        if scale == 100 {
490
            // Report the error at the position of the decimal itself, rather
491
            // than the next position.
492
42
            Err(error::Repr::TrailingDecimalPoint(self.index - 1))
493
        } else {
494
159k
            Ok(Num::Decimal(Decimal::from_integer_scaled_1000(
495
159k
                Integer::try_from(sign * magnitude).unwrap(),
496
159k
            )))
497
        }
498
512k
    }
499
500
4.40k
    pub(crate) fn parse_date(&mut self) -> Result<Date, error::Repr> {
501
        // https://httpwg.org/specs/rfc9651.html#parse-date
502
503
4.40k
        if self.peek() != Some(b'@') {
504
0
            return Err(error::Repr::ExpectedStartOfDate(self.index));
505
4.40k
        }
506
507
4.40k
        match self.version {
508
0
            Version::Rfc8941 => return Err(error::Repr::Rfc8941Date(self.index)),
509
4.40k
            Version::Rfc9651 => {}
510
        }
511
512
4.40k
        let start = self.index;
513
4.40k
        self.next();
514
515
4.40k
        match self.parse_number()? {
516
4.36k
            Num::Integer(seconds) => Ok(Date::from_unix_seconds(seconds)),
517
2
            Num::Decimal(_) => Err(error::Repr::NonIntegerDate(start)),
518
        }
519
4.40k
    }
520
521
58.0k
    pub(crate) fn parse_display_string(&mut self) -> Result<Cow<'de, str>, error::Repr> {
522
        // https://httpwg.org/specs/rfc9651.html#parse-display
523
524
58.0k
        if self.peek() != Some(b'%') {
525
0
            return Err(error::Repr::ExpectedStartOfDisplayString(self.index));
526
58.0k
        }
527
528
58.0k
        match self.version {
529
0
            Version::Rfc8941 => return Err(error::Repr::Rfc8941DisplayString(self.index)),
530
58.0k
            Version::Rfc9651 => {}
531
        }
532
533
58.0k
        self.next();
534
535
58.0k
        if self.peek() != Some(b'"') {
536
58
            return Err(error::Repr::ExpectedQuote(self.index));
537
58.0k
        }
538
539
58.0k
        self.next();
540
541
58.0k
        let start = self.index;
542
58.0k
        let mut output = Cow::Borrowed(&[] as &[u8]);
543
544
26.5M
        while let Some(curr_char) = self.peek() {
545
26.5M
            match curr_char {
546
                b'"' => {
547
57.3k
                    self.next();
548
57.3k
                    return match output {
549
51.0k
                        Cow::Borrowed(output) => match std::str::from_utf8(output) {
550
51.0k
                            Ok(output) => Ok(Cow::Borrowed(output)),
551
0
                            Err(err) => Err(error::Repr::InvalidUtf8InDisplayString(
552
0
                                start + err.valid_up_to(),
553
0
                            )),
554
                        },
555
6.34k
                        Cow::Owned(output) => match StdString::from_utf8(output) {
556
6.19k
                            Ok(output) => Ok(Cow::Owned(output)),
557
149
                            Err(err) => Err(error::Repr::InvalidUtf8InDisplayString(
558
149
                                start + err.utf8_error().valid_up_to(),
559
149
                            )),
560
                        },
561
                    };
562
                }
563
26.4M
                0x00..=0x1f | 0x7f..=0xff => {
564
128
                    return Err(error::Repr::InvalidDisplayStringCharacter(self.index));
565
                }
566
                b'%' => {
567
13.6k
                    self.next();
568
569
13.6k
                    let mut octet = 0;
570
571
13.6k
                    for _ in 0..2 {
572
27.2k
                        octet = (octet << 4)
573
27.2k
                            + match self.peek() {
574
27.1k
                                Some(c @ b'0'..=b'9') => {
575
22.5k
                                    self.next();
576
22.5k
                                    c - b'0'
577
                                }
578
4.52k
                                Some(c @ b'a'..=b'f') => {
579
4.47k
                                    self.next();
580
4.47k
                                    c - b'a' + 10
581
                                }
582
                                None => {
583
47
                                    return Err(error::Repr::UnterminatedEscapeSequence(self.index))
584
                                }
585
                                Some(_) => {
586
117
                                    return Err(error::Repr::InvalidEscapeSequence(self.index))
587
                                }
588
                            };
589
                    }
590
591
13.4k
                    output.to_mut().push(octet);
592
                }
593
                _ => {
594
26.4M
                    self.next();
595
26.4M
                    match output {
596
24.1M
                        Cow::Borrowed(ref mut output) => *output = &self.input[start..self.index],
597
2.33M
                        Cow::Owned(ref mut output) => output.push(curr_char),
598
                    }
599
                }
600
            }
601
        }
602
327
        Err(error::Repr::UnterminatedDisplayString(self.index))
603
58.0k
    }
604
605
6.16M
    pub(crate) fn parse_parameters(
606
6.16M
        &mut self,
607
6.16M
        mut visitor: impl ParameterVisitor<'de>,
608
6.16M
    ) -> Result<(), error::Repr> {
609
        // https://httpwg.org/specs/rfc9651.html#parse-param
610
611
7.95M
        while let Some(b';') = self.peek() {
612
1.79M
            self.next();
613
1.79M
            self.consume_sp_chars();
614
615
1.79M
            let param_name = self.parse_key()?;
616
1.79M
            let param_value = match self.peek() {
617
                Some(b'=') => {
618
112k
                    self.next();
619
112k
                    self.parse_bare_item()?
620
                }
621
1.67M
                _ => BareItemFromInput::Boolean(true),
622
            };
623
            // Note: It is up to the visitor to properly handle duplicate keys.
624
1.79M
            visitor.parameter(param_name, param_value)?;
625
        }
626
627
6.16M
        visitor.finish()?;
628
6.16M
        Ok(())
629
6.16M
    }
630
631
4.25M
    pub(crate) fn parse_key(&mut self) -> Result<&'de KeyRef, error::Repr> {
632
        // https://httpwg.org/specs/rfc9651.html#parse-key
633
634
4.25M
        match self.parse_non_empty_str(
635
4.25M
            utils::is_allowed_start_key_char,
636
4.25M
            utils::is_allowed_inner_key_char,
637
4.25M
        ) {
638
355
            None => Err(error::Repr::ExpectedStartOfKey(self.index)),
639
4.25M
            Some(str) => Ok(KeyRef::from_validated_str(str)),
640
        }
641
4.25M
    }
642
643
4.92M
    fn consume_ows_chars(&mut self) {
644
4.94M
        while let Some(b' ' | b'\t') = self.peek() {
645
17.9k
            self.next();
646
17.9k
        }
647
4.92M
    }
648
649
5.62M
    fn consume_sp_chars(&mut self) {
650
9.48M
        while let Some(b' ') = self.peek() {
651
3.86M
            self.next();
652
3.86M
        }
653
5.62M
    }
654
655
    #[cfg(test)]
656
    pub(crate) fn remaining(&self) -> &[u8] {
657
        &self.input[self.index..]
658
    }
659
}