use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
pub(crate) const ADDRESS_IPV4: u8 = 1;
pub(crate) const ADDRESS_IPV6: u8 = 2;
pub(crate) struct Writer<'a> {
buffer: &'a mut [u8],
position: usize,
}
impl<'a> Writer<'a> {
pub fn new(buffer: &'a mut [u8]) -> Self {
Self { buffer, position: 0 }
}
pub fn position(&self) -> usize {
self.position
}
pub fn write_u8(&mut self, value: u8) {
self.buffer[self.position] = value;
self.position += 1;
}
pub fn write_u16(&mut self, value: u16) {
self.write_bytes(&value.to_le_bytes());
}
pub fn write_u32(&mut self, value: u32) {
self.write_bytes(&value.to_le_bytes());
}
pub fn write_u64(&mut self, value: u64) {
self.write_bytes(&value.to_le_bytes());
}
pub fn write_bytes(&mut self, bytes: &[u8]) {
self.buffer[self.position..self.position + bytes.len()].copy_from_slice(bytes);
self.position += bytes.len();
}
pub fn zero_pad_to_end(&mut self) {
self.buffer[self.position..].fill(0);
self.position = self.buffer.len();
}
pub fn write_address(&mut self, address: SocketAddr) {
match address {
SocketAddr::V4(address) => {
self.write_u8(ADDRESS_IPV4);
self.write_bytes(&address.ip().octets());
self.write_u16(address.port());
}
SocketAddr::V6(address) => {
self.write_u8(ADDRESS_IPV6);
for segment in address.ip().segments() {
self.write_u16(segment);
}
self.write_u16(address.port());
}
}
}
}
pub(crate) struct Reader<'a> {
buffer: &'a [u8],
position: usize,
}
impl<'a> Reader<'a> {
pub fn new(buffer: &'a [u8]) -> Self {
Self { buffer, position: 0 }
}
fn read_slice(&mut self, length: usize) -> Option<&'a [u8]> {
let slice = self.buffer.get(self.position..self.position + length)?;
self.position += length;
Some(slice)
}
pub fn read_u8(&mut self) -> Option<u8> {
let byte = *self.buffer.get(self.position)?;
self.position += 1;
Some(byte)
}
pub fn read_u16(&mut self) -> Option<u16> {
Some(u16::from_le_bytes(self.read_slice(2)?.try_into().unwrap()))
}
pub fn read_u32(&mut self) -> Option<u32> {
Some(u32::from_le_bytes(self.read_slice(4)?.try_into().unwrap()))
}
pub fn read_u64(&mut self) -> Option<u64> {
Some(u64::from_le_bytes(self.read_slice(8)?.try_into().unwrap()))
}
pub fn read_bytes<const N: usize>(&mut self) -> Option<[u8; N]> {
Some(self.read_slice(N)?.try_into().unwrap())
}
pub fn read_address(&mut self) -> Option<SocketAddr> {
match self.read_u8()? {
ADDRESS_IPV4 => {
let octets: [u8; 4] = self.read_bytes()?;
let port = self.read_u16()?;
Some(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::from(octets), port)))
}
ADDRESS_IPV6 => {
let mut segments = [0u16; 8];
for segment in &mut segments {
*segment = self.read_u16()?;
}
let port = self.read_u16()?;
Some(SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::from(segments), port, 0, 0)))
}
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn round_trip() {
let mut buffer = [0u8; 64];
let mut writer = Writer::new(&mut buffer);
writer.write_u8(0xAB);
writer.write_u16(0x1234);
writer.write_u32(0xDEADBEEF);
writer.write_u64(0x1122334455667788);
writer.write_bytes(&[1, 2, 3]);
let written = writer.position();
let mut reader = Reader::new(&buffer[..written]);
assert_eq!(reader.read_u8(), Some(0xAB));
assert_eq!(reader.read_u16(), Some(0x1234));
assert_eq!(reader.read_u32(), Some(0xDEADBEEF));
assert_eq!(reader.read_u64(), Some(0x1122334455667788));
assert_eq!(reader.read_bytes::<3>(), Some([1, 2, 3]));
assert_eq!(reader.read_u8(), None);
}
#[test]
fn little_endian_on_the_wire() {
let mut buffer = [0u8; 8];
Writer::new(&mut buffer).write_u64(0x1122334455667788);
assert_eq!(buffer, [0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11]);
}
#[test]
fn address_round_trip() {
let addresses: [SocketAddr; 3] = [
"127.0.0.1:40000".parse().unwrap(),
"[::1]:50000".parse().unwrap(),
"[fe80::202:b3ff:fe1e:8329]:65535".parse().unwrap(),
];
for address in addresses {
let mut buffer = [0u8; 32];
let mut writer = Writer::new(&mut buffer);
writer.write_address(address);
let written = writer.position();
let mut reader = Reader::new(&buffer[..written]);
assert_eq!(reader.read_address(), Some(address));
}
}
#[test]
fn invalid_address_type_rejected() {
let buffer = [3u8, 0, 0, 0, 0, 0, 0];
assert_eq!(Reader::new(&buffer).read_address(), None);
}
}