pub const ETHERTYPE_BOUNDARY: u16 = 0x0600;
pub const ETHERTYPE_IPV4: u16 = 0x0800;
pub const ETHERTYPE_IPV6: u16 = 0x86DD;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub enum TypeField {
NextHeader {
h_len: u8,
h_type: u8,
},
EtherType(u16),
}
impl TypeField {
pub fn from_u16(raw: u16) -> Self {
if raw < ETHERTYPE_BOUNDARY {
let h_len = ((raw >> 8) & 0x07) as u8;
let h_type = (raw & 0x00FF) as u8;
TypeField::NextHeader { h_len, h_type }
} else {
TypeField::EtherType(raw)
}
}
pub fn to_u16(self) -> u16 {
match self {
TypeField::NextHeader { h_len, h_type } => ((h_len as u16 & 0x07) << 8) | h_type as u16,
TypeField::EtherType(et) => et,
}
}
pub fn is_next_header(self) -> bool {
matches!(self, TypeField::NextHeader { .. })
}
pub fn name(&self) -> &'static str {
match self {
TypeField::NextHeader { .. } => "next-header",
TypeField::EtherType(_) => "ethertype",
}
}
}
dvb_common::impl_spec_display!(TypeField);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn boundary_split() {
assert!(TypeField::from_u16(0x05FF).is_next_header());
assert!(!TypeField::from_u16(0x0600).is_next_header());
assert_eq!(TypeField::from_u16(0x0800), TypeField::EtherType(0x0800));
assert_eq!(TypeField::from_u16(0x86DD), TypeField::EtherType(0x86DD));
}
#[test]
fn next_header_split_round_trips() {
let t = TypeField::from_u16(0x0301);
assert_eq!(
t,
TypeField::NextHeader {
h_len: 3,
h_type: 0x01
}
);
assert_eq!(t.to_u16(), 0x0301);
let m = TypeField::from_u16(0x0001);
assert_eq!(
m,
TypeField::NextHeader {
h_len: 0,
h_type: 0x01
}
);
assert_eq!(m.to_u16(), 0x0001);
}
#[test]
fn all_u16_round_trip() {
for raw in 0u32..=0xFFFF {
let raw = raw as u16;
assert_eq!(TypeField::from_u16(raw).to_u16(), raw, "raw={raw:#06X}");
}
}
}