use super::BitReader;
use oximedia_core::{OxiError, OxiResult};
impl BitReader<'_> {
pub fn read_exp_golomb(&mut self) -> OxiResult<u64> {
let mut leading_zeros: u8 = 0;
while self.read_bit()? == 0 {
leading_zeros += 1;
if leading_zeros > 63 {
return Err(OxiError::InvalidData(
"Exp-Golomb code too long (> 63 leading zeros)".to_string(),
));
}
}
if leading_zeros == 0 {
return Ok(0);
}
let suffix = self.read_bits(leading_zeros)?;
Ok((1u64 << leading_zeros) - 1 + suffix)
}
#[allow(clippy::cast_possible_wrap)]
pub fn read_signed_exp_golomb(&mut self) -> OxiResult<i64> {
let ue = self.read_exp_golomb()?;
let abs_value = ue.div_ceil(2) as i64;
if ue & 1 == 0 {
Ok(-abs_value)
} else {
Ok(abs_value)
}
}
#[inline]
pub fn read_ue(&mut self) -> OxiResult<u64> {
self.read_exp_golomb()
}
#[inline]
pub fn read_se(&mut self) -> OxiResult<i64> {
self.read_signed_exp_golomb()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_read_exp_golomb_zero() {
let data = [0b10000000];
let mut reader = BitReader::new(&data);
assert_eq!(
reader
.read_exp_golomb()
.expect("read_exp_golomb should succeed"),
0
);
assert_eq!(reader.bits_read(), 1);
}
#[test]
fn test_read_exp_golomb_one() {
let data = [0b01000000];
let mut reader = BitReader::new(&data);
assert_eq!(
reader
.read_exp_golomb()
.expect("read_exp_golomb should succeed"),
1
);
assert_eq!(reader.bits_read(), 3);
}
#[test]
fn test_read_exp_golomb_two() {
let data = [0b01100000];
let mut reader = BitReader::new(&data);
assert_eq!(
reader
.read_exp_golomb()
.expect("read_exp_golomb should succeed"),
2
);
assert_eq!(reader.bits_read(), 3);
}
#[test]
fn test_read_exp_golomb_three() {
let data = [0b00100000];
let mut reader = BitReader::new(&data);
assert_eq!(
reader
.read_exp_golomb()
.expect("read_exp_golomb should succeed"),
3
);
assert_eq!(reader.bits_read(), 5);
}
#[test]
fn test_read_exp_golomb_four() {
let data = [0b00101000];
let mut reader = BitReader::new(&data);
assert_eq!(
reader
.read_exp_golomb()
.expect("read_exp_golomb should succeed"),
4
);
assert_eq!(reader.bits_read(), 5);
}
#[test]
fn test_read_exp_golomb_five() {
let data = [0b00110000];
let mut reader = BitReader::new(&data);
assert_eq!(
reader
.read_exp_golomb()
.expect("read_exp_golomb should succeed"),
5
);
assert_eq!(reader.bits_read(), 5);
}
#[test]
fn test_read_exp_golomb_six() {
let data = [0b00111000];
let mut reader = BitReader::new(&data);
assert_eq!(
reader
.read_exp_golomb()
.expect("read_exp_golomb should succeed"),
6
);
assert_eq!(reader.bits_read(), 5);
}
#[test]
fn test_read_exp_golomb_seven() {
let data = [0b00010000];
let mut reader = BitReader::new(&data);
assert_eq!(
reader
.read_exp_golomb()
.expect("read_exp_golomb should succeed"),
7
);
assert_eq!(reader.bits_read(), 7);
}
#[test]
fn test_read_exp_golomb_large() {
let data = [0b00011110];
let mut reader = BitReader::new(&data);
assert_eq!(
reader
.read_exp_golomb()
.expect("read_exp_golomb should succeed"),
14
);
}
#[test]
fn test_read_signed_exp_golomb_zero() {
let data = [0b10000000];
let mut reader = BitReader::new(&data);
assert_eq!(
reader
.read_signed_exp_golomb()
.expect("read_signed_exp_golomb should succeed"),
0
);
}
#[test]
fn test_read_signed_exp_golomb_positive_one() {
let data = [0b01000000];
let mut reader = BitReader::new(&data);
assert_eq!(
reader
.read_signed_exp_golomb()
.expect("read_signed_exp_golomb should succeed"),
1
);
}
#[test]
fn test_read_signed_exp_golomb_negative_one() {
let data = [0b01100000];
let mut reader = BitReader::new(&data);
assert_eq!(
reader
.read_signed_exp_golomb()
.expect("read_signed_exp_golomb should succeed"),
-1
);
}
#[test]
fn test_read_signed_exp_golomb_positive_two() {
let data = [0b00100000];
let mut reader = BitReader::new(&data);
assert_eq!(
reader
.read_signed_exp_golomb()
.expect("read_signed_exp_golomb should succeed"),
2
);
}
#[test]
fn test_read_signed_exp_golomb_negative_two() {
let data = [0b00101000];
let mut reader = BitReader::new(&data);
assert_eq!(
reader
.read_signed_exp_golomb()
.expect("read_signed_exp_golomb should succeed"),
-2
);
}
#[test]
fn test_read_signed_exp_golomb_sequence() {
let test_cases: [(u64, i64); 7] =
[(0, 0), (1, 1), (2, -1), (3, 2), (4, -2), (5, 3), (6, -3)];
for (ue_val, expected_se) in test_cases {
let abs_value = ((ue_val + 1) / 2) as i64;
let se_val = if ue_val & 1 == 0 {
-abs_value
} else {
abs_value
};
assert_eq!(
se_val, expected_se,
"ue({ue_val}) should map to se({expected_se})"
);
}
}
#[test]
fn test_read_multiple_exp_golomb() {
let data = [0b10100000];
let mut reader = BitReader::new(&data);
assert_eq!(
reader
.read_exp_golomb()
.expect("read_exp_golomb should succeed"),
0
);
assert_eq!(
reader
.read_exp_golomb()
.expect("read_exp_golomb should succeed"),
1
);
}
#[test]
fn test_read_ue_alias() {
let data = [0b01000000];
let mut reader = BitReader::new(&data);
assert_eq!(reader.read_ue().expect("read_ue should succeed"), 1);
}
#[test]
fn test_read_se_alias() {
let data = [0b01100000];
let mut reader = BitReader::new(&data);
assert_eq!(reader.read_se().expect("read_se should succeed"), -1);
}
#[test]
fn test_exp_golomb_eof() {
let data = [0b00000000];
let mut reader = BitReader::new(&data);
let result = reader.read_exp_golomb();
assert!(result.is_err());
}
#[test]
fn test_exp_golomb_boundary_values() {
let data = [0b00011110];
let mut reader = BitReader::new(&data);
assert_eq!(
reader
.read_exp_golomb()
.expect("read_exp_golomb should succeed"),
14
);
let data = [0b00011111];
let mut reader = BitReader::new(&data);
assert_eq!(
reader
.read_exp_golomb()
.expect("read_exp_golomb should succeed"),
14
);
}
#[test]
fn test_exp_golomb_consecutive_zeros() {
let data = [0b11110000]; let mut reader = BitReader::new(&data);
assert_eq!(
reader
.read_exp_golomb()
.expect("read_exp_golomb should succeed"),
0
);
assert_eq!(
reader
.read_exp_golomb()
.expect("read_exp_golomb should succeed"),
0
);
assert_eq!(
reader
.read_exp_golomb()
.expect("read_exp_golomb should succeed"),
0
);
assert_eq!(
reader
.read_exp_golomb()
.expect("read_exp_golomb should succeed"),
0
);
}
#[test]
fn test_signed_exp_golomb_range() {
let test_cases = [
(0b10000000, 0), (0b01000000, 1), (0b01100000, -1), (0b00100000, 2), (0b00101000, -2), (0b00110000, 3), (0b00111000, -3), ];
for (data_byte, expected) in test_cases {
let data = [data_byte];
let mut reader = BitReader::new(&data);
assert_eq!(
reader
.read_signed_exp_golomb()
.expect("read_signed_exp_golomb should succeed"),
expected
);
}
}
#[test]
fn test_signed_exp_golomb_large_values() {
let data = [0b00010100];
let mut reader = BitReader::new(&data);
assert_eq!(
reader
.read_signed_exp_golomb()
.expect("read_signed_exp_golomb should succeed"),
5
);
let data = [0b00010110];
let mut reader = BitReader::new(&data);
assert_eq!(
reader
.read_signed_exp_golomb()
.expect("read_signed_exp_golomb should succeed"),
-5
);
}
#[test]
fn test_exp_golomb_mixed_with_other_reads() {
let data = [0b11100000]; let mut reader = BitReader::new(&data);
assert!(reader.read_flag().expect("read_flag should succeed"));
assert!(reader.read_flag().expect("read_flag should succeed"));
assert_eq!(
reader
.read_exp_golomb()
.expect("read_exp_golomb should succeed"),
0
);
}
#[test]
fn test_exp_golomb_too_many_leading_zeros() {
let data = [0x00; 10]; let mut reader = BitReader::new(&data);
let result = reader.read_exp_golomb();
assert!(result.is_err());
}
#[test]
fn test_exp_golomb_insufficient_suffix_bits() {
let data = [0b00000001]; let mut reader = BitReader::new(&data);
let result = reader.read_exp_golomb();
assert!(result.is_err());
}
#[test]
fn test_exp_golomb_arithmetic() {
let data = [0b00010110];
let mut reader = BitReader::new(&data);
assert_eq!(
reader
.read_exp_golomb()
.expect("read_exp_golomb should succeed"),
10
);
}
#[test]
fn test_signed_zero_mapping() {
let data = [0b10000000];
let mut reader = BitReader::new(&data);
let value = reader.read_se().expect("read_se should succeed");
assert_eq!(value, 0);
}
#[test]
fn test_alternating_signed_pattern() {
let data = [
0b01001100, 0b10000000, ];
let mut reader = BitReader::new(&data);
assert_eq!(reader.read_se().expect("read_se should succeed"), 1);
assert_eq!(reader.read_se().expect("read_se should succeed"), -1);
assert_eq!(reader.read_se().expect("read_se should succeed"), 2);
}
#[test]
fn test_ue_se_alias_consistency() {
let data = [0b01000000, 0b01100000];
let mut reader = BitReader::new(&data);
let ue_val = reader.read_ue().expect("read_ue should succeed");
let se_val = reader.read_se().expect("read_se should succeed");
let mut reader2 = BitReader::new(&data);
assert_eq!(
reader2
.read_exp_golomb()
.expect("read_exp_golomb should succeed"),
ue_val
);
assert_eq!(
reader2
.read_signed_exp_golomb()
.expect("read_signed_exp_golomb should succeed"),
se_val
);
}
}