use std::io::Write;
use arrayvec::ArrayVec;
use crate::binary::int::DecodedInt;
use crate::binary::var_int::VarInt;
use crate::binary::var_uint::VarUInt;
use crate::decimal::Sign;
use crate::ion_data::IonEq;
use crate::result::{IonFailure, IonResult};
use crate::{Decimal, Int, IonError};
const MAX_INLINE_LENGTH: usize = 13;
const DECIMAL_BUFFER_SIZE: usize = 32;
const DECIMAL_POSITIVE_ZERO: Decimal = Decimal {
coefficient_value: Int::ZERO,
coefficient_sign: Sign::Positive,
exponent: 0,
};
pub trait DecimalBinaryEncoder {
fn encode_decimal(&mut self, decimal: &Decimal) -> IonResult<usize>;
fn encode_decimal_value(&mut self, decimal: &Decimal) -> IonResult<usize>;
}
impl<W> DecimalBinaryEncoder for W
where
W: Write,
{
fn encode_decimal(&mut self, decimal: &Decimal) -> IonResult<usize> {
if decimal.ion_eq(&DECIMAL_POSITIVE_ZERO) {
return Ok(0);
}
let mut bytes_written: usize = 0;
bytes_written += VarInt::write_i64(self, decimal.exponent)?;
match decimal.coefficient().as_int() {
Some(int) if int == Int::ZERO => {
}
Some(int) => {
bytes_written += DecodedInt::write(self, int)?;
}
None => {
bytes_written += DecodedInt::write_negative_zero(self)?;
}
}
Ok(bytes_written)
}
fn encode_decimal_value(&mut self, decimal: &Decimal) -> IonResult<usize> {
let mut bytes_written: usize = 0;
let mut encoded: ArrayVec<u8, DECIMAL_BUFFER_SIZE> = ArrayVec::new();
encoded.encode_decimal(decimal).map_err(|_e| {
IonError::encoding_error("found a decimal that was too large for the configured buffer")
})?;
let type_descriptor: u8;
if encoded.len() <= MAX_INLINE_LENGTH {
type_descriptor = 0x50 | encoded.len() as u8;
self.write_all(&[type_descriptor])?;
bytes_written += 1;
} else {
type_descriptor = 0x5E;
self.write_all(&[type_descriptor])?;
bytes_written += 1;
bytes_written += VarUInt::write_u64(self, encoded.len() as u64)?;
}
self.write_all(&encoded[..])?;
bytes_written += encoded.len();
Ok(bytes_written)
}
}
#[cfg(test)]
mod binary_decimal_tests {
use crate::lazy::any_encoding::AnyEncoding;
use crate::lazy::encoder::writer::Writer;
use crate::lazy::encoding::{BinaryEncoding_1_0, Encoding};
use crate::lazy::reader::Reader;
use rstest::*;
use super::*;
#[test]
fn decimal_0d0_is_a_special_zero_value() {
assert_eq!(DECIMAL_POSITIVE_ZERO, Decimal::new(0, 0));
assert!(DECIMAL_POSITIVE_ZERO.ion_eq(&Decimal::new(0, 0)));
assert_eq!(DECIMAL_POSITIVE_ZERO, Decimal::new(0, 10));
assert!(!DECIMAL_POSITIVE_ZERO.ion_eq(&Decimal::new(0, 10)));
assert_eq!(DECIMAL_POSITIVE_ZERO, Decimal::new(0, 100));
assert!(!DECIMAL_POSITIVE_ZERO.ion_eq(&Decimal::new(0, 100)));
}
#[rstest]
#[case::exactly_zero(Decimal::new(0, 0), 1)]
#[case::zero_with_nonzero_exp(Decimal::new(0, 10), 2)]
#[case::meaning_of_life(Decimal::new(42, 0), 3)]
fn bytes_written(#[case] input: Decimal, #[case] expected: usize) -> IonResult<()> {
let mut buf = vec![];
let written = buf.encode_decimal_value(&input)?;
assert_eq!(buf.len(), expected);
assert_eq!(written, expected);
Ok(())
}
#[rstest]
#[case::foo(Decimal::new(i128::MAX, 0))]
#[case::foo(Decimal::new(i128::MAX - 1, 0))]
#[case::foo(Decimal::new(i128::MIN, 0))]
#[case::foo(Decimal::new(i128::MIN + 1, 0))]
#[case::foo(Decimal::new(i128::MAX, i32::MAX))]
#[case::foo(Decimal::new(i128::MAX - 1, i32::MAX))]
#[case::foo(Decimal::new(i128::MIN, i32::MIN))]
#[case::foo(Decimal::new(i128::MIN, i32::MIN))]
#[case::foo(Decimal::new(i128::MIN + 1, i32::MIN))]
fn roundtrip_decimals_with_extreme_values(#[case] value: Decimal) -> IonResult<()> {
let mut writer = Writer::new(BinaryEncoding_1_0::default_write_config(), Vec::new())?;
writer.write(&value)?;
let output = writer.close()?;
let mut reader = Reader::new(AnyEncoding, output)?;
let after_round_trip = reader.expect_next()?.read()?.expect_decimal()?;
assert_eq!(value, after_round_trip);
Ok(())
}
}