Coverage Report

Created: 2025-07-12 06:37

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