1use core::borrow::Borrow;
10use core::fmt::Debug;
11use core::num::NonZeroUsize;
12use core::time::Duration;
13use core::usize;
14
15use net_types::ip::IpAddress;
16use net_types::MulticastAddr;
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() {
153 Ok(res)
154 } else {
155 Err(ExactConversionError::NotExact)
156 }
157 }
158}
159
160create_protocol_enum!(
161 #[allow(missing_docs)]
169 #[derive(PartialEq, Eq, Copy, Clone, PartialOrd, Ord)]
170 pub enum GroupRecordType: u8 {
171 ModeIsInclude, 0x01, "Mode Is Include";
172 ModeIsExclude, 0x02, "Mode Is Exclude";
173 ChangeToIncludeMode, 0x03, "Change To Include Mode";
174 ChangeToExcludeMode, 0x04, "Change To Exclude Mode";
175 AllowNewSources, 0x05, "Allow New Sources";
176 BlockOldSources, 0x06, "Block Old Sources";
177 }
178);
179
180impl GroupRecordType {
181 fn allow_split(&self) -> bool {
212 match self {
213 GroupRecordType::ModeIsInclude
214 | GroupRecordType::ChangeToIncludeMode
215 | GroupRecordType::AllowNewSources
216 | GroupRecordType::BlockOldSources => true,
217 GroupRecordType::ModeIsExclude | GroupRecordType::ChangeToExcludeMode => false,
218 }
219 }
220}
221
222#[derive(PartialEq, Eq, Debug, Clone, Copy, Default)]
230pub struct QQIC(u8);
231
232impl QQIC {
233 pub fn new_lossy(value: Duration) -> Result<Self, OverflowError> {
235 Self::lossy_try_from(value)
236 }
237
238 pub fn new_exact(value: Duration) -> Result<Self, ExactConversionError> {
240 Self::exact_try_from(value)
241 }
242}
243
244impl LinExpConversion<Duration> for QQIC {
245 const NUM_MANT_BITS: u8 = 4;
246 const NUM_EXP_BITS: u8 = 3;
247
248 fn lossy_try_from(value: Duration) -> Result<Self, OverflowError> {
249 let secs: u32 = value.as_secs().try_into().map_err(|_| OverflowError)?;
250 let code = Self::lossy_try_from_expanded(secs)?.try_into().map_err(|_| OverflowError)?;
251 Ok(Self(code))
252 }
253}
254
255impl From<QQIC> for Duration {
256 fn from(code: QQIC) -> Self {
257 let secs: u64 = QQIC::to_expanded(code.0.into()).into();
258 Duration::from_secs(secs)
259 }
260}
261
262impl From<QQIC> for u8 {
263 fn from(QQIC(v): QQIC) -> Self {
264 v
265 }
266}
267
268impl From<u8> for QQIC {
269 fn from(value: u8) -> Self {
270 Self(value)
271 }
272}
273
274#[derive(PartialEq, Eq, Debug, Clone, Copy, Default)]
282pub struct QRV(u8);
283
284impl QRV {
285 const QRV_MAX: u8 = 7;
286
287 pub fn new(robustness_value: u8) -> Self {
302 if robustness_value > Self::QRV_MAX {
303 return QRV(0);
304 }
305 QRV(robustness_value)
306 }
307}
308
309impl From<QRV> for u8 {
310 fn from(qrv: QRV) -> u8 {
311 qrv.0
312 }
313}
314
315pub trait GmpReportGroupRecord<A: IpAddress> {
324 fn group(&self) -> MulticastAddr<A>;
326
327 fn record_type(&self) -> GroupRecordType;
329
330 fn sources(&self) -> impl Iterator<Item: Borrow<A>> + '_;
332}
333
334impl<A, I> GmpReportGroupRecord<A> for (MulticastAddr<A>, GroupRecordType, I)
335where
336 A: IpAddress,
337 I: Iterator<Item: Borrow<A>> + Clone,
338{
339 fn group(&self) -> MulticastAddr<A> {
340 self.0
341 }
342
343 fn record_type(&self) -> GroupRecordType {
344 self.1
345 }
346
347 fn sources(&self) -> impl Iterator<Item: Borrow<A>> + '_ {
348 self.2.clone()
349 }
350}
351
352#[derive(Clone)]
353struct OverrideGroupRecordSources<R> {
354 record: R,
355 limit: NonZeroUsize,
356 skip: usize,
357}
358
359impl<R, A> GmpReportGroupRecord<A> for OverrideGroupRecordSources<R>
360where
361 A: IpAddress,
362 R: GmpReportGroupRecord<A>,
363{
364 fn group(&self) -> MulticastAddr<A> {
365 self.record.group()
366 }
367
368 fn record_type(&self) -> GroupRecordType {
369 self.record.record_type()
370 }
371
372 fn sources(&self) -> impl Iterator<Item: Borrow<A>> + '_ {
373 self.record.sources().skip(self.skip).take(self.limit.get())
374 }
375}
376
377#[derive(Debug, Eq, PartialEq)]
379pub struct InvalidConstraintsError;
380
381pub(crate) fn group_record_split_iterator<A, I>(
382 max_len: usize,
383 group_header: usize,
384 groups: I,
385) -> Result<
386 impl Iterator<Item: Iterator<Item: GmpReportGroupRecord<A>> + Clone>,
387 InvalidConstraintsError,
388>
389where
390 A: IpAddress,
391 I: Iterator<Item: GmpReportGroupRecord<A> + Clone> + Clone,
392{
393 if group_header + core::mem::size_of::<A>() > max_len {
395 return Err(InvalidConstraintsError);
396 }
397 let mut groups = groups.peekable();
402 let mut skip = 0;
405 Ok(core::iter::from_fn(move || {
406 let start = groups.clone();
407 let mut take = 0;
408 let mut len = 0;
409 loop {
410 let group = match groups.peek() {
411 Some(group) => group,
412 None => break,
413 };
414 len += group_header;
415 if len > max_len {
417 break;
418 }
419
420 let skipped = core::mem::replace(&mut skip, 0);
423 let sources = group.sources();
424 if take == 0 {
425 let mut sources = sources.skip(skipped).enumerate();
430 loop {
431 let Some((i, _)) = sources.next() else { break };
436
437 len += core::mem::size_of::<A>();
438 if len > max_len {
439 let limit = NonZeroUsize::new(i).expect("can't fit a single source");
443 let record = if group.record_type().allow_split() {
444 skip = skipped + i;
447 group.clone()
448 } else {
449 drop(sources);
453 groups.next().unwrap()
454 };
455 return Some(either::Either::Left(core::iter::once(
456 OverrideGroupRecordSources { record, limit, skip: skipped },
457 )));
458 }
459 }
460 if skipped != 0 {
465 drop(sources);
468 let group = groups.next().unwrap();
469 return Some(either::Either::Left(core::iter::once(
470 OverrideGroupRecordSources {
471 record: group,
472 limit: NonZeroUsize::MAX,
473 skip: skipped,
474 },
475 )));
476 }
477 } else {
478 assert_eq!(skipped, 0);
480 len += sources.count() * core::mem::size_of::<A>();
483 if len > max_len {
484 break;
485 }
486 }
487
488 let _: Option<_> = groups.next();
490 take += 1;
491 }
492
493 if take == 0 {
494 None
495 } else {
496 Some(either::Either::Right(start.take(take).map(|record| OverrideGroupRecordSources {
497 record,
498 limit: NonZeroUsize::MAX,
499 skip: 0,
500 })))
501 }
502 }))
503}
504
505#[cfg(test)]
506mod tests {
507 use core::ops::Range;
508
509 use super::*;
510
511 use ip_test_macro::ip_test;
512 use net_types::ip::{Ip, Ipv4Addr, Ipv6Addr};
513
514 fn empty_iter<A: IpAddress>() -> impl Iterator<Item: GmpReportGroupRecord<A> + Clone> + Clone {
515 core::iter::empty::<(MulticastAddr<A>, GroupRecordType, core::iter::Empty<A>)>()
516 }
517
518 fn addr<I: Ip>(i: u8) -> I::Addr {
519 I::map_ip_out(
520 i,
521 |i| Ipv4Addr::new([0, 0, 0, i]),
522 |i| Ipv6Addr::from_bytes([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, i]),
523 )
524 }
525
526 fn mcast_addr<I: Ip>(i: u8) -> MulticastAddr<I::Addr> {
527 MulticastAddr::new(I::map_ip_out(
528 i,
529 |i| Ipv4Addr::new([224, 0, 0, i]),
530 |i| Ipv6Addr::from_bytes([0xFF, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, i]),
531 ))
532 .unwrap()
533 }
534
535 fn addr_iter_range<I: Ip>(range: Range<u8>) -> impl Iterator<Item = I::Addr> + Clone {
536 range.into_iter().map(|i| addr::<I>(i))
537 }
538
539 fn collect<I, A>(iter: I) -> Vec<Vec<(MulticastAddr<A>, GroupRecordType, Vec<A>)>>
540 where
541 I: Iterator<Item: Iterator<Item: GmpReportGroupRecord<A>>>,
542 A: IpAddress,
543 {
544 iter.map(|groups| {
545 groups
546 .map(|g| {
547 (
548 g.group(),
549 g.record_type(),
550 g.sources().map(|b| b.borrow().clone()).collect::<Vec<_>>(),
551 )
552 })
553 .collect::<Vec<_>>()
554 })
555 .collect::<Vec<_>>()
556 }
557
558 const GROUP_RECORD_HEADER: usize = 1;
559
560 #[ip_test(I)]
561 fn split_rejects_small_lengths<I: Ip>() {
562 assert_eq!(
563 group_record_split_iterator(
564 GROUP_RECORD_HEADER,
565 GROUP_RECORD_HEADER,
566 empty_iter::<I::Addr>()
567 )
568 .map(collect),
569 Err(InvalidConstraintsError)
570 );
571 assert_eq!(
572 group_record_split_iterator(
573 GROUP_RECORD_HEADER + core::mem::size_of::<I::Addr>() - 1,
574 GROUP_RECORD_HEADER,
575 empty_iter::<I::Addr>()
576 )
577 .map(collect),
578 Err(InvalidConstraintsError)
579 );
580 assert_eq!(
582 group_record_split_iterator(
583 GROUP_RECORD_HEADER + core::mem::size_of::<I::Addr>(),
584 GROUP_RECORD_HEADER,
585 empty_iter::<I::Addr>()
586 )
587 .map(collect),
588 Ok(vec![])
589 );
590 }
591
592 #[ip_test(I)]
593 fn basic_split<I: Ip>() {
594 let iter = group_record_split_iterator(
595 GROUP_RECORD_HEADER + core::mem::size_of::<I::Addr>() * 2,
596 GROUP_RECORD_HEADER,
597 [
598 (mcast_addr::<I>(1), GroupRecordType::ModeIsInclude, addr_iter_range::<I>(1..2)),
599 (mcast_addr::<I>(2), GroupRecordType::ModeIsExclude, addr_iter_range::<I>(2..4)),
600 (
601 mcast_addr::<I>(3),
602 GroupRecordType::ChangeToIncludeMode,
603 addr_iter_range::<I>(0..0),
604 ),
605 (
606 mcast_addr::<I>(4),
607 GroupRecordType::ChangeToExcludeMode,
608 addr_iter_range::<I>(0..0),
609 ),
610 ]
611 .into_iter(),
612 )
613 .unwrap();
614
615 let report1 = vec![(
616 mcast_addr::<I>(1),
617 GroupRecordType::ModeIsInclude,
618 addr_iter_range::<I>(1..2).collect::<Vec<_>>(),
619 )];
620 let report2 = vec![(
621 mcast_addr::<I>(2),
622 GroupRecordType::ModeIsExclude,
623 addr_iter_range::<I>(2..4).collect::<Vec<_>>(),
624 )];
625 let report3 = vec![
626 (mcast_addr::<I>(3), GroupRecordType::ChangeToIncludeMode, vec![]),
627 (mcast_addr::<I>(4), GroupRecordType::ChangeToExcludeMode, vec![]),
628 ];
629 assert_eq!(collect(iter), vec![report1, report2, report3]);
630 }
631
632 #[ip_test(I)]
633 fn sources_split<I: Ip>() {
634 let iter = group_record_split_iterator(
635 GROUP_RECORD_HEADER + core::mem::size_of::<I::Addr>(),
636 GROUP_RECORD_HEADER,
637 [
638 (mcast_addr::<I>(1), GroupRecordType::ModeIsInclude, addr_iter_range::<I>(0..0)),
639 (mcast_addr::<I>(2), GroupRecordType::ModeIsInclude, addr_iter_range::<I>(0..3)),
640 (mcast_addr::<I>(3), GroupRecordType::ModeIsInclude, addr_iter_range::<I>(0..0)),
641 ]
642 .into_iter(),
643 )
644 .unwrap();
645
646 let report1 = vec![(mcast_addr::<I>(1), GroupRecordType::ModeIsInclude, vec![])];
647 let report2 = vec![(
648 mcast_addr::<I>(2),
649 GroupRecordType::ModeIsInclude,
650 addr_iter_range::<I>(0..1).collect::<Vec<_>>(),
651 )];
652 let report3 = vec![(
653 mcast_addr::<I>(2),
654 GroupRecordType::ModeIsInclude,
655 addr_iter_range::<I>(1..2).collect::<Vec<_>>(),
656 )];
657 let report4 = vec![(
658 mcast_addr::<I>(2),
659 GroupRecordType::ModeIsInclude,
660 addr_iter_range::<I>(2..3).collect::<Vec<_>>(),
661 )];
662 let report5 = vec![(mcast_addr::<I>(3), GroupRecordType::ModeIsInclude, vec![])];
663 assert_eq!(collect(iter), vec![report1, report2, report3, report4, report5]);
664 }
665
666 #[ip_test(I)]
667 fn sources_truncate<I: Ip>() {
668 let iter = group_record_split_iterator(
669 GROUP_RECORD_HEADER + core::mem::size_of::<I::Addr>(),
670 GROUP_RECORD_HEADER,
671 [
672 (mcast_addr::<I>(1), GroupRecordType::ModeIsInclude, addr_iter_range::<I>(0..0)),
673 (mcast_addr::<I>(2), GroupRecordType::ModeIsExclude, addr_iter_range::<I>(0..2)),
674 (mcast_addr::<I>(3), GroupRecordType::ModeIsInclude, addr_iter_range::<I>(2..3)),
675 ]
676 .into_iter(),
677 )
678 .unwrap();
679
680 let report1 = vec![(mcast_addr::<I>(1), GroupRecordType::ModeIsInclude, vec![])];
681 let report2 = vec![(
684 mcast_addr::<I>(2),
685 GroupRecordType::ModeIsExclude,
686 addr_iter_range::<I>(0..1).collect::<Vec<_>>(),
687 )];
688 let report3 = vec![(
689 mcast_addr::<I>(3),
690 GroupRecordType::ModeIsInclude,
691 addr_iter_range::<I>(2..3).collect::<Vec<_>>(),
692 )];
693 assert_eq!(collect(iter), vec![report1, report2, report3]);
694 }
695
696 #[ip_test(I)]
699 fn odd_split<I: Ip>() {
700 let iter = group_record_split_iterator(
701 GROUP_RECORD_HEADER + core::mem::size_of::<I::Addr>() * 4,
702 GROUP_RECORD_HEADER,
703 [
704 (mcast_addr::<I>(1), GroupRecordType::ModeIsInclude, addr_iter_range::<I>(0..5)),
705 (mcast_addr::<I>(2), GroupRecordType::ModeIsExclude, addr_iter_range::<I>(5..6)),
706 ]
707 .into_iter(),
708 )
709 .unwrap();
710
711 let report1 = vec![(
712 mcast_addr::<I>(1),
713 GroupRecordType::ModeIsInclude,
714 addr_iter_range::<I>(0..4).collect::<Vec<_>>(),
715 )];
716 let report2 = vec![(
717 mcast_addr::<I>(1),
718 GroupRecordType::ModeIsInclude,
719 addr_iter_range::<I>(4..5).collect::<Vec<_>>(),
720 )];
721 let report3 = vec![(
722 mcast_addr::<I>(2),
723 GroupRecordType::ModeIsExclude,
724 addr_iter_range::<I>(5..6).collect::<Vec<_>>(),
725 )];
726 assert_eq!(collect(iter), vec![report1, report2, report3]);
727 }
728
729 #[ip_test(I)]
732 fn split_off_large_group<I: Ip>() {
733 let iter = group_record_split_iterator(
734 (GROUP_RECORD_HEADER + core::mem::size_of::<I::Addr>()) * 2,
735 GROUP_RECORD_HEADER,
736 [
737 (mcast_addr::<I>(1), GroupRecordType::ModeIsInclude, addr_iter_range::<I>(0..1)),
738 (mcast_addr::<I>(2), GroupRecordType::ModeIsInclude, addr_iter_range::<I>(1..3)),
740 (mcast_addr::<I>(3), GroupRecordType::ModeIsInclude, addr_iter_range::<I>(3..4)),
741 (mcast_addr::<I>(4), GroupRecordType::ModeIsExclude, addr_iter_range::<I>(4..6)),
744 ]
745 .into_iter(),
746 )
747 .unwrap();
748
749 let report1 = vec![(
750 mcast_addr::<I>(1),
751 GroupRecordType::ModeIsInclude,
752 addr_iter_range::<I>(0..1).collect::<Vec<_>>(),
753 )];
754 let report2 = vec![(
755 mcast_addr::<I>(2),
756 GroupRecordType::ModeIsInclude,
757 addr_iter_range::<I>(1..3).collect::<Vec<_>>(),
758 )];
759 let report3 = vec![(
760 mcast_addr::<I>(3),
761 GroupRecordType::ModeIsInclude,
762 addr_iter_range::<I>(3..4).collect::<Vec<_>>(),
763 )];
764 let report4 = vec![(
765 mcast_addr::<I>(4),
766 GroupRecordType::ModeIsExclude,
767 addr_iter_range::<I>(4..6).collect::<Vec<_>>(),
768 )];
769 assert_eq!(collect(iter), vec![report1, report2, report3, report4]);
770 }
771}