1use core::fmt;
16
17use crate::{
18 class,
19 error::{ErrorKind, Result, err},
20 limits::{DuplicatePolicy, Limits},
21 scan::control_kind,
22 text::{Word, is_ows, parse_quoted, token_len, validated_word},
23};
24
25#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
37pub struct Param<'a> {
38 pub name: &'a [u8],
40 pub value: Word<'a>,
42}
43
44pub const MAX_CHECKED_PARAMS: usize = 64;
67
68pub(crate) struct Names {
72 at: [usize; MAX_CHECKED_PARAMS],
73 len: usize,
74 seen: u64,
75}
76
77impl Names {
78 pub(crate) const fn new() -> Self {
79 Self {
80 at: [0; MAX_CHECKED_PARAMS],
81 len: 0,
82 seen: 0,
83 }
84 }
85
86 pub(crate) fn repeats(
90 &mut self,
91 text: &[u8],
92 off: usize,
93 name: &[u8],
94 in_name: fn(u8) -> bool,
95 ) -> bool {
96 let bit = name_bit(name);
97 let l = name.len();
98 let earlier = self.at.get(..self.len).unwrap_or(&[]);
99 if self.seen & bit != 0
100 && earlier.iter().any(|&o| {
101 text.get(o + l).is_none_or(|&b| !in_name(b))
102 && text
103 .get(o..o + l)
104 .is_some_and(|e| e.eq_ignore_ascii_case(name))
105 })
106 {
107 return true;
108 }
109 self.seen |= bit;
110 if let Some(slot) = self.at.get_mut(self.len) {
111 *slot = off;
112 self.len += 1;
113 }
114 false
115 }
116}
117
118#[inline]
121fn name_bit(name: &[u8]) -> u64 {
122 let first = name.first().map_or(0, u8::to_ascii_lowercase);
123 let last = name.last().map_or(0, u8::to_ascii_lowercase);
124 let h = (name.len() as u64)
125 .wrapping_mul(31)
126 .wrapping_add(u64::from(first))
127 .wrapping_mul(31)
128 .wrapping_add(u64::from(last));
129 1u64 << (h & 63)
130}
131
132pub fn parse<'a>(s: &'a [u8], limits: &Limits) -> Result<(Params<'a>, usize)> {
159 let policy = limits.duplicate_params;
160 let refuse = policy == DuplicatePolicy::Refuse;
161 let max = if refuse {
162 usize::from(limits.params).min(MAX_CHECKED_PARAMS)
163 } else {
164 usize::from(limits.params)
165 };
166 let at = |rest: &[u8]| s.len() - rest.len();
169 let (mut done, mut count) = (s, 0usize);
170 let mut names = Names::new();
171 while let [b';', after @ ..] = skip_ws(done) {
172 let r = skip_ws(after);
173 let n = token_len(r);
174 if n == 0 {
175 match r.first() {
176 None | Some(b';') => {
177 done = r;
178 continue;
179 }
180 Some(b',') => {
183 done = after;
184 break;
185 }
186 Some(&b) => {
187 let kind = if crate::scan::is_control(b) {
188 control_kind(b)
189 } else {
190 ErrorKind::NotToken
191 };
192 return err(kind, at(r));
193 }
194 }
195 }
196 let name_at = at(r);
197 let (name, r) = r.split_at(n);
198 let value = match r {
199 [b'=', value @ ..] => value,
200 [b, ..] if is_ows(*b) => {
201 let kind = if skip_ws(r).first() == Some(&b'=') {
202 ErrorKind::InvalidByte
203 } else {
204 ErrorKind::MissingValue
205 };
206 return err(kind, at(r));
207 }
208 [b'"', ..] => return err(ErrorKind::MissingValue, at(r)),
209 [b, ..] if *b != b';' && *b != b',' && class::is_field_text(*b) => {
210 return err(ErrorKind::NotToken, at(r));
211 }
212 [b, ..] if !class::is_field_text(*b) => return err(control_kind(*b), at(r)),
213 _ => return err(ErrorKind::MissingValue, at(r)),
214 };
215 let used = match value {
216 [b, ..] if is_ows(*b) => return err(ErrorKind::InvalidByte, at(value)),
217 [] | [b';' | b',', ..] => return err(ErrorKind::MissingValue, at(value)),
218 [b'"', ..] => parse_quoted(value).map_err(|e| e.shifted(at(value)))?.1,
219 [b, ..] => match token_len(value) {
220 0 => return err(control_or_invalid(*b), at(value)),
221 n => n,
222 },
223 };
224 count += 1;
225 if count > max {
226 return err(ErrorKind::TooManyMembers, name_at);
227 }
228 if refuse && names.repeats(s, name_at, name, class::is_tchar) {
229 return err(ErrorKind::DuplicateParameter, name_at);
230 }
231 done = value.get(used..).unwrap_or(&[]);
232 }
233 let used = at(done);
234 Ok((
235 Params {
236 text: s.get(..used).unwrap_or(&[]),
237 count: u32::try_from(count).unwrap_or(u32::MAX),
238 policy,
239 },
240 used,
241 ))
242}
243
244#[inline]
246fn skip_ws(s: &[u8]) -> &[u8] {
247 match s {
248 [b' ' | b'\t', ..] => {
249 let n = s
250 .iter()
251 .position(|&b| b != b' ' && b != b'\t')
252 .unwrap_or(s.len());
253 s.get(n..).unwrap_or(&[])
254 }
255 _ => s,
256 }
257}
258
259#[inline]
260const fn control_or_invalid(b: u8) -> ErrorKind {
261 if crate::scan::is_control(b) {
262 control_kind(b)
263 } else {
264 ErrorKind::InvalidByte
265 }
266}
267
268#[derive(Clone, Copy, PartialEq, Eq, Hash)]
283pub struct Params<'a> {
284 pub(crate) text: &'a [u8],
285 pub(crate) count: u32,
286 pub(crate) policy: DuplicatePolicy,
287}
288
289impl<'a> Params<'a> {
290 #[must_use]
304 pub fn iter(&self) -> ParamIter<'a> {
305 ParamIter { rest: self.text }
306 }
307
308 #[must_use]
322 pub fn get(&self, name: &str) -> Option<Word<'a>> {
323 let name = name.as_bytes();
324 let last = self.policy == DuplicatePolicy::Last;
325 let mut found = None;
326 let mut rest = self.text;
332 while let [b';', after @ ..] = skip_ws(rest) {
333 let tail = skip_ws(after);
334 let value = match tail.split_at_checked(name.len()) {
335 Some((head, [b'=', value @ ..]))
338 if (head == name || head.eq_ignore_ascii_case(name))
339 && head.first().is_some_and(|&b| class::is_tchar(b))
340 && !name.contains(&b'=') =>
341 {
342 let (word, used) = validated_word(value);
343 if !last {
344 return Some(word);
345 }
346 found = Some(word);
347 rest = value.get(used..).unwrap_or(&[]);
348 continue;
349 }
350 _ => match token_len(tail) {
351 0 => {
352 rest = tail;
353 continue;
354 }
355 n => tail.get(n + 1..).unwrap_or(&[]),
356 },
357 };
358 rest = value.get(validated_word(value).1..).unwrap_or(&[]);
359 }
360 found
361 }
362
363 pub(crate) fn split_at_name(&self, name: &[u8]) -> (Params<'a>, Option<Param<'a>>) {
368 for (index, p) in self.iter().enumerate() {
369 if p.name.eq_ignore_ascii_case(name) {
370 let off = (p.name.as_ptr() as usize).saturating_sub(self.text.as_ptr() as usize);
372 let head = self.text.get(..off).unwrap_or(&[]);
373 let semi = head.iter().rposition(|&b| b == b';').unwrap_or(0);
374 let before = Params {
375 text: self.text.get(..semi).unwrap_or(&[]),
376 count: u32::try_from(index).unwrap_or(u32::MAX),
377 policy: self.policy,
378 };
379 return (before, Some(p));
380 }
381 }
382 (*self, None)
383 }
384
385 #[must_use]
396 pub const fn len(&self) -> usize {
397 self.count as usize
398 }
399
400 #[must_use]
412 pub const fn is_empty(&self) -> bool {
413 self.count == 0
414 }
415
416 #[must_use]
429 pub const fn raw(&self) -> &'a [u8] {
430 self.text
431 }
432}
433
434impl fmt::Debug for Params<'_> {
435 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
436 f.write_str("Params(")?;
437 f.debug_list()
438 .entries(
439 self.iter()
440 .map(|p| core::str::from_utf8(p.name).unwrap_or("?")),
441 )
442 .finish()?;
443 f.write_str(")")
444 }
445}
446
447#[derive(Clone, Debug)]
462pub struct ParamIter<'a> {
463 rest: &'a [u8],
464}
465
466impl<'a> Iterator for ParamIter<'a> {
467 type Item = Param<'a>;
468
469 fn next(&mut self) -> Option<Param<'a>> {
470 loop {
471 let [b';', after @ ..] = skip_ws(self.rest) else {
473 self.rest = &[];
474 return None;
475 };
476 let tail = skip_ws(after);
477 let n = token_len(tail);
478 let Some((name, [_, value_text @ ..])) = (n > 0).then(|| tail.split_at(n)) else {
479 self.rest = tail;
480 continue;
481 };
482 let (value, used) = validated_word(value_text);
483 self.rest = value_text.get(used..).unwrap_or(&[]);
484 return Some(Param { name, value });
485 }
486 }
487}
488
489#[cfg(test)]
490mod tests {
491 use std::{format, string::String, vec, vec::Vec};
492
493 use proptest::prelude::{Just, Strategy, prop, prop_assert_eq, prop_oneof};
494
495 use super::*;
496
497 fn p(s: &[u8]) -> Result<(Params<'_>, usize)> {
498 parse(s, &Limits::DEFAULT)
499 }
500
501 fn names<'a>(ps: &Params<'a>) -> Vec<&'a [u8]> {
502 ps.iter().map(|x| x.name).collect()
503 }
504
505 #[test]
506 fn test_params_edge_cases() {
507 assert_eq!(p(b"").unwrap().1, 0);
508 assert!(p(b";").unwrap().0.is_empty());
509 assert_eq!(p(b";").unwrap().1, 1);
510 assert_eq!(p(b";;").unwrap().1, 2);
511 let (ps, used) = p(b"; a=b").unwrap();
512 assert_eq!((used, names(&ps)), (5, [&b"a"[..]].to_vec()));
513 assert_eq!(p(b";a=").unwrap_err().kind(), ErrorKind::MissingValue);
514 assert_eq!(p(b";a").unwrap_err().kind(), ErrorKind::MissingValue);
515 assert_eq!(p(b";a;b=c").unwrap_err().kind(), ErrorKind::MissingValue);
516 assert_eq!(p(b";a =b").unwrap_err().kind(), ErrorKind::InvalidByte);
517 assert_eq!(p(b";a= b").unwrap_err().kind(), ErrorKind::InvalidByte);
518 assert_eq!(p(b";@=b").unwrap_err().kind(), ErrorKind::NotToken);
519 assert_eq!(p(b";a@=b").unwrap_err().kind(), ErrorKind::NotToken);
520 assert_eq!(p(b";a=\"\"").unwrap().0.get("a").map(|w| w.len()), Some(0));
521 assert!(
522 p(b";a=\"x\\\"y\"")
523 .unwrap()
524 .0
525 .get("a")
526 .unwrap()
527 .eq_bytes(b"x\"y")
528 );
529 assert_eq!(
530 p(b";a=\"x").unwrap_err().kind(),
531 ErrorKind::UnterminatedQuote
532 );
533 assert_eq!(p(b";a=b\rc").unwrap().1, 4);
534 assert_eq!(p(b";a=\x01").unwrap_err().kind(), ErrorKind::Control);
535 }
536
537 #[test]
538 fn test_params_stop_at_list_separator_and_trailing_ows() {
539 let (ps, used) = p(b";a=1 , b").unwrap();
540 assert_eq!((used, ps.len()), (4, 1));
541 let (_, used) = p(b";a=1; ,b").unwrap();
542 assert_eq!(used, 5);
543 let (_, used) = p(b" ;a=1 x").unwrap();
544 assert_eq!(used, 5);
545 }
546
547 #[test]
548 fn test_params_duplicate_refused_case_insensitive() {
549 let e = p(b";A=b;a=c").unwrap_err();
550 assert_eq!((e.kind(), e.offset()), (ErrorKind::DuplicateParameter, 5));
551 assert!(p(b";ab=1;ba=2;aa=3;bb=4").is_ok());
552 }
553
554 #[test]
555 fn test_params_first_and_last_policies() {
556 let first = Limits::DEFAULT.duplicate_params(DuplicatePolicy::First);
557 let last = Limits::DEFAULT.duplicate_params(DuplicatePolicy::Last);
558 let (a, _) = parse(b";a=1;A=2", &first).unwrap();
559 let (b, _) = parse(b";a=1;A=2", &last).unwrap();
560 assert!(a.get("a").unwrap().eq_bytes(b"1"));
561 assert!(b.get("a").unwrap().eq_bytes(b"2"));
562 assert_eq!(a.len(), 2);
563 }
564
565 #[test]
566 fn test_params_count_limit() {
567 let mut v = Vec::new();
568 for i in 0..257 {
569 v.extend_from_slice(format!(";p{i}=v").as_bytes());
570 }
571 let cut = v.len() - format!(";p{}=v", 256).len();
572 for policy in [DuplicatePolicy::First, DuplicatePolicy::Last] {
574 let l = Limits::DEFAULT.duplicate_params(policy);
575 assert_eq!(parse(&v[..cut], &l).unwrap().0.len(), 256);
576 assert_eq!(parse(&v, &l).unwrap_err().kind(), ErrorKind::TooManyMembers);
577 }
578 let upto = |n: usize| {
580 (0..n)
581 .map(|i| format!(";p{i}=v"))
582 .collect::<Vec<_>>()
583 .concat()
584 };
585 assert_eq!(p(upto(MAX_CHECKED_PARAMS).as_bytes()).unwrap().0.len(), 64);
586 let over = upto(MAX_CHECKED_PARAMS + 1);
587 let e = p(over.as_bytes()).unwrap_err();
588 let last = over.rfind(';').unwrap() + 1;
589 assert_eq!((e.kind(), e.offset()), (ErrorKind::TooManyMembers, last));
590 let wide = Limits::DEFAULT.max_params(u16::MAX);
591 assert_eq!(
592 parse(over.as_bytes(), &wide).unwrap_err().kind(),
593 ErrorKind::TooManyMembers
594 );
595 assert_eq!(
596 parse(upto(3).as_bytes(), &Limits::DEFAULT.max_params(2))
597 .unwrap_err()
598 .kind(),
599 ErrorKind::TooManyMembers
600 );
601 }
602
603 #[test]
604 fn test_params_duplicate_found_in_every_table_slot() {
605 let base = (0..63)
609 .map(|i| format!(";p{i}=\"v;p{i}=w\""))
610 .collect::<Vec<_>>()
611 .concat();
612 for i in 0..63 {
613 let dup = format!("{base};P{i}=x");
614 let e = p(dup.as_bytes()).unwrap_err();
615 assert_eq!(
616 (e.kind(), e.offset()),
617 (ErrorKind::DuplicateParameter, base.len() + 1),
618 "{i}"
619 );
620 }
621 for other in ["p", "p1x", "xp1", "p100"] {
622 assert!(p(format!("{base};{other}=x").as_bytes()).is_ok(), "{other}");
623 }
624 }
625
626 #[test]
627 fn test_params_debug_prints_names_only() {
628 let (ps, _) = p(b";user=alice;token=\"s3cr3t\"").unwrap();
629 let d = format!("{ps:?}");
630 assert_eq!(d, "Params([\"user\", \"token\"])");
631 assert!(!d.contains("s3cr3t") && !d.contains("alice"));
632 }
633
634 #[test]
635 fn test_params_iter_skips_empty_parameters() {
636 let (ps, _) = p(b";; a=1 ;;b=\"x;y\";").unwrap();
637 assert_eq!(names(&ps), [&b"a"[..], b"b"]);
638 assert!(ps.get("b").unwrap().eq_bytes(b"x;y"));
639 assert_eq!(ps.raw(), b";; a=1 ;;b=\"x;y\";");
640 }
641
642 #[test]
643 fn test_params_get_matches_whole_names_only() {
644 let (ps, _) = p(b";;a=\"\"").unwrap();
647 assert!(ps.get(";a").is_none());
648 assert!(ps.get("a").is_some_and(|v| v.eq_bytes(b"")));
649 let (ps, _) = p(b";a=b;c=d").unwrap();
650 for name in ["a=b", "a=b;c", "", ";", "=", "a;", " a"] {
651 assert!(ps.get(name).is_none(), "{name}");
652 }
653 assert!(ps.get("C").is_some_and(|v| v.eq_bytes(b"d")));
654 }
655
656 mod reference {
659 use super::super::*;
660 use crate::text::{parse_word, skip_ows};
661
662 pub(super) fn parse<'a>(s: &'a [u8], limits: &Limits) -> Result<(Params<'a>, usize)> {
663 let policy = limits.duplicate_params;
664 let mut count: usize = 0;
665 let mut used = 0usize;
666 let mut seen: u64 = 0;
667 loop {
668 let ows = skip_ows(s.get(used..).unwrap_or(&[]));
669 let semi = used + ows;
670 if s.get(semi) != Some(&b';') {
671 break;
672 }
673 let mut i = semi + 1;
674 i += skip_ows(s.get(i..).unwrap_or(&[]));
675 match s.get(i) {
676 None | Some(b';') => {
677 used = i;
678 continue;
679 }
680 Some(&b) if !class::is_tchar(b) => {
681 if b == b',' {
684 used = semi + 1;
685 break;
686 }
687 return err(
688 if crate::scan::is_control(b) {
689 control_kind(b)
690 } else {
691 ErrorKind::NotToken
692 },
693 i,
694 );
695 }
696 Some(_) => {}
697 }
698 let name_start = i;
699 let name_len = s
700 .get(i..)
701 .unwrap_or(&[])
702 .iter()
703 .take_while(|&&b| class::is_tchar(b))
704 .count();
705 let name = s.get(name_start..name_start + name_len).unwrap_or(&[]);
706 i += name_len;
707 match s.get(i) {
708 Some(b'=') => i += 1,
709 Some(&b) if is_ows(b) => {
710 let after = i + skip_ows(s.get(i..).unwrap_or(&[]));
711 if s.get(after) == Some(&b'=') {
712 return err(ErrorKind::InvalidByte, i);
713 }
714 return err(ErrorKind::MissingValue, i);
715 }
716 Some(b'"') => return err(ErrorKind::MissingValue, i),
717 Some(&b) if b != b';' && b != b',' && class::is_field_text(b) => {
718 return err(ErrorKind::NotToken, i);
719 }
720 Some(&b) if !class::is_field_text(b) => return err(control_kind(b), i),
721 _ => return err(ErrorKind::MissingValue, i),
722 }
723 let value_start = i;
724 match s.get(i) {
725 Some(&b) if is_ows(b) => return err(ErrorKind::InvalidByte, i),
726 None | Some(b';' | b',') => return err(ErrorKind::MissingValue, i),
727 _ => {}
728 }
729 let rest = s.get(i..).unwrap_or(&[]);
730 let (_, n) = parse_word(rest).map_err(|e| match e.kind() {
731 ErrorKind::Empty => crate::error::ParseError::new(
732 control_or_invalid(rest.first().copied().unwrap_or(0)),
733 value_start,
734 ),
735 _ => e.shifted(value_start),
736 })?;
737 i += n;
738 count += 1;
739 if count > usize::from(limits.params) {
740 return err(ErrorKind::TooManyMembers, name_start);
741 }
742 if policy == DuplicatePolicy::Refuse {
743 let bit = name_bit(name);
744 if seen & bit != 0 {
745 let earlier = Params {
746 text: s.get(..used).unwrap_or(&[]),
747 count: 0,
748 policy,
749 };
750 if earlier.iter().any(|p| p.name.eq_ignore_ascii_case(name)) {
751 return err(ErrorKind::DuplicateParameter, name_start);
752 }
753 }
754 seen |= bit;
755 }
756 used = i;
757 }
758 Ok((
759 Params {
760 text: s.get(..used).unwrap_or(&[]),
761 count: u32::try_from(count).unwrap_or(u32::MAX),
762 policy,
763 },
764 used,
765 ))
766 }
767 }
768
769 fn param_text() -> impl Strategy<Value = Vec<u8>> {
771 let piece = prop_oneof![
772 Just(b";".to_vec()),
773 Just(b" ".to_vec()),
774 Just(b"\t".to_vec()),
775 Just(b"=".to_vec()),
776 Just(b",".to_vec()),
777 Just(b"\"a\\\"b;c=d\"".to_vec()),
778 Just(b"\"\\".to_vec()),
779 Just(b"\r".to_vec()),
780 Just(b"\x7f".to_vec()),
781 "[a-cA-C]{1,3}".prop_map(String::into_bytes),
782 "[0-9]{1,3}".prop_map(String::into_bytes),
783 "[(){}@/]".prop_map(String::into_bytes),
784 "; ?[a-cA-C]{1,2}=[0-9a-c]{1,2}".prop_map(String::into_bytes),
785 "; ?[a-c]=\"[a-c;,= ]{0,3}\"".prop_map(String::into_bytes),
786 ];
787 prop::collection::vec(piece, 0..16).prop_map(|v| v.concat())
788 }
789
790 fn outcome(
791 r: Result<(Params<'_>, usize)>,
792 ) -> core::result::Result<(Vec<u8>, usize, usize), (ErrorKind, usize)> {
793 r.map(|(p, used)| (p.raw().to_vec(), p.len(), used))
794 .map_err(|e| (e.kind(), e.offset()))
795 }
796
797 fn reference_iter(raw: &[u8]) -> Vec<Param<'_>> {
799 let mut out = Vec::new();
800 let mut i = 0;
801 loop {
802 i += crate::text::skip_ows(raw.get(i..).unwrap_or(&[]));
803 if raw.get(i) != Some(&b';') {
804 return out;
805 }
806 i += 1;
807 i += crate::text::skip_ows(raw.get(i..).unwrap_or(&[]));
808 let Ok((name, n)) = crate::text::parse_token(raw.get(i..).unwrap_or(&[])) else {
809 continue;
810 };
811 i += n + 1;
812 let (value, n) = crate::text::parse_word(raw.get(i..).unwrap_or(&[])).unwrap();
813 i += n;
814 out.push(Param { name, value });
815 }
816 }
817
818 proptest::proptest! {
819 #![proptest_config(proptest::prelude::ProptestConfig::with_cases(4096))]
820
821 #[test]
822 fn test_parse_agrees_with_reference(v in param_text()) {
823 for policy in [DuplicatePolicy::Refuse, DuplicatePolicy::First, DuplicatePolicy::Last] {
824 let limits = Limits::DEFAULT.duplicate_params(policy);
825 prop_assert_eq!(outcome(parse(&v, &limits)), outcome(reference::parse(&v, &limits)));
826 }
827 }
828
829 #[test]
830 fn test_iter_and_get_agree_with_checked_reads(v in param_text(), name in "[a-cA-C=; \"]{0,4}") {
831 for policy in [DuplicatePolicy::Refuse, DuplicatePolicy::First, DuplicatePolicy::Last] {
832 let limits = Limits::DEFAULT.duplicate_params(policy);
833 let Ok((p, _)) = parse(&v, &limits) else { continue };
834 let all: Vec<Param<'_>> = p.iter().collect();
835 prop_assert_eq!(&all, &reference_iter(p.raw()));
836 prop_assert_eq!(all.len(), p.len());
837 let mut matching = all.iter().filter(|x| x.name.eq_ignore_ascii_case(name.as_bytes()));
838 let expected = if policy == DuplicatePolicy::Last { matching.next_back() } else { matching.next() };
839 prop_assert_eq!(p.get(&name), expected.map(|x| x.value));
840 for x in &all {
841 let n = core::str::from_utf8(x.name).unwrap();
842 let mut same = all.iter().filter(|y| y.name.eq_ignore_ascii_case(x.name));
843 let expected = if policy == DuplicatePolicy::Last { same.next_back() } else { same.next() };
844 prop_assert_eq!(p.get(n), expected.map(|y| y.value));
845 }
846 }
847 }
848 }
849}