use crate::common::bits::ZSTD_highbit32;
pub use crate::common::fse_decompress::{FSE_MAX_MEMORY_USAGE, FSE_MAX_TABLELOG, FSE_MIN_TABLELOG};
pub const FSE_DEFAULT_MEMORY_USAGE: u32 = 13;
pub const FSE_DEFAULT_TABLELOG: u32 = FSE_DEFAULT_MEMORY_USAGE - 2;
pub const FSE_NCOUNTBOUND: usize = 512;
pub type FSE_CTable = u32;
pub fn FSE_NCountWriteBound(maxSymbolValue: u32, tableLog: u32) -> usize {
let maxHeaderSize = maxSymbolValue
.wrapping_add(1)
.wrapping_mul(tableLog)
.wrapping_add(4) .wrapping_add(2) .wrapping_div(8)
.wrapping_add(1) .wrapping_add(2) as usize; if maxSymbolValue != 0 {
maxHeaderSize
} else {
FSE_NCOUNTBOUND
}
}
fn FSE_minTableLog(srcSize: usize, maxSymbolValue: u32) -> u32 {
debug_assert!(srcSize > 1);
let minBitsSrc = ZSTD_highbit32(srcSize as u32) + 1;
let minBitsSymbols = ZSTD_highbit32(maxSymbolValue) + 2;
minBitsSrc.min(minBitsSymbols)
}
pub fn FSE_optimalTableLog_internal(
maxTableLog: u32,
srcSize: usize,
maxSymbolValue: u32,
minus: u32,
) -> u32 {
debug_assert!(srcSize > 1);
let maxBitsSrc = ZSTD_highbit32((srcSize as u32).wrapping_sub(1)).wrapping_sub(minus);
let mut tableLog = maxTableLog;
let minBits = FSE_minTableLog(srcSize, maxSymbolValue);
if tableLog == 0 {
tableLog = FSE_DEFAULT_TABLELOG;
}
if maxBitsSrc < tableLog {
tableLog = maxBitsSrc;
}
if minBits > tableLog {
tableLog = minBits;
}
tableLog.clamp(FSE_MIN_TABLELOG, FSE_MAX_TABLELOG)
}
pub fn FSE_optimalTableLog(maxTableLog: u32, srcSize: usize, maxSymbolValue: u32) -> u32 {
FSE_optimalTableLog_internal(maxTableLog, srcSize, maxSymbolValue, 2)
}
#[repr(C)]
#[derive(Debug, Clone, Copy, Default)]
pub struct FSE_symbolCompressionTransform {
pub deltaFindState: i32,
pub deltaNbBits: u32,
}
#[inline(always)]
pub fn FSE_getMaxNbBits(symbolTT: &[FSE_symbolCompressionTransform], symbolValue: u32) -> u32 {
(symbolTT[symbolValue as usize].deltaNbBits + ((1 << 16) - 1)) >> 16
}
#[inline(always)]
fn ct_header_write(ct: &mut [FSE_CTable], tableLog: u16, maxSymbolValue: u16) {
ct[0] = (tableLog as u32) | ((maxSymbolValue as u32) << 16);
}
#[inline(always)]
pub fn ct_header_tableLog(ct: &[FSE_CTable]) -> u32 {
ct[0] & 0xFFFF
}
#[inline(always)]
pub fn ct_header_maxSV(ct: &[FSE_CTable]) -> u32 {
(ct[0] >> 16) & 0xFFFF
}
#[inline(always)]
fn ct_u16_write(ct: &mut [FSE_CTable], i: usize, val: u16) {
let slot = 1 + (i / 2);
let prev = ct[slot];
if i % 2 == 0 {
ct[slot] = (prev & 0xFFFF_0000) | val as u32;
} else {
ct[slot] = (prev & 0x0000_FFFF) | ((val as u32) << 16);
}
}
#[inline(always)]
fn symbolTT_offset(tableLog: u32) -> usize {
let tableSize = 1usize << tableLog;
if tableLog == 0 {
2
} else {
1 + tableSize / 2
}
}
#[inline(always)]
fn symbolTT_write(
ct: &mut [FSE_CTable],
tableLog: u32,
s: usize,
t: FSE_symbolCompressionTransform,
) {
let base = symbolTT_offset(tableLog) + 2 * s;
ct[base] = t.deltaFindState as u32;
ct[base + 1] = t.deltaNbBits;
}
#[inline(always)]
pub fn symbolTT_read(ct: &[FSE_CTable], tableLog: u32, s: usize) -> FSE_symbolCompressionTransform {
let base = symbolTT_offset(tableLog) + 2 * s;
let pair: &[FSE_CTable; 2] = ct[base..base + 2]
.try_into()
.expect("symbolTT_read: 2 slots");
FSE_symbolCompressionTransform {
deltaFindState: pair[0] as i32,
deltaNbBits: pair[1],
}
}
#[inline(always)]
unsafe fn symbolTT_read_unchecked(
ct: &[FSE_CTable],
tableLog: u32,
s: usize,
) -> FSE_symbolCompressionTransform {
let base = symbolTT_offset(tableLog) + 2 * s;
let p0 = unsafe { *ct.get_unchecked(base) };
let p1 = unsafe { *ct.get_unchecked(base + 1) };
FSE_symbolCompressionTransform {
deltaFindState: p0 as i32,
deltaNbBits: p1,
}
}
#[inline(always)]
pub fn FSE_CTABLE_SIZE_U32(tableLog: u32, maxSymbolValue: u32) -> usize {
symbolTT_offset(tableLog) + 2 * (maxSymbolValue as usize + 1)
}
#[inline(always)]
pub const fn FSE_BUILD_CTABLE_WORKSPACE_SIZE_U32(maxSymbolValue: u32, tableLog: u32) -> usize {
(((maxSymbolValue as usize + 2) + (1usize << tableLog)) / 2)
+ core::mem::size_of::<u64>() / core::mem::size_of::<u32>()
}
#[inline(always)]
pub const fn FSE_BUILD_CTABLE_WORKSPACE_SIZE(maxSymbolValue: u32, tableLog: u32) -> usize {
core::mem::size_of::<u32>() * FSE_BUILD_CTABLE_WORKSPACE_SIZE_U32(maxSymbolValue, tableLog)
}
#[inline(always)]
fn wksp_u16_read(buf: &[u8], index: usize) -> u16 {
let offset = index * 2;
u16::from_ne_bytes([buf[offset], buf[offset + 1]])
}
#[inline(always)]
fn wksp_u16_write(buf: &mut [u8], index: usize, value: u16) {
let offset = index * 2;
let bytes = value.to_ne_bytes();
buf[offset] = bytes[0];
buf[offset + 1] = bytes[1];
}
pub fn FSE_buildCTable_rle(ct: &mut [FSE_CTable], symbolValue: u8) -> usize {
ct_header_write(ct, 0, symbolValue as u16);
if ct.len() >= 2 {
ct[1] = 0;
}
symbolTT_write(
ct,
0,
symbolValue as usize,
FSE_symbolCompressionTransform {
deltaNbBits: 0,
deltaFindState: 0,
},
);
0
}
pub fn FSE_bitCost(ct: &[FSE_CTable], tableLog: u32, symbolValue: u32, accuracyLog: u32) -> u32 {
let tt = symbolTT_read(ct, tableLog, symbolValue as usize);
let minNbBits = tt.deltaNbBits >> 16;
let threshold = (minNbBits + 1) << 16;
debug_assert!(tableLog < 16);
debug_assert!(accuracyLog < 31 - tableLog);
let tableSize = 1u32 << tableLog;
let deltaFromThreshold = threshold.wrapping_sub(tt.deltaNbBits.wrapping_add(tableSize));
let normalizedDeltaFromThreshold = (deltaFromThreshold << accuracyLog) >> tableLog;
let bitMultiplier = 1u32 << accuracyLog;
(minNbBits + 1) * bitMultiplier - normalizedDeltaFromThreshold
}
pub fn FSE_buildCTable_wksp(
ct: &mut [FSE_CTable],
normalizedCounter: &[i16],
maxSymbolValue: u32,
tableLog: u32,
workSpace: &mut [u8],
) -> usize {
use crate::common::error::{ErrorCode, ERROR};
use crate::common::fse_decompress::FSE_TABLESTEP;
let tableSize = 1u32 << tableLog;
let tableMask = (tableSize - 1) as usize;
let step = FSE_TABLESTEP(tableSize) as usize;
let maxSV1 = maxSymbolValue + 1;
let cumul_bytes = (maxSV1 as usize + 1) * 2;
let ts_bytes = tableSize as usize;
let layout_needed = cumul_bytes + ts_bytes + 8; let needed = FSE_BUILD_CTABLE_WORKSPACE_SIZE(maxSymbolValue, tableLog).max(layout_needed);
if workSpace.len() < needed {
return ERROR(ErrorCode::TableLogTooLarge);
}
let (cumul_bytes_slice, rest) = workSpace.split_at_mut(cumul_bytes);
let (tableSymbol, spread) = rest.split_at_mut(ts_bytes);
debug_assert!(tableLog < 16);
ct_header_write(ct, tableLog as u16, maxSymbolValue as u16);
let mut highThreshold: i64 = tableSize as i64 - 1;
wksp_u16_write(cumul_bytes_slice, 0, 0);
for u in 1..=maxSV1 as usize {
let nc = normalizedCounter[u - 1];
let prev = wksp_u16_read(cumul_bytes_slice, u - 1);
if nc == -1 {
wksp_u16_write(cumul_bytes_slice, u, prev + 1);
tableSymbol[highThreshold as usize] = (u - 1) as u8;
highThreshold -= 1;
} else {
debug_assert!(nc >= 0);
wksp_u16_write(cumul_bytes_slice, u, prev + nc as u16);
}
}
wksp_u16_write(cumul_bytes_slice, maxSV1 as usize, (tableSize + 1) as u16);
if highThreshold == tableSize as i64 - 1 {
let mut pos = 0usize;
for (s, &nc) in normalizedCounter.iter().enumerate().take(maxSV1 as usize) {
let n = nc as i32;
for i in 0..n as usize {
spread[pos + i] = s as u8;
}
if n > 0 {
pos += n as usize;
}
}
let unroll = 2usize;
let mut position = 0usize;
let mut s = 0usize;
while s < tableSize as usize {
for u in 0..unroll {
let uPos = (position + u * step) & tableMask;
tableSymbol[uPos] = spread[s + u];
}
position = (position + unroll * step) & tableMask;
s += unroll;
}
} else {
let mut position = 0usize;
for (symbol, &freq) in normalizedCounter.iter().enumerate().take(maxSV1 as usize) {
for _ in 0..freq {
tableSymbol[position] = symbol as u8;
position = (position + step) & tableMask;
while position as i64 > highThreshold {
position = (position + step) & tableMask;
}
}
}
}
for (u, &sym) in tableSymbol.iter().enumerate().take(tableSize as usize) {
let s = sym as usize;
let idx = wksp_u16_read(cumul_bytes_slice, s) as usize;
wksp_u16_write(cumul_bytes_slice, s, idx as u16 + 1);
ct_u16_write(ct, idx, (tableSize as usize + u) as u16);
}
let mut total: u32 = 0;
for (s, &nc) in normalizedCounter
.iter()
.enumerate()
.take(maxSymbolValue as usize + 1)
{
let t: FSE_symbolCompressionTransform = match nc {
0 => {
let dnb = ((tableLog + 1) << 16) - (1u32 << tableLog);
FSE_symbolCompressionTransform {
deltaNbBits: dnb,
deltaFindState: 0,
}
}
-1 | 1 => {
let dnb = (tableLog << 16) - (1u32 << tableLog);
let t = FSE_symbolCompressionTransform {
deltaNbBits: dnb,
deltaFindState: total as i32 - 1,
};
total += 1;
t
}
n if n > 1 => {
let nu = n as u32;
let maxBitsOut = tableLog - crate::common::bits::ZSTD_highbit32(nu - 1);
let minStatePlus = nu << maxBitsOut;
let dnb = (maxBitsOut << 16) - minStatePlus;
let t = FSE_symbolCompressionTransform {
deltaNbBits: dnb,
deltaFindState: total as i32 - nu as i32,
};
total += nu;
t
}
_ => unreachable!("normalizedCounter must be >= -1"),
};
symbolTT_write(ct, tableLog, s, t);
}
0
}
fn FSE_writeNCount_generic(
buffer: &mut [u8],
normalizedCounter: &[i16],
maxSymbolValue: u32,
tableLog: u32,
writeIsSafe: bool,
) -> usize {
use crate::common::error::{ErrorCode, ERROR};
let oend = buffer.len();
let mut out: usize = 0;
let tableSize = 1i32 << tableLog;
let mut bitStream: u32 = 0;
let mut bitCount: i32 = 0;
let mut symbol: u32 = 0;
let alphabetSize = maxSymbolValue + 1;
let mut previousIs0 = false;
bitStream = bitStream.wrapping_add((tableLog - FSE_MIN_TABLELOG) << bitCount);
bitCount += 4;
let mut remaining: i32 = tableSize + 1; let mut threshold: i32 = tableSize;
let mut nbBits: i32 = tableLog as i32 + 1;
let flush16 = |buffer: &mut [u8], out: &mut usize, bitStream: &mut u32| -> Option<usize> {
if !writeIsSafe && oend.saturating_sub(*out) < 2 {
return Some(ERROR(ErrorCode::DstSizeTooSmall));
}
buffer[*out] = *bitStream as u8;
buffer[*out + 1] = (*bitStream >> 8) as u8;
*out += 2;
*bitStream >>= 16;
None
};
while symbol < alphabetSize && remaining > 1 {
if previousIs0 {
let start_base = symbol;
while symbol < alphabetSize && normalizedCounter[symbol as usize] == 0 {
symbol += 1;
}
if symbol == alphabetSize {
break; }
let mut start = start_base;
while symbol >= start + 24 {
start += 24;
bitStream = bitStream.wrapping_add(0xFFFFu32 << bitCount);
if let Some(err) = flush16(buffer, &mut out, &mut bitStream) {
return err;
}
}
while symbol >= start + 3 {
start += 3;
bitStream = bitStream.wrapping_add(3u32 << bitCount);
bitCount += 2;
}
bitStream = bitStream.wrapping_add((symbol - start) << bitCount);
bitCount += 2;
if bitCount > 16 {
if let Some(err) = flush16(buffer, &mut out, &mut bitStream) {
return err;
}
bitCount -= 16;
}
}
{
let count_s = normalizedCounter[symbol as usize] as i32;
symbol += 1;
let max = (2 * threshold - 1) - remaining;
remaining -= if count_s < 0 { -count_s } else { count_s };
let mut count = count_s + 1; if count >= threshold {
count += max; }
bitStream = bitStream.wrapping_add((count as u32) << bitCount);
bitCount += nbBits;
bitCount -= (count < max) as i32;
previousIs0 = count == 1;
if remaining < 1 {
return ERROR(ErrorCode::Generic);
}
while remaining < threshold {
nbBits -= 1;
threshold >>= 1;
}
}
if bitCount > 16 {
if let Some(err) = flush16(buffer, &mut out, &mut bitStream) {
return err;
}
bitCount -= 16;
}
}
if remaining != 1 {
return ERROR(ErrorCode::Generic);
}
debug_assert!(symbol <= alphabetSize);
if !writeIsSafe && oend.saturating_sub(out) < 2 {
return ERROR(ErrorCode::DstSizeTooSmall);
}
buffer[out] = bitStream as u8;
buffer[out + 1] = (bitStream >> 8) as u8;
out += ((bitCount + 7) / 8) as usize;
out
}
pub fn FSE_writeNCount(
buffer: &mut [u8],
normalizedCounter: &[i16],
maxSymbolValue: u32,
tableLog: u32,
) -> usize {
use crate::common::error::{ErrorCode, ERROR};
if tableLog > FSE_MAX_TABLELOG {
return ERROR(ErrorCode::TableLogTooLarge);
}
if tableLog < FSE_MIN_TABLELOG {
return ERROR(ErrorCode::Generic);
}
let writeIsSafe = buffer.len() >= FSE_NCountWriteBound(maxSymbolValue, tableLog);
FSE_writeNCount_generic(
buffer,
normalizedCounter,
maxSymbolValue,
tableLog,
writeIsSafe,
)
}
#[derive(Debug, Clone, Copy)]
pub struct FSE_CState_t {
pub value: isize,
pub stateLog: u32,
}
#[inline(always)]
#[allow(dead_code)]
fn ct_u16_read(ct: &[FSE_CTable], i: usize) -> u16 {
let slot = 1 + (i / 2);
let w = ct[slot];
if i % 2 == 0 {
w as u16
} else {
(w >> 16) as u16
}
}
#[inline(always)]
unsafe fn ct_u16_read_unchecked(ct: &[FSE_CTable], i: usize) -> u16 {
let slot = 1 + (i / 2);
let w = unsafe { *ct.get_unchecked(slot) };
if i % 2 == 0 {
w as u16
} else {
(w >> 16) as u16
}
}
#[inline(always)]
pub fn FSE_initCState(st: &mut FSE_CState_t, ct: &[FSE_CTable]) {
let tableLog = ct_header_tableLog(ct);
st.value = 1isize << tableLog;
st.stateLog = tableLog;
}
#[inline(always)]
pub fn FSE_initCState2(st: &mut FSE_CState_t, ct: &[FSE_CTable], symbol: u32) {
FSE_initCState(st, ct);
let tableLog = st.stateLog;
let tt = unsafe { symbolTT_read_unchecked(ct, tableLog, symbol as usize) };
let nbBitsOut = (tt.deltaNbBits + (1 << 15)) >> 16;
let v = ((nbBitsOut as isize) << 16) - (tt.deltaNbBits as isize);
let idx = ((v >> nbBitsOut) + tt.deltaFindState as isize) as usize;
st.value = unsafe { ct_u16_read_unchecked(ct, idx) } as isize;
}
#[inline(always)]
pub fn FSE_encodeSymbol(
bitC: &mut crate::common::bitstream::BIT_CStream_t,
st: &mut FSE_CState_t,
ct: &[FSE_CTable],
symbol: u32,
) {
let tableLog = st.stateLog;
let tt = unsafe { symbolTT_read_unchecked(ct, tableLog, symbol as usize) };
let nbBitsOut = ((st.value as isize + tt.deltaNbBits as isize) >> 16) as u32;
crate::common::bitstream::BIT_addBits(bitC, st.value as usize, nbBitsOut);
let idx = ((st.value >> nbBitsOut) + tt.deltaFindState as isize) as usize;
st.value = unsafe { ct_u16_read_unchecked(ct, idx) } as isize;
}
#[inline(always)]
pub fn FSE_flushCState(bitC: &mut crate::common::bitstream::BIT_CStream_t, st: &FSE_CState_t) {
crate::common::bitstream::BIT_addBits(bitC, st.value as usize, st.stateLog);
crate::common::bitstream::BIT_flushBits(bitC);
}
fn FSE_compress_usingCTable_generic<const FAST: bool>(
dst: &mut [u8],
src: &[u8],
ct: &[FSE_CTable],
) -> usize {
use crate::common::bitstream::{
BIT_closeCStream, BIT_flushBits, BIT_flushBitsFast, BIT_initCStream,
};
let srcSize = src.len();
if srcSize <= 2 {
return 0;
}
let dst_cap = dst.len();
let (mut bitC, init_err) = BIT_initCStream(dst, dst_cap);
if crate::common::error::ERR_isError(init_err) {
return 0;
}
let container_bits = (core::mem::size_of::<usize>() as u32) * 8;
let mut ip = srcSize; let mut state1 = FSE_CState_t {
value: 0,
stateLog: 0,
};
let mut state2 = FSE_CState_t {
value: 0,
stateLog: 0,
};
if srcSize % 2 == 0 {
ip -= 1;
FSE_initCState2(&mut state2, ct, src[ip] as u32);
ip -= 1;
FSE_initCState2(&mut state1, ct, src[ip] as u32);
} else {
ip -= 1;
FSE_initCState2(&mut state1, ct, src[ip] as u32);
ip -= 1;
FSE_initCState2(&mut state2, ct, src[ip] as u32);
ip -= 1;
FSE_encodeSymbol(&mut bitC, &mut state1, ct, src[ip] as u32);
if FAST {
BIT_flushBitsFast(&mut bitC);
} else {
BIT_flushBits(&mut bitC);
}
}
let mut remaining = srcSize - 2;
if container_bits > FSE_MAX_TABLELOG * 4 + 7 && remaining & 2 != 0 {
ip -= 1;
FSE_encodeSymbol(&mut bitC, &mut state2, ct, src[ip] as u32);
ip -= 1;
FSE_encodeSymbol(&mut bitC, &mut state1, ct, src[ip] as u32);
if FAST {
BIT_flushBitsFast(&mut bitC);
} else {
BIT_flushBits(&mut bitC);
}
remaining -= 2;
}
while ip > 0 {
ip -= 1;
FSE_encodeSymbol(&mut bitC, &mut state2, ct, src[ip] as u32);
if container_bits < FSE_MAX_TABLELOG * 2 + 7 {
if FAST {
BIT_flushBitsFast(&mut bitC);
} else {
BIT_flushBits(&mut bitC);
}
}
ip -= 1;
FSE_encodeSymbol(&mut bitC, &mut state1, ct, src[ip] as u32);
if container_bits > FSE_MAX_TABLELOG * 4 + 7 {
ip -= 1;
FSE_encodeSymbol(&mut bitC, &mut state2, ct, src[ip] as u32);
ip -= 1;
FSE_encodeSymbol(&mut bitC, &mut state1, ct, src[ip] as u32);
}
if FAST {
BIT_flushBitsFast(&mut bitC);
} else {
BIT_flushBits(&mut bitC);
}
}
FSE_flushCState(&mut bitC, &state2);
FSE_flushCState(&mut bitC, &state1);
let _ = remaining;
BIT_closeCStream(&mut bitC)
}
pub fn FSE_compress_usingCTable(dst: &mut [u8], src: &[u8], ct: &[FSE_CTable]) -> usize {
let fast = dst.len() >= FSE_BLOCKBOUND(src.len());
if fast {
FSE_compress_usingCTable_generic::<true>(dst, src, ct)
} else {
FSE_compress_usingCTable_generic::<false>(dst, src, ct)
}
}
#[inline(always)]
pub const fn FSE_BLOCKBOUND(size: usize) -> usize {
size + (size >> 7) + 4 + core::mem::size_of::<usize>()
}
#[inline(always)]
pub const fn FSE_COMPRESSBOUND(size: usize) -> usize {
FSE_NCOUNTBOUND + FSE_BLOCKBOUND(size)
}
#[inline(always)]
pub fn FSE_compressBound(srcSize: usize) -> usize {
FSE_COMPRESSBOUND(srcSize)
}
const RTB_TABLE: [u32; 8] = [0, 473195, 504333, 520860, 550000, 700000, 750000, 830000];
fn FSE_normalizeM2(
norm: &mut [i16],
tableLog: u32,
count: &[u32],
mut total: usize,
maxSymbolValue: u32,
lowProbCount: i16,
) -> usize {
const NOT_YET_ASSIGNED: i16 = -2;
let lowThreshold = (total >> tableLog) as u32;
let mut lowOne = (total.wrapping_mul(3) >> (tableLog + 1)) as u32;
let mut distributed: u32 = 0;
for s in 0..=maxSymbolValue as usize {
if count[s] == 0 {
norm[s] = 0;
continue;
}
if count[s] <= lowThreshold {
norm[s] = lowProbCount;
distributed += 1;
total -= count[s] as usize;
continue;
}
if count[s] <= lowOne {
norm[s] = 1;
distributed += 1;
total -= count[s] as usize;
continue;
}
norm[s] = NOT_YET_ASSIGNED;
}
let mut ToDistribute = (1u32 << tableLog) - distributed;
if ToDistribute == 0 {
return 0;
}
if total / ToDistribute as usize > lowOne as usize {
lowOne = (total.wrapping_mul(3) / ToDistribute.wrapping_mul(2) as usize) as u32;
for s in 0..=maxSymbolValue as usize {
if norm[s] == NOT_YET_ASSIGNED && count[s] <= lowOne {
norm[s] = 1;
distributed += 1;
total -= count[s] as usize;
}
}
ToDistribute = (1u32 << tableLog) - distributed;
}
if distributed == maxSymbolValue + 1 {
let mut maxV = 0usize;
let mut maxC = 0u32;
for (s, &c) in count.iter().enumerate().take(maxSymbolValue as usize + 1) {
if c > maxC {
maxV = s;
maxC = c;
}
}
norm[maxV] += ToDistribute as i16;
return 0;
}
if total == 0 {
let alphabet = (maxSymbolValue + 1) as usize;
let mut s = 0usize;
while ToDistribute > 0 {
if norm[s] > 0 {
ToDistribute -= 1;
norm[s] += 1;
}
s = (s + 1) % alphabet;
}
return 0;
}
let vStepLog = 62u32 - tableLog;
let mid = (1u64 << (vStepLog - 1)) - 1;
let rStep = ((1u64 << vStepLog) * ToDistribute as u64 + mid) / ((total as u32) as u64);
let mut tmpTotal = mid;
for s in 0..=maxSymbolValue as usize {
if norm[s] == NOT_YET_ASSIGNED {
let end = tmpTotal.wrapping_add((count[s] as u64).wrapping_mul(rStep));
let sStart = (tmpTotal >> vStepLog) as u32;
let sEnd = (end >> vStepLog) as u32;
let weight = sEnd - sStart;
if weight < 1 {
return crate::common::error::ERROR(crate::common::error::ErrorCode::Generic);
}
norm[s] = weight as i16;
tmpTotal = end;
}
}
0
}
pub fn FSE_normalizeCount(
normalizedCounter: &mut [i16],
mut tableLog: u32,
count: &[u32],
total: usize,
maxSymbolValue: u32,
useLowProbCount: u32,
) -> usize {
use crate::common::error::{ErrorCode, ERROR};
if tableLog == 0 {
tableLog = FSE_DEFAULT_TABLELOG;
}
if tableLog < FSE_MIN_TABLELOG {
return ERROR(ErrorCode::Generic);
}
if tableLog > FSE_MAX_TABLELOG {
return ERROR(ErrorCode::TableLogTooLarge);
}
if tableLog < FSE_minTableLog(total, maxSymbolValue) {
return ERROR(ErrorCode::Generic);
}
let lowProbCount: i16 = if useLowProbCount != 0 { -1 } else { 1 };
let scale = 62u32 - tableLog;
let step = (1u64 << 62) / ((total as u32) as u64);
let vStep = 1u64 << (scale - 20);
let mut stillToDistribute: i32 = 1 << tableLog;
let mut largest: u32 = 0;
let mut largestP: i16 = 0;
let lowThreshold = (total >> tableLog) as u32;
for s in 0..=maxSymbolValue as usize {
if count[s] as usize == total {
return 0; }
if count[s] == 0 {
normalizedCounter[s] = 0;
continue;
}
if count[s] <= lowThreshold {
normalizedCounter[s] = lowProbCount;
stillToDistribute -= 1;
} else {
let scaled = (count[s] as u64).wrapping_mul(step);
let mut proba = (scaled >> scale) as i16;
if proba < 8 {
let restToBeat = vStep * RTB_TABLE[proba as usize] as u64;
let excess = scaled.wrapping_sub((proba as u64) << scale);
if excess > restToBeat {
proba += 1;
}
}
if proba > largestP {
largestP = proba;
largest = s as u32;
}
normalizedCounter[s] = proba;
stillToDistribute -= proba as i32;
}
}
if -stillToDistribute >= (normalizedCounter[largest as usize] >> 1) as i32 {
let rc = FSE_normalizeM2(
normalizedCounter,
tableLog,
count,
total,
maxSymbolValue,
lowProbCount,
);
if crate::common::error::ERR_isError(rc) {
return rc;
}
} else {
normalizedCounter[largest as usize] += stillToDistribute as i16;
}
tableLog as usize
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn optimal_tablelog_clamps_to_min_and_max() {
assert!(FSE_optimalTableLog(0, 32, 10) >= FSE_MIN_TABLELOG);
assert!(FSE_optimalTableLog(99, 10_000, 255) <= FSE_MAX_TABLELOG);
}
#[test]
fn optimal_tablelog_zero_means_default() {
assert_eq!(FSE_optimalTableLog(0, 65536, 255), FSE_DEFAULT_TABLELOG);
}
#[test]
fn optimal_tablelog_internal_wraps_minus_like_upstream_u32() {
assert_eq!(FSE_optimalTableLog(0, 2, 255), FSE_DEFAULT_TABLELOG);
assert_eq!(
FSE_optimalTableLog_internal(FSE_MAX_TABLELOG, 2, 255, 5),
FSE_MAX_TABLELOG
);
}
#[test]
fn ncount_write_bound_zero_maxsymbol_is_default() {
assert_eq!(FSE_NCountWriteBound(0, 8), FSE_NCOUNTBOUND);
}
#[test]
fn ncount_write_bound_scales_with_params() {
let small = FSE_NCountWriteBound(31, 6);
let large = FSE_NCountWriteBound(255, 12);
assert!(large > small);
assert_eq!(small, 27);
}
#[test]
fn write_ncount_rejects_tiny_output_buffer() {
let norm: [i16; 2] = [16, 16];
let mut dst = [0u8; 1];
let rc = FSE_writeNCount(&mut dst, &norm, 1, FSE_MIN_TABLELOG);
assert_eq!(
crate::common::error::ERR_getErrorCode(rc),
crate::common::error::ErrorCode::DstSizeTooSmall
);
}
#[test]
fn normalize_count_sum_equals_table_size() {
let count = [100u32, 200, 300, 150, 150, 100];
let mut norm = [0i16; 16];
let total: usize = count.iter().map(|&c| c as usize).sum();
let rc = FSE_normalizeCount(&mut norm, 8, &count, total, 5, 0);
assert!(
!crate::common::error::ERR_isError(rc),
"err: {}",
crate::common::error::ERR_getErrorName(rc)
);
assert_eq!(rc, 8); let sum: i32 = norm
.iter()
.take(6)
.map(|&n| if n < 0 { 1 } else { n as i32 })
.sum();
assert_eq!(sum, 1 << 8, "normalized counts must sum to table size");
}
#[test]
fn normalize_count_rle_returns_zero() {
let mut count = [0u32; 8];
count[3] = 500;
let mut norm = [0i16; 8];
let rc = FSE_normalizeCount(&mut norm, 6, &count, 500, 7, 0);
assert_eq!(rc, 0);
}
#[test]
fn normalize_count_uses_wrapping_u64_products() {
let count = [u32::MAX, 2];
let total = count.iter().map(|&c| c as usize).sum();
let mut norm = [0i16; 2];
let _ = FSE_normalizeCount(&mut norm, FSE_MIN_TABLELOG, &count, total, 1, 0);
}
#[test]
fn normalize_count_rejects_tablelog_too_small_for_alphabet() {
let count = [10u32, 10, 10, 10, 10, 10];
let mut norm = [0i16; 8];
let rc = FSE_normalizeCount(&mut norm, 4, &count, 60, 5, 0);
assert!(crate::common::error::ERR_isError(rc));
}
#[test]
fn FSE_DTABLE_SIZE_U32_and_WKSP_SIZE_match_upstream_formula() {
use crate::common::fse_decompress::{FSE_BUILD_DTABLE_WKSP_SIZE, FSE_DTABLE_SIZE_U32};
for tl in [5u32, 9, 12] {
assert_eq!(FSE_DTABLE_SIZE_U32(tl), 1 + (1 << tl));
}
for (tl, msv) in [(6u32, 35u32), (9, 35), (12, 255)] {
let expected = 2 * (msv as usize + 1) + (1usize << tl) + 8;
assert_eq!(FSE_BUILD_DTABLE_WKSP_SIZE(tl, msv), expected);
}
}
#[test]
fn FSE_CTABLE_SIZE_U32_matches_upstream_formula() {
for (tl, max_sym) in [(6u32, 35u32), (9, 35), (11, 255), (12, 255)] {
let expected: usize = 1 + (1usize << (tl - 1)) + (max_sym as usize + 1) * 2;
assert_eq!(
FSE_CTABLE_SIZE_U32(tl, max_sym),
expected,
"FSE_CTABLE_SIZE_U32({tl}, {max_sym})",
);
}
assert_eq!(FSE_CTABLE_SIZE_U32(0, 0), 4);
assert_eq!(FSE_CTABLE_SIZE_U32(0, 255), 2 + 2 * 256);
}
#[test]
fn FSE_BUILD_CTABLE_WORKSPACE_SIZE_matches_upstream_formula() {
for (tl, max_sym) in [(6u32, 35u32), (9, 35), (11, 255), (12, 255)] {
let expected_u32 = (((max_sym as usize + 2) + (1usize << tl)) / 2) + 2;
assert_eq!(
FSE_BUILD_CTABLE_WORKSPACE_SIZE_U32(max_sym, tl),
expected_u32,
"FSE_BUILD_CTABLE_WORKSPACE_SIZE_U32({max_sym}, {tl})",
);
assert_eq!(
FSE_BUILD_CTABLE_WORKSPACE_SIZE(max_sym, tl),
expected_u32 * core::mem::size_of::<u32>(),
"FSE_BUILD_CTABLE_WORKSPACE_SIZE({max_sym}, {tl})",
);
}
}
#[test]
fn fse_memory_and_tablelog_constants_match_upstream() {
assert_eq!(FSE_MAX_MEMORY_USAGE, 14);
assert_eq!(FSE_DEFAULT_MEMORY_USAGE, 13);
assert_eq!(FSE_MAX_TABLELOG, 12);
assert_eq!(FSE_DEFAULT_TABLELOG, 11);
assert_eq!(FSE_NCOUNTBOUND, 512);
assert_eq!(FSE_MAX_TABLELOG, FSE_MAX_MEMORY_USAGE - 2);
assert_eq!(FSE_DEFAULT_TABLELOG, FSE_DEFAULT_MEMORY_USAGE - 2);
assert_eq!(FSE_MIN_TABLELOG, 5);
assert_eq!(crate::common::fse_decompress::FSE_TABLELOG_ABSOLUTE_MAX, 15);
const _: () =
assert!(FSE_MAX_TABLELOG <= crate::common::fse_decompress::FSE_TABLELOG_ABSOLUTE_MAX);
}
#[test]
fn compress_bound_matches_formula() {
let bound = FSE_compressBound(1024);
let expected = FSE_NCOUNTBOUND + 1024 + (1024 >> 7) + 4 + core::mem::size_of::<usize>();
assert_eq!(bound, expected);
}
#[test]
fn ncount_write_then_read_roundtrip() {
use crate::common::entropy_common::FSE_readNCount;
let count = [100u32, 200, 300, 150, 150, 100];
let total: usize = count.iter().map(|&c| c as usize).sum();
let mut norm = [0i16; 16];
let tableLog = FSE_normalizeCount(&mut norm, 8, &count, total, 5, 0);
assert!(!crate::common::error::ERR_isError(tableLog));
assert_eq!(tableLog, 8);
let mut buf = vec![0u8; FSE_NCountWriteBound(5, tableLog as u32)];
let written = FSE_writeNCount(&mut buf, &norm, 5, tableLog as u32);
assert!(
!crate::common::error::ERR_isError(written),
"writeNCount err: {}",
crate::common::error::ERR_getErrorName(written)
);
assert!(written <= buf.len());
let mut norm_out = [0i16; 16];
let mut msv_out: u32 = 5;
let mut tl_out: u32 = 0;
let consumed = FSE_readNCount(&mut norm_out, &mut msv_out, &mut tl_out, &buf[..written]);
assert!(
!crate::common::error::ERR_isError(consumed),
"readNCount err: {}",
crate::common::error::ERR_getErrorName(consumed)
);
assert_eq!(tl_out, tableLog as u32);
assert_eq!(msv_out, 5);
assert_eq!(&norm_out[..=5], &norm[..=5]);
}
#[test]
fn fse_compress_then_decompress_roundtrip() {
use crate::common::fse_decompress::{
FSE_DTable, FSE_buildDTable_wksp, FSE_decompress_usingDTable,
FSE_BUILD_DTABLE_WKSP_SIZE, FSE_DTABLE_SIZE_U32,
};
let src: Vec<u8> = b"ababcabcabaaaaabcbcbcbaaaa".to_vec();
let mut count = [0u32; 256];
let mut msv: u32 = 255;
let largest = crate::compress::hist::HIST_count_simple(&mut count, &mut msv, &src);
assert!(largest > 0);
let mut norm = [0i16; 256];
let tableLog = FSE_optimalTableLog(0, src.len(), msv);
let rc = FSE_normalizeCount(&mut norm, tableLog, &count, src.len(), msv, 0);
assert!(
!crate::common::error::ERR_isError(rc) && rc != 0,
"normalize returned {rc} (expected nonzero tableLog)"
);
assert_eq!(rc, tableLog as usize);
let ct_size = FSE_CTABLE_SIZE_U32(tableLog, msv);
let mut ct = vec![0u32; ct_size];
let build_ws = vec![0u8; FSE_BUILD_CTABLE_WORKSPACE_SIZE(msv, tableLog)];
let mut build_ws = build_ws;
let rc = FSE_buildCTable_wksp(&mut ct, &norm, msv, tableLog, &mut build_ws);
assert_eq!(rc, 0);
let mut dst = vec![0u8; FSE_compressBound(src.len())];
let written = FSE_compress_usingCTable(&mut dst, &src, &ct);
assert!(
!crate::common::error::ERR_isError(written),
"compress err: {}",
crate::common::error::ERR_getErrorName(written)
);
assert!(written > 0, "empty output");
assert!(
written < src.len(),
"no compression (output {written} >= input {})",
src.len()
);
let dt_size = FSE_DTABLE_SIZE_U32(tableLog);
let mut dt = vec![0u32; dt_size];
let mut dt_ws = vec![0u8; FSE_BUILD_DTABLE_WKSP_SIZE(tableLog, msv)];
let rc = FSE_buildDTable_wksp(
&mut dt as &mut [FSE_DTable],
&norm,
msv,
tableLog,
&mut dt_ws,
);
assert_eq!(rc, 0, "buildDTable err");
let mut out = vec![0u8; src.len()];
let decoded = FSE_decompress_usingDTable(&mut out, &dst[..written], &dt);
assert!(
!crate::common::error::ERR_isError(decoded),
"decode err: {}",
crate::common::error::ERR_getErrorName(decoded)
);
assert_eq!(decoded, src.len(), "decoded length mismatch");
assert_eq!(&out[..decoded], &src[..]);
}
#[test]
fn build_ctable_wksp_writes_header_and_returns_ok() {
let count = [100u32, 200, 300, 150, 150, 100];
let mut norm = [0i16; 16];
let total: usize = count.iter().map(|&c| c as usize).sum();
let tableLog = FSE_normalizeCount(&mut norm, 8, &count, total, 5, 0) as u32;
assert!(!crate::common::error::ERR_isError(tableLog as usize));
let max_sv = 5u32;
let ct_size = FSE_CTABLE_SIZE_U32(tableLog, max_sv);
let mut ct = vec![0u32; ct_size];
let ws_size = FSE_BUILD_CTABLE_WORKSPACE_SIZE(max_sv, tableLog);
let mut wksp = vec![0u8; ws_size];
let rc = FSE_buildCTable_wksp(&mut ct, &norm, max_sv, tableLog, &mut wksp);
assert_eq!(rc, 0, "err: {}", crate::common::error::ERR_getErrorName(rc));
assert_eq!(ct_header_tableLog(&ct), tableLog);
assert_eq!(ct_header_maxSV(&ct), max_sv);
for (s, &n) in norm.iter().enumerate().take(max_sv as usize + 1) {
let t = symbolTT_read(&ct, tableLog, s);
if n == 0 {
let expected = ((tableLog + 1) << 16) - (1u32 << tableLog);
assert_eq!(
t.deltaNbBits, expected,
"zero-count symbol {s} has unexpected deltaNbBits"
);
}
}
}
#[test]
fn optimal_tablelog_internal_uses_minus_slack() {
let a = FSE_optimalTableLog_internal(FSE_MAX_TABLELOG, 2048, 255, 0);
let b = FSE_optimalTableLog_internal(FSE_MAX_TABLELOG, 2048, 255, 5);
assert!(a >= b);
}
#[test]
fn FSE_bitCost_ranks_high_freq_symbols_cheaper_than_low_freq() {
const TABLELOG: u32 = 8;
const MSV: u32 = 7;
const ACCURACY: u32 = 8;
let norm: [i16; 8] = [128, 64, 32, 16, 8, 4, 2, 2];
let mut ct = vec![0u32; FSE_CTABLE_SIZE_U32(TABLELOG, MSV)];
let mut ws = vec![0u8; FSE_BUILD_CTABLE_WORKSPACE_SIZE(MSV, TABLELOG)];
let rc = FSE_buildCTable_wksp(&mut ct, &norm, MSV, TABLELOG, &mut ws);
assert_eq!(rc, 0, "buildCTable failed: {rc:#x}");
let cost_sym0 = FSE_bitCost(&ct, TABLELOG, 0, ACCURACY);
let cost_sym7 = FSE_bitCost(&ct, TABLELOG, 7, ACCURACY);
assert!(
cost_sym0 < cost_sym7,
"expected sym0 (freq 128) cheaper than sym7 (freq 2), got {cost_sym0} vs {cost_sym7}",
);
let costs: Vec<u32> = (0..=7)
.map(|s| FSE_bitCost(&ct, TABLELOG, s, ACCURACY))
.collect();
for w in costs.windows(2) {
assert!(
w[0] <= w[1],
"cost non-monotonic across freq-ordered symbols: {costs:?}"
);
}
}
#[test]
fn buildCTable_rle_stores_single_symbol_with_zero_bitcost() {
const SYM: u8 = 0x42;
let mut ct = vec![0u32; FSE_CTABLE_SIZE_U32(0, SYM as u32)];
let rc = FSE_buildCTable_rle(&mut ct, SYM);
assert_eq!(rc, 0);
assert_eq!(ct_header_tableLog(&ct), 0);
assert_eq!(ct_header_maxSV(&ct), SYM as u32);
let tt = symbolTT_read(&ct, 0, SYM as usize);
assert_eq!(tt.deltaNbBits, 0);
assert_eq!(tt.deltaFindState, 0);
}
}