Skip to main content

etherparse/transport/igmp/
membership_report_v1_slice.rs

1use crate::{igmp::*, *};
2
3/// Zero-copy slice of an IGMPv1 "Membership Report" (type `0x12`).
4///
5/// ```text
6/// 0                   1                   2                   3
7/// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
8/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
9/// |  Type = 0x12  |    Unused     |           Checksum            |
10/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
11/// |                         Group Address                         |
12/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
13/// ```
14#[derive(Clone, Debug, Eq, PartialEq)]
15pub struct MembershipReportV1Slice<'a> {
16    slice: &'a [u8],
17}
18
19impl<'a> MembershipReportV1Slice<'a> {
20    /// Creates a slice from bytes without checking the length.
21    ///
22    /// # Safety
23    ///
24    /// The caller must guarantee that `slice` is at least
25    /// [`MembershipReportV1Type::LEN`] (8) bytes long.
26    #[inline]
27    pub(crate) unsafe fn from_slice_unchecked(slice: &'a [u8]) -> MembershipReportV1Slice<'a> {
28        debug_assert!(slice.len() >= MembershipReportV1Type::LEN);
29        MembershipReportV1Slice { slice }
30    }
31
32    /// Returns the "checksum" value stored in the IGMP header.
33    #[inline]
34    pub fn checksum(&self) -> u16 {
35        // SAFETY: from_slice_unchecked guarantees at least 8 bytes.
36        unsafe { get_unchecked_be_u16(self.slice.as_ptr().add(2)) }
37    }
38
39    /// The IP multicast group address of the group being reported.
40    #[inline]
41    pub fn group_address(&self) -> GroupAddress {
42        // SAFETY: from_slice_unchecked guarantees at least 8 bytes.
43        GroupAddress::new(unsafe {
44            [
45                *self.slice.get_unchecked(4),
46                *self.slice.get_unchecked(5),
47                *self.slice.get_unchecked(6),
48                *self.slice.get_unchecked(7),
49            ]
50        })
51    }
52
53    /// Decodes the fixed fields into an owned [`MembershipReportV1Type`].
54    #[inline]
55    pub fn to_header(&self) -> MembershipReportV1Type {
56        MembershipReportV1Type {
57            group_address: self.group_address(),
58        }
59    }
60
61    /// Returns the bytes after the 8-byte header (usually empty).
62    #[inline]
63    pub fn payload(&self) -> &'a [u8] {
64        // SAFETY: from_slice_unchecked guarantees at least 8 bytes.
65        unsafe {
66            core::slice::from_raw_parts(
67                self.slice.as_ptr().add(MembershipReportV1Type::LEN),
68                self.slice.len() - MembershipReportV1Type::LEN,
69            )
70        }
71    }
72
73    /// Returns the slice containing the entire IGMP message.
74    #[inline]
75    pub fn slice(&self) -> &'a [u8] {
76        self.slice
77    }
78}