mod basic_creator;
mod container_pack;
mod content_pack;
mod directory_pack;
mod manifest_pack;
pub use crate::bases::FileSource;
use crate::bases::InOutStream;
pub(crate) use crate::bases::OutStream;
use crate::bases::*;
use crate::common::{CheckInfo, CompressionType, PackKind};
pub use basic_creator::{BasicCreator, ConcatMode, EntryStoreTrait};
pub use container_pack::{ContainerPackCreator, InContainerFile};
pub use content_pack::{
CacheProgress, CachedContentAdder, CompHint, ContentAdder, ContentPackCreator, Progress,
};
pub use directory_pack::{
schema, Array, ArrayS, BasicEntry, DirectoryPackCreator, EntryStore, EntryTrait,
FullEntryTrait, PropertyName, StoreHandle, Value, ValueHandle, ValueStore, VariantName,
};
pub use manifest_pack::ManifestPackCreator;
use std::fs::OpenOptions;
use std::io::{self, Read, Seek, SeekFrom};
use std::path::PathBuf;
use bstr::ByteVec;
mod private {
use super::*;
pub trait WritableTell {
fn write_data(&mut self, stream: &mut dyn OutStream) -> Result<()>;
fn serialize_tail(&mut self, stream: &mut Serializer) -> Result<()>;
fn write(&mut self, stream: &mut dyn OutStream) -> Result<SizedOffset> {
self.write_data(stream)?;
let offset = stream.tell();
let mut serializer = Serializer::new(BlockCheck::Crc32);
self.serialize_tail(&mut serializer)?;
let size = stream.write_serializer(serializer)?.into();
Ok(SizedOffset { size, offset })
}
}
pub trait Sealed {}
pub enum MaybeFileReader {
Yes(std::io::Take<std::fs::File>),
No(Box<dyn Read + Send>),
}
}
pub struct PackData {
pub uuid: uuid::Uuid,
pub pack_size: Size,
pub pack_kind: PackKind,
pub pack_id: PackId,
pub free_data: Vec<u8>,
pub check_info: CheckInfo,
}
pub(crate) use private::MaybeFileReader;
pub trait InputReader: Read + Seek + Send + 'static {
fn size(&self) -> Size;
fn get_file_source(self: Box<Self>) -> MaybeFileReader;
}
impl<T: AsRef<[u8]> + Send + 'static> InputReader for std::io::Cursor<T> {
fn size(&self) -> Size {
self.get_ref().as_ref().len().into()
}
fn get_file_source(self: Box<Self>) -> MaybeFileReader {
MaybeFileReader::No(self)
}
}
pub struct InputFile {
source: std::fs::File,
position: u64,
origin: u64,
len: u64,
}
impl InputFile {
pub fn open<P: AsRef<std::path::Path>>(path: P) -> Result<Self> {
Self::new(std::fs::File::open(path)?)
}
pub fn new(source: std::fs::File) -> Result<Self> {
Self::new_range(source, 0, None)
}
pub fn new_range(mut source: std::fs::File, origin: u64, size: Option<u64>) -> Result<Self> {
let total_len = source.seek(SeekFrom::End(0))?;
let size = match size {
None => total_len - origin,
Some(s) => s,
};
source.seek(SeekFrom::Start(origin))?;
Ok(Self {
source,
position: origin,
origin,
len: size,
})
}
fn local_position(&self) -> u64 {
self.position - self.origin
}
}
impl Seek for InputFile {
fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
let pos = match pos {
SeekFrom::Start(o) => SeekFrom::Start(self.origin + o),
SeekFrom::Current(o) => SeekFrom::Current(o),
SeekFrom::End(e) => SeekFrom::Start((self.origin as i64 + self.len as i64 + e) as u64),
};
self.position = self.source.seek(pos)?;
Ok(self.position)
}
fn rewind(&mut self) -> std::io::Result<()> {
self.seek(SeekFrom::Start(0))?;
Ok(())
}
#[cfg(feature = "nightly")]
fn stream_len(&mut self) -> std::io::Result<()> {
Ok(self.len)
}
fn stream_position(&mut self) -> std::io::Result<u64> {
Ok(self.position - self.origin)
}
}
impl Read for InputFile {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
let size_to_read = std::cmp::min(
buf.len(),
(self.len - self.local_position()).try_into().unwrap(),
);
let actually_read = self.source.read(&mut buf[..size_to_read])?;
self.position += actually_read as u64;
Ok(actually_read)
}
}
impl InputReader for InputFile {
fn size(&self) -> Size {
self.len.into()
}
fn get_file_source(mut self: Box<Self>) -> MaybeFileReader {
self.rewind().unwrap();
MaybeFileReader::Yes(self.source.take(self.len))
}
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum Compression {
None,
#[cfg(feature = "lz4")]
Lz4(deranged::RangedU32<0, 15>),
#[cfg(feature = "lzma")]
Lzma(deranged::RangedU32<0, 9>),
#[cfg(feature = "zstd")]
Zstd(deranged::RangedI32<-22, 22>),
}
impl Default for Compression {
#[cfg(feature = "zstd")]
fn default() -> Self {
Compression::zstd()
}
#[cfg(all(feature = "lzma", not(feature = "zstd")))]
fn default() -> Self {
Compression::lzma()
}
#[cfg(all(feature = "lz4", not(feature = "lzma"), not(feature = "zstd")))]
fn default() -> Self {
Compression::lz4()
}
#[cfg(all(not(feature = "lz4"), not(feature = "lzma"), not(feature = "zstd")))]
fn default() -> Self {
Compression::None
}
}
impl Compression {
#[cfg(feature = "lz4")]
pub fn lz4() -> Compression {
Compression::Lz4(deranged::RangedU32::new_static::<3>())
}
#[cfg(feature = "lzma")]
pub fn lzma() -> Compression {
Compression::Lzma(deranged::RangedU32::new_static::<9>())
}
#[cfg(feature = "zstd")]
pub fn zstd() -> Compression {
Compression::Zstd(deranged::RangedI32::new_static::<5_i32>())
}
}
impl From<Compression> for CompressionType {
fn from(c: Compression) -> Self {
match c {
Compression::None => CompressionType::None,
#[cfg(feature = "lz4")]
Compression::Lz4(_) => CompressionType::Lz4,
#[cfg(feature = "lzma")]
Compression::Lzma(_) => CompressionType::Lzma,
#[cfg(feature = "zstd")]
Compression::Zstd(_) => CompressionType::Zstd,
}
}
}
pub trait PackRecipient: InOutStream + private::Sealed {
fn close_file(self: Box<Self>) -> Result<Vec<u8>>;
}
#[derive(Debug)]
pub struct NamedFile {
file: std::fs::File,
final_path: PathBuf,
}
impl NamedFile {
fn new<P: AsRef<std::path::Path>>(final_path: P) -> Result<Box<Self>> {
let file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(true)
.open(&final_path)?;
Ok(Box::new(Self {
file,
final_path: final_path.as_ref().to_path_buf(),
}))
}
#[inline]
pub fn into_inner(self) -> std::fs::File {
self.file
}
}
impl Seek for NamedFile {
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
self.file.seek(pos)
}
}
impl io::Write for NamedFile {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.file.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.file.flush()
}
}
impl io::Read for NamedFile {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.file.read(buf)
}
}
impl OutStream for NamedFile {
fn copy(
&mut self,
reader: Box<dyn crate::creator::InputReader>,
) -> IoResult<(u64, MaybeFileReader)> {
self.file.copy(reader)
}
}
impl private::Sealed for NamedFile {}
impl PackRecipient for NamedFile {
fn close_file(self: Box<Self>) -> Result<Vec<u8>> {
Vec::from_path_buf(self.final_path).map_err(|e| format!("{e:?} is not valid utf8").into())
}
}
#[derive(Debug)]
pub struct AtomicOutFile {
temp_file: tempfile::NamedTempFile,
final_path: PathBuf,
}
impl AtomicOutFile {
pub fn new<P: AsRef<std::path::Path>>(final_path: P) -> Result<Box<Self>> {
let parent = final_path.as_ref().parent().unwrap();
let temp_file = tempfile::NamedTempFile::new_in(parent)?;
Ok(Box::new(Self {
temp_file,
final_path: final_path.as_ref().to_path_buf(),
}))
}
}
impl Seek for AtomicOutFile {
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
self.temp_file.seek(pos)
}
}
impl io::Write for AtomicOutFile {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.temp_file.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.temp_file.flush()
}
}
impl io::Read for AtomicOutFile {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.temp_file.read(buf)
}
}
impl OutStream for AtomicOutFile {
fn copy(
&mut self,
reader: Box<dyn crate::creator::InputReader>,
) -> IoResult<(u64, MaybeFileReader)> {
self.temp_file.as_file_mut().copy(reader)
}
}
impl private::Sealed for AtomicOutFile {}
impl PackRecipient for AtomicOutFile {
fn close_file(self: Box<Self>) -> Result<Vec<u8>> {
self.temp_file
.persist(&self.final_path)
.map_err(|e| e.error)?;
Vec::from_path_buf(self.final_path).map_err(|e| format!("{e:?} is not valid utf8").into())
}
}