/rust/registry/src/index.crates.io-1949cf8c6b5b557f/rust_decimal-1.41.0/src/str.rs
Line | Count | Source |
1 | | use crate::{ |
2 | | constants::{BYTES_TO_OVERFLOW_U64, MAX_SCALE, MAX_STR_BUFFER_SIZE, OVERFLOW_U96, WILL_OVERFLOW_U64}, |
3 | | error::{tail_error, Error}, |
4 | | ops::array::{add_by_internal_flattened, add_one_internal, div_by_u32, is_all_zero, mul_by_u32}, |
5 | | Decimal, |
6 | | }; |
7 | | |
8 | | use arrayvec::{ArrayString, ArrayVec}; |
9 | | |
10 | | use alloc::{string::String, vec::Vec}; |
11 | | use core::fmt; |
12 | | |
13 | | // impl that doesn't allocate for serialization purposes. |
14 | 0 | pub(crate) fn to_str_internal( |
15 | 0 | value: &Decimal, |
16 | 0 | append_sign: bool, |
17 | 0 | precision: Option<usize>, |
18 | 0 | ) -> (ArrayString<MAX_STR_BUFFER_SIZE>, Option<usize>) { |
19 | | // Get the scale - where we need to put the decimal point |
20 | 0 | let scale = value.scale() as usize; |
21 | | |
22 | | // Convert to a string and manipulate that (neg at front, inject decimal) |
23 | 0 | let mut chars = ArrayVec::<_, MAX_STR_BUFFER_SIZE>::new(); |
24 | 0 | let mut working = value.mantissa_array3(); |
25 | 0 | while !is_all_zero(&working) { |
26 | 0 | let remainder = div_by_u32(&mut working, 10u32); |
27 | 0 | chars.push(char::from(b'0' + remainder as u8)); |
28 | 0 | } |
29 | 0 | while scale > chars.len() { |
30 | 0 | chars.push('0'); |
31 | 0 | } |
32 | | |
33 | 0 | let (prec, additional) = match precision { |
34 | 0 | Some(prec) => { |
35 | 0 | let max: usize = MAX_SCALE.into(); |
36 | 0 | if prec > max { |
37 | 0 | (max, Some(prec - max)) |
38 | | } else { |
39 | 0 | (prec, None) |
40 | | } |
41 | | } |
42 | 0 | None => (scale, None), |
43 | | }; |
44 | | |
45 | 0 | let len = chars.len(); |
46 | 0 | let whole_len = len - scale; |
47 | 0 | let mut rep = ArrayString::new(); |
48 | | // Append the negative sign if necessary while also keeping track of the length of an "empty" string representation |
49 | 0 | let empty_len = if append_sign && value.is_sign_negative() { |
50 | 0 | rep.push('-'); |
51 | 0 | 1 |
52 | | } else { |
53 | 0 | 0 |
54 | | }; |
55 | 0 | for i in 0..whole_len + prec { |
56 | 0 | if i == len - scale { |
57 | 0 | if i == 0 { |
58 | 0 | rep.push('0'); |
59 | 0 | } |
60 | 0 | rep.push('.'); |
61 | 0 | } |
62 | | |
63 | 0 | if i >= len { |
64 | 0 | rep.push('0'); |
65 | 0 | } else { |
66 | 0 | let c = chars[len - i - 1]; |
67 | 0 | rep.push(c); |
68 | 0 | } |
69 | | } |
70 | | |
71 | | // corner case for when we truncated everything in a low fractional |
72 | 0 | if rep.len() == empty_len { |
73 | 0 | rep.push('0'); |
74 | 0 | } |
75 | | |
76 | 0 | (rep, additional) |
77 | 0 | } |
78 | | |
79 | 0 | pub(crate) fn fmt_scientific_notation( |
80 | 0 | value: &Decimal, |
81 | 0 | exponent_symbol: &str, |
82 | 0 | f: &mut fmt::Formatter<'_>, |
83 | 0 | ) -> fmt::Result { |
84 | | #[cfg(not(feature = "std"))] |
85 | | use alloc::string::ToString; |
86 | | |
87 | 0 | if value.is_zero() { |
88 | 0 | return f.write_str("0e0"); |
89 | 0 | } |
90 | | |
91 | | // Get the scale - this is the e value. With multiples of 10 this may get bigger. |
92 | 0 | let mut exponent = -(value.scale() as isize); |
93 | | |
94 | | // Convert the integral to a string |
95 | 0 | let mut chars = Vec::new(); |
96 | 0 | let mut working = value.mantissa_array3(); |
97 | 0 | while !is_all_zero(&working) { |
98 | 0 | let remainder = div_by_u32(&mut working, 10u32); |
99 | 0 | chars.push(char::from(b'0' + remainder as u8)); |
100 | 0 | } |
101 | | |
102 | | // First of all, apply scientific notation rules. That is: |
103 | | // 1. If non-zero digit comes first, move decimal point left so that e is a positive integer |
104 | | // 2. If decimal point comes first, move decimal point right until after the first non-zero digit |
105 | | // Since decimal notation naturally lends itself this way, we just need to inject the decimal |
106 | | // point in the right place and adjust the exponent accordingly. |
107 | | |
108 | 0 | let len = chars.len(); |
109 | | let mut rep; |
110 | | // We either are operating with a precision specified, or on defaults. Defaults will perform "smart" |
111 | | // reduction of precision. |
112 | 0 | if let Some(precision) = f.precision() { |
113 | 0 | if len > 1 { |
114 | | // If we're zero precision AND it's trailing zeros then strip them |
115 | 0 | if precision == 0 && chars.iter().take(len - 1).all(|c| *c == '0') { |
116 | 0 | rep = chars.iter().skip(len - 1).collect::<String>(); |
117 | 0 | } else { |
118 | | // We may still be zero precision, however we aren't trailing zeros |
119 | 0 | if precision > 0 { |
120 | 0 | chars.insert(len - 1, '.'); |
121 | 0 | } |
122 | 0 | rep = chars |
123 | 0 | .iter() |
124 | 0 | .rev() |
125 | | // Add on extra zeros according to the precision. At least one, since we added a decimal place. |
126 | 0 | .chain(core::iter::repeat(&'0')) |
127 | 0 | .take(if precision == 0 { 1 } else { 2 + precision }) |
128 | 0 | .collect::<String>(); |
129 | | } |
130 | 0 | exponent += (len - 1) as isize; |
131 | 0 | } else if precision > 0 { |
132 | 0 | // We have precision that we want to add |
133 | 0 | chars.push('.'); |
134 | 0 | rep = chars |
135 | 0 | .iter() |
136 | 0 | .chain(core::iter::repeat(&'0')) |
137 | 0 | .take(2 + precision) |
138 | 0 | .collect::<String>(); |
139 | 0 | } else { |
140 | 0 | rep = chars.iter().collect::<String>(); |
141 | 0 | } |
142 | 0 | } else if len > 1 { |
143 | | // If the number is just trailing zeros then we treat it like 0 precision |
144 | 0 | if chars.iter().take(len - 1).all(|c| *c == '0') { |
145 | 0 | rep = chars.iter().skip(len - 1).collect::<String>(); |
146 | 0 | } else { |
147 | 0 | // Otherwise, we need to insert a decimal place and make it a scientific number |
148 | 0 | chars.insert(len - 1, '.'); |
149 | 0 | rep = chars.iter().rev().collect::<String>(); |
150 | 0 | } |
151 | 0 | exponent += (len - 1) as isize; |
152 | 0 | } else { |
153 | 0 | rep = chars.iter().collect::<String>(); |
154 | 0 | } |
155 | | |
156 | 0 | rep.push_str(exponent_symbol); |
157 | 0 | rep.push_str(&exponent.to_string()); |
158 | 0 | f.pad_integral(value.is_sign_positive(), "", &rep) |
159 | 0 | } |
160 | | |
161 | | // dedicated implementation for the most common case. |
162 | | #[inline] |
163 | 0 | pub(crate) fn parse_str_radix_10(str: &str) -> Result<Decimal, Error> { |
164 | 0 | let bytes = str.as_bytes(); |
165 | 0 | if bytes.len() < BYTES_TO_OVERFLOW_U64 { |
166 | 0 | parse_str_radix_10_dispatch::<false, true>(bytes) |
167 | | } else { |
168 | 0 | parse_str_radix_10_dispatch::<true, true>(bytes) |
169 | | } |
170 | 0 | } |
171 | | |
172 | | #[inline] |
173 | 0 | pub(crate) fn parse_str_radix_10_exact(str: &str) -> Result<Decimal, Error> { |
174 | 0 | let bytes = str.as_bytes(); |
175 | 0 | if bytes.len() < BYTES_TO_OVERFLOW_U64 { |
176 | 0 | parse_str_radix_10_dispatch::<false, false>(bytes) |
177 | | } else { |
178 | 0 | parse_str_radix_10_dispatch::<true, false>(bytes) |
179 | | } |
180 | 0 | } |
181 | | |
182 | | #[inline] |
183 | 0 | fn parse_str_radix_10_dispatch<const BIG: bool, const ROUND: bool>(bytes: &[u8]) -> Result<Decimal, Error> { |
184 | 0 | match bytes { |
185 | 0 | [b, rest @ ..] => byte_dispatch_u64::<false, false, false, BIG, true, ROUND>(rest, 0, 0, *b), |
186 | 0 | [] => tail_error("Invalid decimal: empty"), |
187 | | } |
188 | 0 | } Unexecuted instantiation: rust_decimal::str::parse_str_radix_10_dispatch::<false, false> Unexecuted instantiation: rust_decimal::str::parse_str_radix_10_dispatch::<false, true> Unexecuted instantiation: rust_decimal::str::parse_str_radix_10_dispatch::<true, true> Unexecuted instantiation: rust_decimal::str::parse_str_radix_10_dispatch::<true, false> |
189 | | |
190 | | #[inline] |
191 | 0 | fn overflow_64(val: u64) -> bool { |
192 | 0 | val >= WILL_OVERFLOW_U64 |
193 | 0 | } |
194 | | |
195 | | #[inline] |
196 | 0 | pub fn overflow_128(val: u128) -> bool { |
197 | 0 | val >= OVERFLOW_U96 |
198 | 0 | } |
199 | | |
200 | | /// Dispatch the next byte: |
201 | | /// |
202 | | /// * POINT - a decimal point has been seen |
203 | | /// * NEG - we've encountered a `-` and the number is negative |
204 | | /// * HAS - a digit has been encountered (when HAS is false it's invalid) |
205 | | /// * BIG - a number that uses 96 bits instead of only 64 bits |
206 | | /// * FIRST - true if it is the first byte in the string |
207 | | #[inline] |
208 | 0 | fn dispatch_next<const POINT: bool, const NEG: bool, const HAS: bool, const BIG: bool, const ROUND: bool>( |
209 | 0 | bytes: &[u8], |
210 | 0 | data64: u64, |
211 | 0 | scale: u8, |
212 | 0 | ) -> Result<Decimal, Error> { |
213 | 0 | if let Some((next, bytes)) = bytes.split_first() { |
214 | 0 | byte_dispatch_u64::<POINT, NEG, HAS, BIG, false, ROUND>(bytes, data64, scale, *next) |
215 | | } else { |
216 | 0 | handle_data::<NEG, HAS>(data64 as u128, scale) |
217 | | } |
218 | 0 | } Unexecuted instantiation: rust_decimal::str::dispatch_next::<false, false, false, false, false> Unexecuted instantiation: rust_decimal::str::dispatch_next::<false, false, false, false, true> Unexecuted instantiation: rust_decimal::str::dispatch_next::<false, false, false, true, true> Unexecuted instantiation: rust_decimal::str::dispatch_next::<false, false, false, true, false> Unexecuted instantiation: rust_decimal::str::dispatch_next::<false, false, true, false, false> Unexecuted instantiation: rust_decimal::str::dispatch_next::<false, false, true, false, true> Unexecuted instantiation: rust_decimal::str::dispatch_next::<false, false, true, true, false> Unexecuted instantiation: rust_decimal::str::dispatch_next::<false, false, true, true, true> Unexecuted instantiation: rust_decimal::str::dispatch_next::<false, true, false, false, false> Unexecuted instantiation: rust_decimal::str::dispatch_next::<false, true, false, false, true> Unexecuted instantiation: rust_decimal::str::dispatch_next::<false, true, false, true, false> Unexecuted instantiation: rust_decimal::str::dispatch_next::<false, true, false, true, true> Unexecuted instantiation: rust_decimal::str::dispatch_next::<false, true, true, false, false> Unexecuted instantiation: rust_decimal::str::dispatch_next::<false, true, true, false, true> Unexecuted instantiation: rust_decimal::str::dispatch_next::<false, true, true, true, false> Unexecuted instantiation: rust_decimal::str::dispatch_next::<false, true, true, true, true> Unexecuted instantiation: rust_decimal::str::dispatch_next::<true, true, true, true, true> Unexecuted instantiation: rust_decimal::str::dispatch_next::<true, true, true, true, false> Unexecuted instantiation: rust_decimal::str::dispatch_next::<true, true, true, false, false> Unexecuted instantiation: rust_decimal::str::dispatch_next::<true, true, true, false, true> Unexecuted instantiation: rust_decimal::str::dispatch_next::<true, true, false, true, true> Unexecuted instantiation: rust_decimal::str::dispatch_next::<true, true, false, true, false> Unexecuted instantiation: rust_decimal::str::dispatch_next::<true, true, false, false, true> Unexecuted instantiation: rust_decimal::str::dispatch_next::<true, true, false, false, false> Unexecuted instantiation: rust_decimal::str::dispatch_next::<true, false, true, true, true> Unexecuted instantiation: rust_decimal::str::dispatch_next::<true, false, true, true, false> Unexecuted instantiation: rust_decimal::str::dispatch_next::<true, false, true, false, true> Unexecuted instantiation: rust_decimal::str::dispatch_next::<true, false, true, false, false> Unexecuted instantiation: rust_decimal::str::dispatch_next::<true, false, false, true, true> Unexecuted instantiation: rust_decimal::str::dispatch_next::<true, false, false, true, false> Unexecuted instantiation: rust_decimal::str::dispatch_next::<true, false, false, false, true> Unexecuted instantiation: rust_decimal::str::dispatch_next::<true, false, false, false, false> |
219 | | |
220 | | /// Dispatch the next non-digit byte: |
221 | | /// |
222 | | /// * POINT - a decimal point has been seen |
223 | | /// * NEG - we've encountered a `-` and the number is negative |
224 | | /// * HAS - a digit has been encountered (when HAS is false it's invalid) |
225 | | /// * BIG - a number that uses 96 bits instead of only 64 bits |
226 | | /// * FIRST - true if it is the first byte in the string |
227 | | /// * ROUND - attempt to round underflow |
228 | | #[inline(never)] |
229 | 0 | fn non_digit_dispatch_u64< |
230 | 0 | const POINT: bool, |
231 | 0 | const NEG: bool, |
232 | 0 | const HAS: bool, |
233 | 0 | const BIG: bool, |
234 | 0 | const FIRST: bool, |
235 | 0 | const ROUND: bool, |
236 | 0 | >( |
237 | 0 | bytes: &[u8], |
238 | 0 | data64: u64, |
239 | 0 | scale: u8, |
240 | 0 | b: u8, |
241 | 0 | ) -> Result<Decimal, Error> { |
242 | 0 | match b { |
243 | 0 | b'-' if FIRST && !HAS => dispatch_next::<false, true, false, BIG, ROUND>(bytes, data64, scale), |
244 | 0 | b'+' if FIRST && !HAS => dispatch_next::<false, false, false, BIG, ROUND>(bytes, data64, scale), |
245 | 0 | b'_' if HAS => handle_separator::<POINT, NEG, BIG, ROUND>(bytes, data64, scale), |
246 | 0 | b => tail_invalid_digit(b), |
247 | | } |
248 | 0 | } Unexecuted instantiation: rust_decimal::str::non_digit_dispatch_u64::<false, false, false, false, false, false> Unexecuted instantiation: rust_decimal::str::non_digit_dispatch_u64::<false, false, false, false, false, true> Unexecuted instantiation: rust_decimal::str::non_digit_dispatch_u64::<false, false, false, false, true, true> Unexecuted instantiation: rust_decimal::str::non_digit_dispatch_u64::<false, false, false, false, true, false> Unexecuted instantiation: rust_decimal::str::non_digit_dispatch_u64::<false, false, false, true, true, true> Unexecuted instantiation: rust_decimal::str::non_digit_dispatch_u64::<false, false, false, true, true, false> Unexecuted instantiation: rust_decimal::str::non_digit_dispatch_u64::<false, false, false, true, false, true> Unexecuted instantiation: rust_decimal::str::non_digit_dispatch_u64::<false, false, false, true, false, false> Unexecuted instantiation: rust_decimal::str::non_digit_dispatch_u64::<false, false, true, true, false, true> Unexecuted instantiation: rust_decimal::str::non_digit_dispatch_u64::<false, false, true, true, false, false> Unexecuted instantiation: rust_decimal::str::non_digit_dispatch_u64::<false, false, true, false, false, true> Unexecuted instantiation: rust_decimal::str::non_digit_dispatch_u64::<false, false, true, false, false, false> Unexecuted instantiation: rust_decimal::str::non_digit_dispatch_u64::<false, true, true, true, false, true> Unexecuted instantiation: rust_decimal::str::non_digit_dispatch_u64::<false, true, true, true, false, false> Unexecuted instantiation: rust_decimal::str::non_digit_dispatch_u64::<false, true, true, false, false, true> Unexecuted instantiation: rust_decimal::str::non_digit_dispatch_u64::<false, true, true, false, false, false> Unexecuted instantiation: rust_decimal::str::non_digit_dispatch_u64::<false, true, false, true, false, true> Unexecuted instantiation: rust_decimal::str::non_digit_dispatch_u64::<false, true, false, true, false, false> Unexecuted instantiation: rust_decimal::str::non_digit_dispatch_u64::<false, true, false, false, false, true> Unexecuted instantiation: rust_decimal::str::non_digit_dispatch_u64::<false, true, false, false, false, false> Unexecuted instantiation: rust_decimal::str::non_digit_dispatch_u64::<true, true, true, true, false, false> Unexecuted instantiation: rust_decimal::str::non_digit_dispatch_u64::<true, true, true, true, false, true> Unexecuted instantiation: rust_decimal::str::non_digit_dispatch_u64::<true, true, true, false, false, false> Unexecuted instantiation: rust_decimal::str::non_digit_dispatch_u64::<true, true, true, false, false, true> Unexecuted instantiation: rust_decimal::str::non_digit_dispatch_u64::<true, true, false, false, false, false> Unexecuted instantiation: rust_decimal::str::non_digit_dispatch_u64::<true, true, false, false, false, true> Unexecuted instantiation: rust_decimal::str::non_digit_dispatch_u64::<true, true, false, true, false, false> Unexecuted instantiation: rust_decimal::str::non_digit_dispatch_u64::<true, true, false, true, false, true> Unexecuted instantiation: rust_decimal::str::non_digit_dispatch_u64::<true, false, false, false, false, false> Unexecuted instantiation: rust_decimal::str::non_digit_dispatch_u64::<true, false, false, false, false, true> Unexecuted instantiation: rust_decimal::str::non_digit_dispatch_u64::<true, false, false, true, false, false> Unexecuted instantiation: rust_decimal::str::non_digit_dispatch_u64::<true, false, false, true, false, true> Unexecuted instantiation: rust_decimal::str::non_digit_dispatch_u64::<true, false, true, false, false, false> Unexecuted instantiation: rust_decimal::str::non_digit_dispatch_u64::<true, false, true, false, false, true> Unexecuted instantiation: rust_decimal::str::non_digit_dispatch_u64::<true, false, true, true, false, false> Unexecuted instantiation: rust_decimal::str::non_digit_dispatch_u64::<true, false, true, true, false, true> |
249 | | |
250 | | #[inline] |
251 | 0 | fn byte_dispatch_u64< |
252 | 0 | const POINT: bool, |
253 | 0 | const NEG: bool, |
254 | 0 | const HAS: bool, |
255 | 0 | const BIG: bool, |
256 | 0 | const FIRST: bool, |
257 | 0 | const ROUND: bool, |
258 | 0 | >( |
259 | 0 | bytes: &[u8], |
260 | 0 | data64: u64, |
261 | 0 | scale: u8, |
262 | 0 | b: u8, |
263 | 0 | ) -> Result<Decimal, Error> { |
264 | 0 | match b { |
265 | 0 | b'0'..=b'9' => handle_digit_64::<POINT, NEG, BIG, ROUND>(bytes, data64, scale, b - b'0'), |
266 | 0 | b'.' if !POINT => handle_point::<NEG, HAS, BIG, ROUND>(bytes, data64, scale), |
267 | 0 | b => non_digit_dispatch_u64::<POINT, NEG, HAS, BIG, FIRST, ROUND>(bytes, data64, scale, b), |
268 | | } |
269 | 0 | } Unexecuted instantiation: rust_decimal::str::byte_dispatch_u64::<false, false, false, false, false, false> Unexecuted instantiation: rust_decimal::str::byte_dispatch_u64::<false, false, false, false, false, true> Unexecuted instantiation: rust_decimal::str::byte_dispatch_u64::<false, false, false, false, true, true> Unexecuted instantiation: rust_decimal::str::byte_dispatch_u64::<false, false, false, false, true, false> Unexecuted instantiation: rust_decimal::str::byte_dispatch_u64::<false, false, false, true, true, true> Unexecuted instantiation: rust_decimal::str::byte_dispatch_u64::<false, false, false, true, true, false> Unexecuted instantiation: rust_decimal::str::byte_dispatch_u64::<false, false, false, true, false, true> Unexecuted instantiation: rust_decimal::str::byte_dispatch_u64::<false, false, false, true, false, false> Unexecuted instantiation: rust_decimal::str::byte_dispatch_u64::<false, false, true, true, false, true> Unexecuted instantiation: rust_decimal::str::byte_dispatch_u64::<false, false, true, true, false, false> Unexecuted instantiation: rust_decimal::str::byte_dispatch_u64::<false, false, true, false, false, true> Unexecuted instantiation: rust_decimal::str::byte_dispatch_u64::<false, false, true, false, false, false> Unexecuted instantiation: rust_decimal::str::byte_dispatch_u64::<false, true, false, false, false, false> Unexecuted instantiation: rust_decimal::str::byte_dispatch_u64::<false, true, false, false, false, true> Unexecuted instantiation: rust_decimal::str::byte_dispatch_u64::<false, true, false, true, false, false> Unexecuted instantiation: rust_decimal::str::byte_dispatch_u64::<false, true, false, true, false, true> Unexecuted instantiation: rust_decimal::str::byte_dispatch_u64::<false, true, true, false, false, false> Unexecuted instantiation: rust_decimal::str::byte_dispatch_u64::<false, true, true, false, false, true> Unexecuted instantiation: rust_decimal::str::byte_dispatch_u64::<false, true, true, true, false, false> Unexecuted instantiation: rust_decimal::str::byte_dispatch_u64::<false, true, true, true, false, true> Unexecuted instantiation: rust_decimal::str::byte_dispatch_u64::<true, true, true, true, false, false> Unexecuted instantiation: rust_decimal::str::byte_dispatch_u64::<true, true, true, true, false, true> Unexecuted instantiation: rust_decimal::str::byte_dispatch_u64::<true, true, true, false, false, false> Unexecuted instantiation: rust_decimal::str::byte_dispatch_u64::<true, true, true, false, false, true> Unexecuted instantiation: rust_decimal::str::byte_dispatch_u64::<true, true, false, false, false, false> Unexecuted instantiation: rust_decimal::str::byte_dispatch_u64::<true, true, false, false, false, true> Unexecuted instantiation: rust_decimal::str::byte_dispatch_u64::<true, true, false, true, false, false> Unexecuted instantiation: rust_decimal::str::byte_dispatch_u64::<true, true, false, true, false, true> Unexecuted instantiation: rust_decimal::str::byte_dispatch_u64::<true, false, true, true, false, true> Unexecuted instantiation: rust_decimal::str::byte_dispatch_u64::<true, false, true, true, false, false> Unexecuted instantiation: rust_decimal::str::byte_dispatch_u64::<true, false, true, false, false, true> Unexecuted instantiation: rust_decimal::str::byte_dispatch_u64::<true, false, true, false, false, false> Unexecuted instantiation: rust_decimal::str::byte_dispatch_u64::<true, false, false, true, false, true> Unexecuted instantiation: rust_decimal::str::byte_dispatch_u64::<true, false, false, true, false, false> Unexecuted instantiation: rust_decimal::str::byte_dispatch_u64::<true, false, false, false, false, true> Unexecuted instantiation: rust_decimal::str::byte_dispatch_u64::<true, false, false, false, false, false> |
270 | | |
271 | | #[inline(never)] |
272 | 0 | fn handle_digit_64<const POINT: bool, const NEG: bool, const BIG: bool, const ROUND: bool>( |
273 | 0 | bytes: &[u8], |
274 | 0 | data64: u64, |
275 | 0 | scale: u8, |
276 | 0 | digit: u8, |
277 | 0 | ) -> Result<Decimal, Error> { |
278 | | // we have already validated that we cannot overflow |
279 | 0 | let data64 = data64 * 10 + digit as u64; |
280 | 0 | let scale = if POINT { scale + 1 } else { 0 }; |
281 | | |
282 | 0 | if let Some((next, bytes)) = bytes.split_first() { |
283 | 0 | let next = *next; |
284 | 0 | if POINT && BIG && scale >= 28 { |
285 | 0 | if ROUND { |
286 | 0 | maybe_round(data64 as u128, next, scale, POINT, NEG) |
287 | | } else { |
288 | 0 | Err(Error::Underflow) |
289 | | } |
290 | 0 | } else if BIG && overflow_64(data64) { |
291 | 0 | handle_full_128::<POINT, NEG, ROUND>(data64 as u128, bytes, scale, next) |
292 | | } else { |
293 | 0 | byte_dispatch_u64::<POINT, NEG, true, BIG, false, ROUND>(bytes, data64, scale, next) |
294 | | } |
295 | | } else { |
296 | 0 | let data: u128 = data64 as u128; |
297 | | |
298 | 0 | handle_data::<NEG, true>(data, scale) |
299 | | } |
300 | 0 | } Unexecuted instantiation: rust_decimal::str::handle_digit_64::<false, false, false, false> Unexecuted instantiation: rust_decimal::str::handle_digit_64::<false, false, false, true> Unexecuted instantiation: rust_decimal::str::handle_digit_64::<false, false, true, true> Unexecuted instantiation: rust_decimal::str::handle_digit_64::<false, false, true, false> Unexecuted instantiation: rust_decimal::str::handle_digit_64::<false, true, false, false> Unexecuted instantiation: rust_decimal::str::handle_digit_64::<false, true, false, true> Unexecuted instantiation: rust_decimal::str::handle_digit_64::<false, true, true, false> Unexecuted instantiation: rust_decimal::str::handle_digit_64::<false, true, true, true> Unexecuted instantiation: rust_decimal::str::handle_digit_64::<true, true, true, true> Unexecuted instantiation: rust_decimal::str::handle_digit_64::<true, true, true, false> Unexecuted instantiation: rust_decimal::str::handle_digit_64::<true, true, false, false> Unexecuted instantiation: rust_decimal::str::handle_digit_64::<true, true, false, true> Unexecuted instantiation: rust_decimal::str::handle_digit_64::<true, false, true, true> Unexecuted instantiation: rust_decimal::str::handle_digit_64::<true, false, true, false> Unexecuted instantiation: rust_decimal::str::handle_digit_64::<true, false, false, true> Unexecuted instantiation: rust_decimal::str::handle_digit_64::<true, false, false, false> |
301 | | |
302 | | #[inline(never)] |
303 | 0 | fn handle_point<const NEG: bool, const HAS: bool, const BIG: bool, const ROUND: bool>( |
304 | 0 | bytes: &[u8], |
305 | 0 | data64: u64, |
306 | 0 | scale: u8, |
307 | 0 | ) -> Result<Decimal, Error> { |
308 | 0 | dispatch_next::<true, NEG, HAS, BIG, ROUND>(bytes, data64, scale) |
309 | 0 | } Unexecuted instantiation: rust_decimal::str::handle_point::<false, false, false, false> Unexecuted instantiation: rust_decimal::str::handle_point::<false, false, false, true> Unexecuted instantiation: rust_decimal::str::handle_point::<false, false, true, false> Unexecuted instantiation: rust_decimal::str::handle_point::<false, false, true, true> Unexecuted instantiation: rust_decimal::str::handle_point::<false, true, false, false> Unexecuted instantiation: rust_decimal::str::handle_point::<false, true, false, true> Unexecuted instantiation: rust_decimal::str::handle_point::<false, true, true, false> Unexecuted instantiation: rust_decimal::str::handle_point::<false, true, true, true> Unexecuted instantiation: rust_decimal::str::handle_point::<true, true, true, true> Unexecuted instantiation: rust_decimal::str::handle_point::<true, true, true, false> Unexecuted instantiation: rust_decimal::str::handle_point::<true, true, false, true> Unexecuted instantiation: rust_decimal::str::handle_point::<true, true, false, false> Unexecuted instantiation: rust_decimal::str::handle_point::<true, false, true, true> Unexecuted instantiation: rust_decimal::str::handle_point::<true, false, true, false> Unexecuted instantiation: rust_decimal::str::handle_point::<true, false, false, true> Unexecuted instantiation: rust_decimal::str::handle_point::<true, false, false, false> |
310 | | |
311 | | #[inline(never)] |
312 | 0 | fn handle_separator<const POINT: bool, const NEG: bool, const BIG: bool, const ROUND: bool>( |
313 | 0 | bytes: &[u8], |
314 | 0 | data64: u64, |
315 | 0 | scale: u8, |
316 | 0 | ) -> Result<Decimal, Error> { |
317 | 0 | dispatch_next::<POINT, NEG, true, BIG, ROUND>(bytes, data64, scale) |
318 | 0 | } Unexecuted instantiation: rust_decimal::str::handle_separator::<false, false, false, false> Unexecuted instantiation: rust_decimal::str::handle_separator::<false, false, false, true> Unexecuted instantiation: rust_decimal::str::handle_separator::<false, false, true, true> Unexecuted instantiation: rust_decimal::str::handle_separator::<false, false, true, false> Unexecuted instantiation: rust_decimal::str::handle_separator::<false, true, false, false> Unexecuted instantiation: rust_decimal::str::handle_separator::<false, true, false, true> Unexecuted instantiation: rust_decimal::str::handle_separator::<false, true, true, false> Unexecuted instantiation: rust_decimal::str::handle_separator::<false, true, true, true> Unexecuted instantiation: rust_decimal::str::handle_separator::<true, true, true, true> Unexecuted instantiation: rust_decimal::str::handle_separator::<true, true, true, false> Unexecuted instantiation: rust_decimal::str::handle_separator::<true, true, false, false> Unexecuted instantiation: rust_decimal::str::handle_separator::<true, true, false, true> Unexecuted instantiation: rust_decimal::str::handle_separator::<true, false, true, true> Unexecuted instantiation: rust_decimal::str::handle_separator::<true, false, true, false> Unexecuted instantiation: rust_decimal::str::handle_separator::<true, false, false, true> Unexecuted instantiation: rust_decimal::str::handle_separator::<true, false, false, false> |
319 | | |
320 | | #[inline(never)] |
321 | | #[cold] |
322 | 0 | fn tail_invalid_digit(digit: u8) -> Result<Decimal, Error> { |
323 | 0 | match digit { |
324 | 0 | b'.' => tail_error("Invalid decimal: two decimal points"), |
325 | 0 | b'_' => tail_error("Invalid decimal: must start lead with a number"), |
326 | 0 | _ => tail_error("Invalid decimal: unknown character"), |
327 | | } |
328 | 0 | } |
329 | | |
330 | | #[inline(never)] |
331 | | #[cold] |
332 | 0 | fn handle_full_128<const POINT: bool, const NEG: bool, const ROUND: bool>( |
333 | 0 | mut data: u128, |
334 | 0 | bytes: &[u8], |
335 | 0 | scale: u8, |
336 | 0 | next_byte: u8, |
337 | 0 | ) -> Result<Decimal, Error> { |
338 | 0 | let b = next_byte; |
339 | 0 | match b { |
340 | 0 | b'0'..=b'9' => { |
341 | 0 | let digit = u32::from(b - b'0'); |
342 | | |
343 | | // If the data is going to overflow then we should go into recovery mode |
344 | 0 | let next = (data * 10) + digit as u128; |
345 | 0 | if overflow_128(next) { |
346 | 0 | if !POINT { |
347 | 0 | return tail_error("Invalid decimal: overflow from too many digits"); |
348 | 0 | } |
349 | | |
350 | 0 | if ROUND { |
351 | 0 | maybe_round(data, next_byte, scale, POINT, NEG) |
352 | | } else { |
353 | 0 | Err(Error::Underflow) |
354 | | } |
355 | | } else { |
356 | 0 | data = next; |
357 | 0 | let scale = scale + POINT as u8; |
358 | 0 | if let Some((next, bytes)) = bytes.split_first() { |
359 | 0 | let next = *next; |
360 | 0 | if POINT && scale >= 28 { |
361 | 0 | if ROUND { |
362 | | // If it is an underscore at the rounding position we require slightly different handling to look ahead another digit |
363 | 0 | if next == b'_' { |
364 | | // Skip consecutive underscores to find the next actual character |
365 | 0 | let mut remaining_bytes = bytes; |
366 | 0 | let mut next_char = None; |
367 | 0 | while let Some((n, rest)) = remaining_bytes.split_first() { |
368 | 0 | if *n != b'_' { |
369 | 0 | next_char = Some(*n); |
370 | 0 | break; |
371 | 0 | } |
372 | 0 | remaining_bytes = rest; |
373 | | } |
374 | | |
375 | 0 | if let Some(ch) = next_char { |
376 | | // Skip underscores and use the next character for rounding |
377 | 0 | maybe_round(data, ch, scale, POINT, NEG) |
378 | | } else { |
379 | 0 | handle_data::<NEG, true>(data, scale) |
380 | | } |
381 | | } else { |
382 | | // Otherwise, we round as usual |
383 | 0 | maybe_round(data, next, scale, POINT, NEG) |
384 | | } |
385 | | } else { |
386 | 0 | Err(Error::Underflow) |
387 | | } |
388 | | } else { |
389 | 0 | handle_full_128::<POINT, NEG, ROUND>(data, bytes, scale, next) |
390 | | } |
391 | | } else { |
392 | 0 | handle_data::<NEG, true>(data, scale) |
393 | | } |
394 | | } |
395 | | } |
396 | 0 | b'.' if !POINT => { |
397 | | // This call won't tail? |
398 | 0 | if let Some((next, bytes)) = bytes.split_first() { |
399 | 0 | handle_full_128::<true, NEG, ROUND>(data, bytes, scale, *next) |
400 | | } else { |
401 | 0 | handle_data::<NEG, true>(data, scale) |
402 | | } |
403 | | } |
404 | | b'_' => { |
405 | 0 | if let Some((next, bytes)) = bytes.split_first() { |
406 | 0 | handle_full_128::<POINT, NEG, ROUND>(data, bytes, scale, *next) |
407 | | } else { |
408 | 0 | handle_data::<NEG, true>(data, scale) |
409 | | } |
410 | | } |
411 | 0 | b => tail_invalid_digit(b), |
412 | | } |
413 | 0 | } Unexecuted instantiation: rust_decimal::str::handle_full_128::<false, false, false> Unexecuted instantiation: rust_decimal::str::handle_full_128::<false, false, true> Unexecuted instantiation: rust_decimal::str::handle_full_128::<false, true, false> Unexecuted instantiation: rust_decimal::str::handle_full_128::<false, true, true> Unexecuted instantiation: rust_decimal::str::handle_full_128::<true, true, true> Unexecuted instantiation: rust_decimal::str::handle_full_128::<true, true, false> Unexecuted instantiation: rust_decimal::str::handle_full_128::<true, false, true> Unexecuted instantiation: rust_decimal::str::handle_full_128::<true, false, false> |
414 | | |
415 | | #[inline(never)] |
416 | | #[cold] |
417 | 0 | fn maybe_round(mut data: u128, next_byte: u8, mut scale: u8, point: bool, negative: bool) -> Result<Decimal, Error> { |
418 | 0 | let digit = match next_byte { |
419 | 0 | b'0'..=b'9' => u32::from(next_byte - b'0'), |
420 | 0 | b'_' => 0, // This is perhaps an error case, but keep this here for compatibility |
421 | 0 | b'.' if !point => 0, |
422 | 0 | b => return tail_invalid_digit(b), |
423 | | }; |
424 | | |
425 | | // Round at midpoint |
426 | 0 | if digit >= 5 { |
427 | 0 | data += 1; |
428 | | |
429 | | // If the mantissa is now overflowing, round to the next |
430 | | // next least significant digit and discard precision |
431 | 0 | if overflow_128(data) { |
432 | 0 | if scale == 0 { |
433 | 0 | return tail_error("Invalid decimal: overflow from mantissa after rounding"); |
434 | 0 | } |
435 | 0 | data += 4; |
436 | 0 | data /= 10; |
437 | 0 | scale -= 1; |
438 | 0 | } |
439 | 0 | } |
440 | | |
441 | 0 | if negative { |
442 | 0 | handle_data::<true, true>(data, scale) |
443 | | } else { |
444 | 0 | handle_data::<false, true>(data, scale) |
445 | | } |
446 | 0 | } |
447 | | |
448 | | #[inline(never)] |
449 | 0 | fn tail_no_has() -> Result<Decimal, Error> { |
450 | 0 | tail_error("Invalid decimal: no digits found") |
451 | 0 | } |
452 | | |
453 | | #[inline] |
454 | 0 | fn handle_data<const NEG: bool, const HAS: bool>(data: u128, scale: u8) -> Result<Decimal, Error> { |
455 | 0 | debug_assert_eq!(data >> 96, 0); |
456 | 0 | if !HAS { |
457 | 0 | tail_no_has() |
458 | | } else { |
459 | 0 | Ok(Decimal::from_parts( |
460 | 0 | data as u32, |
461 | 0 | (data >> 32) as u32, |
462 | 0 | (data >> 64) as u32, |
463 | 0 | NEG, |
464 | 0 | scale as u32, |
465 | 0 | )) |
466 | | } |
467 | 0 | } Unexecuted instantiation: rust_decimal::str::handle_data::<false, false> Unexecuted instantiation: rust_decimal::str::handle_data::<false, true> Unexecuted instantiation: rust_decimal::str::handle_data::<true, true> Unexecuted instantiation: rust_decimal::str::handle_data::<true, false> |
468 | | |
469 | 0 | pub(crate) fn parse_str_radix_n(str: &str, radix: u32) -> Result<Decimal, Error> { |
470 | 0 | if str.is_empty() { |
471 | 0 | return Err(Error::from("Invalid decimal: empty")); |
472 | 0 | } |
473 | 0 | if radix < 2 { |
474 | 0 | return Err(Error::from("Unsupported radix < 2")); |
475 | 0 | } |
476 | 0 | if radix > 36 { |
477 | | // As per trait documentation |
478 | 0 | return Err(Error::from("Unsupported radix > 36")); |
479 | 0 | } |
480 | | |
481 | 0 | let mut offset = 0; |
482 | 0 | let mut len = str.len(); |
483 | 0 | let bytes = str.as_bytes(); |
484 | 0 | let mut negative = false; // assume positive |
485 | | |
486 | | // handle the sign |
487 | 0 | if bytes[offset] == b'-' { |
488 | 0 | negative = true; // leading minus means negative |
489 | 0 | offset += 1; |
490 | 0 | len -= 1; |
491 | 0 | } else if bytes[offset] == b'+' { |
492 | 0 | // leading + allowed |
493 | 0 | offset += 1; |
494 | 0 | len -= 1; |
495 | 0 | } |
496 | | |
497 | | // should now be at numeric part of the significand |
498 | 0 | let mut digits_before_dot: i32 = -1; // digits before '.', -1 if no '.' |
499 | 0 | let mut coeff = ArrayVec::<_, 96>::new(); // integer significand array |
500 | | |
501 | | // Supporting different radix |
502 | 0 | let (max_n, max_alpha_lower, max_alpha_upper) = if radix <= 10 { |
503 | 0 | (b'0' + (radix - 1) as u8, 0, 0) |
504 | | } else { |
505 | 0 | let adj = (radix - 11) as u8; |
506 | 0 | (b'9', adj + b'a', adj + b'A') |
507 | | }; |
508 | | |
509 | | // Estimate the max precision. All in all, it needs to fit into 96 bits. |
510 | | // Rather than try to estimate, I've included the constants directly in here. We could, |
511 | | // perhaps, replace this with a formula if it's faster - though it does appear to be log2. |
512 | 0 | let estimated_max_precision = match radix { |
513 | 0 | 2 => 96, |
514 | 0 | 3 => 61, |
515 | 0 | 4 => 48, |
516 | 0 | 5 => 42, |
517 | 0 | 6 => 38, |
518 | 0 | 7 => 35, |
519 | 0 | 8 => 32, |
520 | 0 | 9 => 31, |
521 | 0 | 10 => 28, |
522 | 0 | 11 => 28, |
523 | 0 | 12 => 27, |
524 | 0 | 13 => 26, |
525 | 0 | 14 => 26, |
526 | 0 | 15 => 25, |
527 | 0 | 16 => 24, |
528 | 0 | 17 => 24, |
529 | 0 | 18 => 24, |
530 | 0 | 19 => 23, |
531 | 0 | 20 => 23, |
532 | 0 | 21 => 22, |
533 | 0 | 22 => 22, |
534 | 0 | 23 => 22, |
535 | 0 | 24 => 21, |
536 | 0 | 25 => 21, |
537 | 0 | 26 => 21, |
538 | 0 | 27 => 21, |
539 | 0 | 28 => 20, |
540 | 0 | 29 => 20, |
541 | 0 | 30 => 20, |
542 | 0 | 31 => 20, |
543 | 0 | 32 => 20, |
544 | 0 | 33 => 20, |
545 | 0 | 34 => 19, |
546 | 0 | 35 => 19, |
547 | 0 | 36 => 19, |
548 | 0 | _ => return Err(Error::from("Unsupported radix")), |
549 | | }; |
550 | | |
551 | 0 | let mut maybe_round = false; |
552 | 0 | while len > 0 { |
553 | 0 | let b = bytes[offset]; |
554 | 0 | match b { |
555 | 0 | b'0'..=b'9' => { |
556 | 0 | if b > max_n { |
557 | 0 | return Err(Error::from("Invalid decimal: invalid character")); |
558 | 0 | } |
559 | 0 | coeff.push(u32::from(b - b'0')); |
560 | 0 | offset += 1; |
561 | 0 | len -= 1; |
562 | | |
563 | | // If the coefficient is longer than the max, exit early |
564 | 0 | if coeff.len() as u32 > estimated_max_precision { |
565 | 0 | maybe_round = true; |
566 | 0 | break; |
567 | 0 | } |
568 | | } |
569 | 0 | b'a'..=b'z' => { |
570 | 0 | if b > max_alpha_lower { |
571 | 0 | return Err(Error::from("Invalid decimal: invalid character")); |
572 | 0 | } |
573 | 0 | coeff.push(u32::from(b - b'a') + 10); |
574 | 0 | offset += 1; |
575 | 0 | len -= 1; |
576 | | |
577 | 0 | if coeff.len() as u32 > estimated_max_precision { |
578 | 0 | maybe_round = true; |
579 | 0 | break; |
580 | 0 | } |
581 | | } |
582 | 0 | b'A'..=b'Z' => { |
583 | 0 | if b > max_alpha_upper { |
584 | 0 | return Err(Error::from("Invalid decimal: invalid character")); |
585 | 0 | } |
586 | 0 | coeff.push(u32::from(b - b'A') + 10); |
587 | 0 | offset += 1; |
588 | 0 | len -= 1; |
589 | | |
590 | 0 | if coeff.len() as u32 > estimated_max_precision { |
591 | 0 | maybe_round = true; |
592 | 0 | break; |
593 | 0 | } |
594 | | } |
595 | | b'.' => { |
596 | 0 | if digits_before_dot >= 0 { |
597 | 0 | return Err(Error::from("Invalid decimal: two decimal points")); |
598 | 0 | } |
599 | 0 | digits_before_dot = coeff.len() as i32; |
600 | 0 | offset += 1; |
601 | 0 | len -= 1; |
602 | | } |
603 | | b'_' => { |
604 | | // Must start with a number... |
605 | 0 | if coeff.is_empty() { |
606 | 0 | return Err(Error::from("Invalid decimal: must start lead with a number")); |
607 | 0 | } |
608 | 0 | offset += 1; |
609 | 0 | len -= 1; |
610 | | } |
611 | 0 | _ => return Err(Error::from("Invalid decimal: unknown character")), |
612 | | } |
613 | | } |
614 | | |
615 | | // If we exited before the end of the string then do some rounding if necessary |
616 | 0 | if maybe_round && offset < bytes.len() { |
617 | 0 | let next_byte = bytes[offset]; |
618 | 0 | let digit = match next_byte { |
619 | 0 | b'0'..=b'9' => { |
620 | 0 | if next_byte > max_n { |
621 | 0 | return Err(Error::from("Invalid decimal: invalid character")); |
622 | 0 | } |
623 | 0 | u32::from(next_byte - b'0') |
624 | | } |
625 | 0 | b'a'..=b'z' => { |
626 | 0 | if next_byte > max_alpha_lower { |
627 | 0 | return Err(Error::from("Invalid decimal: invalid character")); |
628 | 0 | } |
629 | 0 | u32::from(next_byte - b'a') + 10 |
630 | | } |
631 | 0 | b'A'..=b'Z' => { |
632 | 0 | if next_byte > max_alpha_upper { |
633 | 0 | return Err(Error::from("Invalid decimal: invalid character")); |
634 | 0 | } |
635 | 0 | u32::from(next_byte - b'A') + 10 |
636 | | } |
637 | 0 | b'_' => 0, |
638 | | b'.' => { |
639 | | // Still an error if we have a second dp |
640 | 0 | if digits_before_dot >= 0 { |
641 | 0 | return Err(Error::from("Invalid decimal: two decimal points")); |
642 | 0 | } |
643 | 0 | 0 |
644 | | } |
645 | 0 | _ => return Err(Error::from("Invalid decimal: unknown character")), |
646 | | }; |
647 | | |
648 | | // Round at midpoint |
649 | 0 | let midpoint = if radix & 0x1 == 1 { radix / 2 } else { (radix + 1) / 2 }; |
650 | 0 | if digit >= midpoint { |
651 | 0 | let mut index = coeff.len() - 1; |
652 | | loop { |
653 | 0 | let new_digit = coeff[index] + 1; |
654 | 0 | if new_digit <= 9 { |
655 | 0 | coeff[index] = new_digit; |
656 | 0 | break; |
657 | | } else { |
658 | 0 | coeff[index] = 0; |
659 | 0 | if index == 0 { |
660 | 0 | coeff.insert(0, 1u32); |
661 | 0 | digits_before_dot += 1; |
662 | 0 | coeff.pop(); |
663 | 0 | break; |
664 | 0 | } |
665 | | } |
666 | 0 | index -= 1; |
667 | | } |
668 | 0 | } |
669 | 0 | } |
670 | | |
671 | | // here when no characters left |
672 | 0 | if coeff.is_empty() { |
673 | 0 | return Err(Error::from("Invalid decimal: no digits found")); |
674 | 0 | } |
675 | | |
676 | 0 | let mut scale = if digits_before_dot >= 0 { |
677 | | // we had a decimal place so set the scale |
678 | 0 | (coeff.len() as u32) - (digits_before_dot as u32) |
679 | | } else { |
680 | 0 | 0 |
681 | | }; |
682 | | |
683 | | // Parse this using specified radix |
684 | 0 | let mut data = [0u32, 0u32, 0u32]; |
685 | 0 | let mut tmp = [0u32, 0u32, 0u32]; |
686 | 0 | let len = coeff.len(); |
687 | 0 | for (i, digit) in coeff.iter().enumerate() { |
688 | | // If the data is going to overflow then we should go into recovery mode |
689 | 0 | tmp[0] = data[0]; |
690 | 0 | tmp[1] = data[1]; |
691 | 0 | tmp[2] = data[2]; |
692 | 0 | let overflow = mul_by_u32(&mut tmp, radix); |
693 | 0 | if overflow > 0 { |
694 | | // This means that we have more data to process, that we're not sure what to do with. |
695 | | // This may or may not be an issue - depending on whether we're past a decimal point |
696 | | // or not. |
697 | 0 | if (i as i32) < digits_before_dot && i + 1 < len { |
698 | 0 | return Err(Error::from("Invalid decimal: overflow from too many digits")); |
699 | 0 | } |
700 | | |
701 | 0 | if *digit >= 5 { |
702 | 0 | let carry = add_one_internal(&mut data); |
703 | 0 | if carry > 0 { |
704 | | // Highly unlikely scenario which is more indicative of a bug |
705 | 0 | return Err(Error::from("Invalid decimal: overflow when rounding")); |
706 | 0 | } |
707 | 0 | } |
708 | | // We're also one less digit so reduce the scale |
709 | 0 | let diff = (len - i) as u32; |
710 | 0 | if diff > scale { |
711 | 0 | return Err(Error::from("Invalid decimal: overflow from scale mismatch")); |
712 | 0 | } |
713 | 0 | scale -= diff; |
714 | 0 | break; |
715 | | } else { |
716 | 0 | data[0] = tmp[0]; |
717 | 0 | data[1] = tmp[1]; |
718 | 0 | data[2] = tmp[2]; |
719 | 0 | let carry = add_by_internal_flattened(&mut data, *digit); |
720 | 0 | if carry > 0 { |
721 | | // Highly unlikely scenario which is more indicative of a bug |
722 | 0 | return Err(Error::from("Invalid decimal: overflow from carry")); |
723 | 0 | } |
724 | | } |
725 | | } |
726 | | |
727 | 0 | Ok(Decimal::from_parts(data[0], data[1], data[2], negative, scale)) |
728 | 0 | } |
729 | | |
730 | | #[cfg(test)] |
731 | | mod test { |
732 | | use super::*; |
733 | | use crate::Decimal; |
734 | | use arrayvec::ArrayString; |
735 | | use core::{fmt::Write, str::FromStr}; |
736 | | |
737 | | #[test] |
738 | | fn display_does_not_overflow_max_capacity() { |
739 | | let num = Decimal::from_str("1.2").unwrap(); |
740 | | let mut buffer = ArrayString::<64>::new(); |
741 | | buffer.write_fmt(format_args!("{num:.31}")).unwrap(); |
742 | | assert_eq!("1.2000000000000000000000000000000", buffer.as_str()); |
743 | | } |
744 | | |
745 | | #[test] |
746 | | fn from_str_rounding_0() { |
747 | | assert_eq!( |
748 | | parse_str_radix_10("1.234").unwrap().unpack(), |
749 | | Decimal::new(1234, 3).unpack() |
750 | | ); |
751 | | } |
752 | | |
753 | | #[test] |
754 | | fn from_str_rounding_1() { |
755 | | assert_eq!( |
756 | | parse_str_radix_10("11111_11111_11111.11111_11111_11111") |
757 | | .unwrap() |
758 | | .unpack(), |
759 | | Decimal::from_i128_with_scale(11_111_111_111_111_111_111_111_111_111, 14).unpack() |
760 | | ); |
761 | | } |
762 | | |
763 | | #[test] |
764 | | fn from_str_rounding_2() { |
765 | | assert_eq!( |
766 | | parse_str_radix_10("11111_11111_11111.11111_11111_11115") |
767 | | .unwrap() |
768 | | .unpack(), |
769 | | Decimal::from_i128_with_scale(11_111_111_111_111_111_111_111_111_112, 14).unpack() |
770 | | ); |
771 | | } |
772 | | |
773 | | #[test] |
774 | | fn from_str_rounding_3() { |
775 | | assert_eq!( |
776 | | parse_str_radix_10("11111_11111_11111.11111_11111_11195") |
777 | | .unwrap() |
778 | | .unpack(), |
779 | | Decimal::from_i128_with_scale(1_111_111_111_111_111_111_111_111_1120, 14).unpack() // was Decimal::from_i128_with_scale(1_111_111_111_111_111_111_111_111_112, 13) |
780 | | ); |
781 | | } |
782 | | |
783 | | #[test] |
784 | | fn from_str_rounding_4() { |
785 | | assert_eq!( |
786 | | parse_str_radix_10("99999_99999_99999.99999_99999_99995") |
787 | | .unwrap() |
788 | | .unpack(), |
789 | | Decimal::from_i128_with_scale(10_000_000_000_000_000_000_000_000_000, 13).unpack() // was Decimal::from_i128_with_scale(1_000_000_000_000_000_000_000_000_000, 12) |
790 | | ); |
791 | | } |
792 | | |
793 | | #[test] |
794 | | fn from_str_no_rounding_0() { |
795 | | assert_eq!( |
796 | | parse_str_radix_10_exact("1.234").unwrap().unpack(), |
797 | | Decimal::new(1234, 3).unpack() |
798 | | ); |
799 | | } |
800 | | |
801 | | #[test] |
802 | | fn from_str_no_rounding_1() { |
803 | | assert_eq!( |
804 | | parse_str_radix_10_exact("11111_11111_11111.11111_11111_11111"), |
805 | | Err(Error::Underflow) |
806 | | ); |
807 | | } |
808 | | |
809 | | #[test] |
810 | | fn from_str_no_rounding_2() { |
811 | | assert_eq!( |
812 | | parse_str_radix_10_exact("11111_11111_11111.11111_11111_11115"), |
813 | | Err(Error::Underflow) |
814 | | ); |
815 | | } |
816 | | |
817 | | #[test] |
818 | | fn from_str_no_rounding_3() { |
819 | | assert_eq!( |
820 | | parse_str_radix_10_exact("11111_11111_11111.11111_11111_11195"), |
821 | | Err(Error::Underflow) |
822 | | ); |
823 | | } |
824 | | |
825 | | #[test] |
826 | | fn from_str_no_rounding_4() { |
827 | | assert_eq!( |
828 | | parse_str_radix_10_exact("99999_99999_99999.99999_99999_99995"), |
829 | | Err(Error::Underflow) |
830 | | ); |
831 | | } |
832 | | |
833 | | #[test] |
834 | | fn from_str_many_pointless_chars() { |
835 | | assert_eq!( |
836 | | parse_str_radix_10("00________________________________________________________________001.1") |
837 | | .unwrap() |
838 | | .unpack(), |
839 | | Decimal::from_i128_with_scale(11, 1).unpack() |
840 | | ); |
841 | | } |
842 | | |
843 | | #[test] |
844 | | fn from_str_leading_0s_1() { |
845 | | assert_eq!( |
846 | | parse_str_radix_10("00001.1").unwrap().unpack(), |
847 | | Decimal::from_i128_with_scale(11, 1).unpack() |
848 | | ); |
849 | | } |
850 | | |
851 | | #[test] |
852 | | fn from_str_leading_0s_2() { |
853 | | assert_eq!( |
854 | | parse_str_radix_10("00000_00000_00000_00000_00001.00001") |
855 | | .unwrap() |
856 | | .unpack(), |
857 | | Decimal::from_i128_with_scale(100001, 5).unpack() |
858 | | ); |
859 | | } |
860 | | |
861 | | #[test] |
862 | | fn from_str_leading_0s_3() { |
863 | | assert_eq!( |
864 | | parse_str_radix_10("0.00000_00000_00000_00000_00000_00100") |
865 | | .unwrap() |
866 | | .unpack(), |
867 | | Decimal::from_i128_with_scale(1, 28).unpack() |
868 | | ); |
869 | | } |
870 | | |
871 | | #[test] |
872 | | fn from_str_trailing_0s_1() { |
873 | | assert_eq!( |
874 | | parse_str_radix_10("0.00001_00000_00000").unwrap().unpack(), |
875 | | Decimal::from_i128_with_scale(10_000_000_000, 15).unpack() |
876 | | ); |
877 | | } |
878 | | |
879 | | #[test] |
880 | | fn from_str_trailing_0s_2() { |
881 | | assert_eq!( |
882 | | parse_str_radix_10("0.00001_00000_00000_00000_00000_00000") |
883 | | .unwrap() |
884 | | .unpack(), |
885 | | Decimal::from_i128_with_scale(100_000_000_000_000_000_000_000, 28).unpack() |
886 | | ); |
887 | | } |
888 | | |
889 | | #[test] |
890 | | fn from_str_overflow_1() { |
891 | | assert_eq!( |
892 | | parse_str_radix_10("99999_99999_99999_99999_99999_99999.99999"), |
893 | | // The original implementation returned |
894 | | // Ok(10000_00000_00000_00000_00000_0000) |
895 | | // Which is a bug! |
896 | | Err(Error::from("Invalid decimal: overflow from too many digits")) |
897 | | ); |
898 | | } |
899 | | |
900 | | #[test] |
901 | | fn from_str_overflow_2() { |
902 | | assert!( |
903 | | parse_str_radix_10("99999_99999_99999_99999_99999_11111.11111").is_err(), |
904 | | // The original implementation is 'overflow from scale mismatch' |
905 | | // but we got rid of that now |
906 | | ); |
907 | | } |
908 | | |
909 | | #[test] |
910 | | fn from_str_overflow_3() { |
911 | | assert!( |
912 | | parse_str_radix_10("99999_99999_99999_99999_99999_99994").is_err() // We could not get into 'overflow when rounding' or 'overflow from carry' |
913 | | // in the original implementation because the rounding logic before prevented it |
914 | | ); |
915 | | } |
916 | | |
917 | | #[test] |
918 | | fn from_str_overflow_4() { |
919 | | assert_eq!( |
920 | | // This does not overflow, moving the decimal point 1 more step would result in |
921 | | // 'overflow from too many digits' |
922 | | parse_str_radix_10("99999_99999_99999_99999_99999_999.99") |
923 | | .unwrap() |
924 | | .unpack(), |
925 | | Decimal::from_i128_with_scale(10_000_000_000_000_000_000_000_000_000, 0).unpack() |
926 | | ); |
927 | | } |
928 | | |
929 | | #[test] |
930 | | fn from_str_mantissa_overflow_1() { |
931 | | // reminder: |
932 | | assert_eq!(OVERFLOW_U96, 79_228_162_514_264_337_593_543_950_336); |
933 | | assert_eq!( |
934 | | parse_str_radix_10("79_228_162_514_264_337_593_543_950_33.56") |
935 | | .unwrap() |
936 | | .unpack(), |
937 | | Decimal::from_i128_with_scale(79_228_162_514_264_337_593_543_950_34, 0).unpack() |
938 | | ); |
939 | | // This is a mantissa of OVERFLOW_U96 - 1 just before reaching the last digit. |
940 | | // Previously, this would return Err("overflow from mantissa after rounding") |
941 | | // instead of successfully rounding. |
942 | | } |
943 | | |
944 | | #[test] |
945 | | fn from_str_mantissa_overflow_2() { |
946 | | assert_eq!( |
947 | | parse_str_radix_10("79_228_162_514_264_337_593_543_950_335.6"), |
948 | | Err(Error::from("Invalid decimal: overflow from mantissa after rounding")) |
949 | | ); |
950 | | // this case wants to round to 79_228_162_514_264_337_593_543_950_340. |
951 | | // (79_228_162_514_264_337_593_543_950_336 is OVERFLOW_U96 and too large |
952 | | // to fit in 96 bits) which is also too large for the mantissa so fails. |
953 | | } |
954 | | |
955 | | #[test] |
956 | | fn from_str_mantissa_overflow_3() { |
957 | | // this hits the other avoidable overflow case in maybe_round |
958 | | assert_eq!( |
959 | | parse_str_radix_10("7.92281625142643375935439503356").unwrap().unpack(), |
960 | | Decimal::from_i128_with_scale(79_228_162_514_264_337_593_543_950_34, 27).unpack() |
961 | | ); |
962 | | } |
963 | | |
964 | | #[test] |
965 | | fn from_str_mantissa_overflow_4() { |
966 | | // Same test as above, however with underscores. This causes issues. |
967 | | assert_eq!( |
968 | | parse_str_radix_10("7.9_228_162_514_264_337_593_543_950_335_6") |
969 | | .unwrap() |
970 | | .unpack(), |
971 | | Decimal::from_i128_with_scale(79_228_162_514_264_337_593_543_950_34, 27).unpack() |
972 | | ); |
973 | | } |
974 | | |
975 | | #[test] |
976 | | fn invalid_input_1() { |
977 | | assert_eq!( |
978 | | parse_str_radix_10("1.0000000000000000000000000000.5"), |
979 | | Err(Error::from("Invalid decimal: two decimal points")) |
980 | | ); |
981 | | } |
982 | | |
983 | | #[test] |
984 | | fn invalid_input_2() { |
985 | | assert_eq!( |
986 | | parse_str_radix_10("1.0.5"), |
987 | | Err(Error::from("Invalid decimal: two decimal points")) |
988 | | ); |
989 | | } |
990 | | |
991 | | #[test] |
992 | | fn character_at_rounding_position() { |
993 | | let tests = [ |
994 | | // digit is at the rounding position |
995 | | ( |
996 | | "1.000_000_000_000_000_000_000_000_000_04", |
997 | | Ok(Decimal::from_i128_with_scale( |
998 | | 1_000_000_000_000_000_000_000_000_000_0, |
999 | | 28, |
1000 | | )), |
1001 | | ), |
1002 | | ( |
1003 | | "1.000_000_000_000_000_000_000_000_000_06", |
1004 | | Ok(Decimal::from_i128_with_scale( |
1005 | | 1_000_000_000_000_000_000_000_000_000_1, |
1006 | | 28, |
1007 | | )), |
1008 | | ), |
1009 | | // Decimal point is at the rounding position |
1010 | | ( |
1011 | | "1_000_000_000_000_000_000_000_000_000_0.4", |
1012 | | Ok(Decimal::from_i128_with_scale( |
1013 | | 1_000_000_000_000_000_000_000_000_000_0, |
1014 | | 0, |
1015 | | )), |
1016 | | ), |
1017 | | ( |
1018 | | "1_000_000_000_000_000_000_000_000_000_0.6", |
1019 | | Ok(Decimal::from_i128_with_scale( |
1020 | | 1_000_000_000_000_000_000_000_000_000_1, |
1021 | | 0, |
1022 | | )), |
1023 | | ), |
1024 | | // Placeholder is at the rounding position |
1025 | | ( |
1026 | | "1.000_000_000_000_000_000_000_000_000_0_4", |
1027 | | Ok(Decimal::from_i128_with_scale( |
1028 | | 1_000_000_000_000_000_000_000_000_000_0, |
1029 | | 28, |
1030 | | )), |
1031 | | ), |
1032 | | ( |
1033 | | "1.000_000_000_000_000_000_000_000_000_0_6", |
1034 | | Ok(Decimal::from_i128_with_scale( |
1035 | | 1_000_000_000_000_000_000_000_000_000_1, |
1036 | | 28, |
1037 | | )), |
1038 | | ), |
1039 | | // Multiple placeholders at rounding position |
1040 | | ( |
1041 | | "1.000_000_000_000_000_000_000_000_000_0__4", |
1042 | | Ok(Decimal::from_i128_with_scale( |
1043 | | 1_000_000_000_000_000_000_000_000_000_0, |
1044 | | 28, |
1045 | | )), |
1046 | | ), |
1047 | | ( |
1048 | | "1.000_000_000_000_000_000_000_000_000_0__6", |
1049 | | Ok(Decimal::from_i128_with_scale( |
1050 | | 1_000_000_000_000_000_000_000_000_000_1, |
1051 | | 28, |
1052 | | )), |
1053 | | ), |
1054 | | ( |
1055 | | "1.234567890123456789012345678_9", |
1056 | | Ok(Decimal::from_i128_with_scale(12345678901234567890123456789, 28)), |
1057 | | ), |
1058 | | ( |
1059 | | "0.234567890123456789012345678_9", |
1060 | | Ok(Decimal::from_i128_with_scale(2345678901234567890123456789, 28)), |
1061 | | ), |
1062 | | ( |
1063 | | "0.1234567890123456789012345678_9", |
1064 | | Ok(Decimal::from_i128_with_scale(1234567890123456789012345679, 28)), |
1065 | | ), |
1066 | | ( |
1067 | | "0.1234567890123456789012345678_4", |
1068 | | Ok(Decimal::from_i128_with_scale(1234567890123456789012345678, 28)), |
1069 | | ), |
1070 | | ]; |
1071 | | |
1072 | | for (input, expected) in tests.iter() { |
1073 | | assert_eq!(parse_str_radix_10(input), *expected, "Test input {}", input); |
1074 | | } |
1075 | | } |
1076 | | |
1077 | | #[test] |
1078 | | fn from_str_edge_cases_1() { |
1079 | | assert_eq!(parse_str_radix_10(""), Err(Error::from("Invalid decimal: empty"))); |
1080 | | } |
1081 | | |
1082 | | #[test] |
1083 | | fn from_str_edge_cases_2() { |
1084 | | assert_eq!( |
1085 | | parse_str_radix_10("0.1."), |
1086 | | Err(Error::from("Invalid decimal: two decimal points")) |
1087 | | ); |
1088 | | } |
1089 | | |
1090 | | #[test] |
1091 | | fn from_str_edge_cases_3() { |
1092 | | assert_eq!( |
1093 | | parse_str_radix_10("_"), |
1094 | | Err(Error::from("Invalid decimal: must start lead with a number")) |
1095 | | ); |
1096 | | } |
1097 | | |
1098 | | #[test] |
1099 | | fn from_str_edge_cases_4() { |
1100 | | assert_eq!( |
1101 | | parse_str_radix_10("1?2"), |
1102 | | Err(Error::from("Invalid decimal: unknown character")) |
1103 | | ); |
1104 | | } |
1105 | | |
1106 | | #[test] |
1107 | | fn from_str_edge_cases_5() { |
1108 | | assert_eq!( |
1109 | | parse_str_radix_10("."), |
1110 | | Err(Error::from("Invalid decimal: no digits found")) |
1111 | | ); |
1112 | | } |
1113 | | |
1114 | | #[test] |
1115 | | fn from_str_edge_cases_6() { |
1116 | | // Decimal::MAX + 0.99999 |
1117 | | assert_eq!( |
1118 | | parse_str_radix_10("79_228_162_514_264_337_593_543_950_335.99999"), |
1119 | | Err(Error::from("Invalid decimal: overflow from mantissa after rounding")) |
1120 | | ); |
1121 | | } |
1122 | | |
1123 | | #[test] |
1124 | | fn to_scientific_0() { |
1125 | | #[cfg(not(feature = "std"))] |
1126 | | use alloc::format; |
1127 | | |
1128 | | let dec_zero = format!("{:e}", Decimal::ZERO); |
1129 | | let int_zero = format!("{:e}", 0_u32); |
1130 | | assert_eq!(dec_zero, int_zero); |
1131 | | assert_eq!( |
1132 | | Decimal::from_scientific(&dec_zero) |
1133 | | .unwrap_or_else(|e| panic!("Can't parse {dec_zero} into Decimal: {e:?}")), |
1134 | | Decimal::ZERO |
1135 | | ); |
1136 | | assert_eq!(dec_zero, "0e0"); |
1137 | | } |
1138 | | } |