zstd-complete 0.3.0

A pure Rust Zstandard compressor and decompressor with bounded streaming I/O
Documentation
//! Frame-level adapter for the C fast no-dictionary path.

use alloc::vec::Vec;

use super::{
    c_frame_header::write_frame_header_no_dict,
    cctx_params::CctxParameters,
    compress_bound::compress_bound,
    dictionary::ParsedDictionary,
    dictionary_frame::DictionaryFrameContext,
    fast::FastMatchState,
    fast_block::{
        append_block_fast_ext_dict_with_state_and_policy_in_mode,
        append_block_fast_no_dict_with_policy,
        append_block_fast_no_dict_with_state_and_policy_in_mode, FastBlockEncodeContext,
        FastBlockSource, FastExtDictBlockSource,
    },
    frame_state::{BlockEncodeMode, FrameBlockState},
    params::{CParamMode, CompressionParameters, ZSTD_CONTENTSIZE_UNKNOWN},
};
use crate::common::MAX_BLOCK_SIZE;

pub(crate) fn encode_single_block_frame_fast_no_dict(src: &[u8], level: i32) -> Vec<u8> {
    debug_assert!(src.len() <= MAX_BLOCK_SIZE as usize);
    encode_frame_fast_no_dict(src, level)
}

pub(crate) fn encode_frame_fast_no_dict(src: &[u8], level: i32) -> Vec<u8> {
    let cctx = CctxParameters::for_level(level, src.len() as u64, 0);
    encode_frame_fast_no_dict_with_cctx(src, cctx)
}

pub(crate) fn encode_frame_fast_no_dict_with_cctx(src: &[u8], cctx: CctxParameters) -> Vec<u8> {
    let mut output = Vec::with_capacity(compress_bound(src.len()));
    cctx.assert_resolved();
    let params = cctx.compression;
    let mut frame_state = FrameBlockState::new(params, cctx.max_block_size);
    let mut match_state = FastMatchState::new();
    encode_frame_fast_no_dict_with_cctx_in(
        src,
        cctx,
        &mut frame_state,
        &mut match_state,
        &mut output,
    ).expect("owned legacy codec allocation failed");
    output
}

pub(crate) fn encode_frame_fast_no_dict_with_cctx_in(
    src: &[u8],
    cctx: CctxParameters,
    frame_state: &mut FrameBlockState,
    match_state: &mut FastMatchState,
    output: &mut impl crate::workspace::Growable<u8>,
) -> Result<(), crate::workspace::CapacityError> {
    cctx.assert_resolved();
    let block_encode_mode = BlockEncodeMode::from_cctx(cctx);
    let params = cctx.compression;
    output.clear();
    frame_state.reset_for_frame(params, cctx.max_block_size)?;
    match_state.reset_for_frame(params)?;
    write_frame_header_no_dict(output, src.len(), params)?;

    if src.is_empty() {
        append_block_fast_no_dict_with_policy(
            src,
            true,
            params,
            frame_state.block_config,
            frame_state.repeat_offsets,
            FastBlockEncodeContext {
                previous_huff_table: None,
                huffman_build_scratch: &mut frame_state.huffman_build_scratch,
                fse_build_scratch: &mut frame_state.fse_build_scratch,
                fse_tables: &mut frame_state.fse_tables,
                offset_history: &mut frame_state.offset_history,
            },
            FrameBlockState::block_policy(true),
            output,
        )?;
        return Ok(());
    }

    let mut block_start = 0;
    while block_start < src.len() {
        let block_size = frame_state.next_frame_chunk_block_size(
            &src[block_start..],
            block_start,
            params.strategy,
        );
        let block_end = block_start + block_size;
        let policy = FrameBlockState::block_policy(block_start == 0);
        let encoded_start = output.len();
        let encoded_block = append_block_fast_no_dict_with_state_and_policy_in_mode(
            FastBlockSource {
                src,
                block_range: block_start..block_end,
                loaded_dict_end: 0,
            },
            block_end == src.len(),
            params,
            frame_state.block_config,
            frame_state.repeat_offsets,
            match_state,
            FastBlockEncodeContext {
                previous_huff_table: frame_state.last_huff_table.as_ref(),
                huffman_build_scratch: &mut frame_state.huffman_build_scratch,
                fse_build_scratch: &mut frame_state.fse_build_scratch,
                fse_tables: &mut frame_state.fse_tables,
                offset_history: &mut frame_state.offset_history,
            },
            policy,
            block_encode_mode,
            output,
        )?;
        frame_state.record_encoded_block(
            block_size,
            output.len() - encoded_start,
            encoded_block.repeat_offsets,
            encoded_block.new_huffman_table,
        )?;
        block_start = block_end;
    }
    Ok(())
}

pub(crate) fn encode_frame_fast_with_dictionary(
    src: &[u8],
    level: i32,
    dictionary: ParsedDictionary<'_>,
) -> Vec<u8> {
    let cctx = CctxParameters::for_level_with_mode(
        level,
        src.len() as u64,
        dictionary.content.len(),
        CParamMode::NoAttachDict,
    );
    encode_frame_fast_with_dictionary_and_cctx(src, level, dictionary, cctx, false)
}

pub(crate) fn encode_frame_fast_with_dictionary_and_cctx(
    src: &[u8],
    level: i32,
    dictionary: ParsedDictionary<'_>,
    cctx: CctxParameters,
    prepared_dictionary: bool,
) -> Vec<u8> {
    let cctx = if prepared_dictionary {
        cdict_copy_cctx(level, src.len() as u64, dictionary.raw_size, cctx)
    } else {
        cctx
    };
    let mut context = DictionaryFrameContext::new_with_cctx(src, dictionary, cctx);
    let params = context.cctx.compression;
    let block_encode_mode = BlockEncodeMode::from_cctx(context.cctx);

    let mut match_state = FastMatchState::new();
    match_state.load_cdict_copy_prefix(&context.combined, context.dict_len, params).expect("owned legacy codec allocation failed");

    if src.is_empty() {
        append_block_fast_no_dict_with_policy(
            src,
            true,
            params,
            context.frame_state.block_config,
            context.frame_state.repeat_offsets,
            FastBlockEncodeContext {
                previous_huff_table: context.frame_state.last_huff_table.as_ref(),
                huffman_build_scratch: &mut context.frame_state.huffman_build_scratch,
                fse_build_scratch: &mut context.frame_state.fse_build_scratch,
                fse_tables: &mut context.frame_state.fse_tables,
                offset_history: &mut context.frame_state.offset_history,
            },
            FrameBlockState::block_policy(true),
            &mut context.output,
        ).expect("owned legacy codec allocation failed");
        return context.output;
    }

    let mut block_start = context.dict_len;
    let src_end = context.src_end();
    while block_start < src_end {
        let block_size = context.frame_state.next_frame_chunk_block_size(
            &context.combined[block_start..src_end],
            block_start - context.dict_len,
            params.strategy,
        );
        let block_end = block_start + block_size;
        let policy = FrameBlockState::block_policy(block_start == context.dict_len);
        let loaded_dict_end = context.loaded_dict_end_for_block(block_end, params);
        let block_context = FastBlockEncodeContext {
            previous_huff_table: context.frame_state.last_huff_table.as_ref(),
            huffman_build_scratch: &mut context.frame_state.huffman_build_scratch,
            fse_build_scratch: &mut context.frame_state.fse_build_scratch,
            fse_tables: &mut context.frame_state.fse_tables,
            offset_history: &mut context.frame_state.offset_history,
        };
        let encoded_start = context.output.len();
        let encoded_block = if loaded_dict_end == 0 {
            append_block_fast_no_dict_with_state_and_policy_in_mode(
                FastBlockSource {
                    src: &context.combined,
                    block_range: block_start..block_end,
                    loaded_dict_end,
                },
                block_end == src_end,
                params,
                context.frame_state.block_config,
                context.frame_state.repeat_offsets,
                &mut match_state,
                block_context,
                policy,
                block_encode_mode,
                &mut context.output,
            ).expect("owned legacy codec allocation failed")
        } else {
            append_block_fast_ext_dict_with_state_and_policy_in_mode(
                FastExtDictBlockSource {
                    src: &context.combined,
                    block_range: block_start..block_end,
                    dict_limit: context.dict_len,
                    loaded_dict_end,
                },
                block_end == src_end,
                params,
                context.frame_state.block_config,
                context.frame_state.repeat_offsets,
                &mut match_state,
                block_context,
                policy,
                block_encode_mode,
                &mut context.output,
            ).expect("owned legacy codec allocation failed")
        };
        context.frame_state.record_encoded_block(
            block_size,
            context.output.len() - encoded_start,
            encoded_block.repeat_offsets,
            encoded_block.new_huffman_table,
        ).expect("owned legacy codec allocation failed");
        block_start = block_end;
    }

    context.output
}

fn cdict_copy_cctx(
    level: i32,
    pledged_src_size: u64,
    dictionary_size: usize,
    active_cctx: CctxParameters,
) -> CctxParameters {
    let mut compression = CompressionParameters::for_level_with_mode(
        level,
        ZSTD_CONTENTSIZE_UNKNOWN,
        dictionary_size,
        CParamMode::CreateCDict,
    );
    compression.window_log = active_cctx.compression.window_log;

    let mut cctx =
        CctxParameters::from_compression_parameters(level, compression, pledged_src_size);
    cctx.target_c_block_size = active_cctx.target_c_block_size;
    cctx
}