use std::{cmp::min, mem::transmute};
use crate::{
core::{
read::unchecked_bit_range_be_read,
view::{
View, ViewConversionError,
macros::{gen_field_read, gen_field_write},
},
},
payload::udp::layout::UdpDatagramLayout,
};
#[repr(transparent)]
pub struct UdpDatagramView([u8]);
impl View for UdpDatagramView {
#[inline]
fn has_required_size(buf: &[u8]) -> Result<usize, ViewConversionError> {
if buf.len() < UdpDatagramLayout::HEADER_SIZE_BYTES {
return Err(ViewConversionError::BufferTooSmall {
at: "UdpHeader",
required: UdpDatagramLayout::HEADER_SIZE_BYTES,
actual: buf.len(),
});
}
let header_len: u16 =
unsafe { unchecked_bit_range_be_read(buf, UdpDatagramLayout::LENGTH_RNG) };
if header_len < UdpDatagramLayout::HEADER_SIZE_BYTES as u16 {
return Err(ViewConversionError::Other(
"UDP length field smaller than minimum header size",
));
}
Ok(min(buf.len(), header_len as usize))
}
#[inline]
unsafe fn from_slice_unchecked(buf: &[u8]) -> &Self {
unsafe { transmute(buf) }
}
#[inline]
unsafe fn from_mut_slice_unchecked(buf: &mut [u8]) -> &mut Self {
unsafe { transmute(buf) }
}
#[inline]
unsafe fn from_boxed_unchecked(buf: Box<[u8]>) -> Box<Self> {
unsafe { transmute(buf) }
}
#[inline]
unsafe fn as_bytes_mut(&mut self) -> &mut [u8] {
&mut self.0
}
#[inline]
fn as_bytes_boxed(self: Box<Self>) -> Box<[u8]> {
unsafe { transmute(self) }
}
#[inline]
fn as_bytes(&self) -> &[u8] {
&self.0
}
}
impl UdpDatagramView {
gen_field_read!(src_port, UdpDatagramLayout::SRC_PORT_RNG, u16);
gen_field_read!(dst_port, UdpDatagramLayout::DST_PORT_RNG, u16);
gen_field_read!(length, UdpDatagramLayout::LENGTH_RNG, u16);
gen_field_read!(checksum, UdpDatagramLayout::CHECKSUM_RNG, u16);
gen_field_write!(set_src_port, UdpDatagramLayout::SRC_PORT_RNG, u16);
gen_field_write!(set_dst_port, UdpDatagramLayout::DST_PORT_RNG, u16);
gen_field_write!(set_length, UdpDatagramLayout::LENGTH_RNG, u16);
gen_field_write!(set_checksum, UdpDatagramLayout::CHECKSUM_RNG, u16);
#[inline]
pub fn payload(&self) -> &[u8] {
unsafe { self.0.get_unchecked(UdpDatagramLayout::HEADER_SIZE_BYTES..) }
}
#[inline]
pub fn payload_mut(&mut self) -> &mut [u8] {
unsafe {
self.0
.get_unchecked_mut(UdpDatagramLayout::HEADER_SIZE_BYTES..)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_udp(src: u16, dst: u16, payload: &[u8]) -> Vec<u8> {
let len = (8 + payload.len()) as u16;
let mut buf = Vec::with_capacity(8 + payload.len());
buf.extend_from_slice(&src.to_be_bytes());
buf.extend_from_slice(&dst.to_be_bytes());
buf.extend_from_slice(&len.to_be_bytes());
buf.extend_from_slice(&0u16.to_be_bytes()); buf.extend_from_slice(payload);
buf
}
#[test]
fn udp_packet_view_valid_header() {
let buf = make_udp(1234, 5678, b"hello");
let (view, _) = UdpDatagramView::from_slice(&buf).unwrap();
assert_eq!(view.src_port(), 1234);
assert_eq!(view.dst_port(), 5678);
assert_eq!(view.length(), 13);
assert_eq!(view.payload(), b"hello");
}
#[test]
fn udp_packet_view_truncated_input() {
let buf = [0u8; 7]; assert!(UdpDatagramView::from_slice(&buf).is_err());
}
#[test]
fn udp_packet_view_exact_header_no_payload() {
let buf = make_udp(80, 443, &[]);
let (view, _) = UdpDatagramView::from_slice(&buf).unwrap();
assert_eq!(view.src_port(), 80);
assert_eq!(view.dst_port(), 443);
assert_eq!(view.payload(), b"");
}
}