Skip to main content

parse_net/
param.rs

1//! Parameters: `*( OWS ";" OWS [ parameter ] )` (RFC 9110 section 5.6.6).
2//! [`parse_optional`] reads the RFC 6455 form, where a value may be left out.
3//!
4//! # Examples
5//!
6//! ```
7//! use parse_net::{param, Limits};
8//!
9//! let (p, used) = param::parse(b"; charset=utf-8;format=\"flowed\" , next", &Limits::DEFAULT)?;
10//! assert_eq!(used, 31);
11//! assert!(p.get("Charset").is_some_and(|v| v.eq_bytes(b"utf-8")));
12//! assert_eq!(format!("{p:?}"), "Params([\"charset\", \"format\"])");
13//! # Ok::<(), parse_net::ParseError>(())
14//! ```
15
16use core::fmt;
17
18use crate::{
19    class,
20    error::{ErrorKind, Result, err},
21    limits::{DuplicatePolicy, Limits},
22    scan::control_kind,
23    text::{Word, is_ows, parse_quoted, token_len, validated_word},
24};
25
26/// One parameter: a name and a value.
27///
28/// # Examples
29///
30/// ```
31/// use parse_net::{param, Limits, Param, Word};
32///
33/// let (p, _) = param::parse(b";charset=utf-8", &Limits::DEFAULT)?;
34/// assert_eq!(p.iter().next(), Some(Param { name: b"charset", value: Word::Token(b"utf-8") }));
35/// # Ok::<(), parse_net::ParseError>(())
36/// ```
37#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
38pub struct Param<'a> {
39    /// The name as sent (compare it ASCII case-insensitively).
40    pub name: &'a [u8],
41    /// The value.
42    pub value: Word<'a>,
43}
44
45/// The most parameters one element may have when repeated names are
46/// refused: RFC 9110 parameters under [`DuplicatePolicy::Refuse`],
47/// authentication parameters, and MIME parameters. Their name offsets are
48/// kept on the stack, so a repeat is found by comparing names in place,
49/// never by reading the parameters again (D-072). Without it, the check for
50/// a repeat reads every earlier parameter, values included, and a field of
51/// many parameters costs the square of its length.
52///
53/// # Examples
54///
55/// ```
56/// use parse_net::{param, DuplicatePolicy, ErrorKind, Limits};
57///
58/// let many: String = (0..65).map(|i| format!(";p{i}=v")).collect::<Vec<_>>().concat();
59/// let wide = Limits::DEFAULT.max_params(1000);
60/// assert_eq!(param::parse(many.as_bytes(), &wide).unwrap_err().kind(), ErrorKind::TooManyMembers);
61/// // `First` and `Last` do not look for repeats, so `max_params` applies.
62/// let last = wide.duplicate_params(DuplicatePolicy::Last);
63/// assert_eq!(param::parse(many.as_bytes(), &last)?.0.len(), 65);
64/// assert_eq!(param::MAX_CHECKED_PARAMS, 64);
65/// # Ok::<(), parse_net::ParseError>(())
66/// ```
67pub const MAX_CHECKED_PARAMS: usize = 64;
68
69/// How many name offsets [`Names`] keeps inline. Nearly every list fits, and
70/// the rest of the table is then never written.
71const INLINE_NAMES: usize = 8;
72
73/// The names already seen in one parameter list, for the repeat check: their
74/// offsets in the list's text, compared in place (D-072). The caller keeps
75/// at most [`MAX_CHECKED_PARAMS`] names. `rest` stays `None` until a list
76/// passes [`INLINE_NAMES`], so a short list does not pay to zero the table.
77pub(crate) struct Names {
78    first: [usize; INLINE_NAMES],
79    rest: Option<[usize; MAX_CHECKED_PARAMS - INLINE_NAMES]>,
80    len: usize,
81    seen: u64,
82}
83
84impl Names {
85    #[inline]
86    pub(crate) const fn new() -> Self {
87        Self {
88            first: [0; INLINE_NAMES],
89            rest: None,
90            len: 0,
91            seen: 0,
92        }
93    }
94
95    /// Whether `name`, at `off` in `text`, repeats a recorded name (ASCII
96    /// case-insensitive); records it when it does not. `in_name` is the
97    /// names' byte class: a recorded name ends at the first byte outside it.
98    #[inline]
99    pub(crate) fn repeats(
100        &mut self,
101        text: &[u8],
102        off: usize,
103        name: &[u8],
104        in_name: fn(u8) -> bool,
105    ) -> bool {
106        let bit = name_bit(name);
107        if self.seen & bit == 0 {
108            if let Some(slot) = self.first.get_mut(self.len) {
109                self.seen |= bit;
110                *slot = off;
111                self.len += 1;
112                return false;
113            }
114        }
115        self.repeats_slow(text, off, name, in_name, bit)
116    }
117
118    /// [`repeats`](Self::repeats) when the filter cannot rule a repeat out,
119    /// or the list has passed [`INLINE_NAMES`].
120    #[cold]
121    fn repeats_slow(
122        &mut self,
123        text: &[u8],
124        off: usize,
125        name: &[u8],
126        in_name: fn(u8) -> bool,
127        bit: u64,
128    ) -> bool {
129        if self.seen & bit != 0 {
130            let l = name.len();
131            let same = |&o: &usize| {
132                text.get(o + l).is_none_or(|&b| !in_name(b))
133                    && text
134                        .get(o..o + l)
135                        .is_some_and(|e| e.eq_ignore_ascii_case(name))
136            };
137            let inline = self.first.get(..self.len.min(INLINE_NAMES));
138            let spilled = self.len.saturating_sub(INLINE_NAMES);
139            let rest = self.rest.as_ref().and_then(|r| r.get(..spilled));
140            if inline.unwrap_or(&[]).iter().any(same) || rest.unwrap_or(&[]).iter().any(same) {
141                return true;
142            }
143        }
144        self.seen |= bit;
145        let slot = match self.len.checked_sub(INLINE_NAMES) {
146            None => self.first.get_mut(self.len),
147            Some(i) => self
148                .rest
149                .get_or_insert([0; MAX_CHECKED_PARAMS - INLINE_NAMES])
150                .get_mut(i),
151        };
152        if let Some(slot) = slot {
153            *slot = off;
154            self.len += 1;
155        }
156        false
157    }
158}
159
160/// A small filter over parameter names, so the table of earlier names is
161/// searched only when a repeat could exist.
162#[inline]
163fn name_bit(name: &[u8]) -> u64 {
164    let first = name.first().map_or(0, u8::to_ascii_lowercase);
165    let last = name.last().map_or(0, u8::to_ascii_lowercase);
166    let h = (name.len() as u64)
167        .wrapping_mul(31)
168        .wrapping_add(u64::from(first))
169        .wrapping_mul(31)
170        .wrapping_add(u64::from(last));
171    1u64 << (h & 63)
172}
173
174/// Parses parameters at the start of `s`.
175///
176/// `s` starts at the first `;` (optionally preceded by OWS) or is empty.
177/// Parsing continues as long as the grammar does; the view and the number of
178/// bytes used are returned (OWS after the last parameter is not used), and
179/// whatever follows is the caller's to check. Empty parameters (`;;`, a
180/// trailing `;`) are allowed, as the grammar says. Whitespace around `=` is
181/// not (BWS is not part of this grammar).
182///
183/// # Errors
184///
185/// [`ErrorKind::NotToken`] for a bad name; [`ErrorKind::MissingValue`] for a
186/// name with no `=` or no value; [`ErrorKind::InvalidByte`] for whitespace
187/// around `=`; the quoted-string errors; [`ErrorKind::DuplicateParameter`]
188/// under [`DuplicatePolicy::Refuse`]; [`ErrorKind::TooManyMembers`] beyond [`Limits::max_params`], and under
189/// [`DuplicatePolicy::Refuse`] beyond [`MAX_CHECKED_PARAMS`] as well.
190///
191/// # Examples
192///
193/// ```
194/// use parse_net::{param, ErrorKind, Limits};
195///
196/// let dup = param::parse(b";a=1;A=2", &Limits::DEFAULT).unwrap_err();
197/// assert_eq!((dup.kind(), dup.offset()), (ErrorKind::DuplicateParameter, 5));
198/// assert_eq!(param::parse(b";a =1", &Limits::DEFAULT).unwrap_err().kind(), ErrorKind::InvalidByte);
199/// ```
200pub fn parse<'a>(s: &'a [u8], limits: &Limits) -> Result<(Params<'a>, usize)> {
201    walk::<false>(s, limits)
202}
203
204/// Parses parameters whose value may be left out, at the start of `s`:
205/// `*( OWS ";" OWS [ token [ "=" ( token / quoted-string ) ] ] )`, the
206/// `extension-param` of RFC 6455 section 9.1. permessage-deflate (RFC 7692)
207/// sends `client_max_window_bits` and `server_no_context_takeover` this way.
208///
209/// Everything else is as [`parse`]: the same names, values, empty
210/// parameters, bytes used, count limits, and duplicate policy, and no
211/// whitespace around `=`. A name followed by OWS and then anything but `=`
212/// is a parameter without a value; what follows it is the caller's, as after
213/// any parameter. Wherever [`parse`] succeeds, this succeeds with the same
214/// parameters. RFC 6455 also asks that a quoted value unescape to a token;
215/// that check is the caller's.
216///
217/// # Errors
218///
219/// As [`parse`], except that a name with no `=` is not an error.
220/// [`ErrorKind::MissingValue`] remains for `=` with no value, and for a
221/// quoted string straight after a name.
222///
223/// # Examples
224///
225/// ```
226/// use parse_net::{param, ErrorKind, Limits};
227///
228/// let offer = b"; client_max_window_bits; server_max_window_bits=10, x-webkit";
229/// let (p, used) = param::parse_optional(offer, &Limits::DEFAULT)?;
230/// assert_eq!((p.len(), used), (2, 51));
231/// assert!(p.find("client_max_window_bits").is_some_and(|x| x.value.is_none()));
232/// assert!(p.find("Server_Max_Window_Bits").is_some_and(|x| x.value.is_some_and(|v| v.eq_bytes(b"10"))));
233/// assert!(!p.contains("server_no_context_takeover"));
234///
235/// let dup = param::parse_optional(b";a;A=1", &Limits::DEFAULT).unwrap_err();
236/// assert_eq!((dup.kind(), dup.offset()), (ErrorKind::DuplicateParameter, 3));
237/// assert_eq!(param::parse_optional(b";a=", &Limits::DEFAULT).unwrap_err().kind(), ErrorKind::MissingValue);
238/// # Ok::<(), parse_net::ParseError>(())
239/// ```
240pub fn parse_optional<'a>(s: &'a [u8], limits: &Limits) -> Result<(OptionalParams<'a>, usize)> {
241    let (p, used) = walk::<true>(s, limits)?;
242    Ok((
243        OptionalParams {
244            text: p.text,
245            count: p.count,
246            policy: p.policy,
247        },
248        used,
249    ))
250}
251
252/// The walker of [`parse`] and [`parse_optional`]. `OPTIONAL` lets a name
253/// stand without `=`; [`parse`] compiles without that branch. The result is
254/// [`parse`]'s, so that path builds nothing more; [`parse_optional`] moves
255/// the fields into its own type.
256#[inline]
257fn walk<'a, const OPTIONAL: bool>(s: &'a [u8], limits: &Limits) -> Result<(Params<'a>, usize)> {
258    let policy = limits.duplicate_params;
259    let refuse = policy == DuplicatePolicy::Refuse;
260    let max = if refuse {
261        usize::from(limits.params).min(MAX_CHECKED_PARAMS)
262    } else {
263        usize::from(limits.params)
264    };
265    // The cursor is a shrinking slice; an offset is computed only for an
266    // error or the result. `done` follows the last complete parameter.
267    let at = |rest: &[u8]| s.len() - rest.len();
268    let (mut done, mut count) = (s, 0usize);
269    let mut names = Names::new();
270    while let [b';', after @ ..] = skip_ws(done) {
271        let r = skip_ws(after);
272        let n = token_len(r);
273        if n == 0 {
274            match r.first() {
275                None | Some(b';') => {
276                    done = r;
277                    continue;
278                }
279                // OWS after an empty parameter followed by a list separator
280                // belongs to the caller.
281                Some(b',') => {
282                    done = after;
283                    break;
284                }
285                Some(&b) => {
286                    let kind = if crate::scan::is_control(b) {
287                        control_kind(b)
288                    } else {
289                        ErrorKind::NotToken
290                    };
291                    return err(kind, at(r));
292                }
293            }
294        }
295        let name_at = at(r);
296        let (name, r) = r.split_at(n);
297        // A name with no value ends at the end, `;`, `,`, or OWS that is not
298        // before `=`. Constant false for `parse`, so its walk is unchanged.
299        let bare = OPTIONAL
300            && match r {
301                [] | [b';' | b',', ..] => true,
302                [b, ..] if is_ows(*b) => skip_ws(r).first() != Some(&b'='),
303                _ => false,
304            };
305        // `next` follows the whole parameter.
306        let next = if bare {
307            r
308        } else {
309            let value = match r {
310                [b'=', value @ ..] => value,
311                [b, ..] if is_ows(*b) => {
312                    let kind = if skip_ws(r).first() == Some(&b'=') {
313                        ErrorKind::InvalidByte
314                    } else {
315                        ErrorKind::MissingValue
316                    };
317                    return err(kind, at(r));
318                }
319                [b'"', ..] => return err(ErrorKind::MissingValue, at(r)),
320                [b, ..] if *b != b';' && *b != b',' && class::is_field_text(*b) => {
321                    return err(ErrorKind::NotToken, at(r));
322                }
323                [b, ..] if !class::is_field_text(*b) => return err(control_kind(*b), at(r)),
324                _ => return err(ErrorKind::MissingValue, at(r)),
325            };
326            let used = match value {
327                [b, ..] if is_ows(*b) => return err(ErrorKind::InvalidByte, at(value)),
328                [] | [b';' | b',', ..] => return err(ErrorKind::MissingValue, at(value)),
329                [b'"', ..] => parse_quoted(value).map_err(|e| e.shifted(at(value)))?.1,
330                [b, ..] => match token_len(value) {
331                    0 => return err(control_or_invalid(*b), at(value)),
332                    n => n,
333                },
334            };
335            value.get(used..).unwrap_or(&[])
336        };
337        count += 1;
338        if count > max {
339            return err(ErrorKind::TooManyMembers, name_at);
340        }
341        if refuse && names.repeats(s, name_at, name, class::is_tchar) {
342            return err(ErrorKind::DuplicateParameter, name_at);
343        }
344        done = next;
345    }
346    let used = at(done);
347    Ok((
348        Params {
349            text: s.get(..used).unwrap_or(&[]),
350            count: u32::try_from(count).unwrap_or(u32::MAX),
351            policy,
352        },
353        used,
354    ))
355}
356
357/// Skips SP and HTAB.
358#[inline]
359fn skip_ws(s: &[u8]) -> &[u8] {
360    match s {
361        [b' ' | b'\t', ..] => {
362            let n = s
363                .iter()
364                .position(|&b| b != b' ' && b != b'\t')
365                .unwrap_or(s.len());
366            s.get(n..).unwrap_or(&[])
367        }
368        _ => s,
369    }
370}
371
372#[inline]
373const fn control_or_invalid(b: u8) -> ErrorKind {
374    if crate::scan::is_control(b) {
375        control_kind(b)
376    } else {
377        ErrorKind::InvalidByte
378    }
379}
380
381/// Validated parameters. Iterating them cannot fail.
382///
383/// `Debug` prints the parameter names only: values can be secrets.
384///
385/// # Examples
386///
387/// ```
388/// use parse_net::{param, Limits};
389///
390/// let (p, used) = param::parse(b";q=1;level=\"2\"", &Limits::DEFAULT)?;
391/// assert_eq!((p.len(), used), (2, 14));
392/// assert!(p.get("level").is_some_and(|v| v.eq_bytes(b"2")));
393/// # Ok::<(), parse_net::ParseError>(())
394/// ```
395#[derive(Clone, Copy, PartialEq, Eq, Hash)]
396pub struct Params<'a> {
397    pub(crate) text: &'a [u8],
398    pub(crate) count: u32,
399    pub(crate) policy: DuplicatePolicy,
400}
401
402impl<'a> Params<'a> {
403    /// Every parameter, in order (with `First` or `Last`, duplicates
404    /// included).
405    ///
406    /// # Examples
407    ///
408    /// ```
409    /// use parse_net::{param, Limits};
410    ///
411    /// let (p, _) = param::parse(b";a=1; B=2", &Limits::DEFAULT)?;
412    /// let names: Vec<&[u8]> = p.iter().map(|p| p.name).collect();
413    /// assert_eq!(names, [&b"a"[..], b"B"]);
414    /// # Ok::<(), parse_net::ParseError>(())
415    /// ```
416    #[must_use]
417    pub fn iter(&self) -> ParamIter<'a> {
418        ParamIter { rest: self.text }
419    }
420
421    /// The value of the parameter named `name` (ASCII case-insensitive): the
422    /// first match, or the last under [`DuplicatePolicy::Last`].
423    ///
424    /// # Examples
425    ///
426    /// ```
427    /// use parse_net::{param, Limits};
428    ///
429    /// let (p, _) = param::parse(b";charset=UTF-8", &Limits::DEFAULT)?;
430    /// assert!(p.get("CHARSET").is_some_and(|v| v.eq_bytes(b"UTF-8")));
431    /// assert!(p.get("q").is_none());
432    /// # Ok::<(), parse_net::ParseError>(())
433    /// ```
434    #[must_use]
435    pub fn get(&self, name: &str) -> Option<Word<'a>> {
436        let name = name.as_bytes();
437        let last = self.policy == DuplicatePolicy::Last;
438        let mut found = None;
439        // Validated text: OWS ";" OWS [ name "=" word ] ... When `tail` starts
440        // with a token byte it starts a parameter name, which ends at its
441        // first `=`; so `name` followed by `=` there is that whole name when
442        // `name` holds no `=`. (An empty parameter starts `tail` with `;`.)
443        // No other byte is checked again.
444        let mut rest = self.text;
445        while let [b';', after @ ..] = skip_ws(rest) {
446            let tail = skip_ws(after);
447            let value = match tail.split_at_checked(name.len()) {
448                // An exact compare first: names are usually sent in the case
449                // asked for.
450                Some((head, [b'=', value @ ..]))
451                    if (head == name || head.eq_ignore_ascii_case(name))
452                        && head.first().is_some_and(|&b| class::is_tchar(b))
453                        && !name.contains(&b'=') =>
454                {
455                    let (word, used) = validated_word(value);
456                    if !last {
457                        return Some(word);
458                    }
459                    found = Some(word);
460                    rest = value.get(used..).unwrap_or(&[]);
461                    continue;
462                }
463                _ => match token_len(tail) {
464                    0 => {
465                        rest = tail;
466                        continue;
467                    }
468                    n => tail.get(n + 1..).unwrap_or(&[]),
469                },
470            };
471            rest = value.get(validated_word(value).1..).unwrap_or(&[]);
472        }
473        found
474    }
475
476    /// The parameters before the first one named `name` (ASCII
477    /// case-insensitive), and that parameter; all of them and `None` when no
478    /// parameter has the name. Used to split media-type parameters from
479    /// `q` and the `accept-ext` after it.
480    pub(crate) fn split_at_name(&self, name: &[u8]) -> (Params<'a>, Option<Param<'a>>) {
481        for (index, p) in self.iter().enumerate() {
482            if p.name.eq_ignore_ascii_case(name) {
483                // `p.name` points into `self.text`; the distance is its offset.
484                let off = (p.name.as_ptr() as usize).saturating_sub(self.text.as_ptr() as usize);
485                let head = self.text.get(..off).unwrap_or(&[]);
486                let semi = head.iter().rposition(|&b| b == b';').unwrap_or(0);
487                let mut text = self.text.get(..semi).unwrap_or(&[]);
488                while let [rest @ .., b' ' | b'\t'] = text {
489                    text = rest;
490                }
491                let before = Params {
492                    text,
493                    count: u32::try_from(index).unwrap_or(u32::MAX),
494                    policy: self.policy,
495                };
496                return (before, Some(p));
497            }
498        }
499        (*self, None)
500    }
501
502    /// The number of parameters.
503    ///
504    /// # Examples
505    ///
506    /// ```
507    /// use parse_net::{param, Limits};
508    ///
509    /// assert_eq!(param::parse(b";a=1;;b=2;", &Limits::DEFAULT)?.0.len(), 2);
510    /// # Ok::<(), parse_net::ParseError>(())
511    /// ```
512    #[must_use]
513    pub const fn len(&self) -> usize {
514        self.count as usize
515    }
516
517    /// Whether there are no parameters.
518    ///
519    /// # Examples
520    ///
521    /// ```
522    /// use parse_net::{param, Limits};
523    ///
524    /// assert!(param::parse(b"", &Limits::DEFAULT)?.0.is_empty());
525    /// assert!(!param::parse(b";a=1", &Limits::DEFAULT)?.0.is_empty());
526    /// # Ok::<(), parse_net::ParseError>(())
527    /// ```
528    #[must_use]
529    pub const fn is_empty(&self) -> bool {
530        self.count == 0
531    }
532
533    /// The validated text, from the first `;` to the end of the last
534    /// parameter.
535    ///
536    /// # Examples
537    ///
538    /// ```
539    /// use parse_net::{param, Limits};
540    ///
541    /// let (p, _) = param::parse(b";a=1 ;b=2 , next", &Limits::DEFAULT)?;
542    /// assert_eq!(p.raw(), b";a=1 ;b=2");
543    /// # Ok::<(), parse_net::ParseError>(())
544    /// ```
545    #[must_use]
546    pub const fn raw(&self) -> &'a [u8] {
547        self.text
548    }
549}
550
551impl fmt::Debug for Params<'_> {
552    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
553        f.write_str("Params(")?;
554        f.debug_list()
555            .entries(
556                self.iter()
557                    .map(|p| core::str::from_utf8(p.name).unwrap_or("?")),
558            )
559            .finish()?;
560        f.write_str(")")
561    }
562}
563
564/// The parameters of a validated [`Params`].
565///
566/// # Examples
567///
568/// ```
569/// use parse_net::{param, Limits};
570///
571/// let (p, _) = param::parse(b";a=1;b=2", &Limits::DEFAULT)?;
572/// let mut it = p.iter();
573/// assert_eq!(it.next().map(|p| p.name), Some(&b"a"[..]));
574/// assert_eq!(it.next().map(|p| p.name), Some(&b"b"[..]));
575/// assert!(it.next().is_none());
576/// # Ok::<(), parse_net::ParseError>(())
577/// ```
578#[derive(Clone, Debug)]
579pub struct ParamIter<'a> {
580    rest: &'a [u8],
581}
582
583impl<'a> Iterator for ParamIter<'a> {
584    type Item = Param<'a>;
585
586    fn next(&mut self) -> Option<Param<'a>> {
587        loop {
588            // Validated text: OWS ";" OWS [ name "=" word ] ...
589            let [b';', after @ ..] = skip_ws(self.rest) else {
590                self.rest = &[];
591                return None;
592            };
593            let tail = skip_ws(after);
594            let n = token_len(tail);
595            let Some((name, [_, value_text @ ..])) = (n > 0).then(|| tail.split_at(n)) else {
596                self.rest = tail;
597                continue;
598            };
599            let (value, used) = validated_word(value_text);
600            self.rest = value_text.get(used..).unwrap_or(&[]);
601            return Some(Param { name, value });
602        }
603    }
604}
605
606/// One parameter of [`parse_optional`]: a name and, when one was sent, a
607/// value.
608///
609/// # Examples
610///
611/// ```
612/// use parse_net::{param, Limits, Word};
613///
614/// let (p, _) = param::parse_optional(b";a;b=1", &Limits::DEFAULT)?;
615/// let all: Vec<_> = p.iter().collect();
616/// assert_eq!(all, [
617///     param::OptionalParam { name: b"a", value: None },
618///     param::OptionalParam { name: b"b", value: Some(Word::Token(b"1")) },
619/// ]);
620/// # Ok::<(), parse_net::ParseError>(())
621/// ```
622#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
623pub struct OptionalParam<'a> {
624    /// The name as sent (compare it ASCII case-insensitively).
625    pub name: &'a [u8],
626    /// The value, or `None` for a name sent without `=`.
627    pub value: Option<Word<'a>>,
628}
629
630/// Validated parameters from [`parse_optional`]. Iterating them cannot fail.
631///
632/// `Debug` prints the parameter names only: values can be secrets.
633///
634/// # Examples
635///
636/// ```
637/// use parse_net::{param, Limits};
638///
639/// let (p, used) = param::parse_optional(b"; server_no_context_takeover", &Limits::DEFAULT)?;
640/// assert_eq!((p.len(), used), (1, 28));
641/// assert!(p.contains("server_no_context_takeover"));
642/// assert_eq!(format!("{p:?}"), "OptionalParams([\"server_no_context_takeover\"])");
643/// # Ok::<(), parse_net::ParseError>(())
644/// ```
645#[derive(Clone, Copy, PartialEq, Eq, Hash)]
646pub struct OptionalParams<'a> {
647    text: &'a [u8],
648    count: u32,
649    policy: DuplicatePolicy,
650}
651
652impl<'a> OptionalParams<'a> {
653    /// Every parameter, in order (with `First` or `Last`, duplicates
654    /// included).
655    ///
656    /// # Examples
657    ///
658    /// ```
659    /// use parse_net::{param, Limits};
660    ///
661    /// let (p, _) = param::parse_optional(b";a; B=2", &Limits::DEFAULT)?;
662    /// let names: Vec<&[u8]> = p.iter().map(|p| p.name).collect();
663    /// assert_eq!(names, [&b"a"[..], b"B"]);
664    /// # Ok::<(), parse_net::ParseError>(())
665    /// ```
666    #[must_use]
667    pub fn iter(&self) -> OptionalParamIter<'a> {
668        OptionalParamIter { rest: self.text }
669    }
670
671    /// The parameter named `name` (ASCII case-insensitive): the first match,
672    /// or the last under [`DuplicatePolicy::Last`]. Its `value` tells a name
673    /// sent alone from one sent with a value.
674    ///
675    /// # Examples
676    ///
677    /// ```
678    /// use parse_net::{param, Limits};
679    ///
680    /// let (p, _) = param::parse_optional(b";client_max_window_bits;server_max_window_bits=10", &Limits::DEFAULT)?;
681    /// assert_eq!(p.find("CLIENT_MAX_WINDOW_BITS").map(|x| x.value), Some(None));
682    /// assert!(p.find("server_max_window_bits").and_then(|x| x.value).is_some_and(|v| v.eq_bytes(b"10")));
683    /// assert!(p.find("q").is_none());
684    /// # Ok::<(), parse_net::ParseError>(())
685    /// ```
686    #[must_use]
687    pub fn find(&self, name: &str) -> Option<OptionalParam<'a>> {
688        let name = name.as_bytes();
689        let mut matching = self.iter().filter(|p| p.name.eq_ignore_ascii_case(name));
690        if self.policy == DuplicatePolicy::Last {
691            matching.last()
692        } else {
693            matching.next()
694        }
695    }
696
697    /// Whether a parameter is named `name` (ASCII case-insensitive), with a
698    /// value or without.
699    ///
700    /// # Examples
701    ///
702    /// ```
703    /// use parse_net::{param, Limits};
704    ///
705    /// let (p, _) = param::parse_optional(b";server_no_context_takeover", &Limits::DEFAULT)?;
706    /// assert!(p.contains("Server_No_Context_Takeover"));
707    /// assert!(!p.contains("client_no_context_takeover"));
708    /// # Ok::<(), parse_net::ParseError>(())
709    /// ```
710    #[must_use]
711    pub fn contains(&self, name: &str) -> bool {
712        let name = name.as_bytes();
713        self.iter().any(|p| p.name.eq_ignore_ascii_case(name))
714    }
715
716    /// The number of parameters.
717    ///
718    /// # Examples
719    ///
720    /// ```
721    /// use parse_net::{param, Limits};
722    ///
723    /// assert_eq!(param::parse_optional(b";a;;b=2;", &Limits::DEFAULT)?.0.len(), 2);
724    /// # Ok::<(), parse_net::ParseError>(())
725    /// ```
726    #[must_use]
727    pub const fn len(&self) -> usize {
728        self.count as usize
729    }
730
731    /// Whether there are no parameters.
732    ///
733    /// # Examples
734    ///
735    /// ```
736    /// use parse_net::{param, Limits};
737    ///
738    /// assert!(param::parse_optional(b"", &Limits::DEFAULT)?.0.is_empty());
739    /// assert!(!param::parse_optional(b";a", &Limits::DEFAULT)?.0.is_empty());
740    /// # Ok::<(), parse_net::ParseError>(())
741    /// ```
742    #[must_use]
743    pub const fn is_empty(&self) -> bool {
744        self.count == 0
745    }
746
747    /// The validated text, from the first `;` to the end of the last
748    /// parameter.
749    ///
750    /// # Examples
751    ///
752    /// ```
753    /// use parse_net::{param, Limits};
754    ///
755    /// let (p, _) = param::parse_optional(b";a ;b=2 , next", &Limits::DEFAULT)?;
756    /// assert_eq!(p.raw(), b";a ;b=2");
757    /// # Ok::<(), parse_net::ParseError>(())
758    /// ```
759    #[must_use]
760    pub const fn raw(&self) -> &'a [u8] {
761        self.text
762    }
763}
764
765impl fmt::Debug for OptionalParams<'_> {
766    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
767        f.write_str("OptionalParams(")?;
768        f.debug_list()
769            .entries(
770                self.iter()
771                    .map(|p| core::str::from_utf8(p.name).unwrap_or("?")),
772            )
773            .finish()?;
774        f.write_str(")")
775    }
776}
777
778/// The parameters of a validated [`OptionalParams`].
779///
780/// # Examples
781///
782/// ```
783/// use parse_net::{param, Limits};
784///
785/// let (p, _) = param::parse_optional(b";a;b=2", &Limits::DEFAULT)?;
786/// let mut it = p.iter();
787/// assert_eq!(it.next().map(|p| p.value.is_none()), Some(true));
788/// assert_eq!(it.next().map(|p| p.name), Some(&b"b"[..]));
789/// assert!(it.next().is_none());
790/// # Ok::<(), parse_net::ParseError>(())
791/// ```
792#[derive(Clone, Debug)]
793pub struct OptionalParamIter<'a> {
794    rest: &'a [u8],
795}
796
797impl<'a> Iterator for OptionalParamIter<'a> {
798    type Item = OptionalParam<'a>;
799
800    fn next(&mut self) -> Option<OptionalParam<'a>> {
801        loop {
802            // Validated text: OWS ";" OWS [ name [ "=" word ] ] ...
803            let [b';', after @ ..] = skip_ws(self.rest) else {
804                self.rest = &[];
805                return None;
806            };
807            let tail = skip_ws(after);
808            let n = token_len(tail);
809            if n == 0 {
810                self.rest = tail;
811                continue;
812            }
813            let (name, r) = tail.split_at(n);
814            let [b'=', value_text @ ..] = r else {
815                self.rest = r;
816                return Some(OptionalParam { name, value: None });
817            };
818            let (value, used) = validated_word(value_text);
819            self.rest = value_text.get(used..).unwrap_or(&[]);
820            return Some(OptionalParam {
821                name,
822                value: Some(value),
823            });
824        }
825    }
826}
827
828#[cfg(test)]
829mod tests {
830    use std::{format, string::String, vec, vec::Vec};
831
832    use proptest::prelude::{Just, Strategy, prop, prop_assert_eq, prop_oneof};
833
834    use super::*;
835
836    fn p(s: &[u8]) -> Result<(Params<'_>, usize)> {
837        parse(s, &Limits::DEFAULT)
838    }
839
840    fn names<'a>(ps: &Params<'a>) -> Vec<&'a [u8]> {
841        ps.iter().map(|x| x.name).collect()
842    }
843
844    #[test]
845    fn test_params_edge_cases() {
846        assert_eq!(p(b"").unwrap().1, 0);
847        assert!(p(b";").unwrap().0.is_empty());
848        assert_eq!(p(b";").unwrap().1, 1);
849        assert_eq!(p(b";;").unwrap().1, 2);
850        let (ps, used) = p(b"; a=b").unwrap();
851        assert_eq!((used, names(&ps)), (5, [&b"a"[..]].to_vec()));
852        assert_eq!(p(b";a=").unwrap_err().kind(), ErrorKind::MissingValue);
853        assert_eq!(p(b";a").unwrap_err().kind(), ErrorKind::MissingValue);
854        assert_eq!(p(b";a;b=c").unwrap_err().kind(), ErrorKind::MissingValue);
855        assert_eq!(p(b";a =b").unwrap_err().kind(), ErrorKind::InvalidByte);
856        assert_eq!(p(b";a= b").unwrap_err().kind(), ErrorKind::InvalidByte);
857        assert_eq!(p(b";@=b").unwrap_err().kind(), ErrorKind::NotToken);
858        assert_eq!(p(b";a@=b").unwrap_err().kind(), ErrorKind::NotToken);
859        assert_eq!(p(b";a=\"\"").unwrap().0.get("a").map(|w| w.len()), Some(0));
860        assert!(
861            p(b";a=\"x\\\"y\"")
862                .unwrap()
863                .0
864                .get("a")
865                .unwrap()
866                .eq_bytes(b"x\"y")
867        );
868        assert_eq!(
869            p(b";a=\"x").unwrap_err().kind(),
870            ErrorKind::UnterminatedQuote
871        );
872        assert_eq!(p(b";a=b\rc").unwrap().1, 4);
873        assert_eq!(p(b";a=\x01").unwrap_err().kind(), ErrorKind::Control);
874    }
875
876    #[test]
877    fn test_params_stop_at_list_separator_and_trailing_ows() {
878        let (ps, used) = p(b";a=1 , b").unwrap();
879        assert_eq!((used, ps.len()), (4, 1));
880        let (_, used) = p(b";a=1; ,b").unwrap();
881        assert_eq!(used, 5);
882        let (_, used) = p(b" ;a=1 x").unwrap();
883        assert_eq!(used, 5);
884    }
885
886    #[test]
887    fn test_params_duplicate_refused_case_insensitive() {
888        let e = p(b";A=b;a=c").unwrap_err();
889        assert_eq!((e.kind(), e.offset()), (ErrorKind::DuplicateParameter, 5));
890        assert!(p(b";ab=1;ba=2;aa=3;bb=4").is_ok());
891    }
892
893    #[test]
894    fn test_params_first_and_last_policies() {
895        let first = Limits::DEFAULT.duplicate_params(DuplicatePolicy::First);
896        let last = Limits::DEFAULT.duplicate_params(DuplicatePolicy::Last);
897        let (a, _) = parse(b";a=1;A=2", &first).unwrap();
898        let (b, _) = parse(b";a=1;A=2", &last).unwrap();
899        assert!(a.get("a").unwrap().eq_bytes(b"1"));
900        assert!(b.get("a").unwrap().eq_bytes(b"2"));
901        assert_eq!(a.len(), 2);
902    }
903
904    #[test]
905    fn test_params_count_limit() {
906        let mut v = Vec::new();
907        for i in 0..257 {
908            v.extend_from_slice(format!(";p{i}=v").as_bytes());
909        }
910        let cut = v.len() - format!(";p{}=v", 256).len();
911        // `First` and `Last` read up to `max_params` (256 by default).
912        for policy in [DuplicatePolicy::First, DuplicatePolicy::Last] {
913            let l = Limits::DEFAULT.duplicate_params(policy);
914            assert_eq!(parse(&v[..cut], &l).unwrap().0.len(), 256);
915            assert_eq!(parse(&v, &l).unwrap_err().kind(), ErrorKind::TooManyMembers);
916        }
917        // `Refuse` stops at the size of its name table, whatever the limit.
918        let upto = |n: usize| {
919            (0..n)
920                .map(|i| format!(";p{i}=v"))
921                .collect::<Vec<_>>()
922                .concat()
923        };
924        assert_eq!(p(upto(MAX_CHECKED_PARAMS).as_bytes()).unwrap().0.len(), 64);
925        let over = upto(MAX_CHECKED_PARAMS + 1);
926        let e = p(over.as_bytes()).unwrap_err();
927        let last = over.rfind(';').unwrap() + 1;
928        assert_eq!((e.kind(), e.offset()), (ErrorKind::TooManyMembers, last));
929        let wide = Limits::DEFAULT.max_params(u16::MAX);
930        assert_eq!(
931            parse(over.as_bytes(), &wide).unwrap_err().kind(),
932            ErrorKind::TooManyMembers
933        );
934        assert_eq!(
935            parse(upto(3).as_bytes(), &Limits::DEFAULT.max_params(2))
936                .unwrap_err()
937                .kind(),
938            ErrorKind::TooManyMembers
939        );
940    }
941
942    #[test]
943    fn test_params_duplicate_found_in_every_table_slot() {
944        // A repeat of each of the 64 names is found, in either case, and a
945        // name that is a prefix or an extension of an earlier one is not a
946        // repeat.
947        let base = (0..63)
948            .map(|i| format!(";p{i}=\"v;p{i}=w\""))
949            .collect::<Vec<_>>()
950            .concat();
951        for i in 0..63 {
952            let dup = format!("{base};P{i}=x");
953            let e = p(dup.as_bytes()).unwrap_err();
954            assert_eq!(
955                (e.kind(), e.offset()),
956                (ErrorKind::DuplicateParameter, base.len() + 1),
957                "{i}"
958            );
959        }
960        for other in ["p", "p1x", "xp1", "p100"] {
961            assert!(p(format!("{base};{other}=x").as_bytes()).is_ok(), "{other}");
962        }
963    }
964
965    #[test]
966    fn test_params_debug_prints_names_only() {
967        let (ps, _) = p(b";user=alice;token=\"s3cr3t\"").unwrap();
968        let d = format!("{ps:?}");
969        assert_eq!(d, "Params([\"user\", \"token\"])");
970        assert!(!d.contains("s3cr3t") && !d.contains("alice"));
971    }
972
973    #[test]
974    fn test_params_iter_skips_empty_parameters() {
975        let (ps, _) = p(b";; a=1 ;;b=\"x;y\";").unwrap();
976        assert_eq!(names(&ps), [&b"a"[..], b"b"]);
977        assert!(ps.get("b").unwrap().eq_bytes(b"x;y"));
978        assert_eq!(ps.raw(), b";; a=1 ;;b=\"x;y\";");
979    }
980
981    #[test]
982    fn test_params_get_matches_whole_names_only() {
983        // Found by `test_iter_and_get_agree_with_checked_reads`: an empty
984        // parameter must not let `;a` match across the second `;`.
985        let (ps, _) = p(b";;a=\"\"").unwrap();
986        assert!(ps.get(";a").is_none());
987        assert!(ps.get("a").is_some_and(|v| v.eq_bytes(b"")));
988        let (ps, _) = p(b";a=b;c=d").unwrap();
989        for name in ["a=b", "a=b;c", "", ";", "=", "a;", " a"] {
990            assert!(ps.get(name).is_none(), "{name}");
991        }
992        assert!(ps.get("C").is_some_and(|v| v.eq_bytes(b"d")));
993    }
994
995    fn po(s: &[u8]) -> Result<(OptionalParams<'_>, usize)> {
996        parse_optional(s, &Limits::DEFAULT)
997    }
998
999    fn values<'a>(ps: &OptionalParams<'a>) -> Vec<(&'a [u8], Option<&'a [u8]>)> {
1000        ps.iter()
1001            .map(|x| (x.name, x.value.map(|v| v.as_plain().unwrap())))
1002            .collect()
1003    }
1004
1005    #[test]
1006    fn test_optional_params_permessage_deflate_offers() {
1007        // RFC 7692 section 7.1 offers, as ws-net reads them per element.
1008        let (ps, used) = po(b"; client_max_window_bits").unwrap();
1009        assert_eq!(
1010            (used, values(&ps)),
1011            (24, [(&b"client_max_window_bits"[..], None)].to_vec())
1012        );
1013        let (ps, used) =
1014            po(b"; server_no_context_takeover; client_max_window_bits=15 , permessage-deflate")
1015                .unwrap();
1016        assert_eq!(used, 55);
1017        assert_eq!(
1018            values(&ps),
1019            [
1020                (&b"server_no_context_takeover"[..], None),
1021                (&b"client_max_window_bits"[..], Some(&b"15"[..])),
1022            ]
1023        );
1024        let (ps, _) = po(b";client_max_window_bits=\"10\"").unwrap();
1025        assert!(
1026            ps.find("client_max_window_bits")
1027                .unwrap()
1028                .value
1029                .unwrap()
1030                .eq_bytes(b"10")
1031        );
1032    }
1033
1034    #[test]
1035    fn test_optional_params_edge_cases() {
1036        assert_eq!(po(b"").unwrap().1, 0);
1037        assert_eq!(po(b";;").unwrap().1, 2);
1038        assert_eq!(values(&po(b";a").unwrap().0), [(&b"a"[..], None)]);
1039        assert_eq!(
1040            values(&po(b";a;b").unwrap().0),
1041            [(&b"a"[..], None), (b"b", None)]
1042        );
1043        // OWS after a bare name belongs to the parameter list only when a `;`
1044        // follows; otherwise it and what follows are the caller's.
1045        assert_eq!(po(b";a ;b").unwrap().1, 5);
1046        assert_eq!(po(b";a , b").unwrap().1, 2);
1047        assert_eq!(po(b";a b").unwrap().1, 2);
1048        assert_eq!(po(b";a\t").unwrap().1, 2);
1049        assert_eq!(po(b";a,").unwrap().1, 2);
1050        // `=` still needs a value, and BWS is still refused.
1051        assert_eq!(po(b";a=").unwrap_err().kind(), ErrorKind::MissingValue);
1052        assert_eq!(po(b";a=;b").unwrap_err().kind(), ErrorKind::MissingValue);
1053        assert_eq!(po(b";a=,").unwrap_err().kind(), ErrorKind::MissingValue);
1054        let e = po(b";a =1").unwrap_err();
1055        assert_eq!((e.kind(), e.offset()), (ErrorKind::InvalidByte, 2));
1056        assert_eq!(po(b";a= 1").unwrap_err().kind(), ErrorKind::InvalidByte);
1057        assert_eq!(po(b";a\t=1").unwrap_err().kind(), ErrorKind::InvalidByte);
1058        // A byte that cannot follow a name.
1059        assert_eq!(po(b";a\"x\"").unwrap_err().kind(), ErrorKind::MissingValue);
1060        assert_eq!(po(b";a@").unwrap_err().kind(), ErrorKind::NotToken);
1061        assert_eq!(po(b";a\x01").unwrap_err().kind(), ErrorKind::Control);
1062        assert_eq!(po(b";a\r\n").unwrap_err().kind(), ErrorKind::BareCr);
1063        assert_eq!(po(b";@").unwrap_err().kind(), ErrorKind::NotToken);
1064        assert_eq!(
1065            po(b";a=\"x").unwrap_err().kind(),
1066            ErrorKind::UnterminatedQuote
1067        );
1068    }
1069
1070    #[test]
1071    fn test_optional_params_duplicates_and_limits() {
1072        // A repeat is a repeat with or without a value, in any case.
1073        for dup in [&b";a;A"[..], b";a=1;a", b";a;a=1", b";a; b; A=\"x\""] {
1074            let e = po(dup).unwrap_err();
1075            assert_eq!(e.kind(), ErrorKind::DuplicateParameter, "{dup:?}");
1076        }
1077        let last = Limits::DEFAULT.duplicate_params(DuplicatePolicy::Last);
1078        let first = Limits::DEFAULT.duplicate_params(DuplicatePolicy::First);
1079        let (l, _) = parse_optional(b";a;A=2", &last).unwrap();
1080        let (f, _) = parse_optional(b";a;A=2", &first).unwrap();
1081        assert!(l.find("a").unwrap().value.unwrap().eq_bytes(b"2"));
1082        assert!(f.find("a").unwrap().value.is_none());
1083        assert_eq!(f.len(), 2);
1084        let upto = |n: usize| {
1085            (0..n)
1086                .map(|i| format!(";p{i}"))
1087                .collect::<Vec<_>>()
1088                .concat()
1089        };
1090        assert_eq!(po(upto(MAX_CHECKED_PARAMS).as_bytes()).unwrap().0.len(), 64);
1091        let over = upto(MAX_CHECKED_PARAMS + 1);
1092        let e = po(over.as_bytes()).unwrap_err();
1093        assert_eq!(
1094            (e.kind(), e.offset()),
1095            (ErrorKind::TooManyMembers, over.rfind(';').unwrap() + 1)
1096        );
1097        let bare = upto(257);
1098        assert_eq!(
1099            parse_optional(bare.as_bytes(), &last).unwrap_err().kind(),
1100            ErrorKind::TooManyMembers
1101        );
1102    }
1103
1104    #[test]
1105    fn test_optional_params_lookups_and_debug() {
1106        let (ps, _) = po(b";; a ;b=\"x;y\";token=\"s3cr3t\";").unwrap();
1107        assert_eq!(ps.raw(), b";; a ;b=\"x;y\";token=\"s3cr3t\";");
1108        assert_eq!(ps.find("A").map(|x| x.value), Some(None));
1109        assert!(ps.find("b").unwrap().value.unwrap().eq_bytes(b"x;y"));
1110        assert!(ps.contains("TOKEN") && !ps.contains("y") && !ps.contains(""));
1111        let d = format!("{ps:?}");
1112        assert_eq!(d, "OptionalParams([\"a\", \"b\", \"token\"])");
1113        assert!(!d.contains("s3cr3t"));
1114    }
1115
1116    /// The 0.6 walker, kept as the reference the slice-cursor walker is
1117    /// compared with (`test_parse_agrees_with_reference`).
1118    mod reference {
1119        use super::super::*;
1120        use crate::text::{parse_word, skip_ows};
1121
1122        pub(super) fn parse<'a>(s: &'a [u8], limits: &Limits) -> Result<(Params<'a>, usize)> {
1123            let policy = limits.duplicate_params;
1124            let mut count: usize = 0;
1125            let mut used = 0usize;
1126            let mut seen: u64 = 0;
1127            loop {
1128                let ows = skip_ows(s.get(used..).unwrap_or(&[]));
1129                let semi = used + ows;
1130                if s.get(semi) != Some(&b';') {
1131                    break;
1132                }
1133                let mut i = semi + 1;
1134                i += skip_ows(s.get(i..).unwrap_or(&[]));
1135                match s.get(i) {
1136                    None | Some(b';') => {
1137                        used = i;
1138                        continue;
1139                    }
1140                    Some(&b) if !class::is_tchar(b) => {
1141                        // OWS after an empty parameter followed by a list separator
1142                        // or the end belongs to the caller.
1143                        if b == b',' {
1144                            used = semi + 1;
1145                            break;
1146                        }
1147                        return err(
1148                            if crate::scan::is_control(b) {
1149                                control_kind(b)
1150                            } else {
1151                                ErrorKind::NotToken
1152                            },
1153                            i,
1154                        );
1155                    }
1156                    Some(_) => {}
1157                }
1158                let name_start = i;
1159                let name_len = s
1160                    .get(i..)
1161                    .unwrap_or(&[])
1162                    .iter()
1163                    .take_while(|&&b| class::is_tchar(b))
1164                    .count();
1165                let name = s.get(name_start..name_start + name_len).unwrap_or(&[]);
1166                i += name_len;
1167                match s.get(i) {
1168                    Some(b'=') => i += 1,
1169                    Some(&b) if is_ows(b) => {
1170                        let after = i + skip_ows(s.get(i..).unwrap_or(&[]));
1171                        if s.get(after) == Some(&b'=') {
1172                            return err(ErrorKind::InvalidByte, i);
1173                        }
1174                        return err(ErrorKind::MissingValue, i);
1175                    }
1176                    Some(b'"') => return err(ErrorKind::MissingValue, i),
1177                    Some(&b) if b != b';' && b != b',' && class::is_field_text(b) => {
1178                        return err(ErrorKind::NotToken, i);
1179                    }
1180                    Some(&b) if !class::is_field_text(b) => return err(control_kind(b), i),
1181                    _ => return err(ErrorKind::MissingValue, i),
1182                }
1183                let value_start = i;
1184                match s.get(i) {
1185                    Some(&b) if is_ows(b) => return err(ErrorKind::InvalidByte, i),
1186                    None | Some(b';' | b',') => return err(ErrorKind::MissingValue, i),
1187                    _ => {}
1188                }
1189                let rest = s.get(i..).unwrap_or(&[]);
1190                let (_, n) = parse_word(rest).map_err(|e| match e.kind() {
1191                    ErrorKind::Empty => crate::error::ParseError::new(
1192                        control_or_invalid(rest.first().copied().unwrap_or(0)),
1193                        value_start,
1194                    ),
1195                    _ => e.shifted(value_start),
1196                })?;
1197                i += n;
1198                count += 1;
1199                if count > usize::from(limits.params) {
1200                    return err(ErrorKind::TooManyMembers, name_start);
1201                }
1202                if policy == DuplicatePolicy::Refuse {
1203                    let bit = name_bit(name);
1204                    if seen & bit != 0 {
1205                        let earlier = Params {
1206                            text: s.get(..used).unwrap_or(&[]),
1207                            count: 0,
1208                            policy,
1209                        };
1210                        if earlier.iter().any(|p| p.name.eq_ignore_ascii_case(name)) {
1211                            return err(ErrorKind::DuplicateParameter, name_start);
1212                        }
1213                    }
1214                    seen |= bit;
1215                }
1216                used = i;
1217            }
1218            Ok((
1219                Params {
1220                    text: s.get(..used).unwrap_or(&[]),
1221                    count: u32::try_from(count).unwrap_or(u32::MAX),
1222                    policy,
1223                },
1224                used,
1225            ))
1226        }
1227
1228        /// `parse_optional` read plainly: by index, with the names seen kept
1229        /// in a `Vec`. Returns the raw text, the count, and the bytes used.
1230        pub(super) fn parse_optional(
1231            s: &[u8],
1232            limits: &Limits,
1233        ) -> Result<(std::vec::Vec<u8>, usize, usize)> {
1234            let refuse = limits.duplicate_params == DuplicatePolicy::Refuse;
1235            let max = if refuse {
1236                usize::from(limits.params).min(MAX_CHECKED_PARAMS)
1237            } else {
1238                usize::from(limits.params)
1239            };
1240            let mut seen: std::vec::Vec<&[u8]> = std::vec::Vec::new();
1241            let mut used = 0usize;
1242            loop {
1243                let semi = used + skip_ows(s.get(used..).unwrap_or(&[]));
1244                if s.get(semi) != Some(&b';') {
1245                    break;
1246                }
1247                let mut i = semi + 1;
1248                i += skip_ows(s.get(i..).unwrap_or(&[]));
1249                let Ok((name, n)) = crate::text::parse_token(s.get(i..).unwrap_or(&[])) else {
1250                    match s.get(i) {
1251                        None | Some(b';') => used = i,
1252                        Some(b',') => {
1253                            used = semi + 1;
1254                            break;
1255                        }
1256                        Some(&b) if crate::scan::is_control(b) => return err(control_kind(b), i),
1257                        Some(_) => return err(ErrorKind::NotToken, i),
1258                    }
1259                    continue;
1260                };
1261                let name_start = i;
1262                i += n;
1263                match s.get(i) {
1264                    Some(b'=') => {
1265                        i += 1;
1266                        match s.get(i) {
1267                            Some(&b) if is_ows(b) => return err(ErrorKind::InvalidByte, i),
1268                            None | Some(b';' | b',') => return err(ErrorKind::MissingValue, i),
1269                            _ => {}
1270                        }
1271                        let rest = s.get(i..).unwrap_or(&[]);
1272                        let (_, n) = parse_word(rest).map_err(|e| match e.kind() {
1273                            ErrorKind::Empty => crate::error::ParseError::new(
1274                                control_or_invalid(rest.first().copied().unwrap_or(0)),
1275                                i,
1276                            ),
1277                            _ => e.shifted(i),
1278                        })?;
1279                        i += n;
1280                    }
1281                    None | Some(b';' | b',') => {}
1282                    Some(&b) if is_ows(b) => {
1283                        let after = i + skip_ows(s.get(i..).unwrap_or(&[]));
1284                        if s.get(after) == Some(&b'=') {
1285                            return err(ErrorKind::InvalidByte, i);
1286                        }
1287                    }
1288                    Some(b'"') => return err(ErrorKind::MissingValue, i),
1289                    Some(&b) if class::is_field_text(b) => return err(ErrorKind::NotToken, i),
1290                    Some(&b) => return err(control_kind(b), i),
1291                }
1292                if seen.len() + 1 > max {
1293                    return err(ErrorKind::TooManyMembers, name_start);
1294                }
1295                if refuse && seen.iter().any(|e| e.eq_ignore_ascii_case(name)) {
1296                    return err(ErrorKind::DuplicateParameter, name_start);
1297                }
1298                seen.push(name);
1299                used = i;
1300            }
1301            Ok((s.get(..used).unwrap_or(&[]).to_vec(), seen.len(), used))
1302        }
1303    }
1304
1305    /// Re-reads validated optional-value text with the checked public token
1306    /// and word parsers.
1307    fn reference_optional_iter(raw: &[u8]) -> Vec<OptionalParam<'_>> {
1308        let mut out = Vec::new();
1309        let mut i = 0;
1310        loop {
1311            i += crate::text::skip_ows(raw.get(i..).unwrap_or(&[]));
1312            if raw.get(i) != Some(&b';') {
1313                return out;
1314            }
1315            i += 1;
1316            i += crate::text::skip_ows(raw.get(i..).unwrap_or(&[]));
1317            let Ok((name, n)) = crate::text::parse_token(raw.get(i..).unwrap_or(&[])) else {
1318                continue;
1319            };
1320            i += n;
1321            let value = if raw.get(i) == Some(&b'=') {
1322                let (value, n) = crate::text::parse_word(raw.get(i + 1..).unwrap_or(&[])).unwrap();
1323                i += 1 + n;
1324                Some(value)
1325            } else {
1326                None
1327            };
1328            out.push(OptionalParam { name, value });
1329        }
1330    }
1331
1332    /// Parameter text built from the pieces the grammar turns on.
1333    fn param_text() -> impl Strategy<Value = Vec<u8>> {
1334        let piece = prop_oneof![
1335            Just(b";".to_vec()),
1336            Just(b" ".to_vec()),
1337            Just(b"\t".to_vec()),
1338            Just(b"=".to_vec()),
1339            Just(b",".to_vec()),
1340            Just(b"\"a\\\"b;c=d\"".to_vec()),
1341            Just(b"\"\\".to_vec()),
1342            Just(b"\r".to_vec()),
1343            Just(b"\x7f".to_vec()),
1344            "[a-cA-C]{1,3}".prop_map(String::into_bytes),
1345            "[0-9]{1,3}".prop_map(String::into_bytes),
1346            "[(){}@/]".prop_map(String::into_bytes),
1347            "; ?[a-cA-C]{1,2}=[0-9a-c]{1,2}".prop_map(String::into_bytes),
1348            "; ?[a-c]=\"[a-c;,= ]{0,3}\"".prop_map(String::into_bytes),
1349        ];
1350        prop::collection::vec(piece, 0..16).prop_map(|v| v.concat())
1351    }
1352
1353    fn outcome(
1354        r: Result<(Params<'_>, usize)>,
1355    ) -> core::result::Result<(Vec<u8>, usize, usize), (ErrorKind, usize)> {
1356        r.map(|(p, used)| (p.raw().to_vec(), p.len(), used))
1357            .map_err(|e| (e.kind(), e.offset()))
1358    }
1359
1360    /// Re-reads validated text with the checked public word parser.
1361    fn reference_iter(raw: &[u8]) -> Vec<Param<'_>> {
1362        let mut out = Vec::new();
1363        let mut i = 0;
1364        loop {
1365            i += crate::text::skip_ows(raw.get(i..).unwrap_or(&[]));
1366            if raw.get(i) != Some(&b';') {
1367                return out;
1368            }
1369            i += 1;
1370            i += crate::text::skip_ows(raw.get(i..).unwrap_or(&[]));
1371            let Ok((name, n)) = crate::text::parse_token(raw.get(i..).unwrap_or(&[])) else {
1372                continue;
1373            };
1374            i += n + 1;
1375            let (value, n) = crate::text::parse_word(raw.get(i..).unwrap_or(&[])).unwrap();
1376            i += n;
1377            out.push(Param { name, value });
1378        }
1379    }
1380
1381    proptest::proptest! {
1382        #![proptest_config(proptest::prelude::ProptestConfig::with_cases(4096))]
1383
1384        #[test]
1385        fn test_parse_agrees_with_reference(v in param_text()) {
1386            for policy in [DuplicatePolicy::Refuse, DuplicatePolicy::First, DuplicatePolicy::Last] {
1387                let limits = Limits::DEFAULT.duplicate_params(policy);
1388                prop_assert_eq!(outcome(parse(&v, &limits)), outcome(reference::parse(&v, &limits)));
1389            }
1390        }
1391
1392        #[test]
1393        fn test_iter_and_get_agree_with_checked_reads(v in param_text(), name in "[a-cA-C=; \"]{0,4}") {
1394            for policy in [DuplicatePolicy::Refuse, DuplicatePolicy::First, DuplicatePolicy::Last] {
1395                let limits = Limits::DEFAULT.duplicate_params(policy);
1396                let Ok((p, _)) = parse(&v, &limits) else { continue };
1397                let all: Vec<Param<'_>> = p.iter().collect();
1398                prop_assert_eq!(&all, &reference_iter(p.raw()));
1399                prop_assert_eq!(all.len(), p.len());
1400                let mut matching = all.iter().filter(|x| x.name.eq_ignore_ascii_case(name.as_bytes()));
1401                let expected = if policy == DuplicatePolicy::Last { matching.next_back() } else { matching.next() };
1402                prop_assert_eq!(p.get(&name), expected.map(|x| x.value));
1403                for x in &all {
1404                    let n = core::str::from_utf8(x.name).unwrap();
1405                    let mut same = all.iter().filter(|y| y.name.eq_ignore_ascii_case(x.name));
1406                    let expected = if policy == DuplicatePolicy::Last { same.next_back() } else { same.next() };
1407                    prop_assert_eq!(p.get(n), expected.map(|y| y.value));
1408                }
1409            }
1410        }
1411
1412        #[test]
1413        fn test_parse_optional_agrees_with_reference(v in param_text()) {
1414            for policy in [DuplicatePolicy::Refuse, DuplicatePolicy::First, DuplicatePolicy::Last] {
1415                let limits = Limits::DEFAULT.duplicate_params(policy);
1416                let got = parse_optional(&v, &limits)
1417                    .map(|(p, used)| (p.raw().to_vec(), p.len(), used))
1418                    .map_err(|e| (e.kind(), e.offset()));
1419                let want = reference::parse_optional(&v, &limits).map_err(|e| (e.kind(), e.offset()));
1420                prop_assert_eq!(got, want);
1421            }
1422        }
1423
1424        #[test]
1425        fn test_parse_optional_extends_parse(v in param_text()) {
1426            // Where `parse` reads the text, or refuses it for anything but a
1427            // missing value, `parse_optional` does the same.
1428            for policy in [DuplicatePolicy::Refuse, DuplicatePolicy::First, DuplicatePolicy::Last] {
1429                let limits = Limits::DEFAULT.duplicate_params(policy);
1430                let opt = parse_optional(&v, &limits);
1431                match parse(&v, &limits) {
1432                    Ok((p, used)) => {
1433                        let (o, o_used) = opt.unwrap();
1434                        prop_assert_eq!((o.raw(), o.len(), o_used), (p.raw(), p.len(), used));
1435                        let with: Vec<OptionalParam<'_>> = p.iter()
1436                            .map(|x| OptionalParam { name: x.name, value: Some(x.value) })
1437                            .collect();
1438                        prop_assert_eq!(o.iter().collect::<Vec<_>>(), with);
1439                    }
1440                    Err(e) if e.kind() != ErrorKind::MissingValue => {
1441                        let o = opt.unwrap_err();
1442                        prop_assert_eq!((o.kind(), o.offset()), (e.kind(), e.offset()));
1443                    }
1444                    Err(_) => {}
1445                }
1446            }
1447        }
1448
1449        #[test]
1450        fn test_optional_iter_and_find_agree_with_checked_reads(v in param_text(), name in "[a-cA-C=; \"]{0,4}") {
1451            for policy in [DuplicatePolicy::Refuse, DuplicatePolicy::First, DuplicatePolicy::Last] {
1452                let limits = Limits::DEFAULT.duplicate_params(policy);
1453                let Ok((p, _)) = parse_optional(&v, &limits) else { continue };
1454                let all: Vec<OptionalParam<'_>> = p.iter().collect();
1455                prop_assert_eq!(&all, &reference_optional_iter(p.raw()));
1456                prop_assert_eq!(all.len(), p.len());
1457                let mut matching = all.iter().filter(|x| x.name.eq_ignore_ascii_case(name.as_bytes()));
1458                let expected = if policy == DuplicatePolicy::Last { matching.next_back() } else { matching.next() };
1459                prop_assert_eq!(p.find(&name), expected.copied());
1460                prop_assert_eq!(p.contains(&name), expected.is_some());
1461            }
1462        }
1463    }
1464}