use std::sync::OnceLock;
use super::container_pack::ContainerPack;
use super::content_pack::ContentPack;
use super::directory_pack::{DirectoryPack, EntryStorage};
use super::locator::{ChainedLocator, FsLocator, PackLocatorTrait};
use super::manifest_pack::ManifestPack;
use super::{ByteRegion, Index, MayMissPack, ValueStorage};
use crate::bases::*;
use crate::common::{ContentAddress, Pack, PackHeader, PackKind};
use std::io::Read;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use uuid::Uuid;
pub struct Container {
manifest_pack: ManifestPack,
locator: Arc<dyn PackLocatorTrait>,
directory_pack: Arc<DirectoryPack>,
value_storage: Arc<ValueStorage>,
entry_storage: Arc<EntryStorage>,
packs: Vec<OnceLock<ContentPack>>,
}
pub fn open_as_container_pack(reader: Reader) -> Result<ContainerPack> {
reader.parse_block_unchecked_at::<PackHeader>(Offset::zero())?;
let (pack_header, offset) = match reader.parse_block_at::<PackHeader>(Offset::zero()) {
Ok(pack_header) => (pack_header, Offset::zero()),
Err(_) => {
let mut buffer_reader = [0u8; 64];
reader
.create_stream((reader.size() - Size::new(64)).into(), Size::new(64), false)?
.read_exact(&mut buffer_reader)?;
buffer_reader.reverse();
let end_reader: Reader = buffer_reader.into();
let pack_header = end_reader.parse_block_at::<PackHeader>(Offset::zero())?;
let origin = reader.size() - pack_header.file_size;
(pack_header, origin.into())
}
};
match pack_header.magic {
PackKind::Container => {
ContainerPack::new(reader.cut(offset, pack_header.file_size, false)?)
}
_ => Ok(ContainerPack::new_fake(
reader.cut(offset, pack_header.file_size, false)?,
pack_header.uuid,
)),
}
}
impl Container {
pub fn new<P: AsRef<Path>>(path: P) -> Result<Self> {
let locator = Arc::new(FsLocator::new(
path.as_ref().parent().unwrap().to_path_buf(),
));
Self::new_with_locator(path, locator)
}
pub fn new_with_locator<P: AsRef<Path>>(
path: P,
locator: Arc<dyn PackLocatorTrait>,
) -> Result<Self> {
let path: PathBuf = path.as_ref().into();
let reader = Reader::from(FileSource::open(path)?);
let container_pack = Arc::new(open_as_container_pack(reader)?);
let reader = container_pack.get_manifest_pack_reader()?;
if reader.is_none() {
return Err("Impossible to locate the manifest_pack".into());
}
let reader = reader.unwrap();
let manifest_reader_size = reader.size();
let manifest_pack =
ManifestPack::new(reader.cut(Offset::zero(), manifest_reader_size, true)?)?;
let locators: Vec<Arc<dyn PackLocatorTrait>> = vec![container_pack, locator];
let locator = Arc::new(ChainedLocator::new(locators));
let pack_info = manifest_pack.get_directory_pack_info();
let directory_pack = Arc::new(DirectoryPack::new(
locator
.locate(pack_info.uuid, &pack_info.pack_location)?
.unwrap(),
)?);
let value_storage = directory_pack.create_value_storage();
let entry_storage = directory_pack.create_entry_storage();
let mut packs = Vec::new();
packs.resize_with((manifest_pack.max_id() + 1) as usize, Default::default);
Ok(Self {
manifest_pack,
locator,
directory_pack,
value_storage,
entry_storage,
packs,
})
}
}
impl Container {
pub fn pack_count(&self) -> PackCount {
self.manifest_pack.pack_count()
}
pub fn get_pack(&self, pack_id: PackId) -> Result<MayMissPack<&ContentPack>> {
let cache_slot = &self.packs[pack_id.into_usize()];
if cache_slot.get().is_none() {
match self._get_pack(pack_id)? {
MayMissPack::MISSING(pack_info) => return Ok(MayMissPack::MISSING(pack_info)),
MayMissPack::FOUND(p) => {
let _ = cache_slot.set(p);
}
}
}
Ok(MayMissPack::FOUND(cache_slot.get().unwrap()))
}
pub fn get_bytes(&self, content: ContentAddress) -> Result<MayMissPack<ByteRegion>> {
let pack = self.get_pack(content.pack_id)?;
pack.map(|p| p.get_content(content.content_id)).transpose()
}
fn _get_pack(&self, pack_id: PackId) -> Result<MayMissPack<ContentPack>> {
let pack_info = self.manifest_pack.get_content_pack_info(pack_id)?;
let pack_reader = self
.locator
.locate(pack_info.uuid, &pack_info.pack_location)?;
match pack_reader {
None => Ok(MayMissPack::MISSING(pack_info.clone())),
Some(r) => Ok(MayMissPack::FOUND(ContentPack::new(r)).transpose()?),
}
}
pub fn get_directory_pack(&self) -> &Arc<DirectoryPack> {
&self.directory_pack
}
pub fn get_value_storage(&self) -> &Arc<ValueStorage> {
&self.value_storage
}
pub fn get_entry_storage(&self) -> &Arc<EntryStorage> {
&self.entry_storage
}
pub fn get_index_for_name(&self, name: &str) -> Result<Index> {
self.directory_pack.get_index_from_name(name)
}
pub fn check(&self) -> Result<bool> {
if !self.manifest_pack.check()? {
return Ok(false);
}
if !self.directory_pack.check()? {
return Ok(false);
}
for pack_info in self.manifest_pack.get_pack_infos().iter() {
let pack_reader = self
.locator
.locate(pack_info.uuid, &pack_info.pack_location)?;
if let Some(r) = pack_reader {
let pseudo_container_pack = open_as_container_pack(r)?;
if !pseudo_container_pack.check()? {
return Ok(false);
}
}
}
Ok(true)
}
pub fn uuid(&self) -> Uuid {
self.manifest_pack.uuid()
}
}