From 2d3334e5e6bdfda507911f798f1851749a8a60a2 Mon Sep 17 00:00:00 2001 From: Richard Giliam Date: Tue, 30 Apr 2024 17:01:40 -0700 Subject: [PATCH 1/4] 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 7b9efa7c..c2e8f16f 100644 --- a/src/lazy/binary/raw/v1_1/reader.rs +++ b/src/lazy/binary/raw/v1_1/reader.rs @@ -377,6 +377,61 @@ 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(()) + } + fn blobs() -> IonResult<()> { let data: Vec = vec![ 0xe0, 0x01, 0x01, 0xea, // IVM diff --git a/src/lazy/binary/raw/v1_1/type_descriptor.rs b/src/lazy/binary/raw/v1_1/type_descriptor.rs index 2d76a09c..96dda27e 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)), (0x9, _) => (InlineSymbol, low_nibble, Some(IonType::Symbol)), @@ -114,6 +115,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 ef5b697a..23a39e21 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. From f1a970a44c00ad4496e44a972dcfecb612aa71b1 Mon Sep 17 00:00:00 2001 From: Richard Giliam Date: Tue, 7 May 2024 16:41:29 -0700 Subject: [PATCH 2/4] Address PR feedback --- src/lazy/binary/raw/v1_1/reader.rs | 18 ++---------------- src/lazy/binary/raw/v1_1/value.rs | 7 +------ 2 files changed, 3 insertions(+), 22 deletions(-) diff --git a/src/lazy/binary/raw/v1_1/reader.rs b/src/lazy/binary/raw/v1_1/reader.rs index c2e8f16f..e01a2c2c 100644 --- a/src/lazy/binary/raw/v1_1/reader.rs +++ b/src/lazy/binary/raw/v1_1/reader.rs @@ -377,18 +377,6 @@ 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<()> { @@ -417,16 +405,14 @@ mod tests { // TODO: Implement Half-precision. // assert_eq!(reader.next()?.expect_value()?.read()?.expect_float()?, 3.14); - assert_closely_eq!( + assert_eq!( reader.next()?.expect_value()?.read()?.expect_float()? as f32, 3.1415927f32, - f32::EPSILON ); - assert_closely_eq!( + assert_eq!( reader.next()?.expect_value()?.read()?.expect_float()?, std::f64::consts::PI, - f64::EPSILON ); Ok(()) diff --git a/src/lazy/binary/raw/v1_1/value.rs b/src/lazy/binary/raw/v1_1/value.rs index 23a39e21..2d3e3c1f 100644 --- a/src/lazy/binary/raw/v1_1/value.rs +++ b/src/lazy/binary/raw/v1_1/value.rs @@ -255,12 +255,7 @@ impl<'top> LazyRawBinaryValue_1_1<'top> { } 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 - )); - } + _ => unreachable!("found a float value with illegal byte size"), }; Ok(RawValueRef::Float(value)) } From ef49f33331f0208c07bfadd527bc8e8374c03eca Mon Sep 17 00:00:00 2001 From: Richard Giliam Date: Tue, 7 May 2024 17:21:41 -0700 Subject: [PATCH 3/4] Address renamed field.. --- src/lazy/binary/raw/v1_1/value.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lazy/binary/raw/v1_1/value.rs b/src/lazy/binary/raw/v1_1/value.rs index 2d3e3c1f..9b74b09f 100644 --- a/src/lazy/binary/raw/v1_1/value.rs +++ b/src/lazy/binary/raw/v1_1/value.rs @@ -238,7 +238,7 @@ impl<'top> LazyRawBinaryValue_1_1<'top> { fn read_float(&self) -> ValueParseResult<'top, BinaryEncoding_1_1> { debug_assert!(self.encoded_value.ion_type() == IonType::Float); - let value = match self.encoded_value.value_length { + let value = match self.encoded_value.value_body_length { 8 => { let mut buffer = [0; 8]; let val_bytes = self.input.slice(1, 8); From 91b37d76f50fc2fb960244e7a44672c1b0764029 Mon Sep 17 00:00:00 2001 From: Richard Giliam Date: Fri, 10 May 2024 16:04:15 -0700 Subject: [PATCH 4/4] Address feedback; replace use of `byte_range` with `slice` since we don't need the ImmutableBuffer --- src/lazy/binary/raw/v1_1/value.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lazy/binary/raw/v1_1/value.rs b/src/lazy/binary/raw/v1_1/value.rs index 9b74b09f..d1b87e63 100644 --- a/src/lazy/binary/raw/v1_1/value.rs +++ b/src/lazy/binary/raw/v1_1/value.rs @@ -241,15 +241,15 @@ impl<'top> LazyRawBinaryValue_1_1<'top> { let value = match self.encoded_value.value_body_length { 8 => { let mut buffer = [0; 8]; - let val_bytes = self.input.slice(1, 8); - buffer[..8].copy_from_slice(val_bytes.bytes()); + let val_bytes = self.input.bytes_range(1, 8); + buffer[..8].copy_from_slice(val_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()); + let val_bytes = self.input.bytes_range(1, 4); + buffer[..4].copy_from_slice(val_bytes); f32::from_le_bytes(buffer).into() }