use crate::layers::{EthernetSlice, Ipv4Slice, Layers, TcpSlice, UdpSlice};
pub const ETH_BITS: usize = 14 * 8; pub const IPV4_BITS: usize = 20 * 8; pub const TCP_BITS: usize = 20 * 8; pub const UDP_BITS: usize = 8 * 8;
pub fn bits_per_row(eth: bool, ipv4: bool, tcp: bool, udp: bool) -> usize {
let mut n = 0;
if eth {
n += ETH_BITS;
}
if ipv4 {
n += IPV4_BITS;
}
if tcp {
n += TCP_BITS;
}
if udp {
n += UDP_BITS;
}
n
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
#[non_exhaustive]
pub enum NPrintBit {
Absent,
Zero,
One,
}
impl NPrintBit {
pub fn from_raw(raw: i8) -> Self {
match raw {
0 => Self::Zero,
1 => Self::One,
_ => Self::Absent,
}
}
pub fn as_raw(&self) -> i8 {
match self {
Self::Absent => -1,
Self::Zero => 0,
Self::One => 1,
}
}
pub fn is_present(&self) -> bool {
!matches!(self, Self::Absent)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct NPrintRow {
pub bits: Vec<NPrintBit>,
}
impl NPrintRow {
pub fn absent(width: usize) -> Self {
Self {
bits: vec![NPrintBit::Absent; width],
}
}
pub(crate) fn fill_ethernet(&mut self, off: usize, eth: &EthernetSlice<'_>) {
encode_bytes(&mut self.bits, off, eth.header());
}
pub(crate) fn fill_ipv4(&mut self, off: usize, ip: &Ipv4Slice<'_>) {
let h = ip.header();
let n = h.len().min(20);
encode_bytes(&mut self.bits, off, &h[..n]);
}
pub(crate) fn fill_tcp(&mut self, off: usize, tcp: &TcpSlice<'_>) {
let h = tcp.header();
let n = h.len().min(20);
encode_bytes(&mut self.bits, off, &h[..n]);
}
pub(crate) fn fill_udp(&mut self, off: usize, udp: &UdpSlice<'_>) {
encode_bytes(&mut self.bits, off, udp.header());
}
}
fn encode_bytes(dst: &mut [NPrintBit], off: usize, src: &[u8]) {
for (i, byte) in src.iter().enumerate() {
for bit in 0..8 {
let dst_idx = off + i * 8 + bit;
if dst_idx >= dst.len() {
return;
}
let val = (byte >> (7 - bit)) & 0x01;
dst[dst_idx] = if val == 1 {
NPrintBit::One
} else {
NPrintBit::Zero
};
}
}
}
pub(crate) fn encode_from_layers(
layers: &Layers<'_>,
eth: bool,
ipv4: bool,
tcp: bool,
udp: bool,
) -> NPrintRow {
let width = bits_per_row(eth, ipv4, tcp, udp);
let mut row = NPrintRow::absent(width);
let mut off = 0;
if eth {
if let Some(e) = layers.ethernet() {
row.fill_ethernet(off, e);
}
off += ETH_BITS;
}
if ipv4 {
if let Some(ip) = layers.ipv4() {
row.fill_ipv4(off, ip);
}
off += IPV4_BITS;
}
if tcp {
if let Some(t) = layers.tcp() {
row.fill_tcp(off, t);
}
off += TCP_BITS;
}
if udp {
if let Some(u) = layers.udp() {
row.fill_udp(off, u);
}
let _ = off; }
row
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn absent_row_is_all_absent() {
let r = NPrintRow::absent(32);
assert_eq!(r.bits.len(), 32);
assert!(r.bits.iter().all(|b| *b == NPrintBit::Absent));
}
#[test]
fn encode_bytes_msb_first() {
let mut buf = vec![NPrintBit::Absent; 16];
encode_bytes(&mut buf, 0, &[0b10110010]);
let expected = [
NPrintBit::One,
NPrintBit::Zero,
NPrintBit::One,
NPrintBit::One,
NPrintBit::Zero,
NPrintBit::Zero,
NPrintBit::One,
NPrintBit::Zero,
];
assert_eq!(&buf[..8], &expected);
assert!(buf[8..].iter().all(|b| *b == NPrintBit::Absent));
}
#[test]
fn encode_bytes_truncates_at_dst_end() {
let mut buf = vec![NPrintBit::Absent; 4];
encode_bytes(&mut buf, 0, &[0xFF, 0xFF]); assert_eq!(buf, vec![NPrintBit::One; 4]);
}
#[test]
fn bit_round_trip() {
for raw in [-1i8, 0, 1] {
assert_eq!(NPrintBit::from_raw(raw).as_raw(), raw);
}
assert_eq!(NPrintBit::from_raw(42).as_raw(), -1);
}
#[test]
fn bits_per_row_sums_enabled_regions() {
assert_eq!(bits_per_row(false, false, false, false), 0);
assert_eq!(bits_per_row(true, false, false, false), ETH_BITS);
assert_eq!(bits_per_row(true, true, false, false), ETH_BITS + IPV4_BITS);
assert_eq!(
bits_per_row(true, true, true, true),
ETH_BITS + IPV4_BITS + TCP_BITS + UDP_BITS
);
}
}