use alloc::collections::BTreeMap;
use alloc::sync::Arc;
use alloc::vec::Vec;
use super::core::{Materialized, check_unique, materialize_decode};
use super::{BundleId, DictBundle, DictError, DictId};
use crate::shared::dictwire::{MAX_BUNDLES_PER_STORE, MAX_DICTS_PER_BUNDLE};
use crate::shared::sha256;
#[derive(Debug)]
pub(crate) struct StoredBundle {
pub(crate) dicts: Vec<Arc<Materialized>>,
}
#[derive(Debug)]
struct Entry {
hash: [u8; 32],
dict: Arc<Materialized>,
}
#[derive(Debug, Default)]
pub struct BundleStore {
bundles: BTreeMap<BundleId, Arc<StoredBundle>>,
dicts: BTreeMap<DictId, Entry>,
}
impl BundleStore {
pub fn insert(&mut self, bundle: &DictBundle) -> Result<BundleId, DictError> {
if self.bundles.contains_key(&bundle.id) {
return Ok(bundle.id);
}
if self.bundles.len() >= MAX_BUNDLES_PER_STORE {
return Err(DictError::StoreFull);
}
check_unique(&bundle.dicts)?;
let mut staged: BTreeMap<DictId, Entry> = BTreeMap::new();
let mut list = Vec::with_capacity(bundle.dicts.len());
for dict in &bundle.dicts {
let hash = sha256::digest(dict.content());
let known = staged.get(&dict.id).or_else(|| self.dicts.get(&dict.id));
if let Some(entry) = known {
if entry.hash != hash {
return Err(DictError::ConflictingId(dict.id));
}
list.push(Arc::clone(&entry.dict));
continue;
}
if self.dicts.len() + staged.len() >= MAX_DICTS_PER_BUNDLE {
return Err(DictError::StoreFull);
}
let materialized = Arc::new(materialize_decode(dict)?);
list.push(Arc::clone(&materialized));
staged.insert(
dict.id,
Entry {
hash,
dict: materialized,
},
);
}
self.dicts.append(&mut staged);
self.bundles
.insert(bundle.id, Arc::new(StoredBundle { dicts: list }));
Ok(bundle.id)
}
#[must_use]
pub fn contains(&self, id: &BundleId) -> bool {
self.bundles.contains_key(id)
}
#[must_use]
pub fn len(&self) -> usize {
self.bundles.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.bundles.is_empty()
}
pub(crate) fn get(&self, id: &BundleId) -> Option<&Arc<StoredBundle>> {
self.bundles.get(id)
}
}
impl TryFrom<&DictBundle> for BundleStore {
type Error = DictError;
fn try_from(bundle: &DictBundle) -> Result<Self, Self::Error> {
let mut store = Self::default();
store.insert(bundle)?;
Ok(store)
}
}
impl TryFrom<&[u8]> for BundleStore {
type Error = DictError;
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
Self::try_from(&DictBundle::try_from(bytes)?)
}
}