use super::types::Address;
use crate::{
DecodeError,
DecodeStatus,
EncodeError,
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct UdpDatagram<'payload> {
pub fragment: u8,
pub address: Address,
pub port: u16,
pub payload: &'payload [u8],
}
impl UdpDatagram<'_> {
#[must_use]
pub const fn encoded_len(&self) -> usize {
self.address
.encoded_len()
.saturating_add(self.payload.len())
.saturating_add(5)
}
pub fn encode_into(&self, buffer: &mut Vec<u8>) -> Result<(), EncodeError> {
buffer.reserve(self.encoded_len());
buffer.push(0x00); buffer.push(0x00); buffer.push(self.fragment);
self.address.encode_into(buffer)?;
buffer.extend_from_slice(&self.port.to_be_bytes());
buffer.extend_from_slice(self.payload);
Ok(())
}
pub fn decode(source: &[u8]) -> Result<DecodeStatus<(UdpDatagram<'_>, usize)>, DecodeError> {
let Some((&reserved_high, after_high)) = source.split_first() else {
return Ok(DecodeStatus::Partial);
};
let Some((&reserved_low, after_low)) = after_high.split_first() else {
return Ok(DecodeStatus::Partial);
};
let Some((&fragment, rest)) = after_low.split_first() else {
return Ok(DecodeStatus::Partial);
};
if reserved_high != 0x00 || reserved_low != 0x00 {
return Err(DecodeError::Malformed("reserved bytes are not zero"));
}
let (address, consumed) = match Address::decode_from(rest)? {
| DecodeStatus::Complete(complete) => complete,
| DecodeStatus::Partial => return Ok(DecodeStatus::Partial),
};
let Some(after_address) = rest.get(consumed ..) else {
return Ok(DecodeStatus::Partial);
};
let Some(&[port_high, port_low]) = after_address.first_chunk::<2>() else {
return Ok(DecodeStatus::Partial);
};
let Some(payload) = after_address.get(2 ..) else {
return Ok(DecodeStatus::Partial);
};
Ok(DecodeStatus::Complete((
UdpDatagram {
fragment,
address,
port: u16::from_be_bytes([port_high, port_low]),
payload,
},
consumed.saturating_add(payload.len()).saturating_add(5),
)))
}
}