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