zstd-complete 0.3.0

A pure Rust Zstandard compressor and decompressor with bounded streaming I/O
Documentation
use crate::{
    encoding::blocks::compressed::sequence_cost::{
        cross_entropy_cost_up_to, entropy_cost_up_to, repeat_table_cost_up_to, CodeCounts,
    },
    fse::fse_encoder::{
        ncount_cost_from_probabilities, normalized_probabilities_from_counts,
        normalized_probabilities_from_counts_into, FSETable, FSETableBuildScratch,
    },
};

use crate::encoding::blocks::compressed::sequence_tables::{
    builder::{
        build_sequence_table_from_counts, build_sequence_table_from_counts_with_scratch,
        build_table_from_probabilities_with_builder,
        build_table_from_probabilities_with_builder_and_scratch, TableBuilder,
    },
    FseTableMode,
};

use super::TableSelectionPolicy;

#[allow(clippy::too_many_arguments)]
pub(super) fn choose_c_fast_table<'a>(
    previous: Option<&'a FSETable>,
    previous_repeat_valid: bool,
    default_table: &'a FSETable,
    sequence_count: usize,
    last_code: u8,
    counts: &CodeCounts,
    max_log: u8,
    repeat_table_max_sequences: usize,
    predefined_max_sequences: usize,
    default_norm_log: u8,
    table_builder: TableBuilder,
) -> Result<FseTableMode<'a>, crate::workspace::CapacityError> {
    choose_c_fast_table_impl(
        previous,
        previous_repeat_valid,
        default_table,
        sequence_count,
        last_code,
        counts,
        max_log,
        repeat_table_max_sequences,
        predefined_max_sequences,
        default_norm_log,
        table_builder,
        None,
    )
}

#[allow(clippy::too_many_arguments)]
pub(super) fn choose_c_fast_table_with_scratch<'a>(
    previous: Option<&'a FSETable>,
    previous_repeat_valid: bool,
    default_table: &'a FSETable,
    sequence_count: usize,
    last_code: u8,
    counts: &CodeCounts,
    max_log: u8,
    repeat_table_max_sequences: usize,
    predefined_max_sequences: usize,
    default_norm_log: u8,
    table_builder: TableBuilder,
    scratch: &mut FSETableBuildScratch,
) -> Result<FseTableMode<'a>, crate::workspace::CapacityError> {
    choose_c_fast_table_impl(
        previous,
        previous_repeat_valid,
        default_table,
        sequence_count,
        last_code,
        counts,
        max_log,
        repeat_table_max_sequences,
        predefined_max_sequences,
        default_norm_log,
        table_builder,
        Some(scratch),
    )
}

#[allow(clippy::too_many_arguments)]
fn choose_c_fast_table_impl<'a>(
    previous: Option<&'a FSETable>,
    previous_repeat_valid: bool,
    default_table: &'a FSETable,
    sequence_count: usize,
    last_code: u8,
    counts: &CodeCounts,
    max_log: u8,
    repeat_table_max_sequences: usize,
    predefined_max_sequences: usize,
    default_norm_log: u8,
    table_builder: TableBuilder,
    scratch: Option<&mut FSETableBuildScratch>,
) -> Result<FseTableMode<'a>, crate::workspace::CapacityError> {
    if counts.most_frequent() == counts.total() {
        let first_code = counts.max_symbol() as u8;
        let default_allowed = default_table.can_encode_symbol(first_code);
        return if default_allowed && sequence_count <= 2 {
            Ok(FseTableMode::Predefined(default_table))
        } else {
            Ok(FseTableMode::Rle(first_code))
        };
    }

    let default_allowed = counts.default_allowed(default_table);

    if default_allowed {
        if let Some(previous) = previous {
            if previous_repeat_valid
                && sequence_count < repeat_table_max_sequences
                && counts.default_allowed(previous)
            {
                return Ok(FseTableMode::RepeatLast(previous));
            }
        }

        if sequence_count < predefined_max_sequences
            || counts.most_frequent() < (sequence_count >> (usize::from(default_norm_log) - 1))
        {
            return Ok(FseTableMode::Predefined(default_table));
        }
    }

    Ok(FseTableMode::Encoded(match scratch {
        Some(scratch) => build_sequence_table_from_counts_with_scratch(
            counts,
            last_code,
            max_log,
            table_builder,
            scratch,
        )?,
        None => build_sequence_table_from_counts(counts, last_code, max_log, table_builder)?,
    }))
}

#[allow(clippy::too_many_arguments)]
pub(super) fn choose_c_cost_table<'a>(
    previous: Option<&'a FSETable>,
    default_table: &'a FSETable,
    sequence_count: usize,
    last_code: u8,
    counts: &CodeCounts,
    max_log: u8,
    table_builder: TableBuilder,
) -> Result<FseTableMode<'a>, crate::workspace::CapacityError> {
    if counts.most_frequent() == counts.total() {
        let first_code = counts.max_symbol() as u8;
        let default_allowed = default_table.can_encode_symbol(first_code);
        return if default_allowed && sequence_count <= 2 {
            Ok(FseTableMode::Predefined(default_table))
        } else {
            Ok(FseTableMode::Rle(first_code))
        };
    }

    let max_symbol = counts.max_symbol();
    let (encoded_probs, encoded_acc_log) =
        normalized_probabilities_from_counts(&counts.counts()[..=max_symbol], max_log, true);
    let compressed_cost = ncount_cost_from_probabilities(&encoded_probs, encoded_acc_log)
        + entropy_cost_up_to(counts, max_symbol);
    let basic_cost = cross_entropy_cost_up_to(default_table, counts, max_symbol);
    let repeat_cost =
        previous.and_then(|previous| repeat_table_cost_up_to(previous, counts, max_symbol));

    if basic_cost.is_some_and(|basic_cost| {
        basic_cost <= repeat_cost.unwrap_or(usize::MAX) && basic_cost <= compressed_cost
    }) {
        return Ok(FseTableMode::Predefined(default_table));
    }

    if let Some(previous) = previous {
        if repeat_cost.is_some_and(|repeat_cost| repeat_cost <= compressed_cost) {
            return Ok(FseTableMode::RepeatLast(previous));
        }
    }

    let encoded_table = if counts.counts()[usize::from(last_code)] > 1 {
        build_sequence_table_from_counts(counts, last_code, max_log, table_builder)?
    } else {
        build_table_from_probabilities_with_builder(&encoded_probs, encoded_acc_log, table_builder)?
    };
    Ok(FseTableMode::Encoded(encoded_table))
}

#[allow(clippy::too_many_arguments)]
pub(super) fn choose_c_cost_table_with_scratch<'a>(
    previous: Option<&'a FSETable>,
    default_table: &'a FSETable,
    sequence_count: usize,
    last_code: u8,
    counts: &CodeCounts,
    max_log: u8,
    table_builder: TableBuilder,
    scratch: &mut FSETableBuildScratch,
) -> Result<FseTableMode<'a>, crate::workspace::CapacityError> {
    if counts.most_frequent() == counts.total() {
        let first_code = counts.max_symbol() as u8;
        let default_allowed = default_table.can_encode_symbol(first_code);
        return if default_allowed && sequence_count <= 2 {
            Ok(FseTableMode::Predefined(default_table))
        } else {
            Ok(FseTableMode::Rle(first_code))
        };
    }

    let max_symbol = counts.max_symbol();
    let raw_counts = &counts.counts()[..=max_symbol];
    let mut encoded_probs = [0_i32; 256];
    let encoded_probs = &mut encoded_probs[..raw_counts.len()];
    let encoded_acc_log =
        normalized_probabilities_from_counts_into(raw_counts, max_log, true, encoded_probs);
    let compressed_cost = ncount_cost_from_probabilities(encoded_probs, encoded_acc_log)
        + entropy_cost_up_to(counts, max_symbol);
    let basic_cost = cross_entropy_cost_up_to(default_table, counts, max_symbol);
    let repeat_cost =
        previous.and_then(|previous| repeat_table_cost_up_to(previous, counts, max_symbol));

    if basic_cost.is_some_and(|basic_cost| {
        basic_cost <= repeat_cost.unwrap_or(usize::MAX) && basic_cost <= compressed_cost
    }) {
        return Ok(FseTableMode::Predefined(default_table));
    }

    if let Some(previous) = previous {
        if repeat_cost.is_some_and(|repeat_cost| repeat_cost <= compressed_cost) {
            return Ok(FseTableMode::RepeatLast(previous));
        }
    }

    let encoded_table = if raw_counts[usize::from(last_code)] > 1 {
        build_sequence_table_from_counts_with_scratch(
            counts,
            last_code,
            max_log,
            table_builder,
            scratch,
        )?
    } else {
        build_table_from_probabilities_with_builder_and_scratch(
            encoded_probs,
            encoded_acc_log,
            table_builder,
            Some(scratch),
        )?
    };
    Ok(FseTableMode::Encoded(encoded_table))
}

pub(super) fn sequence_table_selection_policy(
    c_fast_heuristics: bool,
    c_cost_model: bool,
    default_norm_log: u8,
) -> TableSelectionPolicy {
    if c_fast_heuristics {
        TableSelectionPolicy::CFast { default_norm_log }
    } else if c_cost_model {
        TableSelectionPolicy::CCost
    } else {
        TableSelectionPolicy::Legacy
    }
}