mod iter;
#[cfg(test)]
mod tests;
use std::cmp::Ordering;
use std::sync::Arc;
use crate::error::{Error, Result};
use crate::varint;
pub use iter::BlockIter;
pub type Compare = fn(&[u8], &[u8]) -> Ordering;
const U32_BYTES: usize = 4;
#[derive(Debug)]
pub struct BlockBuilder {
buffer: Vec<u8>,
restarts: Vec<u32>,
restart_interval: usize,
since_restart: usize,
last_key: Vec<u8>,
compare: Compare,
}
impl BlockBuilder {
pub fn new(restart_interval: usize, compare: Compare) -> Self {
Self {
buffer: Vec::new(),
restarts: Vec::new(),
restart_interval: restart_interval.max(1),
since_restart: 0,
last_key: Vec::new(),
compare,
}
}
pub fn add(&mut self, key: &[u8], value: &[u8]) -> Result<()> {
let first = self.restarts.is_empty();
if !first && (self.compare)(key, &self.last_key) != Ordering::Greater {
return Err(Error::Malformed {
what: "block",
reason: "keys are not strictly increasing",
});
}
let shared = if first || self.since_restart >= self.restart_interval {
self.restarts.push(length(self.buffer.len(), "block")?);
self.since_restart = 0;
0
} else {
common_prefix(&self.last_key, key)
};
let suffix = key.get(shared..).unwrap_or_default();
varint::put_u32(&mut self.buffer, length(shared, "block key")?);
varint::put_u32(&mut self.buffer, length(suffix.len(), "block key")?);
varint::put_u32(&mut self.buffer, length(value.len(), "block value")?);
self.buffer.extend_from_slice(suffix);
self.buffer.extend_from_slice(value);
self.last_key.truncate(shared);
self.last_key.extend_from_slice(suffix);
self.since_restart = self.since_restart.saturating_add(1);
Ok(())
}
pub fn is_empty(&self) -> bool {
self.restarts.is_empty()
}
pub fn estimated_size(&self) -> usize {
let trailer = self
.restarts
.len()
.saturating_add(1)
.saturating_mul(U32_BYTES);
self.buffer.len().saturating_add(trailer)
}
pub fn finish(&mut self) -> Vec<u8> {
let mut block = std::mem::take(&mut self.buffer);
for restart in &self.restarts {
block.extend_from_slice(&restart.to_le_bytes());
}
let count = u32::try_from(self.restarts.len()).unwrap_or(u32::MAX);
block.extend_from_slice(&count.to_le_bytes());
self.restarts.clear();
self.since_restart = 0;
self.last_key.clear();
block
}
}
#[derive(Debug, Clone)]
pub struct Block {
bytes: Arc<[u8]>,
entries_end: usize,
restart_count: usize,
compare: Compare,
}
impl Block {
pub fn new(bytes: Vec<u8>, compare: Compare) -> Result<Self> {
let malformed = |reason| Error::Malformed {
what: "block",
reason,
};
let Some(count_at) = bytes.len().checked_sub(U32_BYTES) else {
return Err(Error::Truncated {
what: "block",
needed: 4,
available: u64::try_from(bytes.len()).unwrap_or(u64::MAX),
});
};
let restart_count =
usize::try_from(read_u32(&bytes, count_at).ok_or(malformed("trailer"))?)
.map_err(|_| malformed("restart count"))?;
let entries_end = restart_count
.checked_mul(U32_BYTES)
.and_then(|size| count_at.checked_sub(size))
.ok_or(malformed("restart array larger than the block"))?;
let mut previous = None;
for index in 0..restart_count {
let at = index
.checked_mul(U32_BYTES)
.and_then(|offset| entries_end.checked_add(offset))
.ok_or(malformed("restart array"))?;
let restart = usize::try_from(read_u32(&bytes, at).ok_or(malformed("restart array"))?)
.map_err(|_| malformed("restart offset"))?;
if restart >= entries_end || previous.is_some_and(|before| restart <= before) {
return Err(malformed(
"restart offsets out of order or past the entries",
));
}
previous = Some(restart);
}
if restart_count == 0 && entries_end != 0 {
return Err(malformed("entries without a restart point"));
}
if restart_count > 0 && previous_first(&bytes, entries_end) != Some(0) {
return Err(malformed("the first restart point is not the first entry"));
}
Ok(Self {
bytes: bytes.into(),
entries_end,
restart_count,
compare,
})
}
pub fn size_bytes(&self) -> usize {
self.bytes.len()
}
pub fn iter(&self) -> BlockIter {
BlockIter::new(self.clone())
}
fn restart(&self, index: usize) -> Option<usize> {
let at = index
.checked_mul(U32_BYTES)
.and_then(|offset| self.entries_end.checked_add(offset))?;
usize::try_from(read_u32(&self.bytes, at)?).ok()
}
}
fn previous_first(bytes: &[u8], entries_end: usize) -> Option<usize> {
usize::try_from(read_u32(bytes, entries_end)?).ok()
}
fn read_u32(bytes: &[u8], at: usize) -> Option<u32> {
let field = bytes.get(at..at.checked_add(U32_BYTES)?)?;
Some(u32::from_le_bytes(field.try_into().ok()?))
}
fn length(len: usize, what: &'static str) -> Result<u32> {
u32::try_from(len).map_err(|_| Error::TooLarge {
what,
size: u64::try_from(len).unwrap_or(u64::MAX),
limit: u64::from(u32::MAX),
})
}
fn common_prefix(left: &[u8], right: &[u8]) -> usize {
left.iter()
.zip(right)
.take_while(|(left, right)| left == right)
.count()
}