use crate::prolly::error::Error;
use crate::prolly::format::{NodeLayoutSpec, TreeFormat};
const COMPACT_MAGIC_LEN: u64 = 4;
const COMPACT_VERSION: u64 = 2;
#[derive(Clone, Debug)]
pub(crate) struct EncodedNodeSizer {
format: TreeFormat,
leaf: bool,
level: u8,
count: u64,
size: u64,
empty_size: u64,
payload_len: u64,
}
impl EncodedNodeSizer {
pub(crate) fn new(format: TreeFormat, leaf: bool, level: u8) -> Result<Self, Error> {
format.validate()?;
if let NodeLayoutSpec::Custom { id, .. } = &format.node_layout {
return Err(Error::InvalidFormat(format!(
"node layout '{id}' has no registered codec"
)));
}
let format_len = if format == TreeFormat::default() {
0
} else {
u64::try_from(format.canonical_bytes()?.len()).map_err(|_| Error::InvalidNode)?
};
let mut empty_size = COMPACT_MAGIC_LEN
.checked_add(varint_len(COMPACT_VERSION))
.and_then(|size| size.checked_add(varint_len(format_len)))
.and_then(|size| size.checked_add(format_len))
.and_then(|size| size.checked_add(varint_len(u64::from(leaf))))
.and_then(|size| size.checked_add(varint_len(u64::from(level))))
.and_then(|size| size.checked_add(varint_len(0)))
.ok_or(Error::InvalidNode)?;
if matches!(format.node_layout, NodeLayoutSpec::OffsetTable) {
empty_size = empty_size
.checked_add(varint_len(0))
.ok_or(Error::InvalidNode)?;
}
Ok(Self {
format,
leaf,
level,
count: 0,
size: empty_size,
empty_size,
payload_len: 0,
})
}
#[inline]
pub(crate) fn size(&self) -> u64 {
self.size
}
pub(crate) fn size_after(
&self,
previous_key: Option<&[u8]>,
key: &[u8],
value: &[u8],
child_count: Option<u64>,
) -> Result<u64, Error> {
validate_child_count(self.leaf, child_count)?;
let key_len = u64::try_from(key.len()).map_err(|_| Error::InvalidNode)?;
let value_len = u64::try_from(value.len()).map_err(|_| Error::InvalidNode)?;
let next_count = self.count.checked_add(1).ok_or(Error::InvalidNode)?;
let count_width_delta = varint_len(next_count)
.checked_sub(varint_len(self.count))
.ok_or(Error::InvalidNode)?;
let entry_delta = match &self.format.node_layout {
NodeLayoutSpec::PrefixCompressed => {
let shared = previous_key.map_or(0, |previous| common_prefix_len(previous, key));
let shared = u64::try_from(shared).map_err(|_| Error::InvalidNode)?;
let suffix_len = key_len.checked_sub(shared).ok_or(Error::InvalidNode)?;
checked_sum(&[
varint_len(shared),
varint_len(suffix_len),
suffix_len,
varint_len(value_len),
value_len,
child_count.map(varint_len).unwrap_or(0),
])?
}
NodeLayoutSpec::Plain => checked_sum(&[
varint_len(key_len),
key_len,
varint_len(value_len),
value_len,
child_count.map(varint_len).unwrap_or(0),
])?,
NodeLayoutSpec::OffsetTable => {
let value_offset = self
.payload_len
.checked_add(key_len)
.ok_or(Error::InvalidNode)?;
let next_payload_len = value_offset
.checked_add(value_len)
.ok_or(Error::InvalidNode)?;
let payload_width_delta = varint_len(next_payload_len)
.checked_sub(varint_len(self.payload_len))
.ok_or(Error::InvalidNode)?;
checked_sum(&[
varint_len(self.payload_len),
varint_len(key_len),
varint_len(value_offset),
varint_len(value_len),
child_count.map(varint_len).unwrap_or(0),
payload_width_delta,
key_len,
value_len,
])?
}
NodeLayoutSpec::Custom { .. } => return Err(Error::InvalidNode),
};
self.size
.checked_add(count_width_delta)
.and_then(|size| size.checked_add(entry_delta))
.ok_or(Error::InvalidNode)
}
#[allow(dead_code)]
pub(crate) fn push(
&mut self,
previous_key: Option<&[u8]>,
key: &[u8],
value: &[u8],
child_count: Option<u64>,
) -> Result<(), Error> {
let size = self.size_after(previous_key, key, value, child_count)?;
self.push_sized(key, value, child_count, size)
}
pub(crate) fn push_sized(
&mut self,
key: &[u8],
value: &[u8],
_child_count: Option<u64>,
size: u64,
) -> Result<(), Error> {
self.size = size;
self.count = self.count.checked_add(1).ok_or(Error::InvalidNode)?;
let key_len = u64::try_from(key.len()).map_err(|_| Error::InvalidNode)?;
let value_len = u64::try_from(value.len()).map_err(|_| Error::InvalidNode)?;
self.payload_len = self
.payload_len
.checked_add(key_len)
.and_then(|len| len.checked_add(value_len))
.ok_or(Error::InvalidNode)?;
Ok(())
}
pub(crate) fn reset(&mut self) {
self.count = 0;
self.size = self.empty_size;
self.payload_len = 0;
}
#[allow(dead_code)]
pub(crate) fn level(&self) -> u8 {
self.level
}
}
fn validate_child_count(leaf: bool, child_count: Option<u64>) -> Result<(), Error> {
match (leaf, child_count) {
(true, None) | (false, Some(1..)) => Ok(()),
_ => Err(Error::InvalidNode),
}
}
fn checked_sum(values: &[u64]) -> Result<u64, Error> {
values
.iter()
.try_fold(0_u64, |sum, value| sum.checked_add(*value))
.ok_or(Error::InvalidNode)
}
fn common_prefix_len(left: &[u8], right: &[u8]) -> usize {
left.iter()
.zip(right)
.take_while(|(left, right)| left == right)
.count()
}
fn varint_len(mut value: u64) -> u64 {
let mut len = 1;
while value >= 0x80 {
value >>= 7;
len += 1;
}
len
}