atlas-archive-core 1.1.0

High-performance compression library with adaptive context modeling (Loom) and .nyx archives
Documentation
pub mod bwt;
pub mod mtf;
pub mod rle;

use crate::alloc::vec::Vec;

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Transform {
    Rle,
    Bwt,
    BwtSafe, // Blocked BWT with embedded indices
    Mtf,
    Delta,
    PredictiveDelta,
    Yuv,
    ChannelSeparation(usize),
    BlockDelta(usize),
    XorDelta(usize),
    MultiByteXor(usize),
    Auto, // BwtSafe -> Mtf -> Delta -> Rle
}

pub struct TransformChain {
    chain: Vec<Transform>,
}

impl TransformChain {
    pub fn new(chain: Vec<Transform>) -> Self {
        Self { chain }
    }

    pub fn apply(&self, mut data: Vec<u8>) -> (Vec<u8>, Vec<usize>) {
        let mut bwt_indices = Vec::new();
        for t in &self.chain {
            let start_len = data.len();
            match t {
                Transform::Rle => data = rle::rle_encode(&data),
                Transform::Bwt => {
                    let (b, idx) = bwt::bwt_transform(&data);
                    data = b;
                    bwt_indices.push(idx);
                }
                Transform::BwtSafe => {
                    // Safe BWT embeds indices in the stream
                    let (b, _) = bwt::bwt_encode_safe(&data);
                    data = b;
                }
                Transform::Mtf => data = mtf::mtf_transform(&data),
                Transform::Delta => data = crate::poor_compress::DeltaTransform::encode(&data),
                Transform::PredictiveDelta => {
                    data = crate::poor_compress::PredictiveDelta::encode(&data)
                }
                Transform::Yuv => {
                    data = crate::poor_compress::ChannelSeparator::new(3).separate_yuv(&data)
                }
                Transform::ChannelSeparation(channels) => {
                    data = crate::poor_compress::ChannelSeparator::new(*channels).separate(&data)
                }
                Transform::BlockDelta(size) => {
                    data = crate::poor_compress::BlockDeltaTransform::new(*size).encode(&data)
                }
                Transform::XorDelta(size) => {
                    data = crate::poor_compress::XorDeltaTransform::new(*size).encode(&data)
                }
                Transform::MultiByteXor(width) => {
                    data = crate::poor_compress::MultiByteXor::new(*width).encode(&data)
                }
                Transform::Auto => {
                    // Auto Chain: BwtBlocked -> Mtf -> Delta -> Rle
                    #[cfg(feature = "std")]
                    std::println!("[Auto Apply] Start: {} bytes", data.len());

                    let (b, indices) = bwt::bwt_transform_blocked(&data);
                    data = b;
                    bwt_indices.extend(indices);
                    #[cfg(feature = "std")]
                    std::println!("[Auto Apply] After BWT: {} bytes", data.len());

                    data = mtf::mtf_transform(&data);
                    #[cfg(feature = "std")]
                    std::println!("[Auto Apply] After MTF: {} bytes", data.len());

                    data = crate::poor_compress::DeltaTransform::encode(&data);
                    #[cfg(feature = "std")]
                    std::println!("[Auto Apply] After Delta: {} bytes", data.len());

                    data = rle::rle_encode(&data);
                    #[cfg(feature = "std")]
                    std::println!("[Auto Apply] After RLE: {} bytes", data.len());
                }
            }

            #[cfg(feature = "std")]
            if start_len > 0 {
                std::println!(
                    "[Transform {:?}] {} -> {} (Step Ratio: {:.4})",
                    t,
                    start_len,
                    data.len(),
                    data.len() as f64 / start_len as f64
                );
            }
        }
        (data, bwt_indices)
    }

    pub fn inverse(&self, mut data: Vec<u8>, mut bwt_indices: Vec<usize>) -> Vec<u8> {
        for t in self.chain.iter().rev() {
            match t {
                Transform::Rle => data = rle::rle_decode(&data),
                Transform::Bwt => {
                    let idx = bwt_indices.pop().expect("Missing BWT index");
                    data = bwt::bwt_inverse(&data, idx);
                }
                Transform::BwtSafe => {
                    data = bwt::bwt_decode_safe(&data, 0);
                }
                Transform::Mtf => data = mtf::mtf_inverse(&data),
                Transform::Delta => data = crate::poor_compress::DeltaTransform::decode(&data),
                Transform::PredictiveDelta => {
                    data = crate::poor_compress::PredictiveDelta::decode(&data)
                }
                Transform::Yuv => {
                    data = crate::poor_compress::ChannelSeparator::new(3).interleave_yuv(&data)
                }
                Transform::ChannelSeparation(channels) => {
                    data = crate::poor_compress::ChannelSeparator::new(*channels).interleave(&data)
                }
                Transform::BlockDelta(size) => {
                    data = crate::poor_compress::BlockDeltaTransform::new(*size).decode(&data)
                }
                Transform::XorDelta(size) => {
                    data = crate::poor_compress::XorDeltaTransform::new(*size).decode(&data)
                }
                Transform::MultiByteXor(width) => {
                    data = crate::poor_compress::MultiByteXor::new(*width).decode(&data)
                }
                Transform::Auto => {
                    // Inverse Auto: Rle -> Delta -> Mtf -> BwtBlocked
                    #[cfg(feature = "std")]
                    std::println!("[Auto Inverse] Start: {} bytes", data.len());

                    data = rle::rle_decode(&data);
                    #[cfg(feature = "std")]
                    std::println!("[Auto Inverse] After RLE: {} bytes", data.len());

                    data = crate::poor_compress::DeltaTransform::decode(&data);
                    #[cfg(feature = "std")]
                    std::println!("[Auto Inverse] After Delta: {} bytes", data.len());

                    data = mtf::mtf_inverse(&data);
                    #[cfg(feature = "std")]
                    std::println!("[Auto Inverse] After MTF: {} bytes", data.len());

                    // Logic to pop correct number of indices for blocked BWT
                    // The number of blocks depends on the DATA length at this point.
                    // n_blocks = ceil(len / BWT_BLOCK_SIZE)
                    // But if len is 0, n_blocks is 0.
                    let block_size = bwt::BWT_BLOCK_SIZE;
                    let n_blocks = if data.is_empty() {
                        0
                    } else {
                        (data.len() + block_size - 1) / block_size
                    };

                    let start_idx = bwt_indices.len().saturating_sub(n_blocks);
                    let valid_indices: Vec<usize> = bwt_indices.drain(start_idx..).collect();

                    if valid_indices.len() != n_blocks {
                        #[cfg(feature = "std")]
                        std::println!(
                            "[Transform::Auto] Warning: Index count mismatch. Expected {}, got {}",
                            n_blocks,
                            valid_indices.len()
                        );
                    }

                    data = bwt::bwt_inverse_blocked(&data, &valid_indices);
                    #[cfg(feature = "std")]
                    std::println!("[Auto Inverse] After BWT: {} bytes", data.len());
                }
            }
        }
        data
    }
}