use super::{descriptor_body, write_descriptor_header};
use crate::error::{Error, Result};
use alloc::vec::Vec;
use broadcast_common::len::need_out;
use core::net::{Ipv4Addr, Ipv6Addr};
pub(crate) const HEADER_LEN: usize = 2;
pub type MacAddress = [u8; 6];
pub(crate) trait Octets<const N: usize>: Copy {
fn from_octets(b: [u8; N]) -> Self;
fn to_octets(self) -> [u8; N];
}
impl Octets<4> for Ipv4Addr {
fn from_octets(b: [u8; 4]) -> Self {
Self::from(b)
}
fn to_octets(self) -> [u8; 4] {
self.octets()
}
}
impl Octets<16> for Ipv6Addr {
fn from_octets(b: [u8; 16]) -> Self {
Self::from(b)
}
fn to_octets(self) -> [u8; 16] {
self.octets()
}
}
impl Octets<6> for [u8; 6] {
fn from_octets(b: [u8; 6]) -> Self {
b
}
fn to_octets(self) -> [u8; 6] {
self
}
}
pub(crate) trait Entry<const E: usize>: Sized {
fn read(b: &[u8; E]) -> Self;
fn write(&self, b: &mut [u8; E]);
}
pub(crate) fn check_trailing(tag: u8, trailing: &[u8], entry_len: usize) -> Result<()> {
if trailing.len() >= entry_len {
return Err(Error::InvalidDescriptor {
tag,
reason: "trailing is not shorter than one entry (would re-parse as entries)",
});
}
Ok(())
}
pub(crate) fn parse_entries<const E: usize, T: Entry<E>>(body: &[u8]) -> (Vec<T>, &[u8]) {
let (chunks, tail) = body.as_chunks::<E>();
(chunks.iter().map(T::read).collect(), tail)
}
pub(crate) fn write_entries<const E: usize, T: Entry<E>>(
buf: &mut [u8],
mut off: usize,
entries: &[T],
trailing: &[u8],
) -> usize {
for e in entries {
let mut b = [0u8; E];
e.write(&mut b);
buf[off..off + E].copy_from_slice(&b);
off += E;
}
buf[off..off + trailing.len()].copy_from_slice(trailing);
off + trailing.len()
}
pub(crate) fn parse_masked<'a, const N: usize, A: Octets<N>>(
bytes: &'a [u8],
tag: u8,
what: &'static str,
) -> Result<(A, Vec<A>, &'a [u8])> {
let body = descriptor_body(bytes, tag, what, "unexpected descriptor_tag")?;
let (mask, rest) = body
.split_first_chunk::<N>()
.ok_or(Error::InvalidDescriptor {
tag,
reason: "descriptor body shorter than its address mask",
})?;
let (chunks, tail) = rest.as_chunks::<N>();
Ok((
A::from_octets(*mask),
chunks.iter().map(|c| A::from_octets(*c)).collect(),
tail,
))
}
pub(crate) fn write_masked<const N: usize, A: Octets<N>>(
buf: &mut [u8],
tag: u8,
mask: A,
addrs: &[A],
trailing: &[u8],
) -> Result<usize> {
check_trailing(tag, trailing, N)?;
let len = HEADER_LEN + N + addrs.len() * N + trailing.len();
need_out(buf, len)?;
write_descriptor_header(buf, tag, len - HEADER_LEN)?;
buf[HEADER_LEN..HEADER_LEN + N].copy_from_slice(&mask.to_octets());
let mut off = HEADER_LEN + N;
for a in addrs {
buf[off..off + N].copy_from_slice(&a.to_octets());
off += N;
}
buf[off..off + trailing.len()].copy_from_slice(trailing);
Ok(len)
}
pub(crate) fn parse_list<'a, const E: usize, T: Entry<E>>(
bytes: &'a [u8],
tag: u8,
what: &'static str,
) -> Result<(Vec<T>, &'a [u8])> {
let body = descriptor_body(bytes, tag, what, "unexpected descriptor_tag")?;
Ok(parse_entries::<E, T>(body))
}
pub(crate) fn write_list<const E: usize, T: Entry<E>>(
buf: &mut [u8],
tag: u8,
entries: &[T],
trailing: &[u8],
) -> Result<usize> {
check_trailing(tag, trailing, E)?;
let len = HEADER_LEN + entries.len() * E + trailing.len();
need_out(buf, len)?;
write_descriptor_header(buf, tag, len - HEADER_LEN)?;
write_entries(buf, HEADER_LEN, entries, trailing);
Ok(len)
}
pub(crate) const fn list_len(entries: usize, entry_len: usize, trailing: usize) -> usize {
HEADER_LEN + entries * entry_len + trailing
}