Allow compression to be driven by user

This commit is contained in:
Truman Kilen 2025-01-21 12:29:16 -06:00
parent 5cfc8f52bd
commit b2bc86683d
3 changed files with 56 additions and 25 deletions

View file

@ -7,7 +7,7 @@ use crate::{
type Result<T, E = Error> = std::result::Result<T, E>;
pub(crate) struct PartialEntry<D: AsRef<[u8]>> {
pub struct PartialEntry<D: AsRef<[u8]>> {
compression: Option<Compression>,
compressed_size: u64,
uncompressed_size: u64,

View file

@ -6,7 +6,7 @@ mod ext;
mod footer;
mod pak;
pub use {error::*, pak::*};
pub use {data::PartialEntry, error::*, pak::*};
pub const MAGIC: u32 = 0x5A6F12E1;

View file

@ -1,6 +1,6 @@
use crate::data::build_partial_entry;
use crate::entry::Entry;
use crate::{Compression, Error};
use crate::{Compression, Error, PartialEntry};
use super::ext::{ReadExt, WriteExt};
use super::{Version, VersionMajor};
@ -292,29 +292,18 @@ impl<W: Write + Seek> PakWriter<W> {
Ok(())
}
pub fn parallel<'scope, F, E>(&mut self, f: F) -> Result<&mut Self, E>
where
F: Send + Sync + FnOnce(ParallelPakWriter<'scope>) -> Result<(), E>,
E: From<Error> + Send,
{
use pariter::IteratorExt as _;
pub fn entry_builder(&self) -> EntryBuilder {
EntryBuilder {
allowed_compression: self.allowed_compression.clone(),
}
}
let allowed_compression = self.allowed_compression.as_slice();
pariter::scope(|scope: &pariter::Scope<'_>| -> Result<(), E> {
let (tx, rx) = std::sync::mpsc::sync_channel(0);
let handle = scope.spawn(|_| f(ParallelPakWriter { tx }));
let result = rx
.into_iter()
.parallel_map_scoped(scope, |(path, compress, data)| -> Result<_, Error> {
let compression = compress.then_some(allowed_compression).unwrap_or_default();
let partial_entry = build_partial_entry(compression, data)?;
Ok((path, partial_entry))
})
.try_for_each(|message| -> Result<(), Error> {
pub fn write_entry<D: AsRef<[u8]>>(
&mut self,
path: String,
partial_entry: PartialEntry<D>,
) -> Result<(), Error> {
let stream_position = self.writer.stream_position()?;
let (path, partial_entry) = message?;
let entry = partial_entry.build_entry(
self.pak.version,
@ -330,7 +319,31 @@ impl<W: Write + Seek> PakWriter<W> {
self.pak.index.add_entry(path, entry);
partial_entry.write_data(&mut self.writer)?;
Ok(())
}
pub fn parallel<'scope, F, E>(&mut self, f: F) -> Result<&mut Self, E>
where
F: Send + Sync + FnOnce(ParallelPakWriter<'scope>) -> Result<(), E>,
E: From<Error> + Send,
{
use pariter::IteratorExt as _;
pariter::scope(|scope: &pariter::Scope<'_>| -> Result<(), E> {
let (tx, rx) = std::sync::mpsc::sync_channel(0);
let handle = scope.spawn(|_| f(ParallelPakWriter { tx }));
let entry_builder = self.entry_builder();
let result = rx
.into_iter()
.parallel_map_scoped(scope, move |(path, compress, data)| -> Result<_, Error> {
Ok((path, entry_builder.build_entry(compress, data)?))
})
.try_for_each(|message| -> Result<(), Error> {
let (path, partial_entry) = message?;
self.write_entry(path, partial_entry)
});
if let Err(err) = handle.join().unwrap() {
@ -375,6 +388,24 @@ impl AsRef<[u8]> for Data<'_> {
}
}
#[derive(Clone)]
pub struct EntryBuilder {
allowed_compression: Vec<Compression>,
}
impl EntryBuilder {
/// Builds an entry in memory (compressed if requested) which must be written out later
pub fn build_entry<D: AsRef<[u8]> + Send + Sync>(
&self,
compress: bool,
data: D,
) -> Result<PartialEntry<D>, Error> {
let compression = compress
.then_some(self.allowed_compression.as_slice())
.unwrap_or_default();
build_partial_entry(compression, data)
}
}
impl Pak {
fn read<R: Read + Seek>(
reader: &mut R,