use super::super::blocks::sequence_section::ModeType;
use super::super::blocks::sequence_section::Sequence;
use super::super::blocks::sequence_section::SequencesHeader;
use super::scratch::FSEScratch;
use crate::bit_io::BitReaderReversed;
use crate::blocks::sequence_section::{
MAX_LITERAL_LENGTH_CODE, MAX_MATCH_LENGTH_CODE, MAX_OFFSET_CODE,
};
use crate::cpu_kernel::{CpuKernel, CpuKernelTag};
use crate::decoding::errors::{DecodeSequenceError, DecompressBlockError, ExecuteSequencesError};
use crate::fse::SeqFSEDecoder;
pub(crate) const ADVANCE: usize = 8;
pub(crate) const ADVANCE_MASK: usize = ADVANCE - 1;
const _: () = assert!(
ADVANCE.is_power_of_two(),
"ADVANCE must be a power of two; ring indexing uses `i & (ADVANCE - 1)` as `i % ADVANCE`"
);
#[inline]
pub(crate) fn compute_use_long_pipeline(
num_sequences: usize,
ddict_is_cold: bool,
total_history: usize,
offsets_long_share: u32,
) -> bool {
#[cfg(target_pointer_width = "64")]
const MIN_LONG_OFFSET_SHARE: u32 = 7;
#[cfg(not(target_pointer_width = "64"))]
const MIN_LONG_OFFSET_SHARE: u32 = 20;
const HISTORY_THRESHOLD_FOR_PREFETCH: usize = 1 << 24;
num_sequences >= ADVANCE * 2
&& (ddict_is_cold
|| (total_history > HISTORY_THRESHOLD_FOR_PREFETCH
&& offsets_long_share >= MIN_LONG_OFFSET_SHARE))
}
pub(crate) struct SeqStreamSetup<'src, 'fse, K: crate::cpu_kernel::CpuKernel> {
pub(crate) br: BitReaderReversed<'src, K>,
pub(crate) ll_dec: SeqFSEDecoder<'fse>,
pub(crate) ml_dec: SeqFSEDecoder<'fse>,
pub(crate) of_dec: SeqFSEDecoder<'fse>,
pub(crate) max_update_bits: u8,
pub(crate) old_buffer_size: usize,
pub(crate) num_sequences: usize,
pub(crate) use_long_pipeline: bool,
}
pub(crate) fn init_sequence_stream<'src, 'fse, B, K>(
section: &SequencesHeader,
source: &'src [u8],
fse: &'fse mut FSEScratch,
buffer: &mut super::decode_buffer::DecodeBuffer<B>,
dict: Option<&'fse crate::decoding::dictionary::Dictionary>,
) -> Result<SeqStreamSetup<'src, 'fse, K>, DecompressBlockError>
where
B: super::buffer_backend::BufferBackend,
K: crate::cpu_kernel::CpuKernel,
{
let ddict_is_cold = fse.ddict_is_cold;
fse.ddict_is_cold = false;
let bytes_read = maybe_update_fse_tables(section, source, fse)?;
vprintln!("Updating tables used {} bytes", bytes_read);
let bit_stream = &source[bytes_read..];
let mut br = BitReaderReversed::<K>::new(bit_stream);
let mut skipped_bits = 0;
loop {
let val = br.get_bits(1);
skipped_bits += 1;
if val == 1 || skipped_bits > 8 {
break;
}
}
if skipped_bits > 8 {
return Err(DecodeSequenceError::ExtraPadding { skipped_bits }.into());
}
let ll_src = fse.ll_table(dict);
let ml_src = fse.ml_table(dict);
let of_src = fse.of_table(dict);
let mut ll_dec = SeqFSEDecoder::new(ll_src);
let mut ml_dec = SeqFSEDecoder::new(ml_src);
let mut of_dec = SeqFSEDecoder::new(of_src);
ll_dec
.init_state(&mut br)
.map_err(DecodeSequenceError::from)?;
of_dec
.init_state(&mut br)
.map_err(DecodeSequenceError::from)?;
ml_dec
.init_state(&mut br)
.map_err(DecodeSequenceError::from)?;
let max_update_bits = ll_src.accuracy_log + ml_src.accuracy_log + of_src.accuracy_log;
debug_assert!(
max_update_bits <= 56,
"sequence section update bits exceed 56-bit budget"
);
let old_buffer_size = buffer.len();
let num_sequences = section.num_sequences as usize;
let total_history = match buffer
.window_size
.checked_add(buffer.dict_content(dict).len())
{
Some(sum) => sum,
None => usize::MAX,
};
let use_long_pipeline = compute_use_long_pipeline(
num_sequences,
ddict_is_cold,
total_history,
fse.offsets_long_share,
);
Ok(SeqStreamSetup {
br,
ll_dec,
ml_dec,
of_dec,
max_update_bits,
old_buffer_size,
num_sequences,
use_long_pipeline,
})
}
#[allow(clippy::too_many_arguments)]
pub fn decode_and_execute_sequences<'fse, B: super::buffer_backend::BufferBackend>(
section: &SequencesHeader,
source: &[u8],
fse: &'fse mut FSEScratch,
buffer: &mut super::decode_buffer::DecodeBuffer<B>,
offset_hist: &mut [u32; 3],
literals_buffer: &[u8],
literals_len: usize,
dict: Option<&'fse crate::decoding::dictionary::Dictionary>,
kernel: CpuKernelTag,
) -> Result<(), DecompressBlockError> {
match kernel {
CpuKernelTag::Scalar => {
super::seq_decoder_scalar::decode_and_execute_sequences_scalar::<B>(
section,
source,
fse,
buffer,
offset_hist,
literals_buffer,
literals_len,
dict,
)
}
#[cfg(all(
any(target_arch = "x86", target_arch = "x86_64"),
feature = "kernel-sse"
))]
CpuKernelTag::Sse2 => {
unsafe {
decode_and_execute_sequences_sse2::<B>(
section,
source,
fse,
buffer,
offset_hist,
literals_buffer,
literals_len,
dict,
)
}
}
#[cfg(all(target_arch = "x86", feature = "kernel-bmi2"))]
CpuKernelTag::Bmi2 => {
unsafe {
decode_and_execute_sequences_sse2::<B>(
section,
source,
fse,
buffer,
offset_hist,
literals_buffer,
literals_len,
dict,
)
}
}
#[cfg(all(target_arch = "x86_64", feature = "kernel-bmi2"))]
CpuKernelTag::Bmi2 => {
unsafe {
super::seq_decoder_bmi2::decode_and_execute_sequences_bmi2::<B>(
section,
source,
fse,
buffer,
offset_hist,
literals_buffer,
literals_len,
dict,
)
}
}
#[cfg(all(target_arch = "x86", feature = "kernel-avx2"))]
CpuKernelTag::Avx2 => {
unsafe {
decode_and_execute_sequences_x86_avx2::<B>(
section,
source,
fse,
buffer,
offset_hist,
literals_buffer,
literals_len,
dict,
)
}
}
#[cfg(all(target_arch = "x86_64", feature = "kernel-avx2"))]
CpuKernelTag::Avx2 => {
unsafe {
super::seq_decoder_avx2::decode_and_execute_sequences_avx2::<B>(
section,
source,
fse,
buffer,
offset_hist,
literals_buffer,
literals_len,
dict,
)
}
}
#[cfg(all(target_arch = "x86_64", feature = "kernel-vbmi2"))]
CpuKernelTag::Vbmi2 => {
unsafe {
super::seq_decoder_vbmi2::decode_and_execute_sequences_vbmi2::<B>(
section,
source,
fse,
buffer,
offset_hist,
literals_buffer,
literals_len,
dict,
)
}
}
#[cfg(all(target_arch = "aarch64", feature = "kernel-neon"))]
CpuKernelTag::Neon => super::seq_decoder_scalar::decode_and_execute_sequences_impl::<
B,
crate::cpu_kernel::NeonKernel,
>(
section,
source,
fse,
buffer,
offset_hist,
literals_buffer,
literals_len,
dict,
),
#[cfg(all(
target_arch = "aarch64",
feature = "kernel-sve",
any(feature = "std", target_feature = "sve"),
))]
CpuKernelTag::Sve => super::seq_decoder_scalar::decode_and_execute_sequences_impl::<
B,
crate::cpu_kernel::SveKernel,
>(
section,
source,
fse,
buffer,
offset_hist,
literals_buffer,
literals_len,
dict,
),
}
}
#[cfg(all(
any(target_arch = "x86", target_arch = "x86_64"),
feature = "kernel-sse"
))]
#[target_feature(enable = "sse2")]
#[allow(clippy::too_many_arguments)]
unsafe fn decode_and_execute_sequences_sse2<'fse, B: super::buffer_backend::BufferBackend>(
section: &SequencesHeader,
source: &[u8],
fse: &'fse mut FSEScratch,
buffer: &mut super::decode_buffer::DecodeBuffer<B>,
offset_hist: &mut [u32; 3],
literals_buffer: &[u8],
literals_len: usize,
dict: Option<&'fse crate::decoding::dictionary::Dictionary>,
) -> Result<(), DecompressBlockError> {
super::seq_decoder_scalar::decode_and_execute_sequences_impl::<B, crate::cpu_kernel::Sse2Kernel>(
section,
source,
fse,
buffer,
offset_hist,
literals_buffer,
literals_len,
dict,
)
}
#[cfg(all(target_arch = "x86", feature = "kernel-avx2"))]
#[target_feature(enable = "bmi2,avx2")]
#[allow(clippy::too_many_arguments)]
unsafe fn decode_and_execute_sequences_x86_avx2<'fse, B: super::buffer_backend::BufferBackend>(
section: &SequencesHeader,
source: &[u8],
fse: &'fse mut FSEScratch,
buffer: &mut super::decode_buffer::DecodeBuffer<B>,
offset_hist: &mut [u32; 3],
literals_buffer: &[u8],
literals_len: usize,
dict: Option<&'fse crate::decoding::dictionary::Dictionary>,
) -> Result<(), DecompressBlockError> {
super::seq_decoder_scalar::decode_and_execute_sequences_impl::<B, crate::cpu_kernel::Avx2Kernel>(
section,
source,
fse,
buffer,
offset_hist,
literals_buffer,
literals_len,
dict,
)
}
#[derive(Copy, Clone)]
pub(crate) struct ExecSeq {
pub(crate) ll: u32,
pub(crate) ml: u32,
pub(crate) actual_offset: u32,
}
#[cfg_attr(not(debug_assertions), inline(always))]
#[cfg_attr(debug_assertions, inline)]
#[allow(clippy::too_many_arguments)]
pub(crate) fn execute_one_sequence_pipelined<
B: super::buffer_backend::BufferBackend,
K: CpuKernel,
>(
buffer: &mut super::decode_buffer::DecodeBuffer<B>,
dict: Option<&crate::decoding::dictionary::Dictionary>,
dict_content: &[u8],
literals: &[u8],
lit_cur: &mut usize,
lit_len: usize,
seq: Sequence,
resolved_offset: u32,
) -> Result<(), DecompressBlockError> {
let lit_cur_before = *lit_cur;
let high = lit_cur_before
.checked_add(seq.ll as usize)
.filter(|&h| h <= lit_len)
.ok_or(ExecuteSequencesError::NotEnoughBytesForSequence {
wanted: lit_cur_before.saturating_add(seq.ll as usize),
have: lit_len,
})?;
let lits = unsafe { literals.get_unchecked(lit_cur_before..high) };
*lit_cur = high;
if resolved_offset == 0 {
return Err(ExecuteSequencesError::ZeroOffset.into());
}
let inline_literals_ok = B::SUPPORTS_INLINE_SEQUENCE_EXEC;
let offset = resolved_offset as usize;
let prefix_resident = buffer
.len()
.checked_add(lits.len())
.is_some_and(|end| offset <= end);
if !prefix_resident {
if inline_literals_ok
&& buffer
.buffer_mut()
.inline_exec_dict_ok(seq.ll as usize, seq.ml as usize)
&& let Some(dict_src) =
buffer.dict_match_source(dict_content, lits.len(), offset, seq.ml as usize)
{
let lit_src = unsafe { literals.as_ptr().add(lit_cur_before) };
unsafe {
buffer
.buffer_mut()
.exec_sequence_inline_dict(lit_src, seq.ll as usize, dict_src, seq.ml as usize)
.map_err(DecompressBlockError::ExecuteSequencesError)?;
}
if B::INLINE_EXEC_MAINTAINS_OUTPUT_COUNTER {
buffer.advance_output_counter((seq.ll + seq.ml) as u64);
}
return Ok(());
}
buffer
.try_push::<K>(lits)
.map_err(ExecuteSequencesError::from)?;
buffer
.repeat_lookahead_prefetched::<K>(dict, offset, seq.ml as usize)
.map_err(ExecuteSequencesError::from)?;
return Ok(());
}
let inline_path_safe = inline_literals_ok
&& buffer
.buffer_mut()
.inline_exec_ok(seq.ll as usize, seq.ml as usize, offset);
if inline_path_safe {
let lit_src = unsafe { literals.as_ptr().add(lit_cur_before) };
unsafe {
buffer
.buffer_mut()
.exec_sequence_inline(lit_src, seq.ll as usize, offset, seq.ml as usize)
.map_err(DecompressBlockError::ExecuteSequencesError)?;
}
if B::INLINE_EXEC_MAINTAINS_OUTPUT_COUNTER {
buffer.advance_output_counter((seq.ll + seq.ml) as u64);
}
return Ok(());
}
buffer
.try_push::<K>(lits)
.map_err(ExecuteSequencesError::from)?;
buffer
.repeat_lookahead_prefetched::<K>(dict, resolved_offset as usize, seq.ml as usize)
.map_err(ExecuteSequencesError::from)?;
Ok(())
}
pub(crate) const LL_META: [u32; 36] = pack_code_meta(
&[
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 22, 24, 28, 32, 40, 48,
64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536,
],
&[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 3, 4, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16,
],
);
pub(crate) const ML_META: [u32; 53] = pack_code_meta(
&[
3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
27, 28, 29, 30, 31, 32, 33, 34, 35, 37, 39, 41, 43, 47, 51, 59, 67, 83, 99, 131, 259, 515,
1027, 2051, 4099, 8195, 16387, 32771, 65539,
],
&[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
],
);
const fn pack_code_meta<const N: usize>(bases: &[u32; N], extra_bits: &[u8; N]) -> [u32; N] {
let mut out = [0u32; N];
let mut i = 0;
while i < N {
assert!(bases[i] & 0xFF00_0000 == 0, "baseline must fit in 24 bits");
assert!(extra_bits[i] <= 16, "extra_bits exceeds zstd format limit");
out[i] = bases[i] | ((extra_bits[i] as u32) << 24);
i += 1;
}
out
}
pub const LL_MAX_LOG: u8 = 9;
pub const ML_MAX_LOG: u8 = 9;
pub const OF_MAX_LOG: u8 = 8;
pub(crate) fn compute_offsets_long_share(offsets: &crate::fse::SeqFSETable) -> u32 {
const OFFSET_FSE_LOG: u32 = 8;
const LONG_OFFSET_CODE_THRESHOLD: u32 = 22;
let table_log = offsets.accuracy_log as u32;
let raw = offsets
.decode()
.iter()
.filter(|entry| u32::from(entry.num_additional_bits) > LONG_OFFSET_CODE_THRESHOLD)
.count() as u32;
raw << OFFSET_FSE_LOG.saturating_sub(table_log)
}
pub(crate) fn maybe_update_fse_tables(
section: &SequencesHeader,
source: &[u8],
scratch: &mut FSEScratch,
) -> Result<usize, DecodeSequenceError> {
let modes = section
.modes
.ok_or(DecodeSequenceError::MissingCompressionMode)?;
let mut bytes_read = 0;
let ll_mode = modes.ll_mode();
match ll_mode {
ModeType::FSECompressed => {
let bytes = scratch.literal_lengths.build_decoder_fused(
source,
LL_MAX_LOG,
crate::fse::SeqMeta::Packed(&LL_META),
)?;
bytes_read += bytes;
vprintln!("Updating ll table");
vprintln!("Used bytes: {}", bytes);
}
ModeType::RLE => {
vprintln!("Use RLE ll table");
if source.is_empty() {
return Err(DecodeSequenceError::MissingByteForRleLlTable);
}
bytes_read += 1;
if source[0] > MAX_LITERAL_LENGTH_CODE {
return Err(DecodeSequenceError::InvalidRleCode {
axis: "LL",
code: source[0],
});
}
scratch.literal_lengths.build_rle(source[0]);
scratch
.literal_lengths
.enrich_with_packed_seq_meta(&LL_META);
}
ModeType::Predefined => {
vprintln!("Use predefined ll table");
#[cfg(feature = "std")]
{
scratch.mark_ll_predefined(predefined_ll_table());
}
#[cfg(not(feature = "std"))]
{
scratch.literal_lengths.build_from_probabilities(
LL_DEFAULT_ACC_LOG,
&LITERALS_LENGTH_DEFAULT_DISTRIBUTION,
)?;
scratch
.literal_lengths
.enrich_with_packed_seq_meta(&LL_META);
scratch.mark_ll_local();
}
}
ModeType::Repeat => {
vprintln!("Repeat ll table");
}
};
if matches!(ll_mode, ModeType::FSECompressed | ModeType::RLE) {
scratch.mark_ll_local();
}
let of_source = &source[bytes_read..];
let of_mode = modes.of_mode();
match of_mode {
ModeType::FSECompressed => {
let bytes = scratch.offsets.build_decoder_fused(
of_source,
OF_MAX_LOG,
crate::fse::SeqMeta::Offsets,
)?;
vprintln!("Updating of table");
vprintln!("Used bytes: {}", bytes);
bytes_read += bytes;
scratch.offsets_long_share = compute_offsets_long_share(&scratch.offsets);
}
ModeType::RLE => {
vprintln!("Use RLE of table");
if of_source.is_empty() {
return Err(DecodeSequenceError::MissingByteForRleOfTable);
}
bytes_read += 1;
if of_source[0] > MAX_OFFSET_CODE {
return Err(DecodeSequenceError::InvalidRleCode {
axis: "OF",
code: of_source[0],
});
}
scratch.offsets.build_rle(of_source[0]);
scratch.offsets.enrich_for_offsets();
scratch.offsets_long_share = compute_offsets_long_share(&scratch.offsets);
}
ModeType::Predefined => {
vprintln!("Use predefined of table");
#[cfg(feature = "std")]
{
scratch.mark_of_predefined(predefined_of_table().0);
scratch.offsets_long_share = PREDEFINED_OF_LONG_SHARE;
}
#[cfg(not(feature = "std"))]
{
scratch
.offsets
.build_from_probabilities(OF_DEFAULT_ACC_LOG, &OFFSET_DEFAULT_DISTRIBUTION)?;
scratch.offsets.enrich_for_offsets();
scratch.offsets_long_share = compute_offsets_long_share(&scratch.offsets);
scratch.mark_of_local();
}
}
ModeType::Repeat => {
vprintln!("Repeat of table");
}
};
if matches!(of_mode, ModeType::FSECompressed | ModeType::RLE) {
scratch.mark_of_local();
}
let ml_source = &source[bytes_read..];
let ml_mode = modes.ml_mode();
match ml_mode {
ModeType::FSECompressed => {
let bytes = scratch.match_lengths.build_decoder_fused(
ml_source,
ML_MAX_LOG,
crate::fse::SeqMeta::Packed(&ML_META),
)?;
bytes_read += bytes;
vprintln!("Updating ml table");
vprintln!("Used bytes: {}", bytes);
}
ModeType::RLE => {
vprintln!("Use RLE ml table");
if ml_source.is_empty() {
return Err(DecodeSequenceError::MissingByteForRleMlTable);
}
bytes_read += 1;
if ml_source[0] > MAX_MATCH_LENGTH_CODE {
return Err(DecodeSequenceError::InvalidRleCode {
axis: "ML",
code: ml_source[0],
});
}
scratch.match_lengths.build_rle(ml_source[0]);
scratch.match_lengths.enrich_with_packed_seq_meta(&ML_META);
}
ModeType::Predefined => {
vprintln!("Use predefined ml table");
#[cfg(feature = "std")]
{
scratch.mark_ml_predefined(predefined_ml_table());
}
#[cfg(not(feature = "std"))]
{
scratch.match_lengths.build_from_probabilities(
ML_DEFAULT_ACC_LOG,
&MATCH_LENGTH_DEFAULT_DISTRIBUTION,
)?;
scratch.match_lengths.enrich_with_packed_seq_meta(&ML_META);
scratch.mark_ml_local();
}
}
ModeType::Repeat => {
vprintln!("Repeat ml table");
}
};
if matches!(ml_mode, ModeType::FSECompressed | ModeType::RLE) {
scratch.mark_ml_local();
}
Ok(bytes_read)
}
const LL_DEFAULT_ACC_LOG: u8 = 6;
const LITERALS_LENGTH_DEFAULT_DISTRIBUTION: [i32; 36] = [
4, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 1, 1, 1, 1, 1,
-1, -1, -1, -1,
];
#[cfg(feature = "std")]
pub(crate) fn predefined_ll_table() -> &'static crate::fse::SeqFSETable {
use super::scratch::AlignedFSETable;
use std::sync::OnceLock;
static CACHED: OnceLock<AlignedFSETable> = OnceLock::new();
CACHED.get_or_init(|| {
let mut t = crate::fse::SeqFSETable::new(MAX_LITERAL_LENGTH_CODE);
t.build_from_probabilities(LL_DEFAULT_ACC_LOG, &LITERALS_LENGTH_DEFAULT_DISTRIBUTION)
.expect("LITERALS_LENGTH_DEFAULT_DISTRIBUTION is a static RFC 8878 constant");
t.enrich_with_packed_seq_meta(&LL_META);
t.into()
})
}
#[cfg(feature = "std")]
pub(crate) fn predefined_ml_table() -> &'static crate::fse::SeqFSETable {
use super::scratch::AlignedFSETable;
use std::sync::OnceLock;
static CACHED: OnceLock<AlignedFSETable> = OnceLock::new();
CACHED.get_or_init(|| {
let mut t = crate::fse::SeqFSETable::new(MAX_MATCH_LENGTH_CODE);
t.build_from_probabilities(ML_DEFAULT_ACC_LOG, &MATCH_LENGTH_DEFAULT_DISTRIBUTION)
.expect("MATCH_LENGTH_DEFAULT_DISTRIBUTION is a static RFC 8878 constant");
t.enrich_with_packed_seq_meta(&ML_META);
t.into()
})
}
#[cfg(feature = "std")]
pub(crate) fn predefined_of_table() -> (&'static crate::fse::SeqFSETable, u32) {
use super::scratch::AlignedFSETable;
use std::sync::OnceLock;
static CACHED: OnceLock<(AlignedFSETable, u32)> = OnceLock::new();
let cache = CACHED.get_or_init(|| {
let mut t = crate::fse::SeqFSETable::new(MAX_OFFSET_CODE);
t.build_from_probabilities(OF_DEFAULT_ACC_LOG, &OFFSET_DEFAULT_DISTRIBUTION)
.expect("OFFSET_DEFAULT_DISTRIBUTION is a static RFC 8878 constant");
t.enrich_for_offsets();
let share = compute_offsets_long_share(&t);
(t.into(), share)
});
(&cache.0, cache.1)
}
#[cfg(feature = "std")]
const PREDEFINED_OF_LONG_SHARE: u32 = 48;
const ML_DEFAULT_ACC_LOG: u8 = 6;
const MATCH_LENGTH_DEFAULT_DISTRIBUTION: [i32; 53] = [
1, 4, 3, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1,
];
const OF_DEFAULT_ACC_LOG: u8 = 5;
const OFFSET_DEFAULT_DISTRIBUTION: [i32; 29] = [
1, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1,
];
#[cfg(test)]
mod tests;