use rmp_serde::decode::ReadReader;
use serde::{de::DeserializeOwned, Serialize};
use crate::error::CachekitError;
pub const MAX_DECODE_DEPTH: usize = 100;
pub(crate) fn check_structure(bytes: &[u8]) -> Result<(), CachekitError> {
fn reject(what: &str) -> CachekitError {
CachekitError::Serialization(format!("decode bound: {what}"))
}
fn be(bytes: &[u8], pos: usize, width: usize) -> Result<u64, CachekitError> {
let end = pos.checked_add(width).filter(|e| *e <= bytes.len());
let field = &bytes[pos..end.ok_or_else(|| reject("input ends inside a length prefix"))?];
Ok(field.iter().fold(0u64, |acc, b| (acc << 8) | u64::from(*b)))
}
let mut open = [0u64; MAX_DECODE_DEPTH];
let mut depth = 0usize;
let mut pos = 0usize;
let mut pending: u64 = 1; while pending > 0 {
let marker = *bytes
.get(pos)
.ok_or_else(|| reject("input ends before the document is complete"))?;
pos += 1;
pending -= 1;
if let Some(parent) = depth.checked_sub(1) {
open[parent] -= 1;
}
let (prefix, payload, children): (usize, u64, u64) = match marker {
0x00..=0x7f | 0xc0 | 0xc2 | 0xc3 | 0xe0..=0xff => (0, 0, 0),
0x80..=0x8f => (0, 0, 2 * u64::from(marker & 0x0f)),
0x90..=0x9f => (0, 0, u64::from(marker & 0x0f)),
0xa0..=0xbf => (0, u64::from(marker & 0x1f), 0),
0xc1 => return Err(reject("reserved marker 0xc1")),
0xc4 | 0xd9 => (1, be(bytes, pos, 1)?, 0),
0xc5 | 0xda => (2, be(bytes, pos, 2)?, 0),
0xc6 | 0xdb => (4, be(bytes, pos, 4)?, 0),
0xc7 => (1, be(bytes, pos, 1)? + 1, 0), 0xc8 => (2, be(bytes, pos, 2)? + 1, 0),
0xc9 => (4, be(bytes, pos, 4)? + 1, 0),
0xca..=0xd3 => (0, 1u64 << (marker & 0x03), 0), 0xd4..=0xd8 => (0, 1 + (1u64 << (marker - 0xd4)), 0), 0xdc => (2, 0, be(bytes, pos, 2)?),
0xdd => (4, 0, be(bytes, pos, 4)?),
0xde => (2, 0, 2 * be(bytes, pos, 2)?),
0xdf => (4, 0, 2 * be(bytes, pos, 4)?),
};
pos += prefix;
let remaining = (bytes.len() - pos) as u64;
if payload > remaining {
return Err(reject("header declares more bytes than the input holds"));
}
pos += usize::try_from(payload)
.map_err(|_| reject("header declares more bytes than the input holds"))?;
pending += children;
if pending > remaining - payload {
return Err(reject(
"header declares more elements than the input can back",
));
}
if matches!(marker, 0x80..=0x9f | 0xdc..=0xdf) {
if depth == MAX_DECODE_DEPTH {
return Err(reject(&format!(
"nesting deeper than {MAX_DECODE_DEPTH} levels"
)));
}
open[depth] = children;
depth += 1;
}
while depth > 0 && open[depth - 1] == 0 {
depth -= 1;
}
}
Ok(())
}
pub(crate) fn bounded_deserializer(
bytes: &[u8],
) -> Result<rmp_serde::Deserializer<ReadReader<&[u8]>>, CachekitError> {
check_structure(bytes)?;
let mut de = rmp_serde::Deserializer::new(bytes);
de.set_max_depth(MAX_DECODE_DEPTH + 2);
Ok(de)
}
pub fn serialize<T: Serialize>(value: &T) -> Result<Vec<u8>, CachekitError> {
rmp_serde::to_vec_named(value).map_err(|e| CachekitError::Serialization(e.to_string()))
}
pub fn deserialize<T: DeserializeOwned>(bytes: &[u8]) -> Result<T, CachekitError> {
T::deserialize(&mut bounded_deserializer(bytes)?)
.map_err(|e| CachekitError::Serialization(e.to_string()))
}