use std::fmt;
use std::hash::{Hash, Hasher};
use std::slice;
use std::vec;
use xxhash_rust::xxh3::Xxh3;
use crate::{
Packet,
packet::{
Iter,
SEIP,
},
};
#[derive(Clone, Debug)]
pub enum Body {
Unprocessed(Vec<u8>),
Processed(Vec<u8>),
Structured(Vec<Packet>),
}
assert_send_and_sync!(Body);
#[derive(Clone)]
pub struct Container {
body: Body,
body_digest: u64,
}
assert_send_and_sync!(Container);
impl PartialEq for Container {
fn eq(&self, other: &Container) -> bool {
use Body::*;
match (&self.body, &other.body) {
(Unprocessed(_), Unprocessed(_)) =>
self.body_digest == other.body_digest,
(Processed(_), Processed(_)) =>
self.body_digest == other.body_digest,
(Structured(a), Structured(b)) =>
a == b,
_ => false,
}
}
}
impl Eq for Container {}
impl Hash for Container {
fn hash<H: Hasher>(&self, state: &mut H) {
if let Body::Structured(packets) = &self.body {
packets.hash(state);
} else {
self.body_digest.hash(state);
}
}
}
impl Default for Container {
fn default() -> Self {
Self {
body: Body::Structured(Vec::with_capacity(0)),
body_digest: 0,
}
}
}
impl From<Vec<Packet>> for Container {
fn from(packets: Vec<Packet>) -> Self {
Self {
body: Body::Structured(packets),
body_digest: 0,
}
}
}
impl fmt::Debug for Container {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fn fmt_bytes(f: &mut fmt::Formatter, tag: &str, bytes: &[u8],
digest: String)
-> fmt::Result
{
let threshold = 16;
let prefix = &bytes[..std::cmp::min(threshold, bytes.len())];
let mut prefix_fmt = crate::fmt::hex::encode(prefix);
if bytes.len() > threshold {
prefix_fmt.push_str("...");
}
prefix_fmt.push_str(&format!(" ({} bytes)", bytes.len())[..]);
f.debug_struct("Container")
.field(tag, &prefix_fmt)
.field("digest", &digest)
.finish()
}
use Body::*;
match &self.body {
Unprocessed(bytes) =>
fmt_bytes(f, "unprocessed", bytes, self.body_digest()),
Processed(bytes) =>
fmt_bytes(f, "processed", bytes, self.body_digest()),
Structured(packets) =>
f.debug_struct("Container").field("packets", packets).finish(),
}
}
}
impl Container {
pub(crate) fn default_unprocessed() -> Self {
Self {
body: Body::Unprocessed(Vec::with_capacity(0)),
body_digest: Self::empty_body_digest(),
}
}
pub fn children_ref(&self) -> Option<&[Packet]> {
if let Body::Structured(packets) = &self.body {
Some(&packets[..])
} else {
None
}
}
pub fn children_mut(&mut self) -> Option<&mut Vec<Packet>> {
if let Body::Structured(packets) = &mut self.body {
Some(packets)
} else {
None
}
}
pub fn descendants(&self) -> Option<Iter> {
Some(Iter {
children: self.children()?,
child: None,
grandchildren: None,
depth: 0,
})
}
pub fn children(&self) -> Option<slice::Iter<Packet>> {
Some(self.children_ref()?.iter())
}
pub fn into_children(self) -> Option<vec::IntoIter<Packet>> {
if let Body::Structured(packets) = self.body {
Some(packets.into_iter())
} else {
None
}
}
pub fn body(&self) -> &Body {
&self.body
}
pub fn set_body(&mut self, body: Body) -> Body {
use Body::*;
let mut h = Self::make_body_hash();
match &body {
Unprocessed(bytes) => h.update(bytes),
Processed(bytes) => h.update(bytes),
Structured(_) => (),
}
self.set_body_hash(h);
std::mem::replace(&mut self.body, body)
}
fn empty_body_digest() -> u64 {
use std::sync::OnceLock;
static DIGEST: OnceLock<u64> = OnceLock::new();
*DIGEST.get_or_init(|| Container::make_body_hash().digest())
}
pub(crate) fn make_body_hash() -> Box<Xxh3> {
Box::new(Xxh3::new())
}
pub(crate) fn set_body_hash(&mut self, h: Box<Xxh3>) {
self.body_digest = h.digest();
}
pub(crate)
fn body_digest(&self) -> String {
format!("{:08X}", self.body_digest)
}
#[cfg(test)]
fn indent(depth: usize) -> &'static str {
use std::cmp;
let s = " ";
&s[0..cmp::min(depth, s.len())]
}
#[cfg(test)]
pub(crate) fn pretty_print(&self, indent: usize) {
for (i, p) in self.children_ref().iter().enumerate() {
eprintln!("{}{}: {:?}",
Self::indent(indent), i + 1, p);
if let Some(children) = self.children_ref()
.and_then(|c| c.get(i)).and_then(|p| p.container_ref())
{
children.pretty_print(indent + 1);
}
}
}
}
macro_rules! impl_unprocessed_body_forwards {
($typ:ident) => {
impl $typ {
pub(crate) fn container_ref(&self) -> &packet::Container {
&self.container
}
pub(crate) fn container_mut(&mut self) -> &mut packet::Container {
&mut self.container
}
pub fn body(&self) -> &[u8] {
use crate::packet::Body::*;
match self.container.body() {
Unprocessed(bytes) => bytes,
Processed(_) => unreachable!(
"Unprocessed container has processed body"),
Structured(_) => unreachable!(
"Unprocessed container has structured body"),
}
}
pub fn set_body(&mut self, data: Vec<u8>) -> Vec<u8> {
use crate::packet::{Body, Body::*};
match self.container.set_body(Body::Unprocessed(data)) {
Unprocessed(bytes) => bytes,
Processed(_) => unreachable!(
"Unprocessed container has processed body"),
Structured(_) => unreachable!(
"Unprocessed container has structured body"),
}
}
}
};
}
macro_rules! impl_processed_body_forwards {
($typ:ident) => {
impl $typ {
pub fn container_ref(&self) -> &packet::Container {
&self.container
}
pub fn container_mut(&mut self) -> &mut packet::Container {
&mut self.container
}
pub fn body(&self) -> &crate::packet::Body {
self.container_ref().body()
}
pub fn set_body(&mut self, body: crate::packet::Body)
-> crate::packet::Body {
self.container_mut().set_body(body)
}
}
};
}
impl Packet {
pub(crate) fn container_ref(&self) -> Option<&Container> {
match self {
Packet::CompressedData(p) => Some(p.container_ref()),
Packet::SEIP(SEIP::V1(p)) => Some(p.container_ref()),
Packet::SEIP(SEIP::V2(p)) => Some(p.container_ref()),
Packet::Literal(p) => Some(p.container_ref()),
Packet::Unknown(p) => Some(p.container_ref()),
_ => None,
}
}
pub(crate) fn container_mut(&mut self) -> Option<&mut Container> {
match self {
Packet::CompressedData(p) => Some(p.container_mut()),
Packet::SEIP(SEIP::V1(p)) => Some(p.container_mut()),
Packet::SEIP(SEIP::V2(p)) => Some(p.container_mut()),
Packet::Literal(p) => Some(p.container_mut()),
Packet::Unknown(p) => Some(p.container_mut()),
_ => None,
}
}
pub(crate) fn children(& self)
-> Option<impl Iterator<Item = &Packet>> {
self.container_ref().and_then(|c| c.children())
}
pub(crate) fn descendants(&self) -> Option<Iter> {
self.container_ref().and_then(|c| c.descendants())
}
#[cfg(test)]
#[allow(dead_code)] pub(crate) fn unprocessed_body(&self) -> Option<&[u8]> {
self.container_ref().and_then(|c| match c.body() {
Body::Unprocessed(bytes) => Some(&bytes[..]),
_ => None,
})
}
#[cfg(test)]
pub(crate) fn processed_body(&self) -> Option<&[u8]> {
self.container_ref().and_then(|c| match c.body() {
Body::Processed(bytes) => Some(&bytes[..]),
_ => None,
})
}
}