use crate::constants::MAX_PRECISION_U32; use crate::{ ops::array::{div_by_u32, is_all_zero, mul_by_u32}, Decimal, }; use core::fmt; use std::error; #[derive(Debug, Clone)] pub struct InvalidDecimal { inner: Option, } impl fmt::Display for InvalidDecimal { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { if let Some(ref msg) = self.inner { fmt.write_fmt(format_args!("Invalid Decimal: {}", msg)) } else { fmt.write_str("Invalid Decimal") } } } impl error::Error for InvalidDecimal {} pub(in crate::postgres) struct PostgresDecimal { pub neg: bool, pub weight: i16, pub scale: u16, pub digits: D, } impl Decimal { pub(in crate::postgres) fn from_postgres>( PostgresDecimal { neg, scale, digits, weight, }: PostgresDecimal, ) -> Self { let mut digits = digits.into_iter().collect::>(); let fractionals_part_count = digits.len() as i32 + (-weight as i32) - 1; let integers_part_count = weight as i32 + 1; let mut result = Decimal::ZERO; // adding integer part if integers_part_count > 0 { let (start_integers, last) = if integers_part_count > digits.len() as i32 { (integers_part_count - digits.len() as i32, digits.len() as i32) } else { (0, integers_part_count) }; let integers: Vec<_> = digits.drain(..last as usize).collect(); for digit in integers { result *= Decimal::from_i128_with_scale(10i128.pow(4), 0); result += Decimal::new(digit as i64, 0); } result *= Decimal::from_i128_with_scale(10i128.pow(4 * start_integers as u32), 0); } // adding fractional part if fractionals_part_count > 0 { let start_fractionals = if weight < 0 { (-weight as u32) - 1 } else { 0 }; for (i, digit) in digits.into_iter().enumerate() { let fract_pow = 4 * (i as u32 + 1 + start_fractionals); if fract_pow <= MAX_PRECISION_U32 { result += Decimal::new(digit as i64, 0) / Decimal::from_i128_with_scale(10i128.pow(fract_pow), 0); } else if fract_pow == MAX_PRECISION_U32 + 4 { // rounding last digit if digit >= 5000 { result += Decimal::new(1_i64, 0) / Decimal::from_i128_with_scale(10i128.pow(MAX_PRECISION_U32), 0); } } } } result.set_sign_negative(neg); // Rescale to the postgres value, automatically rounding as needed. result.rescale(scale as u32); result } pub(in crate::postgres) fn to_postgres(self) -> PostgresDecimal> { if self.is_zero() { return PostgresDecimal { neg: false, weight: 0, scale: 0, digits: vec![0], }; } let scale = self.scale() as u16; let groups_diff = scale & 0x3; // groups_diff = scale % 4 let mut mantissa = self.mantissa_array4(); if groups_diff > 0 { let remainder = 4 - groups_diff; let power = 10u32.pow(u32::from(remainder)); mul_by_u32(&mut mantissa, power); } // array to store max mantissa of Decimal in Postgres decimal format const MAX_GROUP_COUNT: usize = 8; let mut digits = Vec::with_capacity(MAX_GROUP_COUNT); while !is_all_zero(&mantissa) { let digit = div_by_u32(&mut mantissa, 10000) as u16; digits.push(digit.try_into().unwrap()); } digits.reverse(); let digits_after_decimal = (scale + 3) as u16 / 4; let weight = digits.len() as i16 - digits_after_decimal as i16 - 1; let unnecessary_zeroes = if weight >= 0 { let index_of_decimal = (weight + 1) as usize; digits .get(index_of_decimal..) .expect("enough digits exist") .iter() .rev() .take_while(|i| **i == 0) .count() } else { 0 }; let relevant_digits = digits.len() - unnecessary_zeroes; digits.truncate(relevant_digits); PostgresDecimal { neg: self.is_sign_negative(), digits, scale, weight, } } }