mod archive;
mod config;
mod iterator;
mod memory;
mod parallel;
mod pool;
mod progressive;
mod random;
mod sink;
mod solid;
use crate::format::SIGNATURE_HEADER_SIZE;
use crate::format::parser::ArchiveHeader;
pub(crate) fn calculate_pack_start(header: &ArchiveHeader) -> u64 {
let pack_pos = header.pack_info.as_ref().map(|pi| pi.pack_pos).unwrap_or(0);
SIGNATURE_HEADER_SIZE + pack_pos
}
pub(crate) fn packed_stream_base(header: &ArchiveHeader, folder_index: usize) -> usize {
header
.unpack_info
.as_ref()
.map(|ui| {
ui.folders
.iter()
.take(folder_index)
.map(|f| f.packed_streams.len().max(1))
.sum()
})
.unwrap_or(folder_index)
}
pub(crate) fn folder_packed_size(header: &ArchiveHeader, folder_index: usize) -> u64 {
let base = packed_stream_base(header, folder_index);
let owned = header
.unpack_info
.as_ref()
.and_then(|ui| ui.folders.get(folder_index))
.map(|f| f.packed_streams.len().max(1))
.unwrap_or(1);
header
.pack_info
.as_ref()
.map(|pi| {
(0..owned)
.filter_map(|i| pi.pack_sizes.get(base + i).copied())
.sum()
})
.unwrap_or(0)
}
pub(crate) fn check_is_solid(header: &ArchiveHeader) -> bool {
header
.substreams_info
.as_ref()
.map(|ss| {
ss.num_unpack_streams_in_folders
.iter()
.any(|&count| count > 1)
})
.unwrap_or(false)
}
pub use archive::{ExtractAllResult, StreamingArchive};
pub use config::{CompressionMethod, MemoryEstimate, StreamingConfig, SystemMemoryInfo};
pub use iterator::{EntryIterator, StreamingEntry};
pub use memory::{MemoryGuard, MemoryTracker, TrackedBuffer};
pub use parallel::{
CancellationToken, ParallelExtractionOptions, ParallelExtractionResult, ParallelFolderExtractor,
};
pub use pool::{DecoderPool, EntryLocation, PoolStats, PooledDecoder, SolidEntryLocator};
pub use progressive::{
BoundedReader, ChainedReader, ProgressiveReader, ProgressiveReaderWithCallback,
};
pub use random::{EntryLocator, RandomAccessReader, RandomEntryReader};
pub use sink::{
BoundedVecSink, CountingSink, Crc32Sink, ExtractToSink, NullSink, ProgressSink, TeeSink,
};
pub use solid::{SolidBlockInfo, SolidBlockStreamReader};
#[derive(Debug, Clone)]
pub struct SkippedEntry {
pub index: usize,
pub raw_path: Option<Vec<u8>>,
pub reason: SkipReason,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SkipReason {
InvalidPath(String),
}
impl std::fmt::Display for SkipReason {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SkipReason::InvalidPath(msg) => write!(f, "invalid path: {}", msg),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_module_exports() {
let _ = StreamingConfig::default();
let tracker = MemoryTracker::new(1024);
let _ = tracker.current_usage();
}
#[test]
fn test_config_presets() {
let low = StreamingConfig::low_memory();
let high = StreamingConfig::high_performance();
assert!(low.max_memory_buffer < high.max_memory_buffer);
}
#[test]
fn test_sink_types() {
let mut null_sink = NullSink::new();
std::io::Write::write_all(&mut null_sink, &[1, 2, 3]).unwrap();
assert_eq!(null_sink.bytes_discarded(), 3);
}
}