use std::io::Write;
use std::time::Duration;
use byteorder_slice::byteorder::WriteBytesExt;
use byteorder_slice::result::ReadSlice;
use byteorder_slice::ByteOrder;
use derive_into_owned::IntoOwned;
use super::block_common::{Block, PcapNgBlock};
use super::opt_common::{CommonOption, PcapNgOption, WriteOptTo};
use crate::errors::PcapError;
use crate::pcapng::PcapNgState;
#[derive(Clone, Debug, IntoOwned, Eq, PartialEq)]
pub struct InterfaceStatisticsBlock<'a> {
pub interface_id: u32,
pub timestamp: Duration,
pub options: Vec<InterfaceStatisticsOption<'a>>,
}
impl<'a> PcapNgBlock<'a> for InterfaceStatisticsBlock<'a> {
fn from_slice<B: ByteOrder>(state: &PcapNgState, mut slice: &'a [u8]) -> Result<(&'a [u8], Self), PcapError> {
if slice.len() < 12 {
return Err(PcapError::InvalidField("InterfaceStatisticsBlock: block length < 12"));
}
let interface_id = slice.read_u32::<B>().unwrap();
let timestamp = state.decode_timestamp::<B>(interface_id, &mut slice)?;
let (slice, options) = InterfaceStatisticsOption::opts_from_slice::<B>(state, Some(interface_id), slice)?;
let block = InterfaceStatisticsBlock { interface_id, timestamp, options };
Ok((slice, block))
}
fn write_to<B: ByteOrder, W: Write>(&self, state: &PcapNgState, writer: &mut W) -> Result<usize, PcapError> {
writer.write_u32::<B>(self.interface_id)?;
state.encode_timestamp::<B, W>(self.interface_id, self.timestamp, writer)?;
let opt_len = InterfaceStatisticsOption::write_opts_to::<B, _>(&self.options, state, Some(self.interface_id), writer)?;
Ok(12 + opt_len)
}
fn into_block(self) -> Block<'a> {
Block::InterfaceStatistics(self)
}
}
#[derive(Clone, Debug, IntoOwned, Eq, PartialEq)]
pub enum InterfaceStatisticsOption<'a> {
IsbStartTime(Duration),
IsbEndTime(Duration),
IsbIfRecv(u64),
IsbIfDrop(u64),
IsbFilterAccept(u64),
IsbOsDrop(u64),
IsbUsrDeliv(u64),
Common(CommonOption<'a>),
}
impl<'a> PcapNgOption<'a> for InterfaceStatisticsOption<'a> {
fn from_slice<B: ByteOrder>(state: &PcapNgState, interface_id: Option<u32>, code: u16, mut slice: &'a [u8]) -> Result<Self, PcapError> {
let opt = match code {
2 => InterfaceStatisticsOption::IsbStartTime(state.decode_timestamp::<B>(interface_id.unwrap(), &mut slice)?),
3 => InterfaceStatisticsOption::IsbEndTime(state.decode_timestamp::<B>(interface_id.unwrap(), &mut slice)?),
4 => InterfaceStatisticsOption::IsbIfRecv(slice.read_u64::<B>().map_err(|_| PcapError::IncompleteBuffer(8, slice.len()))?),
5 => InterfaceStatisticsOption::IsbIfDrop(slice.read_u64::<B>().map_err(|_| PcapError::IncompleteBuffer(8, slice.len()))?),
6 => InterfaceStatisticsOption::IsbFilterAccept(slice.read_u64::<B>().map_err(|_| PcapError::IncompleteBuffer(8, slice.len()))?),
7 => InterfaceStatisticsOption::IsbOsDrop(slice.read_u64::<B>().map_err(|_| PcapError::IncompleteBuffer(8, slice.len()))?),
8 => InterfaceStatisticsOption::IsbUsrDeliv(slice.read_u64::<B>().map_err(|_| PcapError::IncompleteBuffer(8, slice.len()))?),
_ => InterfaceStatisticsOption::Common(CommonOption::new::<B>(code, slice)?),
};
Ok(opt)
}
fn write_to<B: ByteOrder, W: Write>(&self, state: &PcapNgState, interface_id: Option<u32>, writer: &mut W) -> Result<usize, PcapError> {
Ok(match self {
InterfaceStatisticsOption::IsbStartTime(a) => write_timestamp::<B, W>(2, a, state, interface_id, writer)?,
InterfaceStatisticsOption::IsbEndTime(a) => write_timestamp::<B, W>(3, a, state, interface_id, writer)?,
InterfaceStatisticsOption::IsbIfRecv(a) => a.write_opt_to::<B, W>(4, writer)?,
InterfaceStatisticsOption::IsbIfDrop(a) => a.write_opt_to::<B, W>(5, writer)?,
InterfaceStatisticsOption::IsbFilterAccept(a) => a.write_opt_to::<B, W>(6, writer)?,
InterfaceStatisticsOption::IsbOsDrop(a) => a.write_opt_to::<B, W>(7, writer)?,
InterfaceStatisticsOption::IsbUsrDeliv(a) => a.write_opt_to::<B, W>(8, writer)?,
InterfaceStatisticsOption::Common(a) => a.write_opt_to::<B, W>(a.code(), writer)?,
})
}
}
fn write_timestamp<B: ByteOrder, W: Write>(
code: u16,
timestamp: &Duration,
state: &PcapNgState,
interface_id: Option<u32>,
writer: &mut W
) -> Result<usize, PcapError> {
const TIMESTAMP_LENGTH: u16 = 8;
const OPTION_LENGTH: usize = 12;
writer.write_u16::<B>(code)?;
writer.write_u16::<B>(TIMESTAMP_LENGTH)?;
state.encode_timestamp::<B, W>(interface_id.unwrap(), *timestamp, writer)?;
Ok(OPTION_LENGTH)
}