Skip to content

Commit

Permalink
Add 1.1 binary reader support for decimals
Browse files Browse the repository at this point in the history
  • Loading branch information
nirosys committed May 9, 2024
1 parent 6bd5532 commit 705749d
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 2 deletions.
56 changes: 56 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,60 @@ mod tests {

Ok(())
}

#[test]
fn decimals() -> IonResult<()> {
use crate::types::decimal::Decimal;

#[rustfmt::skip]
let data: Vec<u8> = vec![
// IVM
0xe0, 0x01, 0x01, 0xea,

// 0d0
0x60,

// 7d8
0x62, 0x01, 0x07,

// 1.27
0xF6, 0x05, 0xFD, 0x7F,

// 0d3
0x61, 0x07,

// -0
0x62, 0x07, 0x00,
];

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

assert_eq!(
reader.next()?.expect_value()?.read()?.expect_decimal()?,
0.into()
);

assert_eq!(
reader.next()?.expect_value()?.read()?.expect_decimal()?,
7.into()
);

assert_eq!(
reader.next()?.expect_value()?.read()?.expect_decimal()?,
1.27f64.try_into()?
);

assert_eq!(
reader.next()?.expect_value()?.read()?.expect_decimal()?,
0.0f64.try_into()?
);

assert_eq!(
reader.next()?.expect_value()?.read()?.expect_decimal()?,
Decimal::negative_zero()
);

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 @@ -42,11 +42,13 @@ impl Opcode {
let (opcode_type, length_code, ion_type) = match (high_nibble, low_nibble) {
(0x5, 0x0..=0x8) => (Integer, low_nibble, Some(IonType::Int)),
(0x5, 0xE..=0xF) => (Boolean, low_nibble, Some(IonType::Bool)),
(0x6, _) => (Decimal, low_nibble, Some(IonType::Decimal)),
(0x8, _) => (String, low_nibble, Some(IonType::String)),
(0xE, 0x0) => (IonVersionMarker, low_nibble, None),
(0xE, 0xA) => (NullNull, low_nibble, Some(IonType::Null)),
(0xE, 0xC..=0xD) => (Nop, low_nibble, None),
(0xF, 0x5) => (LargeInteger, low_nibble, Some(IonType::Int)),
(0xF, 0x6) => (Decimal, 0xFF, Some(IonType::Decimal)),
(0xF, 0x8) => (String, 0xFF, Some(IonType::String)), // 0xFF indicates >15 byte string.
_ => (Invalid, low_nibble, None),
};
Expand Down Expand Up @@ -113,6 +115,7 @@ impl Header {
(OpcodeType::Nop, 0xC) => InOpcode(0),
(OpcodeType::NullNull, 0xA) => InOpcode(0),
(OpcodeType::String, 0..=15) => InOpcode(self.length_code),
(OpcodeType::Decimal, 0..=15) => InOpcode(self.length_code),
_ => FlexUIntFollows,
}
}
Expand Down
28 changes: 26 additions & 2 deletions src/lazy/binary/raw/v1_1/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use crate::{
},
},
decoder::{LazyDecoder, LazyRawValue},
encoder::binary::v1_1::fixed_int::FixedInt,
encoding::BinaryEncoding_1_1,
raw_value_ref::RawValueRef,
},
Expand Down Expand Up @@ -213,7 +214,6 @@ impl<'top> LazyRawBinaryValue_1_1<'top> {

/// Helper method called by [`Self::read`]. Reads the current value as an int.
fn read_int(&self) -> ValueParseResult<'top, BinaryEncoding_1_1> {
use crate::lazy::encoder::binary::v1_1::fixed_int::FixedInt;
debug_assert!(self.encoded_value.ion_type() == IonType::Int);

let header = &self.encoded_value.header();
Expand Down Expand Up @@ -241,7 +241,31 @@ impl<'top> LazyRawBinaryValue_1_1<'top> {

/// Helper method called by [`Self::read`]. Reads the current value as a decimal.
fn read_decimal(&self) -> ValueParseResult<'top, BinaryEncoding_1_1> {
todo!();
use crate::types::decimal::*;

debug_assert!(self.encoded_value.ion_type() == IonType::Decimal);
let length_code = self.encoded_value.header.length_code as usize;
let decimal: Decimal = if length_code == 0 {
Decimal::new(0, 0)
} else {
use crate::lazy::encoder::binary::v1_1::flex_int::FlexInt;

let value_bytes = self.value_body()?;
let exponent = FlexInt::read(value_bytes, 0)?;
let coefficient_size = self.encoded_value.value_length() - exponent.size_in_bytes();
let coefficient = if coefficient_size > 0 {
FixedInt::read(
&value_bytes[exponent.size_in_bytes()..],
coefficient_size,
0,
)?
} else {
0i64.into()
};
Decimal::new(coefficient, exponent.value())
};

Ok(RawValueRef::Decimal(decimal))
}

/// Helper method called by [`Self::read`]. Reads the current value as a timestamp.
Expand Down
14 changes: 14 additions & 0 deletions src/lazy/encoder/binary/v1_1/fixed_int.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::io::Write;

use num_bigint::BigInt;

use crate::decimal::coefficient::Coefficient;
use crate::result::IonFailure;
use crate::types::integer::IntData;
use crate::{Int, IonResult};
Expand Down Expand Up @@ -99,6 +100,19 @@ impl From<FixedInt> for Int {
}
}

impl From<FixedInt> for Coefficient {
fn from(other: FixedInt) -> Self {
other.value.into()
}
}

impl From<i64> for FixedInt {
fn from(other: i64) -> Self {
let encoded_size = FixedInt::encoded_size_i64(other);
FixedInt::new(encoded_size, other)
}
}

#[cfg(test)]
mod tests {
use num_bigint::BigInt;
Expand Down

0 comments on commit 705749d

Please sign in to comment.