/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 | } |