From 84b9f3499d8d4664a943dedd7017fc70248cb605 Mon Sep 17 00:00:00 2001 From: Richard Giliam Date: Tue, 30 Apr 2024 17:01:40 -0700 Subject: [PATCH] Add 1.1 binary reader support for floats --- src/lazy/binary/raw/v1_1/reader.rs | 55 +++++++++++++++++++++ src/lazy/binary/raw/v1_1/type_descriptor.rs | 3 ++ src/lazy/binary/raw/v1_1/value.rs | 28 ++++++++++- 3 files changed, 85 insertions(+), 1 deletion(-) diff --git a/src/lazy/binary/raw/v1_1/reader.rs b/src/lazy/binary/raw/v1_1/reader.rs index 9800c4027..e1cc08887 100644 --- a/src/lazy/binary/raw/v1_1/reader.rs +++ b/src/lazy/binary/raw/v1_1/reader.rs @@ -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 = 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(()) + } } diff --git a/src/lazy/binary/raw/v1_1/type_descriptor.rs b/src/lazy/binary/raw/v1_1/type_descriptor.rs index 144649f29..53d97dcc3 100644 --- a/src/lazy/binary/raw/v1_1/type_descriptor.rs +++ b/src/lazy/binary/raw/v1_1/type_descriptor.rs @@ -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), @@ -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), diff --git a/src/lazy/binary/raw/v1_1/value.rs b/src/lazy/binary/raw/v1_1/value.rs index df0c91cb9..8b2d3c15d 100644 --- a/src/lazy/binary/raw/v1_1/value.rs +++ b/src/lazy/binary/raw/v1_1/value.rs @@ -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.