1use core::borrow::Borrow;
10use core::fmt::Debug;
11use core::num::NonZeroUsize;
12use core::time::Duration;
13use core::usize;
14
15use net_types::MulticastAddr;
16use net_types::ip::IpAddress;
17
18const fn bitmask(n: u8) -> u32 {
21 assert!((n as u32) < u32::BITS);
22 (1 << n) - 1
23}
24
25#[derive(Debug, Eq, PartialEq)]
27pub struct OverflowError;
28
29#[derive(Debug, Eq, PartialEq)]
31pub enum ExactConversionError {
32 Overflow,
34 NotExact,
36}
37
38impl From<OverflowError> for ExactConversionError {
39 fn from(OverflowError: OverflowError) -> Self {
40 Self::Overflow
41 }
42}
43
44pub(crate) trait LinExpConversion<C: Debug + PartialEq + Copy + Clone>:
64 Into<C> + Copy + Clone + Sized
65{
66 const NUM_MANT_BITS: u8;
69 const NUM_EXP_BITS: u8;
71 fn lossy_try_from(value: C) -> Result<Self, OverflowError>;
77
78 const EXP_INCR: u32 = 3;
82 const MANT_BITMASK: u32 = bitmask(Self::NUM_MANT_BITS);
84 const EXP_BITMASK: u32 = bitmask(Self::NUM_EXP_BITS);
86 const SWITCHPOINT: u32 = 0x1 << (Self::NUM_MANT_BITS + Self::NUM_EXP_BITS);
88 const MANT_PREFIX: u32 = 0x1 << Self::NUM_MANT_BITS;
90 const MAX_VALUE: u32 =
92 (Self::MANT_BITMASK | Self::MANT_PREFIX) << (Self::EXP_INCR + Self::EXP_BITMASK);
93
94 fn to_expanded(code: u16) -> u32 {
98 let code = code.into();
99 if code < Self::SWITCHPOINT {
100 code
101 } else {
102 let mant = code & Self::MANT_BITMASK;
103 let exp = (code >> Self::NUM_MANT_BITS) & Self::EXP_BITMASK;
104 (mant | Self::MANT_PREFIX) << (Self::EXP_INCR + exp)
105 }
106 }
107
108 fn lossy_try_from_expanded(value: u32) -> Result<u16, OverflowError> {
120 if value > Self::MAX_VALUE {
121 Err(OverflowError)
122 } else if value < Self::SWITCHPOINT {
123 let code = value.try_into().unwrap();
125 Ok(code)
126 } else {
127 let msb = (u32::BITS - value.leading_zeros()) - 1;
128 let exp = msb - u32::from(Self::NUM_MANT_BITS);
129 let mant = (value >> exp) & Self::MANT_BITMASK;
130 let code = (Self::SWITCHPOINT | ((exp - Self::EXP_INCR) << Self::NUM_MANT_BITS) | mant)
132 .try_into()
133 .unwrap();
134 Ok(code)
135 }
136 }
137
138 fn exact_try_from(value: C) -> Result<Self, ExactConversionError> {
151 let res = Self::lossy_try_from(value)?;
152 if value == res.into() { Ok(res) } else { Err(ExactConversionError::NotExact) }
153 }
154}
155
156create_protocol_enum!(
157 #[allow(missing_docs)]
165 #[derive(PartialEq, Eq, Copy, Clone, PartialOrd, Ord)]
166 pub enum GroupRecordType: u8 {
167 ModeIsInclude, 0x01, "Mode Is Include";
168 ModeIsExclude, 0x02, "Mode Is Exclude";
169 ChangeToIncludeMode, 0x03, "Change To Include Mode";
170 ChangeToExcludeMode, 0x04, "Change To Exclude Mode";
171 AllowNewSources, 0x05, "Allow New Sources";
172 BlockOldSources, 0x06, "Block Old Sources";
173 }
174);
175
176impl GroupRecordType {
177 fn allow_split(&self) -> bool {
208 match self {
209 GroupRecordType::ModeIsInclude
210 | GroupRecordType::ChangeToIncludeMode
211 | GroupRecordType::AllowNewSources
212 | GroupRecordType::BlockOldSources => true,
213 GroupRecordType::ModeIsExclude | GroupRecordType::ChangeToExcludeMode => false,
214 }
215 }
216}
217
218#[derive(PartialEq, Eq, Debug, Clone, Copy, Default)]
226pub struct QQIC(u8);
227
228impl QQIC {
229 pub fn new_lossy(value: Duration) -> Result<Self, OverflowError> {
231 Self::lossy_try_from(value)
232 }
233
234 pub fn new_exact(value: Duration) -> Result<Self, ExactConversionError> {
236 Self::exact_try_from(value)
237 }
238}
239
240impl LinExpConversion<Duration> for QQIC {
241 const NUM_MANT_BITS: u8 = 4;
242 const NUM_EXP_BITS: u8 = 3;
243
244 fn lossy_try_from(value: Duration) -> Result<Self, OverflowError> {
245 let secs: u32 = value.as_secs().try_into().map_err(|_| OverflowError)?;
246 let code = Self::lossy_try_from_expanded(secs)?.try_into().map_err(|_| OverflowError)?;
247 Ok(Self(code))
248 }
249}
250
251impl From<QQIC> for Duration {
252 fn from(code: QQIC) -> Self {
253 let secs: u64 = QQIC::to_expanded(code.0.into()).into();
254 Duration::from_secs(secs)
255 }
256}
257
258impl From<QQIC> for u8 {
259 fn from(QQIC(v): QQIC) -> Self {
260 v
261 }
262}
263
264impl From<u8> for QQIC {
265 fn from(value: u8) -> Self {
266 Self(value)
267 }
268}
269
270#[derive(PartialEq, Eq, Debug, Clone, Copy, Default)]
278pub struct QRV(u8);
279
280impl QRV {
281 const QRV_MAX: u8 = 7;
282
283 pub fn new(robustness_value: u8) -> Self {
298 if robustness_value > Self::QRV_MAX {
299 return QRV(0);
300 }
301 QRV(robustness_value)
302 }
303}
304
305impl From<QRV> for u8 {
306 fn from(qrv: QRV) -> u8 {
307 qrv.0
308 }
309}
310
311pub trait GmpReportGroupRecord<A: IpAddress> {
320 fn group(&self) -> MulticastAddr<A>;
322
323 fn record_type(&self) -> GroupRecordType;
325
326 fn sources(&self) -> impl Iterator<Item: Borrow<A>> + '_;
328}
329
330impl<A, I> GmpReportGroupRecord<A> for (MulticastAddr<A>, GroupRecordType, I)
331where
332 A: IpAddress,
333 I: Iterator<Item: Borrow<A>> + Clone,
334{
335 fn group(&self) -> MulticastAddr<A> {
336 self.0
337 }
338
339 fn record_type(&self) -> GroupRecordType {
340 self.1
341 }
342
343 fn sources(&self) -> impl Iterator<Item: Borrow<A>> + '_ {
344 self.2.clone()
345 }
346}
347
348#[derive(Clone)]
349struct OverrideGroupRecordSources<R> {
350 record: R,
351 limit: NonZeroUsize,
352 skip: usize,
353}
354
355impl<R, A> GmpReportGroupRecord<A> for OverrideGroupRecordSources<R>
356where
357 A: IpAddress,
358 R: GmpReportGroupRecord<A>,
359{
360 fn group(&self) -> MulticastAddr<A> {
361 self.record.group()
362 }
363
364 fn record_type(&self) -> GroupRecordType {
365 self.record.record_type()
366 }
367
368 fn sources(&self) -> impl Iterator<Item: Borrow<A>> + '_ {
369 self.record.sources().skip(self.skip).take(self.limit.get())
370 }
371}
372
373#[derive(Debug, Eq, PartialEq)]
375pub struct InvalidConstraintsError;
376
377pub(crate) fn group_record_split_iterator<A, I>(
378 max_len: usize,
379 group_header: usize,
380 groups: I,
381) -> Result<
382 impl Iterator<Item: Iterator<Item: GmpReportGroupRecord<A>> + Clone>,
383 InvalidConstraintsError,
384>
385where
386 A: IpAddress,
387 I: Iterator<Item: GmpReportGroupRecord<A> + Clone> + Clone,
388{
389 if group_header + core::mem::size_of::<A>() > max_len {
391 return Err(InvalidConstraintsError);
392 }
393 let mut groups = groups.peekable();
398 let mut skip = 0;
401 Ok(core::iter::from_fn(move || {
402 let start = groups.clone();
403 let mut take = 0;
404 let mut len = 0;
405 loop {
406 let group = match groups.peek() {
407 Some(group) => group,
408 None => break,
409 };
410 len += group_header;
411 if len > max_len {
413 break;
414 }
415
416 let skipped = core::mem::replace(&mut skip, 0);
419 let sources = group.sources();
420 if take == 0 {
421 let mut sources = sources.skip(skipped).enumerate();
426 loop {
427 let Some((i, _)) = sources.next() else { break };
432
433 len += core::mem::size_of::<A>();
434 if len > max_len {
435 let limit = NonZeroUsize::new(i).expect("can't fit a single source");
439 let record = if group.record_type().allow_split() {
440 skip = skipped + i;
443 group.clone()
444 } else {
445 drop(sources);
449 groups.next().unwrap()
450 };
451 return Some(either::Either::Left(core::iter::once(
452 OverrideGroupRecordSources { record, limit, skip: skipped },
453 )));
454 }
455 }
456 if skipped != 0 {
461 drop(sources);
464 let group = groups.next().unwrap();
465 return Some(either::Either::Left(core::iter::once(
466 OverrideGroupRecordSources {
467 record: group,
468 limit: NonZeroUsize::MAX,
469 skip: skipped,
470 },
471 )));
472 }
473 } else {
474 assert_eq!(skipped, 0);
476 len += sources.count() * core::mem::size_of::<A>();
479 if len > max_len {
480 break;
481 }
482 }
483
484 let _: Option<_> = groups.next();
486 take += 1;
487 }
488
489 if take == 0 {
490 None
491 } else {
492 Some(either::Either::Right(start.take(take).map(|record| OverrideGroupRecordSources {
493 record,
494 limit: NonZeroUsize::MAX,
495 skip: 0,
496 })))
497 }
498 }))
499}
500
501#[cfg(test)]
502mod tests {
503 use core::ops::Range;
504
505 use super::*;
506
507 use ip_test_macro::ip_test;
508 use net_types::ip::{Ip, Ipv4Addr, Ipv6Addr};
509
510 fn empty_iter<A: IpAddress>() -> impl Iterator<Item: GmpReportGroupRecord<A> + Clone> + Clone {
511 core::iter::empty::<(MulticastAddr<A>, GroupRecordType, core::iter::Empty<A>)>()
512 }
513
514 fn addr<I: Ip>(i: u8) -> I::Addr {
515 I::map_ip_out(
516 i,
517 |i| Ipv4Addr::new([0, 0, 0, i]),
518 |i| Ipv6Addr::from_bytes([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, i]),
519 )
520 }
521
522 fn mcast_addr<I: Ip>(i: u8) -> MulticastAddr<I::Addr> {
523 MulticastAddr::new(I::map_ip_out(
524 i,
525 |i| Ipv4Addr::new([224, 0, 0, i]),
526 |i| Ipv6Addr::from_bytes([0xFF, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, i]),
527 ))
528 .unwrap()
529 }
530
531 fn addr_iter_range<I: Ip>(range: Range<u8>) -> impl Iterator<Item = I::Addr> + Clone {
532 range.into_iter().map(|i| addr::<I>(i))
533 }
534
535 fn collect<I, A>(iter: I) -> Vec<Vec<(MulticastAddr<A>, GroupRecordType, Vec<A>)>>
536 where
537 I: Iterator<Item: Iterator<Item: GmpReportGroupRecord<A>>>,
538 A: IpAddress,
539 {
540 iter.map(|groups| {
541 groups
542 .map(|g| {
543 (
544 g.group(),
545 g.record_type(),
546 g.sources().map(|b| b.borrow().clone()).collect::<Vec<_>>(),
547 )
548 })
549 .collect::<Vec<_>>()
550 })
551 .collect::<Vec<_>>()
552 }
553
554 const GROUP_RECORD_HEADER: usize = 1;
555
556 #[ip_test(I)]
557 fn split_rejects_small_lengths<I: Ip>() {
558 assert_eq!(
559 group_record_split_iterator(
560 GROUP_RECORD_HEADER,
561 GROUP_RECORD_HEADER,
562 empty_iter::<I::Addr>()
563 )
564 .map(collect),
565 Err(InvalidConstraintsError)
566 );
567 assert_eq!(
568 group_record_split_iterator(
569 GROUP_RECORD_HEADER + core::mem::size_of::<I::Addr>() - 1,
570 GROUP_RECORD_HEADER,
571 empty_iter::<I::Addr>()
572 )
573 .map(collect),
574 Err(InvalidConstraintsError)
575 );
576 assert_eq!(
578 group_record_split_iterator(
579 GROUP_RECORD_HEADER + core::mem::size_of::<I::Addr>(),
580 GROUP_RECORD_HEADER,
581 empty_iter::<I::Addr>()
582 )
583 .map(collect),
584 Ok(vec![])
585 );
586 }
587
588 #[ip_test(I)]
589 fn basic_split<I: Ip>() {
590 let iter = group_record_split_iterator(
591 GROUP_RECORD_HEADER + core::mem::size_of::<I::Addr>() * 2,
592 GROUP_RECORD_HEADER,
593 [
594 (mcast_addr::<I>(1), GroupRecordType::ModeIsInclude, addr_iter_range::<I>(1..2)),
595 (mcast_addr::<I>(2), GroupRecordType::ModeIsExclude, addr_iter_range::<I>(2..4)),
596 (
597 mcast_addr::<I>(3),
598 GroupRecordType::ChangeToIncludeMode,
599 addr_iter_range::<I>(0..0),
600 ),
601 (
602 mcast_addr::<I>(4),
603 GroupRecordType::ChangeToExcludeMode,
604 addr_iter_range::<I>(0..0),
605 ),
606 ]
607 .into_iter(),
608 )
609 .unwrap();
610
611 let report1 = vec![(
612 mcast_addr::<I>(1),
613 GroupRecordType::ModeIsInclude,
614 addr_iter_range::<I>(1..2).collect::<Vec<_>>(),
615 )];
616 let report2 = vec![(
617 mcast_addr::<I>(2),
618 GroupRecordType::ModeIsExclude,
619 addr_iter_range::<I>(2..4).collect::<Vec<_>>(),
620 )];
621 let report3 = vec![
622 (mcast_addr::<I>(3), GroupRecordType::ChangeToIncludeMode, vec![]),
623 (mcast_addr::<I>(4), GroupRecordType::ChangeToExcludeMode, vec![]),
624 ];
625 assert_eq!(collect(iter), vec![report1, report2, report3]);
626 }
627
628 #[ip_test(I)]
629 fn sources_split<I: Ip>() {
630 let iter = group_record_split_iterator(
631 GROUP_RECORD_HEADER + core::mem::size_of::<I::Addr>(),
632 GROUP_RECORD_HEADER,
633 [
634 (mcast_addr::<I>(1), GroupRecordType::ModeIsInclude, addr_iter_range::<I>(0..0)),
635 (mcast_addr::<I>(2), GroupRecordType::ModeIsInclude, addr_iter_range::<I>(0..3)),
636 (mcast_addr::<I>(3), GroupRecordType::ModeIsInclude, addr_iter_range::<I>(0..0)),
637 ]
638 .into_iter(),
639 )
640 .unwrap();
641
642 let report1 = vec![(mcast_addr::<I>(1), GroupRecordType::ModeIsInclude, vec![])];
643 let report2 = vec![(
644 mcast_addr::<I>(2),
645 GroupRecordType::ModeIsInclude,
646 addr_iter_range::<I>(0..1).collect::<Vec<_>>(),
647 )];
648 let report3 = vec![(
649 mcast_addr::<I>(2),
650 GroupRecordType::ModeIsInclude,
651 addr_iter_range::<I>(1..2).collect::<Vec<_>>(),
652 )];
653 let report4 = vec![(
654 mcast_addr::<I>(2),
655 GroupRecordType::ModeIsInclude,
656 addr_iter_range::<I>(2..3).collect::<Vec<_>>(),
657 )];
658 let report5 = vec![(mcast_addr::<I>(3), GroupRecordType::ModeIsInclude, vec![])];
659 assert_eq!(collect(iter), vec![report1, report2, report3, report4, report5]);
660 }
661
662 #[ip_test(I)]
663 fn sources_truncate<I: Ip>() {
664 let iter = group_record_split_iterator(
665 GROUP_RECORD_HEADER + core::mem::size_of::<I::Addr>(),
666 GROUP_RECORD_HEADER,
667 [
668 (mcast_addr::<I>(1), GroupRecordType::ModeIsInclude, addr_iter_range::<I>(0..0)),
669 (mcast_addr::<I>(2), GroupRecordType::ModeIsExclude, addr_iter_range::<I>(0..2)),
670 (mcast_addr::<I>(3), GroupRecordType::ModeIsInclude, addr_iter_range::<I>(2..3)),
671 ]
672 .into_iter(),
673 )
674 .unwrap();
675
676 let report1 = vec![(mcast_addr::<I>(1), GroupRecordType::ModeIsInclude, vec![])];
677 let report2 = vec![(
680 mcast_addr::<I>(2),
681 GroupRecordType::ModeIsExclude,
682 addr_iter_range::<I>(0..1).collect::<Vec<_>>(),
683 )];
684 let report3 = vec![(
685 mcast_addr::<I>(3),
686 GroupRecordType::ModeIsInclude,
687 addr_iter_range::<I>(2..3).collect::<Vec<_>>(),
688 )];
689 assert_eq!(collect(iter), vec![report1, report2, report3]);
690 }
691
692 #[ip_test(I)]
695 fn odd_split<I: Ip>() {
696 let iter = group_record_split_iterator(
697 GROUP_RECORD_HEADER + core::mem::size_of::<I::Addr>() * 4,
698 GROUP_RECORD_HEADER,
699 [
700 (mcast_addr::<I>(1), GroupRecordType::ModeIsInclude, addr_iter_range::<I>(0..5)),
701 (mcast_addr::<I>(2), GroupRecordType::ModeIsExclude, addr_iter_range::<I>(5..6)),
702 ]
703 .into_iter(),
704 )
705 .unwrap();
706
707 let report1 = vec![(
708 mcast_addr::<I>(1),
709 GroupRecordType::ModeIsInclude,
710 addr_iter_range::<I>(0..4).collect::<Vec<_>>(),
711 )];
712 let report2 = vec![(
713 mcast_addr::<I>(1),
714 GroupRecordType::ModeIsInclude,
715 addr_iter_range::<I>(4..5).collect::<Vec<_>>(),
716 )];
717 let report3 = vec![(
718 mcast_addr::<I>(2),
719 GroupRecordType::ModeIsExclude,
720 addr_iter_range::<I>(5..6).collect::<Vec<_>>(),
721 )];
722 assert_eq!(collect(iter), vec![report1, report2, report3]);
723 }
724
725 #[ip_test(I)]
728 fn split_off_large_group<I: Ip>() {
729 let iter = group_record_split_iterator(
730 (GROUP_RECORD_HEADER + core::mem::size_of::<I::Addr>()) * 2,
731 GROUP_RECORD_HEADER,
732 [
733 (mcast_addr::<I>(1), GroupRecordType::ModeIsInclude, addr_iter_range::<I>(0..1)),
734 (mcast_addr::<I>(2), GroupRecordType::ModeIsInclude, addr_iter_range::<I>(1..3)),
736 (mcast_addr::<I>(3), GroupRecordType::ModeIsInclude, addr_iter_range::<I>(3..4)),
737 (mcast_addr::<I>(4), GroupRecordType::ModeIsExclude, addr_iter_range::<I>(4..6)),
740 ]
741 .into_iter(),
742 )
743 .unwrap();
744
745 let report1 = vec![(
746 mcast_addr::<I>(1),
747 GroupRecordType::ModeIsInclude,
748 addr_iter_range::<I>(0..1).collect::<Vec<_>>(),
749 )];
750 let report2 = vec![(
751 mcast_addr::<I>(2),
752 GroupRecordType::ModeIsInclude,
753 addr_iter_range::<I>(1..3).collect::<Vec<_>>(),
754 )];
755 let report3 = vec![(
756 mcast_addr::<I>(3),
757 GroupRecordType::ModeIsInclude,
758 addr_iter_range::<I>(3..4).collect::<Vec<_>>(),
759 )];
760 let report4 = vec![(
761 mcast_addr::<I>(4),
762 GroupRecordType::ModeIsExclude,
763 addr_iter_range::<I>(4..6).collect::<Vec<_>>(),
764 )];
765 assert_eq!(collect(iter), vec![report1, report2, report3, report4]);
766 }
767}