use crate::decimal::SpecialValue;
use crate::error::{DecodeError, DecodeResult};
use crate::gamma::BitReader;
use crate::significand::decode_significand;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DecodedDecimal {
pub positive: bool,
pub exponent_positive: bool,
pub exponent: u64,
pub significand: Vec<u8>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DecodedValue {
Regular(DecodedDecimal),
Special(SpecialValue),
}
pub fn decode_to_parts(bytes: &[u8]) -> DecodeResult<DecodedValue> {
if bytes.is_empty() {
return Err(DecodeError::UnexpectedEndOfInput);
}
let mut reader = BitReader::new(bytes);
let sign_bit1 = reader.read_bit()?;
let sign_bit2 = reader.read_bit()?;
match (sign_bit1, sign_bit2) {
(false, false) => {
if !reader.has_bits() || is_all_zeros_from_position(&reader) {
return Ok(DecodedValue::Special(SpecialValue::NegativeInfinity));
}
decode_regular(&mut reader, false)
}
(false, true) => Ok(DecodedValue::Special(SpecialValue::NegativeZero)),
(true, false) => {
if !reader.has_bits() || is_all_zeros_from_position(&reader) {
return Ok(DecodedValue::Special(SpecialValue::PositiveZero));
}
decode_regular(&mut reader, true)
}
(true, true) => {
if !reader.has_bits() {
return Ok(DecodedValue::Special(SpecialValue::PositiveInfinity));
}
let third_bit = reader.read_bit()?;
if third_bit {
Ok(DecodedValue::Special(SpecialValue::NaN))
} else {
Ok(DecodedValue::Special(SpecialValue::PositiveInfinity))
}
}
}
}
fn is_all_zeros_from_position(reader: &BitReader) -> bool {
let pos = reader.position();
let bytes = reader.bytes();
let byte_idx = pos / 8;
let bit_offset = pos % 8;
if byte_idx >= bytes.len() {
return true;
}
let mask = (1u8 << (8 - bit_offset)) - 1;
if bytes[byte_idx] & mask != 0 {
return false;
}
bytes[byte_idx + 1..].iter().all(|&b| b == 0)
}
fn decode_regular(reader: &mut BitReader, positive: bool) -> DecodeResult<DecodedValue> {
let first_bit = reader.read_bit()?;
let mut count = 1usize;
while reader.has_bits() {
match reader.peek_bit() {
Ok(bit) if bit == first_bit => {
reader.read_bit()?;
count += 1;
}
_ => break,
}
}
let terminating_bit = reader.read_bit()?;
if terminating_bit == first_bit {
return Err(DecodeError::InvalidGammaCode);
}
let remaining_value = reader.read_bits(count)?;
let negated = !first_bit;
let remaining_value = if negated {
let mask = (1u64 << count) - 1;
(!remaining_value) & mask
} else {
remaining_value
};
let binary_value = (1u64 << count) | remaining_value;
if binary_value < 2 {
return Err(DecodeError::InvalidGammaCode);
}
let exponent = binary_value - 2;
let exponent_positive = if positive {
!negated } else {
negated };
if exponent == 0 && !exponent_positive {
return Err(DecodeError::ZeroWithNegativeExponent);
}
let significand = decode_significand(reader, !positive)?;
let decoded = DecodedDecimal {
positive,
exponent_positive,
exponent,
significand,
};
Ok(DecodedValue::Regular(decoded))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::encoder::encode_from_parts;
#[test]
fn test_decode_zero() {
let bytes = vec![0b1000_0000]; let decoded = decode_to_parts(&bytes).unwrap();
assert_eq!(decoded, DecodedValue::Special(SpecialValue::PositiveZero));
}
#[test]
fn test_decode_negative_infinity() {
let bytes = vec![0b0000_0000]; let decoded = decode_to_parts(&bytes).unwrap();
assert_eq!(
decoded,
DecodedValue::Special(SpecialValue::NegativeInfinity)
);
}
#[test]
fn test_roundtrip_simple() {
let encoded = encode_from_parts(true, false, 1, &[7, 0, 7, 1, 0, 6]);
let decoded = decode_to_parts(&encoded).unwrap();
if let DecodedValue::Regular(dec) = decoded {
assert!(dec.positive);
assert!(!dec.exponent_positive);
assert_eq!(dec.exponent, 1);
assert!(dec.significand.starts_with(&[7, 0, 7, 1, 0, 6]));
} else {
panic!("Expected regular decimal");
}
}
#[test]
fn test_roundtrip_positive_integer() {
let encoded = encode_from_parts(true, true, 0, &[1]);
println!("Encoded 1: {:08b}", encoded[0]);
let decoded = decode_to_parts(&encoded).unwrap();
if let DecodedValue::Regular(dec) = decoded {
assert!(dec.positive);
assert!(dec.exponent_positive);
assert_eq!(dec.exponent, 0);
assert_eq!(dec.significand[0], 1);
} else {
panic!("Expected regular decimal");
}
}
}