use crate::common::bits::ZSTD_highbit32;
use crate::common::error::{ErrorCode, ERROR};
pub const BIT_DStream_unfinished: u32 = 0;
pub const BIT_DStream_endOfBuffer: u32 = 1;
pub const BIT_DStream_completed: u32 = 2;
pub const BIT_DStream_overflow: u32 = 3;
const CONTAINER_BYTES: usize = core::mem::size_of::<usize>();
const CONTAINER_BITS: u32 = (CONTAINER_BYTES as u32) * 8;
static BIT_DSTREAM_ZERO_FILLED: [u8; CONTAINER_BYTES] = [0; CONTAINER_BYTES];
#[inline(always)]
fn BIT_readContainer(src: &[u8], ptr: usize) -> usize {
debug_assert!(ptr + CONTAINER_BYTES <= src.len());
unsafe {
let p = src.as_ptr().add(ptr);
if core::mem::size_of::<usize>() == 8 {
u64::from_le((p as *const u64).read_unaligned()) as usize
} else {
u32::from_le((p as *const u32).read_unaligned()) as usize
}
}
}
#[derive(Debug)]
pub struct BIT_CStream_t<'a> {
pub bitContainer: usize,
pub bitPos: u32,
pub startPtr: usize,
pub ptr: usize,
pub endPtr: usize,
pub dst: &'a mut [u8],
}
pub fn BIT_initCStream<'a>(
startPtr: &'a mut [u8],
dstCapacity: usize,
) -> (BIT_CStream_t<'a>, usize) {
let (endPtr, rc) = if dstCapacity <= CONTAINER_BYTES {
(0, ERROR(ErrorCode::DstSizeTooSmall))
} else {
(dstCapacity - CONTAINER_BYTES, 0)
};
let bitC = BIT_CStream_t {
bitContainer: 0,
bitPos: 0,
startPtr: 0,
ptr: 0,
endPtr,
dst: startPtr,
};
(bitC, rc)
}
#[inline(always)]
pub fn BIT_addBits(bitC: &mut BIT_CStream_t, value: usize, nbBits: u32) {
debug_assert!(nbBits < 32);
debug_assert!(nbBits + bitC.bitPos < CONTAINER_BITS);
bitC.bitContainer |= BIT_getLowerBits(value, nbBits) << bitC.bitPos;
bitC.bitPos += nbBits;
}
#[inline(always)]
pub fn BIT_addBitsFast(bitC: &mut BIT_CStream_t, value: usize, nbBits: u32) {
debug_assert!(value >> nbBits == 0);
debug_assert!(nbBits + bitC.bitPos < CONTAINER_BITS);
bitC.bitContainer |= value << bitC.bitPos;
bitC.bitPos += nbBits;
}
#[inline(always)]
pub fn BIT_flushBits(bitC: &mut BIT_CStream_t) {
let nbBytes = (bitC.bitPos >> 3) as usize;
debug_assert!(bitC.bitPos < CONTAINER_BITS);
debug_assert!(bitC.ptr <= bitC.endPtr);
debug_assert!(bitC.endPtr + CONTAINER_BYTES <= bitC.dst.len());
unsafe {
let p = bitC.dst.as_mut_ptr().add(bitC.ptr);
if core::mem::size_of::<usize>() == 8 {
(p as *mut u64).write_unaligned((bitC.bitContainer as u64).to_le());
} else {
(p as *mut u32).write_unaligned((bitC.bitContainer as u32).to_le());
}
}
bitC.ptr += nbBytes;
if bitC.ptr > bitC.endPtr {
bitC.ptr = bitC.endPtr;
}
bitC.bitPos &= 7;
if nbBytes * 8 >= CONTAINER_BITS as usize {
bitC.bitContainer = 0;
} else {
bitC.bitContainer >>= nbBytes * 8;
}
}
#[inline(always)]
pub fn BIT_flushBitsFast(bitC: &mut BIT_CStream_t) {
let nbBytes = (bitC.bitPos >> 3) as usize;
debug_assert!(bitC.bitPos < CONTAINER_BITS);
debug_assert!(bitC.ptr <= bitC.endPtr);
debug_assert!(bitC.endPtr + CONTAINER_BYTES <= bitC.dst.len());
unsafe {
let p = bitC.dst.as_mut_ptr().add(bitC.ptr);
if core::mem::size_of::<usize>() == 8 {
(p as *mut u64).write_unaligned((bitC.bitContainer as u64).to_le());
} else {
(p as *mut u32).write_unaligned((bitC.bitContainer as u32).to_le());
}
}
bitC.ptr += nbBytes;
bitC.bitPos &= 7;
if nbBytes * 8 >= CONTAINER_BITS as usize {
bitC.bitContainer = 0;
} else {
bitC.bitContainer >>= nbBytes * 8;
}
}
pub fn BIT_closeCStream(bitC: &mut BIT_CStream_t) -> usize {
BIT_addBitsFast(bitC, 1, 1); BIT_flushBits(bitC);
if bitC.ptr >= bitC.endPtr {
return 0;
}
(bitC.ptr - bitC.startPtr) + (bitC.bitPos > 0) as usize
}
#[derive(Debug, Clone)]
pub struct BIT_DStream_t<'a> {
pub bitContainer: usize,
pub bitsConsumed: u32,
pub ptr: usize,
pub start: usize,
pub limitPtr: usize,
pub src: &'a [u8],
}
#[allow(clippy::derivable_impls)]
impl Default for BIT_DStream_t<'_> {
fn default() -> Self {
Self {
bitContainer: 0,
bitsConsumed: 0,
ptr: 0,
start: 0,
limitPtr: 0,
src: &[],
}
}
}
pub fn BIT_initDStream<'a>(
bitD: &mut BIT_DStream_t<'a>,
srcBuffer: &'a [u8],
srcSize: usize,
) -> usize {
if srcSize < 1 {
*bitD = BIT_DStream_t::default();
return ERROR(ErrorCode::SrcSizeWrong);
}
let src = &srcBuffer[..srcSize];
bitD.src = src;
bitD.start = 0;
bitD.limitPtr = CONTAINER_BYTES;
if srcSize >= CONTAINER_BYTES {
bitD.ptr = srcSize - CONTAINER_BYTES;
bitD.bitContainer = BIT_readContainer(src, bitD.ptr);
let lastByte = src[srcSize - 1];
bitD.bitsConsumed = if lastByte != 0 {
8 - ZSTD_highbit32(lastByte as u32)
} else {
0
};
if lastByte == 0 {
return ERROR(ErrorCode::Generic);
}
} else {
bitD.ptr = 0;
let mut c = src[0] as usize;
match srcSize {
7 => {
c += (src[6] as usize) << (CONTAINER_BITS as usize - 16);
c += (src[5] as usize) << (CONTAINER_BITS as usize - 24);
c += (src[4] as usize) << (CONTAINER_BITS as usize - 32);
c += (src[3] as usize) << 24;
c += (src[2] as usize) << 16;
c += (src[1] as usize) << 8;
}
6 => {
c += (src[5] as usize) << (CONTAINER_BITS as usize - 24);
c += (src[4] as usize) << (CONTAINER_BITS as usize - 32);
c += (src[3] as usize) << 24;
c += (src[2] as usize) << 16;
c += (src[1] as usize) << 8;
}
5 => {
c += (src[4] as usize) << (CONTAINER_BITS as usize - 32);
c += (src[3] as usize) << 24;
c += (src[2] as usize) << 16;
c += (src[1] as usize) << 8;
}
4 => {
c += (src[3] as usize) << 24;
c += (src[2] as usize) << 16;
c += (src[1] as usize) << 8;
}
3 => {
c += (src[2] as usize) << 16;
c += (src[1] as usize) << 8;
}
2 => {
c += (src[1] as usize) << 8;
}
_ => {}
}
bitD.bitContainer = c;
let lastByte = src[srcSize - 1];
bitD.bitsConsumed = if lastByte != 0 {
8 - ZSTD_highbit32(lastByte as u32)
} else {
0
};
if lastByte == 0 {
return ERROR(ErrorCode::CorruptionDetected);
}
bitD.bitsConsumed += (CONTAINER_BYTES - srcSize) as u32 * 8;
}
srcSize
}
#[inline]
pub fn BIT_getUpperBits(bitContainer: usize, start: u32) -> usize {
bitContainer >> start
}
#[inline]
pub fn BIT_getMiddleBits(bitContainer: usize, start: u32, nbBits: u32) -> usize {
let reg_mask = CONTAINER_BITS - 1;
debug_assert!(nbBits < 32);
let mask = (1usize << nbBits).wrapping_sub(1);
(bitContainer >> (start & reg_mask)) & mask
}
#[inline(always)]
pub fn BIT_getLowerBits(bitContainer: usize, nbBits: u32) -> usize {
debug_assert!(nbBits < 32);
let mask = (1usize << nbBits).wrapping_sub(1);
bitContainer & mask
}
#[inline(always)]
pub fn BIT_lookBits(bitD: &BIT_DStream_t, nbBits: u32) -> usize {
let start = CONTAINER_BITS
.wrapping_sub(bitD.bitsConsumed)
.wrapping_sub(nbBits);
BIT_getMiddleBits(bitD.bitContainer, start, nbBits)
}
#[inline(always)]
pub fn BIT_lookBitsFast(bitD: &BIT_DStream_t, nbBits: u32) -> usize {
debug_assert!(nbBits >= 1);
let reg_mask = CONTAINER_BITS - 1;
(bitD.bitContainer << (bitD.bitsConsumed & reg_mask)) >> ((reg_mask + 1 - nbBits) & reg_mask)
}
#[inline(always)]
pub fn BIT_skipBits(bitD: &mut BIT_DStream_t, nbBits: u32) {
bitD.bitsConsumed = bitD.bitsConsumed.wrapping_add(nbBits);
}
#[inline(always)]
pub fn BIT_readBits(bitD: &mut BIT_DStream_t, nbBits: u32) -> usize {
let v = BIT_lookBits(bitD, nbBits);
BIT_skipBits(bitD, nbBits);
v
}
#[inline(always)]
pub fn BIT_readBitsFast(bitD: &mut BIT_DStream_t, nbBits: u32) -> usize {
debug_assert!(nbBits >= 1);
let v = BIT_lookBitsFast(bitD, nbBits);
BIT_skipBits(bitD, nbBits);
v
}
#[inline]
fn BIT_reloadDStream_internal(bitD: &mut BIT_DStream_t) -> u32 {
bitD.ptr -= (bitD.bitsConsumed >> 3) as usize;
bitD.bitsConsumed &= 7;
bitD.bitContainer = BIT_readContainer(bitD.src, bitD.ptr);
BIT_DStream_unfinished
}
#[inline(always)]
pub fn BIT_reloadDStreamFast(bitD: &mut BIT_DStream_t) -> u32 {
if bitD.ptr < bitD.limitPtr {
return BIT_DStream_overflow;
}
BIT_reloadDStream_internal(bitD)
}
#[inline(always)]
pub fn BIT_reloadDStream(bitD: &mut BIT_DStream_t) -> u32 {
if bitD.bitsConsumed > CONTAINER_BITS {
bitD.src = &BIT_DSTREAM_ZERO_FILLED;
bitD.start = 0;
bitD.limitPtr = CONTAINER_BYTES;
bitD.ptr = 0;
return BIT_DStream_overflow;
}
debug_assert!(bitD.ptr >= bitD.start);
if bitD.ptr >= bitD.limitPtr {
return BIT_reloadDStream_internal(bitD);
}
if bitD.ptr == bitD.start {
if bitD.bitsConsumed < CONTAINER_BITS {
return BIT_DStream_endOfBuffer;
}
return BIT_DStream_completed;
}
let mut nbBytes = (bitD.bitsConsumed >> 3) as usize;
let mut result = BIT_DStream_unfinished;
if nbBytes > bitD.ptr - bitD.start {
nbBytes = bitD.ptr - bitD.start;
result = BIT_DStream_endOfBuffer;
}
bitD.ptr -= nbBytes;
bitD.bitsConsumed -= (nbBytes as u32) * 8;
debug_assert!(bitD.ptr + CONTAINER_BYTES <= bitD.src.len());
bitD.bitContainer = BIT_readContainer(bitD.src, bitD.ptr);
result
}
#[inline(always)]
pub fn BIT_endOfDStream(bitD: &BIT_DStream_t) -> u32 {
(bitD.ptr == bitD.start && bitD.bitsConsumed == CONTAINER_BITS) as u32
}
#[cfg(test)]
mod tests {
use super::*;
fn single_byte_stream(payload: u8, payload_bits: u32) -> [u8; 1] {
let mark = 1u8 << payload_bits;
[payload | mark]
}
#[test]
fn init_and_read_single_byte() {
let buf = single_byte_stream(0b101, 3);
let mut d = BIT_DStream_t::default();
let rc = BIT_initDStream(&mut d, &buf, buf.len());
assert_eq!(rc, 1);
let v = BIT_readBits(&mut d, 3);
assert_eq!(v, 0b101);
}
#[test]
fn init_rejects_zero_last_byte() {
let buf = [0u8, 0u8];
let mut d = BIT_DStream_t::default();
d.bitsConsumed = 13;
let rc = BIT_initDStream(&mut d, &buf, buf.len());
assert!(crate::common::error::ERR_isError(rc));
assert_eq!(d.bitsConsumed, 0);
}
#[test]
fn init_rejects_zero_last_byte_large_stream_clears_bits_consumed() {
let mut buf = [0u8; CONTAINER_BYTES];
buf[0] = 1;
let mut d = BIT_DStream_t::default();
d.bitsConsumed = 23;
let rc = BIT_initDStream(&mut d, &buf, buf.len());
assert!(crate::common::error::ERR_isError(rc));
assert_eq!(d.bitsConsumed, 0);
}
#[test]
fn init_rejects_empty() {
let buf = [];
let mut d = BIT_DStream_t::default();
let rc = BIT_initDStream(&mut d, &buf, 0);
assert!(crate::common::error::ERR_isError(rc));
}
#[test]
fn get_lower_bits_matches_bitmask() {
assert_eq!(BIT_getLowerBits(0xFFFF, 4), 0x0F);
assert_eq!(BIT_getLowerBits(0xFFFF, 8), 0xFF);
assert_eq!(BIT_getLowerBits(0, 16), 0);
}
#[test]
fn get_upper_bits_shifts() {
assert_eq!(BIT_getUpperBits(0xABCD, 8), 0xAB);
assert_eq!(BIT_getUpperBits(0x1234_5678, 16), 0x1234);
}
#[test]
fn get_middle_bits_extracts_window() {
assert_eq!(BIT_getMiddleBits(0xABCD, 4, 4), 0xC);
assert_eq!(BIT_getMiddleBits(0xABCD, 8, 8), 0xAB);
let masked = BIT_getMiddleBits(0xABCD, CONTAINER_BITS + 4, 4);
assert_eq!(masked, 0xC);
}
#[test]
fn skipBits_advances_bitsConsumed_cursor() {
let buf = [0x80u8];
let mut d = BIT_DStream_t::default();
let rc = BIT_initDStream(&mut d, &buf, 1);
assert_eq!(rc, 1);
let container_before = d.bitContainer;
let consumed_before = d.bitsConsumed;
BIT_skipBits(&mut d, 5);
assert_eq!(d.bitsConsumed, consumed_before + 5);
assert_eq!(d.bitContainer, container_before);
}
#[test]
fn skipBits_wraps_like_unsigned_c() {
let mut d = BIT_DStream_t {
bitsConsumed: u32::MAX - 1,
..BIT_DStream_t::default()
};
BIT_skipBits(&mut d, 3);
assert_eq!(d.bitsConsumed, 1);
}
#[test]
fn init_cstream_rejects_tiny_capacity() {
let mut buf = [0u8; 4];
let len = buf.len();
let (_bitC, rc) = BIT_initCStream(&mut buf, len);
assert!(crate::common::error::ERR_isError(rc));
}
#[test]
fn cstream_then_dstream_roundtrip() {
let mut buf = vec![0u8; 64];
let cap = buf.len();
let (mut bitC, rc) = BIT_initCStream(&mut buf, cap);
assert_eq!(rc, 0);
BIT_addBits(&mut bitC, 5, 3);
BIT_flushBits(&mut bitC);
BIT_addBits(&mut bitC, 0xF, 4);
BIT_flushBits(&mut bitC);
BIT_addBits(&mut bitC, 0x1A, 5);
BIT_flushBits(&mut bitC);
BIT_addBits(&mut bitC, 0x3FF, 10);
BIT_flushBits(&mut bitC);
let written = BIT_closeCStream(&mut bitC);
assert!(written > 0, "closeCStream should report non-zero size");
let mut bitD = BIT_DStream_t::default();
let rc = BIT_initDStream(&mut bitD, &buf[..written], written);
assert_eq!(rc, written, "init should accept the produced stream");
assert_eq!(BIT_readBits(&mut bitD, 10), 0x3FF);
assert_eq!(BIT_readBits(&mut bitD, 5), 0x1A);
assert_eq!(BIT_readBits(&mut bitD, 4), 0xF);
assert_eq!(BIT_readBits(&mut bitD, 3), 5);
}
#[test]
fn reloadDStream_overflow_returns_overflow_sentinel() {
let mut buf = [0u8; 16];
let cap = buf.len();
let (mut bitC, _) = BIT_initCStream(&mut buf, cap);
BIT_addBits(&mut bitC, 0xFF, 8);
BIT_flushBits(&mut bitC);
let written = BIT_closeCStream(&mut bitC);
let mut bitD = BIT_DStream_t::default();
BIT_initDStream(&mut bitD, &buf[..written], written);
bitD.bitsConsumed = CONTAINER_BITS + 1;
assert_eq!(BIT_reloadDStream(&mut bitD), BIT_DStream_overflow);
}
#[test]
fn reloadDStream_overflow_keeps_fast_reload_in_overflow() {
let mut buf = [0u8; 16];
let cap = buf.len();
let (mut bitC, _) = BIT_initCStream(&mut buf, cap);
BIT_addBits(&mut bitC, 0xFF, 8);
BIT_flushBits(&mut bitC);
let written = BIT_closeCStream(&mut bitC);
let mut bitD = BIT_DStream_t::default();
BIT_initDStream(&mut bitD, &buf[..written], written);
bitD.ptr = bitD.limitPtr;
bitD.bitsConsumed = CONTAINER_BITS + 1;
assert_eq!(BIT_reloadDStream(&mut bitD), BIT_DStream_overflow);
assert_eq!(BIT_reloadDStreamFast(&mut bitD), BIT_DStream_overflow);
}
#[test]
fn reloadDStream_at_start_with_full_bits_returns_completed() {
let mut buf = [0u8; 16];
let cap = buf.len();
let (mut bitC, _) = BIT_initCStream(&mut buf, cap);
BIT_addBits(&mut bitC, 1, 1);
BIT_flushBits(&mut bitC);
let written = BIT_closeCStream(&mut bitC);
let mut bitD = BIT_DStream_t::default();
BIT_initDStream(&mut bitD, &buf[..written], written);
bitD.ptr = bitD.start;
bitD.bitsConsumed = CONTAINER_BITS;
assert_eq!(BIT_reloadDStream(&mut bitD), BIT_DStream_completed);
bitD.bitsConsumed = CONTAINER_BITS - 4;
assert_eq!(BIT_reloadDStream(&mut bitD), BIT_DStream_endOfBuffer);
}
#[test]
fn endOfDStream_reports_true_only_at_start_with_all_bits_consumed() {
let mut buf = vec![0u8; 16];
let cap = buf.len();
let (mut bitC, rc) = BIT_initCStream(&mut buf, cap);
assert_eq!(rc, 0);
BIT_addBits(&mut bitC, 0xAB, 8);
BIT_flushBits(&mut bitC);
let written = BIT_closeCStream(&mut bitC);
let mut bitD = BIT_DStream_t::default();
BIT_initDStream(&mut bitD, &buf[..written], written);
assert_eq!(BIT_endOfDStream(&bitD), 0);
let _ = BIT_readBits(&mut bitD, 8);
bitD.ptr = bitD.start;
bitD.bitsConsumed = CONTAINER_BITS;
assert_eq!(BIT_endOfDStream(&bitD), 1);
bitD.bitsConsumed = CONTAINER_BITS - 1;
assert_eq!(BIT_endOfDStream(&bitD), 0);
}
#[test]
fn readBitsFast_produces_same_values_as_readBits_for_nbBits_ge_1() {
let mut buf = vec![0u8; 64];
let cap = buf.len();
let (mut bitC, rc) = BIT_initCStream(&mut buf, cap);
assert_eq!(rc, 0);
BIT_addBits(&mut bitC, 0x0A, 4);
BIT_flushBits(&mut bitC);
BIT_addBits(&mut bitC, 0x15, 5);
BIT_flushBits(&mut bitC);
BIT_addBits(&mut bitC, 0x2B, 6);
BIT_flushBits(&mut bitC);
let written = BIT_closeCStream(&mut bitC);
assert!(written > 0);
let mut bitD = BIT_DStream_t::default();
let rc = BIT_initDStream(&mut bitD, &buf[..written], written);
assert_eq!(rc, written);
assert_eq!(BIT_readBitsFast(&mut bitD, 6), 0x2B);
assert_eq!(BIT_readBitsFast(&mut bitD, 5), 0x15);
assert_eq!(BIT_readBitsFast(&mut bitD, 4), 0x0A);
}
#[test]
fn close_cstream_reports_zero_on_overflow() {
let mut buf = vec![0u8; CONTAINER_BYTES + 2];
let cap = buf.len();
let (mut bitC, rc) = BIT_initCStream(&mut buf, cap);
assert_eq!(rc, 0);
for _ in 0..20 {
BIT_addBits(&mut bitC, 0xFFFF, 16);
BIT_flushBits(&mut bitC);
}
let out = BIT_closeCStream(&mut bitC);
assert_eq!(out, 0, "overflow must be reported as 0");
}
#[test]
fn large_stream_reload_path() {
let mut buf = [0u8; 32];
buf[31] = 0b1000_0001; let mut d = BIT_DStream_t::default();
let rc = BIT_initDStream(&mut d, &buf, buf.len());
assert_eq!(rc, 32);
assert_eq!(d.ptr, 32 - CONTAINER_BYTES);
assert_eq!(d.bitsConsumed, 1);
}
}