use super::{LoadError, read_u64_at, read_u64_le_unchecked, usize_from_u64};
pub(crate) struct ParsedPayload<'a> {
pub(crate) offsets: &'a [u8],
pub(crate) blobs: &'a [u8],
pub(crate) stride: usize,
}
#[inline]
pub(crate) fn payload_slice<'a>(payload: &ParsedPayload<'a>, r: usize) -> &'a [u8] {
if payload.stride != 0 {
let start = r * payload.stride;
&payload.blobs[start..start + payload.stride]
} else {
let start = read_u64_le_unchecked(payload.offsets, r * 8) as usize;
let end = read_u64_le_unchecked(payload.offsets, (r + 1) * 8) as usize;
&payload.blobs[start..end]
}
}
pub(crate) fn build_id_to_leaf(indices: &[u8], num_items: usize) -> Vec<u32> {
let mut id_to_leaf = vec![0u32; num_items];
for r in 0..num_items {
let id = read_u64_le_unchecked(indices, r * 8) as usize;
id_to_leaf[id] = r as u32;
}
id_to_leaf
}
pub(crate) fn parse_payload_body(
body: &[u8],
num_items: usize,
stride: usize,
) -> Result<ParsedPayload<'_>, LoadError> {
if stride != 0 {
let total = num_items
.checked_mul(stride)
.ok_or(LoadError::IntegerOverflow)?;
if body.len() != total {
return Err(LoadError::LengthMismatch {
expected: total,
actual: body.len(),
});
}
return Ok(ParsedPayload {
offsets: &[],
blobs: body,
stride,
});
}
let offsets_len = num_items
.checked_add(1)
.and_then(|n| n.checked_mul(8))
.ok_or(LoadError::IntegerOverflow)?;
if body.len() < offsets_len {
return Err(LoadError::Truncated);
}
let offsets = &body[..offsets_len];
let mut prev = 0u64;
for i in 0..=num_items {
let off = read_u64_at(offsets, i * 8)?;
if (i == 0 && off != 0) || off < prev {
return Err(LoadError::InvalidTree);
}
prev = off;
}
let blob_total = usize_from_u64(prev)?;
let blobs = &body[offsets_len..];
if blobs.len() != blob_total {
return Err(LoadError::LengthMismatch {
expected: offsets_len + blob_total,
actual: body.len(),
});
}
Ok(ParsedPayload {
offsets,
blobs,
stride: 0,
})
}