use core::fmt;
use core::iter::FusedIterator;
use core::net::{Ipv4Addr, SocketAddrV4};
use crate::{
BvllError, BvllSink, SinkFull, BDT_ENTRY_LENGTH, BIP_ADDRESS_LENGTH, FDT_ENTRY_LENGTH,
};
const ENTRY_LENGTH: usize = BDT_ENTRY_LENGTH;
const _: () = assert!(FDT_ENTRY_LENGTH == ENTRY_LENGTH);
#[must_use]
pub const fn address_to_octets(address: SocketAddrV4) -> [u8; BIP_ADDRESS_LENGTH] {
let [a, b, c, d] = address.ip().octets();
let [high, low] = address.port().to_be_bytes();
[a, b, c, d, high, low]
}
#[must_use]
pub const fn address_from_octets(octets: [u8; BIP_ADDRESS_LENGTH]) -> SocketAddrV4 {
let [a, b, c, d, high, low] = octets;
SocketAddrV4::new(Ipv4Addr::new(a, b, c, d), u16::from_be_bytes([high, low]))
}
pub fn address_from_slice(octets: &[u8]) -> Result<SocketAddrV4, BvllError> {
match <[u8; BIP_ADDRESS_LENGTH]>::try_from(octets) {
Ok(octets) => Ok(address_from_octets(octets)),
Err(_) => Err(BvllError::AddressLength {
length: octets.len(),
}),
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct BdtEntry {
pub address: SocketAddrV4,
pub mask: Ipv4Addr,
}
impl BdtEntry {
#[must_use]
pub const fn new(address: SocketAddrV4, mask: Ipv4Addr) -> Self {
Self { address, mask }
}
#[must_use]
pub const fn to_octets(self) -> [u8; ENTRY_LENGTH] {
let [a, b, c, d, high, low] = address_to_octets(self.address);
let [m0, m1, m2, m3] = self.mask.octets();
[a, b, c, d, high, low, m0, m1, m2, m3]
}
#[must_use]
pub const fn from_octets(octets: [u8; ENTRY_LENGTH]) -> Self {
let [a, b, c, d, high, low, m0, m1, m2, m3] = octets;
Self {
address: address_from_octets([a, b, c, d, high, low]),
mask: Ipv4Addr::new(m0, m1, m2, m3),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct FdtEntry {
pub address: SocketAddrV4,
pub ttl: u16,
pub remaining: u16,
}
impl FdtEntry {
#[must_use]
pub const fn new(address: SocketAddrV4, ttl: u16, remaining: u16) -> Self {
Self {
address,
ttl,
remaining,
}
}
#[must_use]
pub const fn to_octets(self) -> [u8; ENTRY_LENGTH] {
let [a, b, c, d, high, low] = address_to_octets(self.address);
let [t0, t1] = self.ttl.to_be_bytes();
let [r0, r1] = self.remaining.to_be_bytes();
[a, b, c, d, high, low, t0, t1, r0, r1]
}
#[must_use]
pub const fn from_octets(octets: [u8; ENTRY_LENGTH]) -> Self {
let [a, b, c, d, high, low, t0, t1, r0, r1] = octets;
Self {
address: address_from_octets([a, b, c, d, high, low]),
ttl: u16::from_be_bytes([t0, t1]),
remaining: u16::from_be_bytes([r0, r1]),
}
}
}
mod sealed {
pub trait Sealed {}
impl Sealed for super::BdtEntry {}
impl Sealed for super::FdtEntry {}
}
pub trait TableEntry: Copy + sealed::Sealed {
fn from_wire(octets: [u8; ENTRY_LENGTH]) -> Self;
fn to_wire(self) -> [u8; ENTRY_LENGTH];
}
impl TableEntry for BdtEntry {
fn from_wire(octets: [u8; ENTRY_LENGTH]) -> Self {
Self::from_octets(octets)
}
fn to_wire(self) -> [u8; ENTRY_LENGTH] {
self.to_octets()
}
}
impl TableEntry for FdtEntry {
fn from_wire(octets: [u8; ENTRY_LENGTH]) -> Self {
Self::from_octets(octets)
}
fn to_wire(self) -> [u8; ENTRY_LENGTH] {
self.to_octets()
}
}
#[derive(Clone, Copy)]
pub struct Entries<'a, T> {
repr: Repr<'a, T>,
}
#[derive(Clone, Copy)]
enum Repr<'a, T> {
Wire(&'a [[u8; ENTRY_LENGTH]]),
Typed(&'a [T]),
}
pub type BdtEntries<'a> = Entries<'a, BdtEntry>;
pub type FdtEntries<'a> = Entries<'a, FdtEntry>;
impl<'a, T: TableEntry> Entries<'a, T> {
pub fn from_wire(octets: &'a [u8]) -> Result<Self, BvllError> {
match octets.as_chunks::<ENTRY_LENGTH>() {
(entries, []) => Ok(Self {
repr: Repr::Wire(entries),
}),
_ => Err(BvllError::TableLength {
length: octets.len(),
entry_length: ENTRY_LENGTH,
}),
}
}
#[must_use]
pub const fn from_entries(entries: &'a [T]) -> Self {
Self {
repr: Repr::Typed(entries),
}
}
#[must_use]
pub const fn len(&self) -> usize {
match self.repr {
Repr::Wire(entries) => entries.len(),
Repr::Typed(entries) => entries.len(),
}
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.len() == 0
}
#[must_use]
pub fn iter(&self) -> EntryIter<'a, T> {
EntryIter {
repr: match self.repr {
Repr::Wire(entries) => IterRepr::Wire(entries.iter()),
Repr::Typed(entries) => IterRepr::Typed(entries.iter()),
},
}
}
pub(crate) const fn wire_length(&self) -> usize {
self.len() * ENTRY_LENGTH
}
pub(crate) fn write_to<S: BvllSink + ?Sized>(&self, sink: &mut S) -> Result<(), SinkFull> {
match self.repr {
Repr::Wire(entries) => sink.write(entries.as_flattened()),
Repr::Typed(entries) => entries
.iter()
.try_for_each(|entry| sink.write(&entry.to_wire())),
}
}
}
impl<T: TableEntry + PartialEq> PartialEq for Entries<'_, T> {
fn eq(&self, other: &Self) -> bool {
self.iter().eq(other.iter())
}
}
impl<T: TableEntry + Eq> Eq for Entries<'_, T> {}
impl<T: TableEntry + fmt::Debug> fmt::Debug for Entries<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(self.iter()).finish()
}
}
impl<'a, T: TableEntry> IntoIterator for Entries<'a, T> {
type Item = T;
type IntoIter = EntryIter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<'a, T: TableEntry> IntoIterator for &Entries<'a, T> {
type Item = T;
type IntoIter = EntryIter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
#[derive(Debug, Clone)]
pub struct EntryIter<'a, T> {
repr: IterRepr<'a, T>,
}
#[derive(Debug, Clone)]
enum IterRepr<'a, T> {
Wire(core::slice::Iter<'a, [u8; ENTRY_LENGTH]>),
Typed(core::slice::Iter<'a, T>),
}
impl<T: TableEntry> Iterator for EntryIter<'_, T> {
type Item = T;
fn next(&mut self) -> Option<T> {
match &mut self.repr {
IterRepr::Wire(entries) => entries.next().map(|octets| T::from_wire(*octets)),
IterRepr::Typed(entries) => entries.next().copied(),
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
match &self.repr {
IterRepr::Wire(entries) => entries.size_hint(),
IterRepr::Typed(entries) => entries.size_hint(),
}
}
}
impl<T: TableEntry> DoubleEndedIterator for EntryIter<'_, T> {
fn next_back(&mut self) -> Option<T> {
match &mut self.repr {
IterRepr::Wire(entries) => entries.next_back().map(|octets| T::from_wire(*octets)),
IterRepr::Typed(entries) => entries.next_back().copied(),
}
}
}
impl<T: TableEntry> ExactSizeIterator for EntryIter<'_, T> {}
impl<T: TableEntry> FusedIterator for EntryIter<'_, T> {}