mod config;
mod core;
mod error;
mod source;
use crate::compressor::{Backend, EncoderConfig};
pub use config::{
BatchConfig, DirectoryStaging, MemoryStaging, ParallelConfig, ParallelRetentionPolicy,
SegmentSize, SourceConsistency, Staging, TaskCount,
};
pub use error::{ParallelConfigError, ParallelEncodeError, ParallelFinishError};
pub use source::{ArcBytesSource, FileSource, RandomAccessSource, SeekSource, SourceIdentity};
use std::{io::Write, ops::Range, sync::Arc, time::Duration};
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct SegmentId(u64);
impl From<SegmentId> for u64 {
fn from(id: SegmentId) -> Self {
id.0
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct TaskId(u32);
impl From<TaskId> for u32 {
fn from(id: TaskId) -> Self {
id.0
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum StagingKind {
Memory,
Directory,
}
#[derive(Clone, Debug)]
pub struct ParallelStats {
pub input_bytes: u64,
pub output_bytes: u64,
pub segment_size: usize,
pub segment_count: u64,
pub requested_tasks: usize,
pub effective_tasks: usize,
pub serial_fallback: bool,
pub staging_kind: StagingKind,
pub maximum_staged_bytes: u64,
pub context_prefix_bytes: u64,
pub workers_reused: usize,
pub workers_created: usize,
pub retained_worker_bytes: usize,
}
#[derive(Clone, Debug)]
pub struct ParallelOutput {
pub range: Range<usize>,
pub stats: ParallelStats,
}
#[derive(Clone, Debug)]
pub struct ParallelSizeEstimate {
pub input_bytes: u64,
pub segment_count: u64,
pub maximum_staged_bytes: u64,
pub maximum_final_bytes: u64,
pub estimated_active_workspace_bytes: usize,
}
impl ParallelSizeEstimate {
pub const fn maximum_staging_memory_bytes(&self) -> Result<u64, ParallelEncodeError> {
core::staging_memory_bound(self.maximum_staged_bytes, self.segment_count)
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum BatchPoll {
Pending {
completed: usize,
total: usize,
},
Ready,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum WaitStatus {
Ready,
TimedOut,
}
pub struct ParallelCompressor {
inner: core::Compressor,
}
impl ParallelCompressor {
pub fn new(
encoder: EncoderConfig,
parallel: ParallelConfig,
) -> Result<Self, ParallelConfigError> {
Self::with_backend(encoder, parallel, Backend::default())
}
pub fn with_backend(
encoder: EncoderConfig,
parallel: ParallelConfig,
backend: Backend,
) -> Result<Self, ParallelConfigError> {
core::Compressor::new(encoder, parallel, backend).map(|inner| Self { inner })
}
pub const fn encoder_config(&self) -> &EncoderConfig {
&self.inner.encoder
}
pub const fn parallel_config(&self) -> &ParallelConfig {
&self.inner.parallel
}
pub fn reconfigure_parallel(&mut self, config: ParallelConfig) {
self.inner.parallel = config;
self.inner.trim(ParallelRetentionPolicy::CurrentPlan);
}
pub fn retained_worker_count(&self) -> usize {
self.inner.workers.len()
}
pub fn retained_bytes(&self) -> usize {
self.inner.retained_bytes()
}
pub fn trim(&mut self, policy: ParallelRetentionPolicy) {
self.inner.trim(policy);
}
pub fn estimate_source(
&self,
source_len: u64,
config: &BatchConfig,
) -> Result<ParallelSizeEstimate, ParallelEncodeError> {
self.inner.plan(source_len, config).map(|p| p.estimate)
}
pub fn prepare_slice<'encoder, 'input>(
&'encoder mut self,
input: &'input [u8],
config: BatchConfig,
) -> Result<ScopedParallelBatch<'encoder, 'input>, ParallelEncodeError> {
core::Batch::prepare(&mut self.inner, core::Input::Slice(input), config)
.map(|inner| ScopedParallelBatch { inner })
}
pub fn prepare_source<S, T>(
&mut self,
source: T,
config: BatchConfig,
) -> Result<OwnedParallelBatch<'_>, ParallelEncodeError>
where
S: RandomAccessSource + ?Sized,
T: Into<Arc<S>>,
{
let source = Arc::new(core::source::SharedSource(source.into()));
core::Batch::prepare(&mut self.inner, core::Input::Source(source), config)
.map(|inner| ScopedParallelBatch { inner })
}
}
#[must_use = "run the task or its batch will report abandonment"]
pub struct ScopedParallelTask<'input> {
inner: core::Task<'input>,
}
pub type OwnedParallelTask = ScopedParallelTask<'static>;
impl ScopedParallelTask<'_> {
pub const fn id(&self) -> TaskId {
self.inner.id
}
pub fn segment_range(&self) -> Range<SegmentId> {
SegmentId(self.inner.range.start)..SegmentId(self.inner.range.end)
}
pub fn run(self) {
self.inner.run();
}
}
pub struct ScopedParallelBatch<'encoder, 'input> {
inner: core::Batch<'encoder, 'input>,
}
pub type OwnedParallelBatch<'encoder> = ScopedParallelBatch<'encoder, 'static>;
impl<'input> ScopedParallelBatch<'_, 'input> {
pub fn task_count(&self) -> usize {
self.inner.plan.tasks
}
pub fn segment_count(&self) -> u64 {
self.inner.plan.estimate.segment_count
}
pub fn take_tasks(&mut self) -> Result<Vec<ScopedParallelTask<'input>>, ParallelEncodeError> {
self.inner.take_tasks().map(|tasks| {
tasks
.into_iter()
.map(|inner| ScopedParallelTask { inner })
.collect()
})
}
pub fn run_inline(&mut self) -> Result<(), ParallelEncodeError> {
for task in self.take_tasks()? {
task.run();
}
self.wait()
}
pub fn poll(&mut self) -> Result<BatchPoll, ParallelEncodeError> {
self.inner.poll()
}
pub fn wait(&mut self) -> Result<(), ParallelEncodeError> {
self.inner.wait(None).map(|_| ())
}
pub fn wait_timeout(&mut self, timeout: Duration) -> Result<WaitStatus, ParallelEncodeError> {
self.inner.wait(Some(timeout))
}
pub fn cancel(&self) {
self.inner.cancel();
}
pub fn finish_into(mut self, dst: &mut Vec<u8>) -> Result<ParallelOutput, ParallelEncodeError> {
self.inner.finish_into(dst)
}
pub fn finish_to_writer<W: Write>(
mut self,
writer: W,
) -> Result<(W, ParallelStats), ParallelFinishError<W>> {
self.inner.finish_to_writer(writer)
}
}
impl std::fmt::Debug for ParallelCompressor {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ParallelCompressor")
.field("encoder", self.encoder_config())
.field("parallel", self.parallel_config())
.field("retained_workers", &self.retained_worker_count())
.finish_non_exhaustive()
}
}
impl std::fmt::Debug for ScopedParallelTask<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ScopedParallelTask")
.field("id", &self.id())
.field("segments", &self.segment_range())
.finish_non_exhaustive()
}
}
impl std::fmt::Debug for ScopedParallelBatch<'_, '_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ScopedParallelBatch")
.field("tasks", &self.task_count())
.field("segments", &self.segment_count())
.finish_non_exhaustive()
}
}