[go: up one dir, main page]

bson/
decimal128.rs

1//! [BSON Decimal128](https://github.com/mongodb/specifications/blob/master/source/bson-decimal128/decimal128.rst) data type representation
2
3use std::{convert::TryInto, fmt, num::ParseIntError};
4
5use bitvec::prelude::*;
6
7use crate::error::{Decimal128ErrorKind, Error, Result};
8
9/// Struct representing a BSON Decimal128 type.
10///
11/// This type supports conversion to and from human-readable strings via the [std::fmt::Display] and
12/// [std::str::FromStr] traits:
13///
14/// ```rust
15/// # use std::str::FromStr;
16/// # use bson::Decimal128;
17/// # fn example() -> std::result::Result<(), Box<dyn std::error::Error>> {
18/// let value: Decimal128 = "3.14159".parse()?;
19/// assert_eq!("3.14159", format!("{}", value));
20/// let scientific = Decimal128::from_str("1.05E+3")?;
21/// assert_eq!("1.05E+3", scientific.to_string());
22/// # Ok(())
23/// # }
24/// # example().unwrap()
25/// ```
26#[derive(Copy, Clone, Hash, PartialEq, Eq)]
27pub struct Decimal128 {
28    /// BSON bytes containing the decimal128. Stored for round tripping.
29    pub(crate) bytes: [u8; 16],
30}
31
32impl Decimal128 {
33    /// Constructs a new `Decimal128` from the provided raw byte representation.
34    pub fn from_bytes(bytes: [u8; 128 / 8]) -> Self {
35        Self { bytes }
36    }
37
38    /// Returns the raw byte representation of this `Decimal128`.
39    pub fn bytes(&self) -> [u8; 128 / 8] {
40        self.bytes
41    }
42
43    #[cfg(feature = "serde")]
44    pub(crate) fn deserialize_from_slice<E: serde::de::Error>(
45        bytes: &[u8],
46    ) -> std::result::Result<Self, E> {
47        let arr: [u8; 128 / 8] = bytes.try_into().map_err(E::custom)?;
48        Ok(Decimal128 { bytes: arr })
49    }
50}
51
52impl fmt::Debug for Decimal128 {
53    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
54        write!(f, "Decimal128({})", hex::encode(self.bytes))
55    }
56}
57
58impl fmt::Display for Decimal128 {
59    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
60        write!(f, "{}", ParsedDecimal128::new(self))
61    }
62}
63
64impl std::str::FromStr for Decimal128 {
65    type Err = Error;
66
67    fn from_str(s: &str) -> Result<Self> {
68        Ok(s.parse::<ParsedDecimal128>()?.pack())
69    }
70}
71
72#[derive(Debug, Clone, PartialEq)]
73struct ParsedDecimal128 {
74    sign: bool,
75    kind: Decimal128Kind,
76}
77
78#[derive(Debug, Clone, PartialEq)]
79enum Decimal128Kind {
80    NaN {
81        signalling: bool,
82    },
83    Infinity,
84    Finite {
85        exponent: Exponent,
86        coefficient: Coefficient,
87    },
88}
89
90#[derive(Debug, Clone, PartialEq)]
91struct Exponent([u8; 2]);
92
93impl Exponent {
94    /// The exponent is stored as an unsigned value; `BIAS` is subtracted to get the actual value.
95    const BIAS: i16 = 6176;
96    /// The minimum representable exponent.  This is distinct from the specifications "min" value,
97    /// which marks the point at which exponents are considered subnormal.
98    const TINY: i16 = -6176;
99    /// The maximum representable exponent.
100    const MAX: i16 = 6111;
101
102    /// The number of unused bits in the parsed representation.
103    const UNUSED_BITS: usize = 2;
104    /// The total number of bits in the packed representation.
105    const PACKED_WIDTH: usize = 14;
106
107    fn from_bits(src_bits: &BitSlice<u8, Msb0>) -> Self {
108        let mut bytes = [0u8; 2];
109        bytes.view_bits_mut::<Msb0>()[Self::UNUSED_BITS..].copy_from_bitslice(src_bits);
110        Self(bytes)
111    }
112
113    fn from_native(value: i16) -> Self {
114        let mut bytes = [0u8; 2];
115        bytes.view_bits_mut::<Msb0>().store_be(value + Self::BIAS);
116        Self(bytes)
117    }
118
119    fn bits(&self) -> &BitSlice<u8, Msb0> {
120        &self.0.view_bits::<Msb0>()[Self::UNUSED_BITS..]
121    }
122
123    fn raw(&self) -> u16 {
124        self.0.view_bits::<Msb0>().load_be::<u16>()
125    }
126
127    fn value(&self) -> i16 {
128        (self.raw() as i16) - Self::BIAS
129    }
130}
131
132#[derive(Debug, Clone, PartialEq)]
133struct Coefficient([u8; 16]);
134
135impl Coefficient {
136    /// The number of unused bits in the parsed representation.
137    const UNUSED_BITS: usize = 14;
138    /// The maximum number of digits allowed in a base-10 string representation of the coefficient.
139    const MAX_DIGITS: usize = 34;
140    /// The maximum allowable value of a coefficient.
141    const MAX_VALUE: u128 = 9_999_999_999_999_999_999_999_999_999_999_999;
142
143    fn from_bits(src_prefix: &BitSlice<u8, Msb0>, src_suffix: &BitSlice<u8, Msb0>) -> Result<Self> {
144        let mut bytes = [0u8; 16];
145        let bits = &mut bytes.view_bits_mut::<Msb0>()[Self::UNUSED_BITS..];
146        let prefix_len = src_prefix.len();
147        bits[0..prefix_len].copy_from_bitslice(src_prefix);
148        bits[prefix_len..].copy_from_bitslice(src_suffix);
149        let out = Self(bytes);
150        if out.value() > Self::MAX_VALUE {
151            Err(Error::decimal128(Decimal128ErrorKind::Overflow {}))
152        } else {
153            Ok(out)
154        }
155    }
156
157    fn from_native(value: u128) -> Self {
158        let mut bytes = [0u8; 16];
159        bytes.view_bits_mut::<Msb0>().store_be(value);
160        Self(bytes)
161    }
162
163    fn bits(&self) -> &BitSlice<u8, Msb0> {
164        &self.0.view_bits::<Msb0>()[Self::UNUSED_BITS..]
165    }
166
167    fn value(&self) -> u128 {
168        self.0.view_bits::<Msb0>().load_be::<u128>()
169    }
170}
171
172impl ParsedDecimal128 {
173    fn new(source: &Decimal128) -> Self {
174        // BSON byte order is the opposite of the decimal128 spec byte order, so flip 'em.  The rest
175        // of this method could be rewritten to not need this, but readability is helped by
176        // keeping the implementation congruent with the spec.
177        let tmp: [u8; 16] = {
178            let mut tmp = [0u8; 16];
179            tmp.view_bits_mut::<Msb0>()
180                .store_be(source.bytes.view_bits::<Msb0>().load_le::<u128>());
181            tmp
182        };
183        let src_bits = tmp.view_bits::<Msb0>();
184
185        let sign = src_bits[0];
186        let kind = if src_bits[1..5].all() {
187            // Special value
188            if src_bits[5] {
189                Decimal128Kind::NaN {
190                    signalling: src_bits[6],
191                }
192            } else {
193                Decimal128Kind::Infinity
194            }
195        } else {
196            // Finite value
197            let exponent_offset;
198            let coeff_prefix;
199            if src_bits[1..3].all() {
200                exponent_offset = 3;
201                coeff_prefix = bits![static u8, Msb0; 1, 0, 0];
202            } else {
203                exponent_offset = 1;
204                coeff_prefix = bits![static u8, Msb0; 0];
205            }
206            let coeff_offset = exponent_offset + Exponent::PACKED_WIDTH;
207
208            let exponent = Exponent::from_bits(&src_bits[exponent_offset..coeff_offset]);
209            let coefficient = match Coefficient::from_bits(coeff_prefix, &src_bits[coeff_offset..])
210            {
211                Ok(c) => c,
212                // Invalid coefficients get silently replaced with zero.
213                Err(_) => Coefficient([0u8; 16]),
214            };
215            Decimal128Kind::Finite {
216                exponent,
217                coefficient,
218            }
219        };
220        ParsedDecimal128 { sign, kind }
221    }
222
223    fn pack(&self) -> Decimal128 {
224        let mut tmp = [0u8; 16];
225        let dest_bits = tmp.view_bits_mut::<Msb0>();
226
227        dest_bits.set(0, self.sign);
228
229        match &self.kind {
230            Decimal128Kind::NaN { signalling } => {
231                dest_bits[1..6].copy_from_bitslice(bits![u8, Msb0; 1, 1, 1, 1, 1]);
232                dest_bits.set(6, *signalling);
233            }
234            Decimal128Kind::Infinity => {
235                dest_bits[1..6].copy_from_bitslice(bits![u8, Msb0; 1, 1, 1, 1, 0]);
236            }
237            Decimal128Kind::Finite {
238                exponent,
239                coefficient,
240            } => {
241                let mut coeff_bits = coefficient.bits();
242                let exponent_offset;
243                if coeff_bits[0] {
244                    dest_bits.set(1, true);
245                    dest_bits.set(2, true);
246                    coeff_bits = &coeff_bits[3..];
247                    exponent_offset = 3;
248                } else {
249                    coeff_bits = &coeff_bits[1..];
250                    exponent_offset = 1;
251                };
252                let coeff_offset = exponent_offset + Exponent::PACKED_WIDTH;
253                dest_bits[exponent_offset..coeff_offset].copy_from_bitslice(exponent.bits());
254                dest_bits[coeff_offset..].copy_from_bitslice(coeff_bits);
255            }
256        }
257
258        let mut bytes = [0u8; 16];
259        bytes
260            .view_bits_mut::<Msb0>()
261            .store_le(tmp.view_bits::<Msb0>().load_be::<u128>());
262        Decimal128 { bytes }
263    }
264}
265
266impl fmt::Display for ParsedDecimal128 {
267    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
268        // MongoDB diverges from the IEEE spec and requires no sign for NaN
269        if self.sign && !matches!(&self.kind, Decimal128Kind::NaN { .. }) {
270            write!(f, "-")?;
271        }
272        match &self.kind {
273            Decimal128Kind::NaN {
274                signalling: _signalling,
275            } => {
276                /* Likewise, MongoDB requires no 's' prefix for signalling.
277                if *signalling {
278                    write!(f, "s")?;
279                }
280                */
281                write!(f, "NaN")?;
282            }
283            Decimal128Kind::Infinity => write!(f, "Infinity")?,
284            Decimal128Kind::Finite {
285                exponent,
286                coefficient,
287            } => {
288                let coeff_str = format!("{}", coefficient.value());
289                let exp_val = exponent.value();
290                let adj_exp = exp_val + (coeff_str.len() as i16) - 1;
291                if exp_val <= 0 && adj_exp >= -6 {
292                    // Plain notation
293                    if exp_val == 0 {
294                        write!(f, "{}", coeff_str)?;
295                    } else {
296                        let dec_charlen = exp_val.unsigned_abs() as usize;
297                        if dec_charlen >= coeff_str.len() {
298                            write!(f, "0.")?;
299                            write!(f, "{}", "0".repeat(dec_charlen - coeff_str.len()))?;
300                            write!(f, "{}", coeff_str)?;
301                        } else {
302                            let (pre, post) = coeff_str.split_at(coeff_str.len() - dec_charlen);
303                            write!(f, "{}", pre)?;
304                            write!(f, ".")?;
305                            write!(f, "{}", post)?;
306                        }
307                    }
308                } else {
309                    // Exponential notation
310                    let (pre, post) = coeff_str.split_at(1);
311                    write!(f, "{}", pre)?;
312                    if !post.is_empty() {
313                        write!(f, ".{}", post)?;
314                    }
315                    write!(f, "E")?;
316                    if adj_exp > 0 {
317                        write!(f, "+")?;
318                    }
319                    write!(f, "{}", adj_exp)?;
320                }
321            }
322        }
323        Ok(())
324    }
325}
326
327impl std::str::FromStr for ParsedDecimal128 {
328    type Err = Error;
329
330    fn from_str(mut s: &str) -> Result<Self> {
331        let sign;
332        if let Some(rest) = s.strip_prefix(&['-', '+'][..]) {
333            sign = s.starts_with('-');
334            s = rest;
335        } else {
336            sign = false;
337        }
338        let kind = match s.to_ascii_lowercase().as_str() {
339            "nan" => Decimal128Kind::NaN { signalling: false },
340            "snan" => Decimal128Kind::NaN { signalling: true },
341            "infinity" | "inf" => Decimal128Kind::Infinity,
342            finite_str => {
343                // Split into parts
344                let mut decimal_str;
345                let exp_str;
346                match finite_str.split_once('e') {
347                    None => {
348                        decimal_str = finite_str;
349                        exp_str = "0";
350                    }
351                    Some((_, "")) => {
352                        return Err(Error::decimal128(Decimal128ErrorKind::EmptyExponent {}))
353                    }
354                    Some((pre, post)) => {
355                        decimal_str = pre;
356                        exp_str = post;
357                    }
358                }
359                // Initially parse the exponent as an i128 to handle corner cases with zero
360                // coefficients and very large exponents.
361                let mut wide_exp = exp_str.parse::<i128>().map_err(|e| {
362                    Error::decimal128(Decimal128ErrorKind::InvalidExponent {}).with_message(e)
363                })?;
364
365                // Remove decimal point and adjust exponent
366                let joined_str;
367                if let Some((pre, post)) = decimal_str.split_once('.') {
368                    let exp_adj = post
369                        .len()
370                        .try_into()
371                        .map_err(|_| Error::decimal128(Decimal128ErrorKind::Underflow {}))?;
372                    wide_exp = wide_exp
373                        .checked_sub(exp_adj)
374                        .ok_or_else(|| Error::decimal128(Decimal128ErrorKind::Underflow {}))?;
375                    joined_str = format!("{}{}", pre, post);
376                    decimal_str = &joined_str;
377                }
378
379                // Strip leading zeros
380                let rest = decimal_str.trim_start_matches('0');
381                decimal_str = if rest.is_empty() { "0" } else { rest };
382
383                // Check decimal precision
384                {
385                    let len = decimal_str.len();
386                    if len > Coefficient::MAX_DIGITS {
387                        decimal_str = round_decimal_str(decimal_str, Coefficient::MAX_DIGITS)?;
388                        let exp_adj = (len - decimal_str.len())
389                            .try_into()
390                            .map_err(|_| Error::decimal128(Decimal128ErrorKind::Overflow {}))?;
391                        wide_exp = wide_exp
392                            .checked_add(exp_adj)
393                            .ok_or_else(|| Error::decimal128(Decimal128ErrorKind::Overflow {}))?;
394                    }
395                }
396
397                // Check exponent limits
398                const TINY: i128 = Exponent::TINY as i128;
399                if wide_exp < TINY {
400                    if decimal_str != "0" {
401                        let delta = (TINY - wide_exp)
402                            .try_into()
403                            .map_err(|_| Error::decimal128(Decimal128ErrorKind::Overflow {}))?;
404                        let new_precision = decimal_str
405                            .len()
406                            .checked_sub(delta)
407                            .ok_or_else(|| Error::decimal128(Decimal128ErrorKind::Overflow {}))?;
408                        decimal_str = round_decimal_str(decimal_str, new_precision)?;
409                    }
410                    wide_exp = Exponent::TINY.into();
411                }
412                let padded_str;
413                const MAX: i128 = Exponent::MAX as i128;
414                if wide_exp > MAX {
415                    if decimal_str != "0" {
416                        let delta = (wide_exp - MAX)
417                            .try_into()
418                            .map_err(|_| Error::decimal128(Decimal128ErrorKind::Overflow {}))?;
419                        if decimal_str
420                            .len()
421                            .checked_add(delta)
422                            .ok_or_else(|| Error::decimal128(Decimal128ErrorKind::Overflow {}))?
423                            > Coefficient::MAX_DIGITS
424                        {
425                            return Err(Error::decimal128(Decimal128ErrorKind::Overflow {}));
426                        }
427                        padded_str = format!("{}{}", decimal_str, "0".repeat(delta));
428                        decimal_str = &padded_str;
429                    }
430                    wide_exp = Exponent::MAX.into();
431                }
432
433                // Assemble the final value
434                let exp: i16 = wide_exp
435                    .try_into()
436                    .map_err(|_| Error::decimal128(Decimal128ErrorKind::Overflow {}))?;
437                let exponent = Exponent::from_native(exp);
438                let coeff: u128 = decimal_str.parse().map_err(|e: ParseIntError| {
439                    Error::decimal128(Decimal128ErrorKind::InvalidCoefficient {}).with_message(e)
440                })?;
441                let coefficient = Coefficient::from_native(coeff);
442                Decimal128Kind::Finite {
443                    exponent,
444                    coefficient,
445                }
446            }
447        };
448
449        Ok(Self { sign, kind })
450    }
451}
452
453fn round_decimal_str(s: &str, precision: usize) -> Result<&str> {
454    let (pre, post) = s
455        .split_at_checked(precision)
456        .ok_or_else(|| Error::decimal128(Decimal128ErrorKind::Unparseable {}))?;
457    // Any nonzero trimmed digits mean it would be an imprecise round.
458    if post.chars().any(|c| c != '0') {
459        return Err(Error::decimal128(Decimal128ErrorKind::InexactRounding {}));
460    }
461    Ok(pre)
462}