Skip to content

Commit

Permalink
Add 1.1 binary reader support for floats
Browse files Browse the repository at this point in the history
  • Loading branch information
nirosys committed May 10, 2024
1 parent 6bd5532 commit 84b9f34
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 1 deletion.
55 changes: 55 additions & 0 deletions src/lazy/binary/raw/v1_1/reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -310,4 +310,59 @@ mod tests {

Ok(())
}

macro_rules! assert_closely_eq {
($x:expr, $y:expr, $d:expr) => {
let (a, b) = ($x, $y);
if ((a - b).abs() >= $d) {
panic!(
"float not close enough to expected value: left: {}, right: {}",
a, b
);
}
};
}

#[test]
#[allow(clippy::approx_constant)]
fn floats() -> IonResult<()> {
#[rustfmt::skip]
let data: Vec<u8> = vec![
// IVM
0xe0, 0x01, 0x01, 0xea,
// 0e0
0x5A,

// 3.14 (half-precision)
// 0x5B, 0x42, 0x47,

// 3.1415927 (single-precision)
0x5C, 0xdb, 0x0F, 0x49, 0x40,

// 3.141592653589793 (double-precision)
0x5D, 0x18, 0x2D, 0x44, 0x54, 0xFB, 0x21, 0x09, 0x40,
];

let mut reader = LazyRawBinaryReader_1_1::new(&data);
let _ivm = reader.next()?.expect_ivm()?;

assert_eq!(reader.next()?.expect_value()?.read()?.expect_float()?, 0.0);

// TODO: Implement Half-precision.
// assert_eq!(reader.next()?.expect_value()?.read()?.expect_float()?, 3.14);

assert_closely_eq!(
reader.next()?.expect_value()?.read()?.expect_float()? as f32,
3.1415927f32,
f32::EPSILON
);

assert_closely_eq!(
reader.next()?.expect_value()?.read()?.expect_float()?,
std::f64::consts::PI,
f64::EPSILON
);

Ok(())
}
}
3 changes: 3 additions & 0 deletions src/lazy/binary/raw/v1_1/type_descriptor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ impl Opcode {

let (opcode_type, length_code, ion_type) = match (high_nibble, low_nibble) {
(0x5, 0x0..=0x8) => (Integer, low_nibble, Some(IonType::Int)),
(0x5, 0xA..=0xD) => (Float, low_nibble, Some(IonType::Float)),
(0x5, 0xE..=0xF) => (Boolean, low_nibble, Some(IonType::Bool)),
(0x8, _) => (String, low_nibble, Some(IonType::String)),
(0xE, 0x0) => (IonVersionMarker, low_nibble, None),
Expand Down Expand Up @@ -109,6 +110,8 @@ impl Header {
use LengthType::*;
match (self.ion_type_code, self.length_code) {
(OpcodeType::Boolean, 0xE..=0xF) => InOpcode(0),
(OpcodeType::Float, 0xA) => InOpcode(0),
(OpcodeType::Float, 0xB..=0xD) => InOpcode(1 << (self.length_code - 0xA)),
(OpcodeType::Integer, n) => InOpcode(n),
(OpcodeType::Nop, 0xC) => InOpcode(0),
(OpcodeType::NullNull, 0xA) => InOpcode(0),
Expand Down
28 changes: 27 additions & 1 deletion src/lazy/binary/raw/v1_1/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,33 @@ impl<'top> LazyRawBinaryValue_1_1<'top> {

/// Helper method called by [`Self::read`]. Reads the current value as a float.
fn read_float(&self) -> ValueParseResult<'top, BinaryEncoding_1_1> {
todo!();
debug_assert!(self.encoded_value.ion_type() == IonType::Float);

let value = match self.encoded_value.value_length {
8 => {
let mut buffer = [0; 8];
let val_bytes = self.input.slice(1, 8);
buffer[..8].copy_from_slice(val_bytes.bytes());

f64::from_le_bytes(buffer)
}
4 => {
let mut buffer = [0; 4];
let val_bytes = self.input.slice(1, 4);
buffer[..4].copy_from_slice(val_bytes.bytes());

f32::from_le_bytes(buffer).into()
}
2 => todo!("implement half-precision floats"),
0 => 0.0f64,
n => {
return IonResult::decoding_error(format!(
"found a float value with an illegal bytes size: {:?}",
n
));
}
};
Ok(RawValueRef::Float(value))
}

/// Helper method called by [`Self::read`]. Reads the current value as a decimal.
Expand Down

0 comments on commit 84b9f34

Please sign in to comment.