ataf 0.2.0

An archive format that supports native multithreading for compression and decompression.
Documentation
use crate::{
    compression::Compressor,
    spec::{ArchiveEntryHeader, ArchiveHeader, Serialize},
};
use std::{
    io::{Read, Write},
    marker::PhantomData,
};

pub struct ChunkWriter<W: Write + Send> {
    writer: W,
    chunk_count: u64,
}

impl<W: Write + Send> ChunkWriter<W> {
    pub fn write_chunk(&mut self, chunk: &[u8]) -> std::io::Result<()> {
        self.writer
            .write_all(&u32_to_u24_bytes(chunk.len() as u32))?;
        self.writer.write_all(chunk)?;
        self.chunk_count -= 1;

        Ok(())
    }
}

#[inline]
fn u32_to_u24_bytes(value: u32) -> [u8; 3] {
    [(value >> 16) as u8, (value >> 8) as u8, value as u8]
}

pub struct ArchiveWriter<W: Write + Send, R: Read> {
    writer: W,
    _reader: PhantomData<R>,
    compressor: Box<dyn Compressor<W, R>>,
    header: ArchiveHeader,
}

impl<W: Write + Send, R: Read> ArchiveWriter<W, R> {
    pub fn new(
        mut writer: W,
        compressor: Box<dyn Compressor<W, R>>,
        compression_chunk_size: u32,
    ) -> std::io::Result<Self> {
        let header = ArchiveHeader {
            version: 1,
            compression: String::from(compressor.name()),
            compression_chunk_size,
        };

        header.serialize(&mut writer)?;

        Ok(Self {
            writer,
            _reader: PhantomData,
            compressor,
            header,
        })
    }

    pub fn write_entry(&mut self, entry: ArchiveEntryHeader, mut input: R) -> std::io::Result<()> {
        entry.serialize(&mut self.writer)?;

        let chunk_count = *entry.size / self.header.compression_chunk_size as u64
            + if *entry.size % self.header.compression_chunk_size as u64 > 0 {
                1
            } else {
                0
            };

        let mut chunk_writer = ChunkWriter {
            writer: &mut self.writer,
            chunk_count,
        };

        while chunk_writer.chunk_count > 0 {
            self.compressor.compress(
                &mut input,
                chunk_count as usize,
                self.header.compression_chunk_size,
                &mut chunk_writer,
            )?;
        }

        Ok(())
    }
}