use crate::error::{CodecError, CodecResult};
pub struct GolombDecoder<'a> {
data: &'a [u8],
bit_pos: usize,
}
impl<'a> GolombDecoder<'a> {
pub fn new(data: &'a [u8]) -> Self {
Self { data, bit_pos: 0 }
}
fn read_bit(&mut self) -> CodecResult<bool> {
let byte_index = self.bit_pos >> 3;
if byte_index >= self.data.len() {
return Err(CodecError::InvalidBitstream(
"Golomb decoder: unexpected end of data".to_string(),
));
}
let bit_index = 7 - (self.bit_pos & 7);
let bit = (self.data[byte_index] >> bit_index) & 1;
self.bit_pos += 1;
Ok(bit != 0)
}
fn read_bits(&mut self, n: u32) -> CodecResult<u32> {
let mut value = 0u32;
for _ in 0..n {
value = (value << 1) | (self.read_bit()? as u32);
}
Ok(value)
}
pub fn read_signed(&mut self, k: u32) -> CodecResult<i32> {
let mut q = 0u32;
let max_prefix = 256;
while q < max_prefix {
if self.read_bit()? {
break;
}
q += 1;
}
if q >= max_prefix {
return Err(CodecError::InvalidBitstream(
"Golomb prefix too long".to_string(),
));
}
let r = if k > 0 { self.read_bits(k)? } else { 0 };
let unsigned_val = (q << k) | r;
let signed = if unsigned_val & 1 == 0 {
(unsigned_val >> 1) as i32
} else {
-(((unsigned_val + 1) >> 1) as i32)
};
Ok(signed)
}
#[must_use]
pub fn bit_position(&self) -> usize {
self.bit_pos
}
#[must_use]
pub fn bytes_consumed(&self) -> usize {
(self.bit_pos + 7) >> 3
}
}
pub struct GolombEncoder {
output: Vec<u8>,
current_byte: u8,
bit_count: u8,
}
impl GolombEncoder {
pub fn new() -> Self {
Self {
output: Vec::new(),
current_byte: 0,
bit_count: 0,
}
}
fn write_bit(&mut self, bit: bool) {
self.current_byte = (self.current_byte << 1) | (bit as u8);
self.bit_count += 1;
if self.bit_count == 8 {
self.output.push(self.current_byte);
self.current_byte = 0;
self.bit_count = 0;
}
}
fn write_bits(&mut self, value: u32, n: u32) {
for i in (0..n).rev() {
self.write_bit(((value >> i) & 1) != 0);
}
}
pub fn write_signed(&mut self, value: i32, k: u32) {
let unsigned_val = if value <= 0 {
((-value) as u32) * 2 - if value < 0 { 1 } else { 0 }
} else {
(value as u32) * 2
};
let q = unsigned_val >> k;
let r = unsigned_val & ((1 << k) - 1);
for _ in 0..q {
self.write_bit(false);
}
self.write_bit(true);
if k > 0 {
self.write_bits(r, k);
}
}
pub fn finish(mut self) -> Vec<u8> {
if self.bit_count > 0 {
self.current_byte <<= 8 - self.bit_count;
self.output.push(self.current_byte);
}
self.output
}
}
#[derive(Clone, Debug)]
pub struct GolombContext {
sum: u64,
count: u64,
k: u32,
}
impl GolombContext {
pub fn new() -> Self {
Self {
sum: 0,
count: 0,
k: 0,
}
}
#[must_use]
pub fn k(&self) -> u32 {
self.k
}
pub fn update(&mut self, value: i32) {
let abs_val = value.unsigned_abs() as u64;
self.sum += abs_val;
self.count += 1;
if self.count >= 8 {
let mean = self.sum / self.count;
self.k = 0;
let mut threshold = 1u64;
while threshold <= mean && self.k < 15 {
self.k += 1;
threshold <<= 1;
}
if self.count >= 128 {
self.sum >>= 1;
self.count >>= 1;
}
}
}
pub fn reset(&mut self) {
self.sum = 0;
self.count = 0;
self.k = 0;
}
}
impl Default for GolombContext {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[ignore]
fn test_golomb_roundtrip_zero() {
let mut enc = GolombEncoder::new();
enc.write_signed(0, 0);
let data = enc.finish();
let mut dec = GolombDecoder::new(&data);
let val = dec.read_signed(0).expect("decode");
assert_eq!(val, 0);
}
#[test]
#[ignore]
fn test_golomb_roundtrip_positive() {
for k in 0..4u32 {
for value in [1, 2, 3, 5, 10, 50, 100] {
let mut enc = GolombEncoder::new();
enc.write_signed(value, k);
let data = enc.finish();
let mut dec = GolombDecoder::new(&data);
let decoded = dec.read_signed(k).expect("decode");
assert_eq!(value, decoded, "k={k}, value={value}: got {decoded}");
}
}
}
#[test]
#[ignore]
fn test_golomb_roundtrip_negative() {
for k in 0..4u32 {
for value in [-1, -2, -3, -5, -10, -50, -100] {
let mut enc = GolombEncoder::new();
enc.write_signed(value, k);
let data = enc.finish();
let mut dec = GolombDecoder::new(&data);
let decoded = dec.read_signed(k).expect("decode");
assert_eq!(value, decoded, "k={k}, value={value}: got {decoded}");
}
}
}
#[test]
#[ignore]
fn test_golomb_roundtrip_sequence() {
let values = [0, 1, -1, 2, -2, 10, -10, 0, 5, -3];
let k = 2;
let mut enc = GolombEncoder::new();
for &v in &values {
enc.write_signed(v, k);
}
let data = enc.finish();
let mut dec = GolombDecoder::new(&data);
for &expected in &values {
let got = dec.read_signed(k).expect("decode");
assert_eq!(expected, got);
}
}
#[test]
#[ignore]
fn test_golomb_context_adaptation() {
let mut ctx = GolombContext::new();
assert_eq!(ctx.k(), 0);
for _ in 0..20 {
ctx.update(100);
}
assert!(ctx.k() > 0, "k should increase for large values");
ctx.reset();
for _ in 0..20 {
ctx.update(0);
}
assert_eq!(ctx.k(), 0, "k should be 0 for zero values");
}
#[test]
#[ignore]
fn test_golomb_context_decay() {
let mut ctx = GolombContext::new();
for _ in 0..200 {
ctx.update(10);
}
assert!(ctx.count < 200);
}
#[test]
#[ignore]
fn test_golomb_decoder_eof() {
let data = [0u8; 1];
let mut dec = GolombDecoder::new(&data);
let mut result = Ok(0i32);
for _ in 0..20 {
result = dec.read_signed(4);
if result.is_err() {
break;
}
}
assert!(result.is_err());
}
#[test]
#[ignore]
fn test_golomb_encoder_bits() {
let mut enc = GolombEncoder::new();
enc.write_signed(0, 0);
let data = enc.finish();
assert!(!data.is_empty());
}
}