diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/rust_decimal/src/postgres/common.rs | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/rust_decimal/src/postgres/common.rs')
-rw-r--r-- | third_party/rust/rust_decimal/src/postgres/common.rs | 140 |
1 files changed, 140 insertions, 0 deletions
diff --git a/third_party/rust/rust_decimal/src/postgres/common.rs b/third_party/rust/rust_decimal/src/postgres/common.rs new file mode 100644 index 0000000000..b821b1edaa --- /dev/null +++ b/third_party/rust/rust_decimal/src/postgres/common.rs @@ -0,0 +1,140 @@ +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<String>, +} + +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<D> { + pub neg: bool, + pub weight: i16, + pub scale: u16, + pub digits: D, +} + +impl Decimal { + pub(in crate::postgres) fn from_postgres<D: ExactSizeIterator<Item = u16>>( + PostgresDecimal { + neg, + scale, + digits, + weight, + }: PostgresDecimal<D>, + ) -> Self { + let mut digits = digits.into_iter().collect::<Vec<_>>(); + + 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<Vec<i16>> { + 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, + } + } +} |