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