use core::mem;
use crate::{getter_be, setter_be};
#[repr(C)]
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "wincode", derive(wincode::SchemaRead, wincode::SchemaWrite))]
#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))]
pub struct UdpHdr {
pub src: [u8; 2],
pub dst: [u8; 2],
pub len: [u8; 2],
pub check: [u8; 2],
}
impl UdpHdr {
pub const LEN: usize = mem::size_of::<UdpHdr>();
#[inline]
pub fn src_port(&self) -> u16 {
unsafe { getter_be!(self, src, u16) }
}
#[inline]
pub fn set_src_port(&mut self, src: u16) {
unsafe { setter_be!(self, src, src) }
}
#[inline]
pub fn dst_port(&self) -> u16 {
unsafe { getter_be!(self, dst, u16) }
}
#[inline]
pub fn set_dst_port(&mut self, dst: u16) {
unsafe { setter_be!(self, dst, dst) }
}
#[inline]
pub fn len(&self) -> u16 {
unsafe { getter_be!(self, len, u16) }
}
pub fn is_empty(&self) -> bool {
self.len == [0, 0]
}
#[inline]
pub fn set_len(&mut self, len: u16) {
unsafe { setter_be!(self, len, len) }
}
#[inline]
pub fn checksum(&self) -> u16 {
unsafe { getter_be!(self, check, u16) }
}
#[inline]
pub fn set_checksum(&mut self, check: u16) {
unsafe { setter_be!(self, check, check) }
}
}
#[cfg(test)]
mod test {
use super::UdpHdr;
use core::mem;
#[test]
fn test_udp_hdr_size() {
assert_eq!(UdpHdr::LEN, 8);
assert_eq!(UdpHdr::LEN, mem::size_of::<UdpHdr>());
}
#[test]
fn test_source_port() {
let mut udp_hdr = UdpHdr {
src: [0, 0],
dst: [0, 0],
len: [0, 0],
check: [0, 0],
};
let test_port: u16 = 12345;
udp_hdr.set_src_port(test_port);
assert_eq!(udp_hdr.src_port(), test_port);
assert_eq!(udp_hdr.src, [0x30, 0x39]);
udp_hdr.set_src_port(0);
assert_eq!(udp_hdr.src_port(), 0);
assert_eq!(udp_hdr.src, [0, 0]);
udp_hdr.set_src_port(u16::MAX);
assert_eq!(udp_hdr.src_port(), u16::MAX);
assert_eq!(udp_hdr.src, [0xFF, 0xFF]);
}
#[test]
fn test_dest_port() {
let mut udp_hdr = UdpHdr {
src: [0, 0],
dst: [0, 0],
len: [0, 0],
check: [0, 0],
};
let test_port: u16 = 80;
udp_hdr.set_dst_port(test_port);
assert_eq!(udp_hdr.dst_port(), test_port);
assert_eq!(udp_hdr.dst, [0x00, 0x50]);
udp_hdr.set_dst_port(0);
assert_eq!(udp_hdr.dst_port(), 0);
assert_eq!(udp_hdr.dst, [0, 0]);
udp_hdr.set_dst_port(u16::MAX);
assert_eq!(udp_hdr.dst_port(), u16::MAX);
assert_eq!(udp_hdr.dst, [0xFF, 0xFF]);
}
#[test]
fn test_length() {
let mut udp_hdr = UdpHdr {
src: [0, 0],
dst: [0, 0],
len: [0, 0],
check: [0, 0],
};
let test_len: u16 = 28;
udp_hdr.set_len(test_len);
assert_eq!(udp_hdr.len(), test_len);
assert_eq!(udp_hdr.len, [0x00, 0x1C]);
udp_hdr.set_len(8);
assert_eq!(udp_hdr.len(), 8);
assert_eq!(udp_hdr.len, [0x00, 0x08]);
udp_hdr.set_len(u16::MAX);
assert_eq!(udp_hdr.len(), u16::MAX);
assert_eq!(udp_hdr.len, [0xFF, 0xFF]);
}
#[test]
fn test_empty() {
let mut udp_hdr = UdpHdr {
src: [0, 0],
dst: [0, 0],
len: [0, 0],
check: [0, 0],
};
assert!(udp_hdr.is_empty());
udp_hdr.set_len(8);
assert!(!udp_hdr.is_empty());
udp_hdr.set_len(0);
assert!(udp_hdr.is_empty());
}
#[test]
fn test_checksum() {
let mut udp_hdr = UdpHdr {
src: [0, 0],
dst: [0, 0],
len: [0, 0],
check: [0, 0],
};
let test_checksum: u16 = 0x1234;
udp_hdr.set_checksum(test_checksum);
assert_eq!(udp_hdr.checksum(), test_checksum);
assert_eq!(udp_hdr.check, [0x12, 0x34]);
udp_hdr.set_checksum(0);
assert_eq!(udp_hdr.checksum(), 0);
assert_eq!(udp_hdr.check, [0, 0]);
udp_hdr.set_checksum(u16::MAX);
assert_eq!(udp_hdr.checksum(), u16::MAX);
assert_eq!(udp_hdr.check, [0xFF, 0xFF]);
}
#[test]
fn test_complete_udp_header() {
let mut udp_hdr = UdpHdr {
src: [0, 0],
dst: [0, 0],
len: [0, 0],
check: [0, 0],
};
udp_hdr.set_src_port(12345);
udp_hdr.set_dst_port(80);
udp_hdr.set_len(28); udp_hdr.set_checksum(0x1234);
assert_eq!(udp_hdr.src_port(), 12345);
assert_eq!(udp_hdr.dst_port(), 80);
assert_eq!(udp_hdr.len(), 28);
assert_eq!(udp_hdr.checksum(), 0x1234);
assert_eq!(udp_hdr.src, [0x30, 0x39]); assert_eq!(udp_hdr.dst, [0x00, 0x50]); assert_eq!(udp_hdr.len, [0x00, 0x1C]); assert_eq!(udp_hdr.check, [0x12, 0x34]); }
#[test]
#[cfg(feature = "wincode")]
fn test_serialize() {
use core::mem;
let udp = UdpHdr {
src: 4242_u16.to_be_bytes(),
dst: 4789_u16.to_be_bytes(),
len: 42_u16.to_be_bytes(),
check: 0_u16.to_be_bytes(),
};
let mut bytes = [0u8; mem::size_of::<UdpHdr>()];
wincode::serialize_into(bytes.as_mut_slice(), &udp).unwrap();
}
}