use std::sync::Arc;
use corium_core::IndexOrder;
use corium_store::{BlobStore, DbRoot, RootStore, SegmentCache, StoreError, db_root_name};
pub struct SegmentSource<S> {
store: Arc<S>,
cache: SegmentCache,
}
impl<S: BlobStore + RootStore> SegmentSource<S> {
#[must_use]
pub fn new(store: Arc<S>) -> Self {
Self {
store,
cache: SegmentCache::default(),
}
}
pub fn index_root(&self, db: &str) -> Result<Option<DbRoot>, StoreError> {
Ok(self
.store
.get_root(&db_root_name(db))?
.as_deref()
.and_then(DbRoot::decode))
}
pub fn segment(
&self,
root: &DbRoot,
order: IndexOrder,
) -> Result<Option<Arc<[u8]>>, StoreError> {
let Some(roots) = &root.roots else {
return Ok(None);
};
let slot = match order {
IndexOrder::Eavt => 0,
IndexOrder::Aevt => 1,
IndexOrder::Avet => 2,
IndexOrder::Vaet => 3,
};
self.cache.get_or_load(self.store.as_ref(), &roots[slot])
}
pub fn segment_keys(bytes: &[u8]) -> Result<Vec<Vec<u8>>, StoreError> {
let mut keys = Vec::new();
let mut input = bytes;
while !input.is_empty() {
let (len_bytes, rest) = input
.split_at_checked(8)
.ok_or_else(|| StoreError::Io(std::io::Error::other("truncated segment")))?;
let len = usize::try_from(u64::from_be_bytes(len_bytes.try_into().unwrap_or_default()))
.map_err(|_| StoreError::Io(std::io::Error::other("segment key too large")))?;
let (key, rest) = rest
.split_at_checked(len)
.ok_or_else(|| StoreError::Io(std::io::Error::other("truncated segment key")))?;
keys.push(key.to_vec());
input = rest;
}
Ok(keys)
}
}