use crate::{Error, MaxDepthExceeded, Result, wire::Signature};
#[derive(Debug, Default, Clone, Copy)]
pub(crate) struct ContainerDepths {
structure: u8,
array: u8,
total: u8,
}
impl ContainerDepths {
pub fn inc_structure(mut self) -> Result<Self> {
self.structure += 1;
if self.structure > MAX_STRUCT_DEPTH {
return Err(Error::MaxDepthExceeded(MaxDepthExceeded::Structure));
}
self.total += 1;
self.check()
}
pub fn inc_array(self, element: &Signature) -> Result<Self> {
let depths = self.enter_array()?;
match element {
Signature::U8
| Signature::I16
| Signature::U16
| Signature::I32
| Signature::U32
| Signature::I64
| Signature::U64
| Signature::F64 => depths.check(),
#[cfg(unix)]
Signature::Fd => depths.check(),
_ => Ok(depths),
}
}
pub fn inc_dict(self) -> Result<Self> {
let mut depths = self.enter_array()?;
depths.total += 1;
Ok(depths)
}
#[inline]
pub fn inc_variant(self, signature: &Signature) -> Result<Self> {
let type_depth = match signature {
Signature::Array(_) | Signature::Dict { .. } | Signature::Structure(_) => {
type_depth(signature)
}
_ => 1,
};
if usize::from(self.total) + type_depth > usize::from(MAX_TOTAL_DEPTH) {
return Err(Error::MaxDepthExceeded(MaxDepthExceeded::Container));
}
Ok(Self {
structure: 0,
array: 0,
total: self.total + 1,
})
}
pub fn check(self) -> Result<Self> {
if self.total > MAX_TOTAL_DEPTH {
return Err(Error::MaxDepthExceeded(MaxDepthExceeded::Container));
}
Ok(self)
}
fn enter_array(mut self) -> Result<Self> {
self.array += 1;
if self.array > MAX_ARRAY_DEPTH {
return Err(Error::MaxDepthExceeded(MaxDepthExceeded::Array));
}
self.total += 1;
Ok(self)
}
}
fn type_depth(signature: &Signature) -> usize {
match signature {
Signature::Array(child) => 1 + type_depth(child.signature()),
Signature::Dict { value, .. } => 2 + type_depth(value.signature()),
Signature::Structure(fields) => 1 + fields.iter().map(type_depth).max().unwrap_or(0),
_ => 1,
}
}
const MAX_STRUCT_DEPTH: u8 = 32;
const MAX_ARRAY_DEPTH: u8 = 32;
const MAX_TOTAL_DEPTH: u8 = 64;