use crate::descriptors::entry_list::{Entry, list_len, parse_list, write_list};
use crate::error::Result;
use alloc::vec::Vec;
use broadcast_common::{Parse, Serialize};
use core::net::Ipv6Addr;
pub const TAG: u8 = 0x12;
const ENTRY_LEN: usize = 34;
const ADDR_LEN: usize = 16;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct Ipv6SourceDestSlash {
pub source_addr: Ipv6Addr,
pub source_slash_mask: u8,
pub dest_addr: Ipv6Addr,
pub dest_slash_mask: u8,
}
impl Entry<ENTRY_LEN> for Ipv6SourceDestSlash {
fn read(b: &[u8; ENTRY_LEN]) -> Self {
let mut s = [0u8; ADDR_LEN];
let mut d = [0u8; ADDR_LEN];
s.copy_from_slice(&b[..ADDR_LEN]);
d.copy_from_slice(&b[ADDR_LEN + 1..2 * ADDR_LEN + 1]);
Self {
source_addr: Ipv6Addr::from(s),
source_slash_mask: b[ADDR_LEN],
dest_addr: Ipv6Addr::from(d),
dest_slash_mask: b[2 * ADDR_LEN + 1],
}
}
fn write(&self, b: &mut [u8; ENTRY_LEN]) {
b[..ADDR_LEN].copy_from_slice(&self.source_addr.octets());
b[ADDR_LEN] = self.source_slash_mask;
b[ADDR_LEN + 1..2 * ADDR_LEN + 1].copy_from_slice(&self.dest_addr.octets());
b[2 * ADDR_LEN + 1] = self.dest_slash_mask;
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
pub struct TargetIpv6SourceSlashDescriptor<'a> {
pub entries: Vec<Ipv6SourceDestSlash>,
pub trailing: &'a [u8],
}
impl<'a> Parse<'a> for TargetIpv6SourceSlashDescriptor<'a> {
type Error = crate::error::Error;
fn parse(bytes: &'a [u8]) -> Result<Self> {
let (entries, trailing) = parse_list::<ENTRY_LEN, Ipv6SourceDestSlash>(
bytes,
TAG,
"TargetIpv6SourceSlashDescriptor",
)?;
Ok(Self { entries, trailing })
}
}
impl Serialize for TargetIpv6SourceSlashDescriptor<'_> {
type Error = crate::error::Error;
fn serialized_len(&self) -> usize {
list_len(self.entries.len(), ENTRY_LEN, self.trailing.len())
}
fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
write_list::<ENTRY_LEN, _>(buf, TAG, &self.entries, self.trailing)
}
}
impl<'a> crate::traits::DescriptorDef<'a> for TargetIpv6SourceSlashDescriptor<'a> {
const TAG: u8 = TAG;
const NAME: &'static str = "TARGET_IPV6_SOURCE_SLASH";
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn serialize_rejects_entry_sized_trailing() {
let mut d = TargetIpv6SourceSlashDescriptor::parse(&[TAG, 0]).unwrap();
let long = [0u8; ENTRY_LEN];
d.trailing = &long;
assert!(matches!(
&(d.serialize_into(&mut [0u8; 64])),
Err(crate::error::Error::InvalidDescriptor { .. })
));
let mut buf = [0u8; 64];
d.trailing = &long[..ENTRY_LEN - 1];
let n = d.serialize_into(&mut buf).unwrap();
assert_eq!(
TargetIpv6SourceSlashDescriptor::parse(&buf[..n]).unwrap(),
d
);
}
#[test]
fn parse_and_round_trip() {
let s = Ipv6Addr::new(0xfe80, 0, 0, 0, 0x0ba3, 0x00a0, 0x0008, 0x00be);
let mut bytes = vec![TAG, 34];
bytes.extend_from_slice(&s.octets());
bytes.push(128);
bytes.extend_from_slice(&Ipv6Addr::LOCALHOST.octets());
bytes.push(64);
let d = TargetIpv6SourceSlashDescriptor::parse(&bytes).unwrap();
assert_eq!(d.entries.len(), 1);
assert_eq!(d.entries[0].source_addr, s);
assert_eq!(d.entries[0].source_slash_mask, 128);
assert_eq!(d.entries[0].dest_addr, Ipv6Addr::LOCALHOST);
assert_eq!(d.entries[0].dest_slash_mask, 64);
let mut out = vec![0u8; d.serialized_len()];
d.serialize_into(&mut out).unwrap();
assert_eq!(out, bytes);
}
#[test]
fn empty_and_partial_tail_round_trip() {
for bytes in [&[TAG, 0][..], &[TAG, 3, 7, 7, 7][..]] {
let d = TargetIpv6SourceSlashDescriptor::parse(bytes).unwrap();
let mut out = vec![0u8; d.serialized_len()];
d.serialize_into(&mut out).unwrap();
assert_eq!(out, bytes);
}
assert!(matches!(
TargetIpv6SourceSlashDescriptor::parse(&[0x11, 0]),
Err(crate::Error::InvalidDescriptor { .. })
));
}
}