use crate::igmp::{GroupAddress, MaxResponseCode, Qrv};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct MembershipQueryWithSourcesHeader {
pub max_response_code: MaxResponseCode,
pub group_address: GroupAddress,
pub raw_byte_8: u8,
pub qqic: u8,
pub num_of_sources: u16,
}
impl MembershipQueryWithSourcesHeader {
pub const LEN: usize = 12;
pub const RAW_BYTE_8_MASK_FLAGS: u8 = 0b1111_0000;
pub const RAW_BYTE_8_OFFSET_FLAGS: u8 = 4;
pub const RAW_BYTE_8_MASK_S_FLAG: u8 = 0b0000_1000;
pub const RAW_BYTE_8_MASK_QRV: u8 = 0b0000_0111;
pub fn flags(&self) -> u8 {
(self.raw_byte_8 & Self::RAW_BYTE_8_MASK_FLAGS) >> Self::RAW_BYTE_8_OFFSET_FLAGS
}
pub fn set_flags(&mut self, value: u8) {
self.raw_byte_8 = (self.raw_byte_8 & (!Self::RAW_BYTE_8_MASK_FLAGS))
| ((value << Self::RAW_BYTE_8_OFFSET_FLAGS) & Self::RAW_BYTE_8_MASK_FLAGS);
}
pub fn s_flag(&self) -> bool {
0 != (self.raw_byte_8 & Self::RAW_BYTE_8_MASK_S_FLAG)
}
pub fn set_s_flag(&mut self, value: bool) {
if value {
self.raw_byte_8 |= Self::RAW_BYTE_8_MASK_S_FLAG;
} else {
self.raw_byte_8 &= !Self::RAW_BYTE_8_MASK_S_FLAG;
}
}
pub fn qrv(&self) -> Qrv {
unsafe { Qrv::new_unchecked(self.raw_byte_8 & Self::RAW_BYTE_8_MASK_QRV) }
}
pub fn set_qrv(&mut self, value: Qrv) {
self.raw_byte_8 = (self.raw_byte_8 & (!Self::RAW_BYTE_8_MASK_QRV))
| (value.value() & Self::RAW_BYTE_8_MASK_QRV);
}
}
#[cfg(test)]
mod test {
use super::*;
use alloc::format;
use proptest::prelude::*;
proptest! {
#[test]
fn flags_get(raw_byte_8 in any::<u8>()) {
let header = MembershipQueryWithSourcesHeader {
max_response_code: MaxResponseCode(0),
group_address: GroupAddress::new([0, 0, 0, 0]),
raw_byte_8,
qqic: 0,
num_of_sources: 0,
};
prop_assert_eq!(header.flags(), raw_byte_8 >> 4);
}
}
proptest! {
#[test]
fn flags_set(
raw_byte_8 in any::<u8>(),
value in any::<u8>(),
) {
let mut header = MembershipQueryWithSourcesHeader {
max_response_code: MaxResponseCode(0),
group_address: GroupAddress::new([0, 0, 0, 0]),
raw_byte_8,
qqic: 0,
num_of_sources: 0,
};
header.set_flags(value);
prop_assert_eq!(header.flags(), value & 0b0000_1111);
prop_assert_eq!(
header.raw_byte_8 & !MembershipQueryWithSourcesHeader::RAW_BYTE_8_MASK_FLAGS,
raw_byte_8 & !MembershipQueryWithSourcesHeader::RAW_BYTE_8_MASK_FLAGS
);
}
}
proptest! {
#[test]
fn flags_roundtrip(
raw_byte_8 in any::<u8>(),
value in 0u8..=0b0000_1111,
) {
let mut header = MembershipQueryWithSourcesHeader {
max_response_code: MaxResponseCode(0),
group_address: GroupAddress::new([0, 0, 0, 0]),
raw_byte_8,
qqic: 0,
num_of_sources: 0,
};
header.set_flags(value);
prop_assert_eq!(header.flags(), value);
}
}
proptest! {
#[test]
fn s_flag_get(raw_byte_8 in any::<u8>()) {
let header = MembershipQueryWithSourcesHeader {
max_response_code: MaxResponseCode(0),
group_address: GroupAddress::new([0, 0, 0, 0]),
raw_byte_8,
qqic: 0,
num_of_sources: 0,
};
prop_assert_eq!(
header.s_flag(),
0 != (raw_byte_8 & MembershipQueryWithSourcesHeader::RAW_BYTE_8_MASK_S_FLAG)
);
}
}
proptest! {
#[test]
fn s_flag_set(
raw_byte_8 in any::<u8>(),
value in any::<bool>(),
) {
let mut header = MembershipQueryWithSourcesHeader {
max_response_code: MaxResponseCode(0),
group_address: GroupAddress::new([0, 0, 0, 0]),
raw_byte_8,
qqic: 0,
num_of_sources: 0,
};
header.set_s_flag(value);
prop_assert_eq!(header.s_flag(), value);
prop_assert_eq!(
header.raw_byte_8 & !MembershipQueryWithSourcesHeader::RAW_BYTE_8_MASK_S_FLAG,
raw_byte_8 & !MembershipQueryWithSourcesHeader::RAW_BYTE_8_MASK_S_FLAG
);
}
}
proptest! {
#[test]
fn qrv_get(raw_byte_8 in any::<u8>()) {
let header = MembershipQueryWithSourcesHeader {
max_response_code: MaxResponseCode(0),
group_address: GroupAddress::new([0, 0, 0, 0]),
raw_byte_8,
qqic: 0,
num_of_sources: 0,
};
prop_assert_eq!(
header.qrv().value(),
raw_byte_8 & MembershipQueryWithSourcesHeader::RAW_BYTE_8_MASK_QRV
);
}
}
proptest! {
#[test]
fn qrv_set(
raw_byte_8 in any::<u8>(),
value in 0u8..=Qrv::MAX_U8,
) {
let qrv = Qrv::try_new(value).unwrap();
let mut header = MembershipQueryWithSourcesHeader {
max_response_code: MaxResponseCode(0),
group_address: GroupAddress::new([0, 0, 0, 0]),
raw_byte_8,
qqic: 0,
num_of_sources: 0,
};
header.set_qrv(qrv);
prop_assert_eq!(header.qrv().value(), value);
prop_assert_eq!(
header.raw_byte_8 & !MembershipQueryWithSourcesHeader::RAW_BYTE_8_MASK_QRV,
raw_byte_8 & !MembershipQueryWithSourcesHeader::RAW_BYTE_8_MASK_QRV
);
}
}
}