use super::{Error, Result};
pub const CHUNK: u64 = 1_048_576;
pub const STATE_LIMIT: u64 = 256 * CHUNK;
pub const CATALOG_LIMIT: u64 = 16 * CHUNK;
pub const RECORD_LIMIT: u64 = 1_000_000;
pub const IMAGE_LIMIT: u64 = 25 * CHUNK;
pub const IMAGE_COUNT: u64 = 1024;
pub const ID_LIMIT: u64 = 256;
const MAX_SLICES: usize = (CATALOG_LIMIT / CHUNK) as usize;
const MAX_DECLARATION: usize = 1 + 8 + 8 + 32 + 32 * MAX_SLICES;
const MAX_MANIFEST: usize = 8 + 32 + 8 + (8 + 8 + 32 + 24);
pub const MAX_DESCRIPTOR_BYTES: usize = 7 + 5 * 32 + 8 + 3 * MAX_DECLARATION + MAX_MANIFEST;
pub fn valid(ok: bool) -> Result<()> {
if ok { Ok(()) } else { Err(Error::Invalid) }
}
pub fn bound(value: u64, maximum: u64) -> Result<()> {
if value <= maximum {
Ok(())
} else {
Err(Error::ResourceLimit)
}
}
pub fn add(a: u64, b: u64) -> Result<u64> {
a.checked_add(b).ok_or(Error::Invalid)
}
pub fn size(value: u64) -> Result<usize> {
usize::try_from(value).map_err(|_| Error::ResourceLimit)
}
pub fn number(value: usize) -> Result<u64> {
u64::try_from(value).map_err(|_| Error::ResourceLimit)
}
pub fn count(total: u64) -> u64 {
(total / CHUNK + u64::from(!total.is_multiple_of(CHUNK))).max(1)
}
pub fn u64_bytes(out: &mut Vec<u8>, value: u64) {
out.extend_from_slice(&value.to_be_bytes());
}
pub fn bytes(out: &mut Vec<u8>, value: &[u8]) -> Result<()> {
u64_bytes(out, number(value.len())?);
out.extend_from_slice(value);
Ok(())
}
pub fn text(out: &mut Vec<u8>, value: &str) -> Result<()> {
valid(!value.is_empty())?;
bound(number(value.len())?, ID_LIMIT)?;
bytes(out, value.as_bytes())
}
pub struct Reader<'a>(pub &'a [u8]);
impl<'a> Reader<'a> {
pub fn take(&mut self, n: usize) -> Result<&'a [u8]> {
valid(n <= self.0.len())?;
let (value, rest) = self.0.split_at(n);
self.0 = rest;
Ok(value)
}
pub fn array<const N: usize>(&mut self) -> Result<[u8; N]> {
self.take(N)?.try_into().map_err(|_| Error::Invalid)
}
pub fn byte(&mut self) -> Result<u8> {
Ok(self.array::<1>()?[0])
}
pub fn flag(&mut self) -> Result<bool> {
match self.byte()? {
0 => Ok(false),
1 => Ok(true),
_ => Err(Error::Invalid),
}
}
pub fn u64(&mut self) -> Result<u64> {
Ok(u64::from_be_bytes(self.array()?))
}
pub fn bytes(&mut self, maximum: u64) -> Result<&'a [u8]> {
let len = self.u64()?;
bound(len, maximum)?;
self.take(size(len)?)
}
pub fn text(&mut self) -> Result<String> {
let value = self.bytes(ID_LIMIT)?;
valid(!value.is_empty())?;
Ok(std::str::from_utf8(value)
.map_err(|_| Error::Invalid)?
.to_owned())
}
pub fn end(self) -> Result<()> {
valid(self.0.is_empty())
}
}
pub fn stream(class: u8, records: &[Vec<u8>]) -> Result<Vec<u8>> {
bound(number(records.len())?, RECORD_LIMIT)?;
let mut out = b"AVBC\0\x01".to_vec();
out.push(class);
u64_bytes(&mut out, number(records.len())?);
for record in records {
bound(
add(number(out.len())?, add(8, number(record.len())?)?)?,
CATALOG_LIMIT,
)?;
bytes(&mut out, record)?;
}
Ok(out)
}
pub fn read_stream(input: &[u8], class: u8) -> Result<Vec<&[u8]>> {
bound(number(input.len())?, CATALOG_LIMIT)?;
let mut r = Reader(input);
valid(r.take(6)? == b"AVBC\0\x01" && r.byte()? == class)?;
let n = r.u64()?;
bound(n, RECORD_LIMIT)?;
valid(n <= number(r.0.len())? / 8)?;
let mut out = Vec::new();
for _ in 0..n {
out.push(r.bytes(CATALOG_LIMIT)?);
}
r.end()?;
Ok(out)
}