/rust/registry/src/index.crates.io-1949cf8c6b5b557f/jiff-0.2.16/src/fmt/util.rs
Line | Count | Source |
1 | | use crate::{ |
2 | | error::{err, ErrorContext}, |
3 | | fmt::Parsed, |
4 | | util::{c::Sign, escape, parse, t}, |
5 | | Error, SignedDuration, Span, Unit, |
6 | | }; |
7 | | |
8 | | /// A simple formatter for converting `i64` values to ASCII byte strings. |
9 | | /// |
10 | | /// This avoids going through the formatting machinery which seems to |
11 | | /// substantially slow things down. |
12 | | /// |
13 | | /// The `itoa` crate does the same thing as this formatter, but is a bit |
14 | | /// faster. We roll our own which is a bit slower, but gets us enough of a win |
15 | | /// to be satisfied with and with (almost) pure safe code. |
16 | | /// |
17 | | /// By default, this only includes the sign if it's negative. To always include |
18 | | /// the sign, set `force_sign` to `true`. |
19 | | #[derive(Clone, Copy, Debug)] |
20 | | pub(crate) struct DecimalFormatter { |
21 | | force_sign: Option<bool>, |
22 | | minimum_digits: u8, |
23 | | padding_byte: u8, |
24 | | } |
25 | | |
26 | | impl DecimalFormatter { |
27 | | /// Creates a new decimal formatter using the default configuration. |
28 | 0 | pub(crate) const fn new() -> DecimalFormatter { |
29 | 0 | DecimalFormatter { |
30 | 0 | force_sign: None, |
31 | 0 | minimum_digits: 0, |
32 | 0 | padding_byte: b'0', |
33 | 0 | } |
34 | 0 | } |
35 | | |
36 | | /// Format the given value using this configuration as a signed decimal |
37 | | /// ASCII number. |
38 | | #[cfg_attr(feature = "perf-inline", inline(always))] |
39 | 0 | pub(crate) const fn format_signed(&self, value: i64) -> Decimal { |
40 | 0 | Decimal::signed(self, value) |
41 | 0 | } |
42 | | |
43 | | /// Format the given value using this configuration as an unsigned decimal |
44 | | /// ASCII number. |
45 | | #[cfg_attr(feature = "perf-inline", inline(always))] |
46 | 0 | pub(crate) const fn format_unsigned(&self, value: u64) -> Decimal { |
47 | 0 | Decimal::unsigned(self, value) |
48 | 0 | } |
49 | | |
50 | | /// Forces the sign to be rendered, even if it's positive. |
51 | | /// |
52 | | /// When `zero_is_positive` is true, then a zero value is formatted with a |
53 | | /// positive sign. Otherwise, it is formatted with a negative sign. |
54 | | /// |
55 | | /// Regardless of this setting, a sign is never emitted when formatting an |
56 | | /// unsigned integer. |
57 | | #[cfg(test)] |
58 | | pub(crate) const fn force_sign( |
59 | | self, |
60 | | zero_is_positive: bool, |
61 | | ) -> DecimalFormatter { |
62 | | DecimalFormatter { force_sign: Some(zero_is_positive), ..self } |
63 | | } |
64 | | |
65 | | /// The minimum number of digits/padding that this number should be |
66 | | /// formatted with. If the number would have fewer digits than this, then |
67 | | /// it is padded out with the padding byte (which is zero by default) until |
68 | | /// the minimum is reached. |
69 | | /// |
70 | | /// The minimum number of digits is capped at the maximum number of digits |
71 | | /// for an i64 value (19) or a u64 value (20). |
72 | 0 | pub(crate) const fn padding(self, mut digits: u8) -> DecimalFormatter { |
73 | 0 | if digits > Decimal::MAX_I64_DIGITS { |
74 | 0 | digits = Decimal::MAX_I64_DIGITS; |
75 | 0 | } |
76 | 0 | DecimalFormatter { minimum_digits: digits, ..self } |
77 | 0 | } |
78 | | |
79 | | /// The padding byte to use when `padding` is set. |
80 | | /// |
81 | | /// The default is `0`. |
82 | 0 | pub(crate) const fn padding_byte(self, byte: u8) -> DecimalFormatter { |
83 | 0 | DecimalFormatter { padding_byte: byte, ..self } |
84 | 0 | } |
85 | | |
86 | | /// Returns the minimum number of digits for a signed value. |
87 | 0 | const fn get_signed_minimum_digits(&self) -> u8 { |
88 | 0 | if self.minimum_digits <= Decimal::MAX_I64_DIGITS { |
89 | 0 | self.minimum_digits |
90 | | } else { |
91 | 0 | Decimal::MAX_I64_DIGITS |
92 | | } |
93 | 0 | } |
94 | | |
95 | | /// Returns the minimum number of digits for an unsigned value. |
96 | 0 | const fn get_unsigned_minimum_digits(&self) -> u8 { |
97 | 0 | if self.minimum_digits <= Decimal::MAX_U64_DIGITS { |
98 | 0 | self.minimum_digits |
99 | | } else { |
100 | 0 | Decimal::MAX_U64_DIGITS |
101 | | } |
102 | 0 | } |
103 | | } |
104 | | |
105 | | impl Default for DecimalFormatter { |
106 | 0 | fn default() -> DecimalFormatter { |
107 | 0 | DecimalFormatter::new() |
108 | 0 | } |
109 | | } |
110 | | |
111 | | /// A formatted decimal number that can be converted to a sequence of bytes. |
112 | | #[derive(Debug)] |
113 | | pub(crate) struct Decimal { |
114 | | buf: [u8; Self::MAX_LEN as usize], |
115 | | start: u8, |
116 | | end: u8, |
117 | | } |
118 | | |
119 | | impl Decimal { |
120 | | /// Discovered via |
121 | | /// `i64::MIN.to_string().len().max(u64::MAX.to_string().len())`. |
122 | | const MAX_LEN: u8 = 20; |
123 | | /// Discovered via `i64::MAX.to_string().len()`. |
124 | | const MAX_I64_DIGITS: u8 = 19; |
125 | | /// Discovered via `u64::MAX.to_string().len()`. |
126 | | const MAX_U64_DIGITS: u8 = 20; |
127 | | |
128 | | /// Using the given formatter, turn the value given into an unsigned |
129 | | /// decimal representation using ASCII bytes. |
130 | | #[cfg_attr(feature = "perf-inline", inline(always))] |
131 | 0 | const fn unsigned( |
132 | 0 | formatter: &DecimalFormatter, |
133 | 0 | mut value: u64, |
134 | 0 | ) -> Decimal { |
135 | 0 | let mut decimal = Decimal { |
136 | 0 | buf: [0; Self::MAX_LEN as usize], |
137 | 0 | start: Self::MAX_LEN, |
138 | 0 | end: Self::MAX_LEN, |
139 | 0 | }; |
140 | | loop { |
141 | 0 | decimal.start -= 1; |
142 | | |
143 | 0 | let digit = (value % 10) as u8; |
144 | 0 | value /= 10; |
145 | 0 | decimal.buf[decimal.start as usize] = b'0' + digit; |
146 | 0 | if value == 0 { |
147 | 0 | break; |
148 | 0 | } |
149 | | } |
150 | | |
151 | 0 | while decimal.len() < formatter.get_unsigned_minimum_digits() { |
152 | 0 | decimal.start -= 1; |
153 | 0 | decimal.buf[decimal.start as usize] = formatter.padding_byte; |
154 | 0 | } |
155 | 0 | decimal |
156 | 0 | } |
157 | | |
158 | | /// Using the given formatter, turn the value given into a signed decimal |
159 | | /// representation using ASCII bytes. |
160 | | #[cfg_attr(feature = "perf-inline", inline(always))] |
161 | 0 | const fn signed(formatter: &DecimalFormatter, mut value: i64) -> Decimal { |
162 | | // Specialize the common case to generate tighter codegen. |
163 | 0 | if value >= 0 && formatter.force_sign.is_none() { |
164 | 0 | let mut decimal = Decimal { |
165 | 0 | buf: [0; Self::MAX_LEN as usize], |
166 | 0 | start: Self::MAX_LEN, |
167 | 0 | end: Self::MAX_LEN, |
168 | 0 | }; |
169 | | loop { |
170 | 0 | decimal.start -= 1; |
171 | | |
172 | 0 | let digit = (value % 10) as u8; |
173 | 0 | value /= 10; |
174 | 0 | decimal.buf[decimal.start as usize] = b'0' + digit; |
175 | 0 | if value == 0 { |
176 | 0 | break; |
177 | 0 | } |
178 | | } |
179 | | |
180 | 0 | while decimal.len() < formatter.get_signed_minimum_digits() { |
181 | 0 | decimal.start -= 1; |
182 | 0 | decimal.buf[decimal.start as usize] = formatter.padding_byte; |
183 | 0 | } |
184 | 0 | return decimal; |
185 | 0 | } |
186 | 0 | Decimal::signed_cold(formatter, value) |
187 | 0 | } |
188 | | |
189 | | #[cold] |
190 | | #[inline(never)] |
191 | 0 | const fn signed_cold(formatter: &DecimalFormatter, value: i64) -> Decimal { |
192 | 0 | let sign = value.signum(); |
193 | 0 | let Some(mut value) = value.checked_abs() else { |
194 | 0 | let buf = [ |
195 | 0 | b'-', b'9', b'2', b'2', b'3', b'3', b'7', b'2', b'0', b'3', |
196 | 0 | b'6', b'8', b'5', b'4', b'7', b'7', b'5', b'8', b'0', b'8', |
197 | 0 | ]; |
198 | 0 | return Decimal { buf, start: 0, end: Self::MAX_LEN }; |
199 | | }; |
200 | 0 | let mut decimal = Decimal { |
201 | 0 | buf: [0; Self::MAX_LEN as usize], |
202 | 0 | start: Self::MAX_LEN, |
203 | 0 | end: Self::MAX_LEN, |
204 | 0 | }; |
205 | | loop { |
206 | 0 | decimal.start -= 1; |
207 | | |
208 | 0 | let digit = (value % 10) as u8; |
209 | 0 | value /= 10; |
210 | 0 | decimal.buf[decimal.start as usize] = b'0' + digit; |
211 | 0 | if value == 0 { |
212 | 0 | break; |
213 | 0 | } |
214 | | } |
215 | 0 | while decimal.len() < formatter.get_signed_minimum_digits() { |
216 | 0 | decimal.start -= 1; |
217 | 0 | decimal.buf[decimal.start as usize] = formatter.padding_byte; |
218 | 0 | } |
219 | 0 | if sign < 0 { |
220 | 0 | decimal.start -= 1; |
221 | 0 | decimal.buf[decimal.start as usize] = b'-'; |
222 | 0 | } else if let Some(zero_is_positive) = formatter.force_sign { |
223 | 0 | let ascii_sign = |
224 | 0 | if sign > 0 || zero_is_positive { b'+' } else { b'-' }; |
225 | 0 | decimal.start -= 1; |
226 | 0 | decimal.buf[decimal.start as usize] = ascii_sign; |
227 | 0 | } |
228 | 0 | decimal |
229 | 0 | } |
230 | | |
231 | | /// Returns the total number of ASCII bytes (including the sign) that are |
232 | | /// used to represent this decimal number. |
233 | | #[inline] |
234 | 0 | const fn len(&self) -> u8 { |
235 | 0 | self.end - self.start |
236 | 0 | } |
237 | | |
238 | | /// Returns the ASCII representation of this decimal as a byte slice. |
239 | | /// |
240 | | /// The slice returned is guaranteed to be valid ASCII. |
241 | | #[inline] |
242 | 0 | fn as_bytes(&self) -> &[u8] { |
243 | 0 | &self.buf[usize::from(self.start)..usize::from(self.end)] |
244 | 0 | } |
245 | | |
246 | | /// Returns the ASCII representation of this decimal as a string slice. |
247 | | #[inline] |
248 | 0 | pub(crate) fn as_str(&self) -> &str { |
249 | | // SAFETY: This is safe because all bytes written to `self.buf` are |
250 | | // guaranteed to be ASCII (including in its initial state), and thus, |
251 | | // any subsequence is guaranteed to be valid UTF-8. |
252 | 0 | unsafe { core::str::from_utf8_unchecked(self.as_bytes()) } |
253 | 0 | } |
254 | | } |
255 | | |
256 | | /// A simple formatter for converting fractional components to ASCII byte |
257 | | /// strings. |
258 | | /// |
259 | | /// We only support precision to 9 decimal places, which corresponds to |
260 | | /// nanosecond precision as a fractional second component. |
261 | | #[derive(Clone, Copy, Debug)] |
262 | | pub(crate) struct FractionalFormatter { |
263 | | precision: Option<u8>, |
264 | | } |
265 | | |
266 | | impl FractionalFormatter { |
267 | | /// Creates a new fractional formatter using the given precision settings. |
268 | 0 | pub(crate) const fn new() -> FractionalFormatter { |
269 | 0 | FractionalFormatter { precision: None } |
270 | 0 | } |
271 | | |
272 | | /// Format the given value using this configuration as a decimal ASCII |
273 | | /// fractional number. |
274 | 0 | pub(crate) const fn format(&self, value: u32) -> Fractional { |
275 | 0 | Fractional::new(self, value) |
276 | 0 | } |
277 | | |
278 | | /// Set the precision. |
279 | | /// |
280 | | /// If the `precision` is greater than `9`, then it is clamped to `9`. |
281 | | /// |
282 | | /// When the precision is not set, then it is automatically determined |
283 | | /// based on the value. |
284 | 0 | pub(crate) const fn precision( |
285 | 0 | self, |
286 | 0 | precision: Option<u8>, |
287 | 0 | ) -> FractionalFormatter { |
288 | 0 | let precision = match precision { |
289 | 0 | None => None, |
290 | 0 | Some(p) if p > 9 => Some(9), |
291 | 0 | Some(p) => Some(p), |
292 | | }; |
293 | 0 | FractionalFormatter { precision, ..self } |
294 | 0 | } |
295 | | |
296 | | /// Returns true if and only if at least one digit will be written for the |
297 | | /// given value. |
298 | | /// |
299 | | /// This is useful for callers that need to know whether to write |
300 | | /// a decimal separator, e.g., `.`, before the digits. |
301 | 0 | pub(crate) fn will_write_digits(self, value: u32) -> bool { |
302 | 0 | self.precision.map_or_else(|| value != 0, |p| p > 0) |
303 | 0 | } |
304 | | |
305 | | /// Returns true if and only if this formatter has an explicit non-zero |
306 | | /// precision setting. |
307 | | /// |
308 | | /// This is useful for determining whether something like `0.000` needs to |
309 | | /// be written in the case of a `precision=Some(3)` setting and a zero |
310 | | /// value. |
311 | 0 | pub(crate) fn has_non_zero_fixed_precision(self) -> bool { |
312 | 0 | self.precision.map_or(false, |p| p > 0) |
313 | 0 | } |
314 | | |
315 | | /// Returns true if and only if this formatter has fixed zero precision. |
316 | | /// That is, no matter what is given as input, a fraction is never written. |
317 | 0 | pub(crate) fn has_zero_fixed_precision(self) -> bool { |
318 | 0 | self.precision.map_or(false, |p| p == 0) |
319 | 0 | } |
320 | | } |
321 | | |
322 | | /// A formatted fractional number that can be converted to a sequence of bytes. |
323 | | #[derive(Debug)] |
324 | | pub(crate) struct Fractional { |
325 | | buf: [u8; Self::MAX_LEN as usize], |
326 | | end: u8, |
327 | | } |
328 | | |
329 | | impl Fractional { |
330 | | /// Since we don't support precision bigger than this. |
331 | | const MAX_LEN: u8 = 9; |
332 | | |
333 | | /// Using the given formatter, turn the value given into a fractional |
334 | | /// decimal representation using ASCII bytes. |
335 | | /// |
336 | | /// Note that the fractional number returned *may* expand to an empty |
337 | | /// slice of bytes. This occurs whenever the precision is set to `0`, or |
338 | | /// when the precision is not set and the value is `0`. Any non-zero |
339 | | /// explicitly set precision guarantees that the slice returned is not |
340 | | /// empty. |
341 | | /// |
342 | | /// This panics if the value given isn't in the range `0..=999_999_999`. |
343 | 0 | pub(crate) const fn new( |
344 | 0 | formatter: &FractionalFormatter, |
345 | 0 | mut value: u32, |
346 | 0 | ) -> Fractional { |
347 | 0 | assert!(value <= 999_999_999); |
348 | 0 | let mut fractional = Fractional { |
349 | 0 | buf: [b'0'; Self::MAX_LEN as usize], |
350 | 0 | end: Self::MAX_LEN, |
351 | 0 | }; |
352 | 0 | let mut i = 9; |
353 | | loop { |
354 | 0 | i -= 1; |
355 | | |
356 | 0 | let digit = (value % 10) as u8; |
357 | 0 | value /= 10; |
358 | 0 | fractional.buf[i] += digit; |
359 | 0 | if value == 0 { |
360 | 0 | break; |
361 | 0 | } |
362 | | } |
363 | 0 | if let Some(precision) = formatter.precision { |
364 | 0 | fractional.end = precision; |
365 | 0 | } else { |
366 | 0 | while fractional.end > 0 |
367 | 0 | && fractional.buf[fractional.end as usize - 1] == b'0' |
368 | 0 | { |
369 | 0 | fractional.end -= 1; |
370 | 0 | } |
371 | | } |
372 | 0 | fractional |
373 | 0 | } |
374 | | |
375 | | /// Returns the ASCII representation of this fractional number as a byte |
376 | | /// slice. The slice returned may be empty. |
377 | | /// |
378 | | /// The slice returned is guaranteed to be valid ASCII. |
379 | 0 | pub(crate) fn as_bytes(&self) -> &[u8] { |
380 | 0 | &self.buf[..usize::from(self.end)] |
381 | 0 | } |
382 | | |
383 | | /// Returns the ASCII representation of this fractional number as a string |
384 | | /// slice. The slice returned may be empty. |
385 | 0 | pub(crate) fn as_str(&self) -> &str { |
386 | | // SAFETY: This is safe because all bytes written to `self.buf` are |
387 | | // guaranteed to be ASCII (including in its initial state), and thus, |
388 | | // any subsequence is guaranteed to be valid UTF-8. |
389 | 0 | unsafe { core::str::from_utf8_unchecked(self.as_bytes()) } |
390 | 0 | } |
391 | | } |
392 | | |
393 | | /// A container for holding a partially parsed duration. |
394 | | /// |
395 | | /// This is used for parsing into `Span`, `SignedDuration` and (hopefully |
396 | | /// soon) `std::time::Duration`. It's _also_ used for both the ISO 8601 |
397 | | /// duration and "friendly" format. |
398 | | /// |
399 | | /// This replaced a significant chunk of code that was bespoke to each |
400 | | /// combination of duration type _and_ format. |
401 | | /// |
402 | | /// The idea behind it is that we parse each duration component as an unsigned |
403 | | /// 64-bit integer and keep track of the sign separately. This is a critical |
404 | | /// aspect that was motivated by being able to roundtrip all legal values of |
405 | | /// a 96-bit signed integer number of nanoseconds (i.e., `SignedDuration`). |
406 | | /// In particular, if we used `i64` to represent each component, then it |
407 | | /// makes it much more difficult to parse, e.g., `9223372036854775808 |
408 | | /// seconds ago`. Namely, `9223372036854775808` is not a valid `i64` but |
409 | | /// `-9223372036854775808` is. Notably, the sign is indicated by a suffix, |
410 | | /// so we don't know it's negative when parsing the integer itself. So we |
411 | | /// represent all components as their unsigned absolute value and apply the |
412 | | /// sign at the end. |
413 | | /// |
414 | | /// This also centralizes a lot of thorny duration math and opens up the |
415 | | /// opportunity for tighter optimization. |
416 | | #[derive(Debug, Default)] |
417 | | pub(crate) struct DurationUnits { |
418 | | /// The parsed unit values in descending order. That is, nanoseconds are |
419 | | /// at index 0 while years are at index 9. |
420 | | values: [u64; 10], |
421 | | /// Any fractional component parsed. The fraction is necessarily a fraction |
422 | | /// of the minimum unit if present. |
423 | | fraction: Option<u32>, |
424 | | /// The sign of the duration. This may be set at any time. |
425 | | /// |
426 | | /// Note that this defaults to zero! So callers will always want to set |
427 | | /// this. |
428 | | sign: Sign, |
429 | | /// The smallest unit value that was explicitly set. |
430 | | min: Option<Unit>, |
431 | | /// The largest unit value that was explicitly set. |
432 | | max: Option<Unit>, |
433 | | /// Whether there are any non-zero units. |
434 | | any_non_zero_units: bool, |
435 | | } |
436 | | |
437 | | impl DurationUnits { |
438 | | /// Set the duration component value for the given unit. |
439 | | /// |
440 | | /// The value here is always unsigned. To deal with negative values, set |
441 | | /// the sign independently. It will be accounted for when using one of this |
442 | | /// type's methods for converting to a concrete duration type. |
443 | | /// |
444 | | /// # Panics |
445 | | /// |
446 | | /// When this is called after `set_fraction`. |
447 | | /// |
448 | | /// # Errors |
449 | | /// |
450 | | /// Since this is meant to be used in service of duration parsing and all |
451 | | /// duration parsing proceeds from largest to smallest units, this will |
452 | | /// return an error if the given unit is bigger than or equal to any |
453 | | /// previously set unit. This also implies that this can only be called |
454 | | /// at most once for each unit value. |
455 | | #[cfg_attr(feature = "perf-inline", inline(always))] |
456 | 0 | pub(crate) fn set_unit_value( |
457 | 0 | &mut self, |
458 | 0 | unit: Unit, |
459 | 0 | value: u64, |
460 | 0 | ) -> Result<(), Error> { |
461 | 0 | assert!(self.fraction.is_none()); |
462 | | |
463 | 0 | if let Some(min) = self.min { |
464 | 0 | if min <= unit { |
465 | 0 | return Err(err!( |
466 | 0 | "found value {value:?} with unit {unit} \ |
467 | 0 | after unit {prev_unit}, but units must be \ |
468 | 0 | written from largest to smallest \ |
469 | 0 | (and they can't be repeated)", |
470 | 0 | unit = unit.singular(), |
471 | 0 | prev_unit = min.singular(), |
472 | 0 | )); |
473 | 0 | } |
474 | 0 | } |
475 | | // Given the above check, the given unit must be smaller than any we |
476 | | // have seen so far. |
477 | 0 | self.min = Some(unit); |
478 | | // The maximum unit is always the first unit set, since we can never |
479 | | // see a unit bigger than it without an error occurring. |
480 | 0 | if self.max.is_none() { |
481 | 0 | self.max = Some(unit); |
482 | 0 | } |
483 | 0 | self.values[unit.as_usize()] = value; |
484 | 0 | self.any_non_zero_units = self.any_non_zero_units || value != 0; |
485 | 0 | Ok(()) |
486 | 0 | } |
487 | | |
488 | | /// A convenience routine for setting values parsed from an `HH:MM:SS` |
489 | | /// format (including the fraction). |
490 | | /// |
491 | | /// # Errors |
492 | | /// |
493 | | /// This forwards errors from `DurationUnits::set_unit_value`. It will also |
494 | | /// return an error is the minimum parsed unit (so far) is smaller than |
495 | | /// days. (Since `HH:MM:SS` can only appear after units of years, months, |
496 | | /// weeks or days.) |
497 | 0 | pub(crate) fn set_hms( |
498 | 0 | &mut self, |
499 | 0 | hours: u64, |
500 | 0 | minutes: u64, |
501 | 0 | seconds: u64, |
502 | 0 | fraction: Option<u32>, |
503 | 0 | ) -> Result<(), Error> { |
504 | 0 | if let Some(min) = self.min { |
505 | 0 | if min <= Unit::Hour { |
506 | 0 | return Err(err!( |
507 | 0 | "found `HH:MM:SS` after unit {min}, \ |
508 | 0 | but `HH:MM:SS` can only appear after \ |
509 | 0 | years, months, weeks or days", |
510 | 0 | min = min.singular(), |
511 | 0 | )); |
512 | 0 | } |
513 | 0 | } |
514 | 0 | self.set_unit_value(Unit::Hour, hours)?; |
515 | 0 | self.set_unit_value(Unit::Minute, minutes)?; |
516 | 0 | self.set_unit_value(Unit::Second, seconds)?; |
517 | 0 | if let Some(fraction) = fraction { |
518 | 0 | self.set_fraction(fraction)?; |
519 | 0 | } |
520 | 0 | Ok(()) |
521 | 0 | } |
522 | | |
523 | | /// Set the fractional value. |
524 | | /// |
525 | | /// This is always interpreted as a fraction of the minimal unit. |
526 | | /// |
527 | | /// Callers must ensure this is called after the last call to |
528 | | /// `DurationUnits::set_unit_value`. |
529 | | /// |
530 | | /// # Panics |
531 | | /// |
532 | | /// When `fraction` is not in the range `0..=999_999_999`. Callers are |
533 | | /// expected to uphold this invariant. |
534 | | /// |
535 | | /// # Errors |
536 | | /// |
537 | | /// This will return an error if the minimum unit is `Unit::Nanosecond`. |
538 | | /// (Because fractional nanoseconds are not supported.) This will also |
539 | | /// return an error if the minimum unit is bigger than `Unit::Hour`. |
540 | 0 | pub(crate) fn set_fraction(&mut self, fraction: u32) -> Result<(), Error> { |
541 | 0 | assert!(fraction <= 999_999_999); |
542 | 0 | if self.min == Some(Unit::Nanosecond) { |
543 | 0 | return Err(err!("fractional nanoseconds are not supported")); |
544 | 0 | } |
545 | 0 | if let Some(min) = self.min { |
546 | 0 | if min > Unit::Hour { |
547 | 0 | return Err(err!( |
548 | 0 | "fractional {plural} are not supported", |
549 | 0 | plural = min.plural() |
550 | 0 | )); |
551 | 0 | } |
552 | 0 | } |
553 | 0 | self.fraction = Some(fraction); |
554 | 0 | Ok(()) |
555 | 0 | } |
556 | | |
557 | | /// Set the sign associated with the components. |
558 | | /// |
559 | | /// The sign applies to the entire duration. There is no support for |
560 | | /// having some components signed and some unsigned. |
561 | | /// |
562 | | /// If no sign is set, then it is assumed to be zero. Note also that |
563 | | /// even if a sign is explicitly set *and* all unit values are zero, |
564 | | /// then the sign will be set to zero. |
565 | 0 | pub(crate) fn set_sign(&mut self, sign: Sign) { |
566 | 0 | self.sign = sign; |
567 | 0 | } |
568 | | |
569 | | /// Convert these duration components to a `Span`. |
570 | | /// |
571 | | /// # Errors |
572 | | /// |
573 | | /// If any individual unit exceeds the limits of a `Span`, or if the units |
574 | | /// combine to exceed what can be represented by a `Span`, then this |
575 | | /// returns an error. |
576 | | /// |
577 | | /// This also returns an error if no units were set. |
578 | | #[cfg_attr(feature = "perf-inline", inline(always))] |
579 | 0 | pub(crate) fn to_span(&self) -> Result<Span, Error> { |
580 | | // When every unit value is less than this, *and* there is |
581 | | // no fractional component, then we trigger a fast path that |
582 | | // doesn't need to bother with error handling and careful |
583 | | // handling of the sign. |
584 | | // |
585 | | // Why do we use the maximum year value? Because years are |
586 | | // the "biggest" unit, it follows that there can't be any |
587 | | // other unit whose limit is smaller than years as a |
588 | | // dimenionless quantity. That is, if all parsed unit values |
589 | | // are no bigger than the maximum year, then we know all |
590 | | // parsed unit values are necessarily within their |
591 | | // appropriate limits. |
592 | | const LIMIT: u64 = t::SpanYears::MAX_SELF.get_unchecked() as u64; |
593 | | |
594 | | // If we have a fraction or a particularly large unit, |
595 | | // bail out to the general case. |
596 | 0 | if self.fraction.is_some() |
597 | 0 | || self.values.iter().any(|&value| value > LIMIT) |
598 | | // If no unit was set, it's an error case. |
599 | 0 | || self.max.is_none() |
600 | | { |
601 | 0 | return self.to_span_general(); |
602 | 0 | } |
603 | | |
604 | 0 | let mut span = Span::new(); |
605 | | |
606 | 0 | let years = self.values[Unit::Year.as_usize()] as i16; |
607 | 0 | let months = self.values[Unit::Month.as_usize()] as i32; |
608 | 0 | let weeks = self.values[Unit::Week.as_usize()] as i32; |
609 | 0 | let days = self.values[Unit::Day.as_usize()] as i32; |
610 | 0 | let hours = self.values[Unit::Hour.as_usize()] as i32; |
611 | 0 | let mins = self.values[Unit::Minute.as_usize()] as i64; |
612 | 0 | let secs = self.values[Unit::Second.as_usize()] as i64; |
613 | 0 | let millis = self.values[Unit::Millisecond.as_usize()] as i64; |
614 | 0 | let micros = self.values[Unit::Microsecond.as_usize()] as i64; |
615 | 0 | let nanos = self.values[Unit::Nanosecond.as_usize()] as i64; |
616 | | |
617 | 0 | span = span.years_unchecked(years); |
618 | 0 | span = span.months_unchecked(months); |
619 | 0 | span = span.weeks_unchecked(weeks); |
620 | 0 | span = span.days_unchecked(days); |
621 | 0 | span = span.hours_unchecked(hours); |
622 | 0 | span = span.minutes_unchecked(mins); |
623 | 0 | span = span.seconds_unchecked(secs); |
624 | 0 | span = span.milliseconds_unchecked(millis); |
625 | 0 | span = span.microseconds_unchecked(micros); |
626 | 0 | span = span.nanoseconds_unchecked(nanos); |
627 | | |
628 | | // The unchecked setters above don't manipulate |
629 | | // the sign, which defaults to zero. So we need to |
630 | | // set it even when it's positive. |
631 | 0 | span = span.sign_unchecked(self.get_sign().as_ranged_integer()); |
632 | | |
633 | 0 | Ok(span) |
634 | 0 | } |
635 | | |
636 | | /// The "general" implementation of `DurationUnits::to_span`. |
637 | | /// |
638 | | /// This handles all possible cases, including fractional units, with good |
639 | | /// error handling. Basically, we take this path when we think an error |
640 | | /// _could_ occur. But this function is more bloaty and does more work, so |
641 | | /// the more it can be avoided, the better. |
642 | | #[cold] |
643 | | #[inline(never)] |
644 | 0 | fn to_span_general(&self) -> Result<Span, Error> { |
645 | 0 | fn error_context(unit: Unit, value: i64) -> Error { |
646 | 0 | err!( |
647 | 0 | "failed to set value {value:?} as {unit} unit on span", |
648 | 0 | unit = unit.singular(), |
649 | | ) |
650 | 0 | } |
651 | | |
652 | | #[cfg_attr(feature = "perf-inline", inline(always))] |
653 | 0 | fn set_time_unit( |
654 | 0 | unit: Unit, |
655 | 0 | value: i64, |
656 | 0 | span: Span, |
657 | 0 | set: impl FnOnce(Span) -> Result<Span, Error>, |
658 | 0 | ) -> Result<Span, Error> { |
659 | | #[cold] |
660 | | #[inline(never)] |
661 | 0 | fn fractional_fallback( |
662 | 0 | err: Error, |
663 | 0 | unit: Unit, |
664 | 0 | value: i64, |
665 | 0 | span: Span, |
666 | 0 | ) -> Result<Span, Error> { |
667 | | // Fractional calendar units aren't supported. Neither are |
668 | | // fractional nanoseconds. So there's nothing we can do in |
669 | | // this case. |
670 | 0 | if unit > Unit::Hour || unit == Unit::Nanosecond { |
671 | 0 | Err(err) |
672 | | } else { |
673 | | // This is annoying, but because we can write out a larger |
674 | | // number of hours/minutes/seconds than what we actually |
675 | | // support, we need to be prepared to parse an unbalanced |
676 | | // span if our time units are too big here. In essence, |
677 | | // this lets a single time unit "overflow" into smaller |
678 | | // units if it exceeds the limits. |
679 | 0 | fractional_time_to_span(unit, value, 0, span) |
680 | | } |
681 | 0 | } |
682 | | |
683 | 0 | set(span) |
684 | 0 | .or_else(|err| fractional_fallback(err, unit, value, span)) Unexecuted instantiation: <jiff::fmt::util::DurationUnits>::to_span_general::set_time_unit::<<jiff::fmt::util::DurationUnits>::to_span_general::{closure#4}>::{closure#0}Unexecuted instantiation: <jiff::fmt::util::DurationUnits>::to_span_general::set_time_unit::<<jiff::fmt::util::DurationUnits>::to_span_general::{closure#5}>::{closure#0}Unexecuted instantiation: <jiff::fmt::util::DurationUnits>::to_span_general::set_time_unit::<<jiff::fmt::util::DurationUnits>::to_span_general::{closure#6}>::{closure#0}Unexecuted instantiation: <jiff::fmt::util::DurationUnits>::to_span_general::set_time_unit::<<jiff::fmt::util::DurationUnits>::to_span_general::{closure#7}>::{closure#0}Unexecuted instantiation: <jiff::fmt::util::DurationUnits>::to_span_general::set_time_unit::<<jiff::fmt::util::DurationUnits>::to_span_general::{closure#8}>::{closure#0}Unexecuted instantiation: <jiff::fmt::util::DurationUnits>::to_span_general::set_time_unit::<<jiff::fmt::util::DurationUnits>::to_span_general::{closure#9}>::{closure#0} |
685 | 0 | .with_context(|| error_context(unit, value)) Unexecuted instantiation: <jiff::fmt::util::DurationUnits>::to_span_general::set_time_unit::<<jiff::fmt::util::DurationUnits>::to_span_general::{closure#4}>::{closure#1}Unexecuted instantiation: <jiff::fmt::util::DurationUnits>::to_span_general::set_time_unit::<<jiff::fmt::util::DurationUnits>::to_span_general::{closure#5}>::{closure#1}Unexecuted instantiation: <jiff::fmt::util::DurationUnits>::to_span_general::set_time_unit::<<jiff::fmt::util::DurationUnits>::to_span_general::{closure#6}>::{closure#1}Unexecuted instantiation: <jiff::fmt::util::DurationUnits>::to_span_general::set_time_unit::<<jiff::fmt::util::DurationUnits>::to_span_general::{closure#7}>::{closure#1}Unexecuted instantiation: <jiff::fmt::util::DurationUnits>::to_span_general::set_time_unit::<<jiff::fmt::util::DurationUnits>::to_span_general::{closure#8}>::{closure#1}Unexecuted instantiation: <jiff::fmt::util::DurationUnits>::to_span_general::set_time_unit::<<jiff::fmt::util::DurationUnits>::to_span_general::{closure#9}>::{closure#1} |
686 | 0 | } Unexecuted instantiation: <jiff::fmt::util::DurationUnits>::to_span_general::set_time_unit::<<jiff::fmt::util::DurationUnits>::to_span_general::{closure#4}>Unexecuted instantiation: <jiff::fmt::util::DurationUnits>::to_span_general::set_time_unit::<<jiff::fmt::util::DurationUnits>::to_span_general::{closure#5}>Unexecuted instantiation: <jiff::fmt::util::DurationUnits>::to_span_general::set_time_unit::<<jiff::fmt::util::DurationUnits>::to_span_general::{closure#6}>Unexecuted instantiation: <jiff::fmt::util::DurationUnits>::to_span_general::set_time_unit::<<jiff::fmt::util::DurationUnits>::to_span_general::{closure#7}>Unexecuted instantiation: <jiff::fmt::util::DurationUnits>::to_span_general::set_time_unit::<<jiff::fmt::util::DurationUnits>::to_span_general::{closure#8}>Unexecuted instantiation: <jiff::fmt::util::DurationUnits>::to_span_general::set_time_unit::<<jiff::fmt::util::DurationUnits>::to_span_general::{closure#9}> |
687 | | |
688 | 0 | let (min, _) = self.get_min_max_units()?; |
689 | 0 | let mut span = Span::new(); |
690 | | |
691 | 0 | if self.values[Unit::Year.as_usize()] != 0 { |
692 | 0 | let value = self.get_unit_value(Unit::Year)?; |
693 | 0 | span = span |
694 | 0 | .try_years(value) |
695 | 0 | .with_context(|| error_context(Unit::Year, value))?; |
696 | 0 | } |
697 | 0 | if self.values[Unit::Month.as_usize()] != 0 { |
698 | 0 | let value = self.get_unit_value(Unit::Month)?; |
699 | 0 | span = span |
700 | 0 | .try_months(value) |
701 | 0 | .with_context(|| error_context(Unit::Month, value))?; |
702 | 0 | } |
703 | 0 | if self.values[Unit::Week.as_usize()] != 0 { |
704 | 0 | let value = self.get_unit_value(Unit::Week)?; |
705 | 0 | span = span |
706 | 0 | .try_weeks(value) |
707 | 0 | .with_context(|| error_context(Unit::Week, value))?; |
708 | 0 | } |
709 | 0 | if self.values[Unit::Day.as_usize()] != 0 { |
710 | 0 | let value = self.get_unit_value(Unit::Day)?; |
711 | 0 | span = span |
712 | 0 | .try_days(value) |
713 | 0 | .with_context(|| error_context(Unit::Day, value))?; |
714 | 0 | } |
715 | 0 | if self.values[Unit::Hour.as_usize()] != 0 { |
716 | 0 | let value = self.get_unit_value(Unit::Hour)?; |
717 | 0 | span = set_time_unit(Unit::Hour, value, span, |span| { |
718 | 0 | span.try_hours(value) |
719 | 0 | })?; |
720 | 0 | } |
721 | 0 | if self.values[Unit::Minute.as_usize()] != 0 { |
722 | 0 | let value = self.get_unit_value(Unit::Minute)?; |
723 | 0 | span = set_time_unit(Unit::Minute, value, span, |span| { |
724 | 0 | span.try_minutes(value) |
725 | 0 | })?; |
726 | 0 | } |
727 | 0 | if self.values[Unit::Second.as_usize()] != 0 { |
728 | 0 | let value = self.get_unit_value(Unit::Second)?; |
729 | 0 | span = set_time_unit(Unit::Second, value, span, |span| { |
730 | 0 | span.try_seconds(value) |
731 | 0 | })?; |
732 | 0 | } |
733 | 0 | if self.values[Unit::Millisecond.as_usize()] != 0 { |
734 | 0 | let value = self.get_unit_value(Unit::Millisecond)?; |
735 | 0 | span = set_time_unit(Unit::Millisecond, value, span, |span| { |
736 | 0 | span.try_milliseconds(value) |
737 | 0 | })?; |
738 | 0 | } |
739 | 0 | if self.values[Unit::Microsecond.as_usize()] != 0 { |
740 | 0 | let value = self.get_unit_value(Unit::Microsecond)?; |
741 | 0 | span = set_time_unit(Unit::Microsecond, value, span, |span| { |
742 | 0 | span.try_microseconds(value) |
743 | 0 | })?; |
744 | 0 | } |
745 | 0 | if self.values[Unit::Nanosecond.as_usize()] != 0 { |
746 | 0 | let value = self.get_unit_value(Unit::Nanosecond)?; |
747 | 0 | span = set_time_unit(Unit::Nanosecond, value, span, |span| { |
748 | 0 | span.try_nanoseconds(value) |
749 | 0 | })?; |
750 | 0 | } |
751 | | |
752 | 0 | if let Some(fraction) = self.get_fraction()? { |
753 | 0 | let value = self.get_unit_value(min)?; |
754 | 0 | span = fractional_time_to_span(min, value, fraction, span)?; |
755 | 0 | } |
756 | | |
757 | 0 | Ok(span) |
758 | 0 | } |
759 | | |
760 | | /// Convert these duration components to a `SignedDuration`. |
761 | | /// |
762 | | /// # Errors |
763 | | /// |
764 | | /// If the total number of nanoseconds represented by all units combined |
765 | | /// exceeds what can bit in a 96-bit signed integer, then an error is |
766 | | /// returned. |
767 | | /// |
768 | | /// An error is also returned if any calendar units (days or greater) were |
769 | | /// set or if no units were set. |
770 | | #[cfg_attr(feature = "perf-inline", inline(always))] |
771 | 0 | pub(crate) fn to_signed_duration(&self) -> Result<SignedDuration, Error> { |
772 | | // When every unit value is less than this, *and* there is |
773 | | // no fractional component, then we trigger a fast path that |
774 | | // doesn't need to bother with error handling and careful |
775 | | // handling of the sign. |
776 | | // |
777 | | // Why `999`? Well, I think it's nice to use one limit for all |
778 | | // units to make the comparisons simpler (although we could |
779 | | // use more targeted values to admit more cases, I didn't try |
780 | | // that). But specifically, this means we can have `999ms 999us |
781 | | // 999ns` as a maximal subsecond value without overflowing |
782 | | // the nanosecond component of a `SignedDuration`. This lets |
783 | | // us "just do math" without needing to check each result and |
784 | | // handle errors. |
785 | | const LIMIT: u64 = 999; |
786 | | |
787 | 0 | if self.fraction.is_some() |
788 | 0 | || self.values[..Unit::Day.as_usize()] |
789 | 0 | .iter() |
790 | 0 | .any(|&value| value > LIMIT) |
791 | 0 | || self.max.map_or(true, |max| max > Unit::Hour) |
792 | | { |
793 | 0 | return self.to_signed_duration_general(); |
794 | 0 | } |
795 | | |
796 | 0 | let hours = self.values[Unit::Hour.as_usize()] as i64; |
797 | 0 | let mins = self.values[Unit::Minute.as_usize()] as i64; |
798 | 0 | let secs = self.values[Unit::Second.as_usize()] as i64; |
799 | 0 | let millis = self.values[Unit::Millisecond.as_usize()] as i32; |
800 | 0 | let micros = self.values[Unit::Microsecond.as_usize()] as i32; |
801 | 0 | let nanos = self.values[Unit::Nanosecond.as_usize()] as i32; |
802 | | |
803 | 0 | let total_secs = (hours * 3600) + (mins * 60) + secs; |
804 | 0 | let total_nanos = (millis * 1_000_000) + (micros * 1_000) + nanos; |
805 | 0 | let mut sdur = |
806 | 0 | SignedDuration::new_without_nano_overflow(total_secs, total_nanos); |
807 | 0 | if self.get_sign().is_negative() { |
808 | 0 | sdur = -sdur; |
809 | 0 | } |
810 | | |
811 | 0 | Ok(sdur) |
812 | 0 | } |
813 | | |
814 | | /// The "general" implementation of `DurationUnits::to_signed_duration`. |
815 | | /// |
816 | | /// This handles all possible cases, including fractional units, with good |
817 | | /// error handling. Basically, we take this path when we think an error |
818 | | /// _could_ occur. But this function is more bloaty and does more work, so |
819 | | /// the more it can be avoided, the better. |
820 | | #[cold] |
821 | | #[inline(never)] |
822 | 0 | fn to_signed_duration_general(&self) -> Result<SignedDuration, Error> { |
823 | 0 | let (min, max) = self.get_min_max_units()?; |
824 | 0 | if max > Unit::Hour { |
825 | 0 | return Err(err!( |
826 | 0 | "parsing {unit} units into a `SignedDuration` is not supported \ |
827 | 0 | (perhaps try parsing into a `Span` instead)", |
828 | 0 | unit = max.singular(), |
829 | 0 | )); |
830 | 0 | } |
831 | | |
832 | 0 | let mut sdur = SignedDuration::ZERO; |
833 | 0 | if self.values[Unit::Hour.as_usize()] != 0 { |
834 | 0 | let value = self.get_unit_value(Unit::Hour)?; |
835 | 0 | sdur = SignedDuration::try_from_hours(value) |
836 | 0 | .and_then(|nanos| sdur.checked_add(nanos)) |
837 | 0 | .ok_or_else(|| { |
838 | 0 | err!( |
839 | 0 | "accumulated `SignedDuration` of `{sdur:?}` \ |
840 | 0 | overflowed when adding {value} of unit {unit}", |
841 | 0 | unit = Unit::Hour.singular(), |
842 | | ) |
843 | 0 | })?; |
844 | 0 | } |
845 | 0 | if self.values[Unit::Minute.as_usize()] != 0 { |
846 | 0 | let value = self.get_unit_value(Unit::Minute)?; |
847 | 0 | sdur = SignedDuration::try_from_mins(value) |
848 | 0 | .and_then(|nanos| sdur.checked_add(nanos)) |
849 | 0 | .ok_or_else(|| { |
850 | 0 | err!( |
851 | 0 | "accumulated `SignedDuration` of `{sdur:?}` \ |
852 | 0 | overflowed when adding {value} of unit {unit}", |
853 | 0 | unit = Unit::Minute.singular(), |
854 | | ) |
855 | 0 | })?; |
856 | 0 | } |
857 | 0 | if self.values[Unit::Second.as_usize()] != 0 { |
858 | 0 | let value = self.get_unit_value(Unit::Second)?; |
859 | 0 | sdur = SignedDuration::from_secs(value) |
860 | 0 | .checked_add(sdur) |
861 | 0 | .ok_or_else(|| { |
862 | 0 | err!( |
863 | 0 | "accumulated `SignedDuration` of `{sdur:?}` \ |
864 | 0 | overflowed when adding {value} of unit {unit}", |
865 | 0 | unit = Unit::Second.singular(), |
866 | | ) |
867 | 0 | })?; |
868 | 0 | } |
869 | 0 | if self.values[Unit::Millisecond.as_usize()] != 0 { |
870 | 0 | let value = self.get_unit_value(Unit::Millisecond)?; |
871 | 0 | sdur = SignedDuration::from_millis(value) |
872 | 0 | .checked_add(sdur) |
873 | 0 | .ok_or_else(|| { |
874 | 0 | err!( |
875 | 0 | "accumulated `SignedDuration` of `{sdur:?}` \ |
876 | 0 | overflowed when adding {value} of unit {unit}", |
877 | 0 | unit = Unit::Millisecond.singular(), |
878 | | ) |
879 | 0 | })?; |
880 | 0 | } |
881 | 0 | if self.values[Unit::Microsecond.as_usize()] != 0 { |
882 | 0 | let value = self.get_unit_value(Unit::Microsecond)?; |
883 | 0 | sdur = SignedDuration::from_micros(value) |
884 | 0 | .checked_add(sdur) |
885 | 0 | .ok_or_else(|| { |
886 | 0 | err!( |
887 | 0 | "accumulated `SignedDuration` of `{sdur:?}` \ |
888 | 0 | overflowed when adding {value} of unit {unit}", |
889 | 0 | unit = Unit::Microsecond.singular(), |
890 | | ) |
891 | 0 | })?; |
892 | 0 | } |
893 | 0 | if self.values[Unit::Nanosecond.as_usize()] != 0 { |
894 | 0 | let value = self.get_unit_value(Unit::Nanosecond)?; |
895 | 0 | sdur = SignedDuration::from_nanos(value) |
896 | 0 | .checked_add(sdur) |
897 | 0 | .ok_or_else(|| { |
898 | 0 | err!( |
899 | 0 | "accumulated `SignedDuration` of `{sdur:?}` \ |
900 | 0 | overflowed when adding {value} of unit {unit}", |
901 | 0 | unit = Unit::Nanosecond.singular(), |
902 | | ) |
903 | 0 | })?; |
904 | 0 | } |
905 | | |
906 | 0 | if let Some(fraction) = self.get_fraction()? { |
907 | 0 | sdur = sdur |
908 | 0 | .checked_add(fractional_duration(min, fraction)?) |
909 | 0 | .ok_or_else(|| { |
910 | 0 | err!( |
911 | 0 | "accumulated `SignedDuration` of `{sdur:?}` \ |
912 | 0 | overflowed when adding 0.{fraction} of unit {unit}", |
913 | 0 | unit = min.singular(), |
914 | | ) |
915 | 0 | })?; |
916 | 0 | } |
917 | | |
918 | 0 | Ok(sdur) |
919 | 0 | } |
920 | | |
921 | | /// Convert these duration components to a `core::time::Duration`. |
922 | | /// |
923 | | /// # Errors |
924 | | /// |
925 | | /// If the total number of nanoseconds represented by all units combined |
926 | | /// exceeds what can bit in a 96-bit signed integer, then an error is |
927 | | /// returned. |
928 | | /// |
929 | | /// An error is also returned if any calendar units (days or greater) were |
930 | | /// set or if no units were set. |
931 | | #[cfg_attr(feature = "perf-inline", inline(always))] |
932 | 0 | pub(crate) fn to_unsigned_duration( |
933 | 0 | &self, |
934 | 0 | ) -> Result<core::time::Duration, Error> { |
935 | | // When every unit value is less than this, *and* there is |
936 | | // no fractional component, then we trigger a fast path that |
937 | | // doesn't need to bother with error handling and careful |
938 | | // handling of the sign. |
939 | | // |
940 | | // Why `999`? Well, I think it's nice to use one limit for all |
941 | | // units to make the comparisons simpler (although we could |
942 | | // use more targeted values to admit more cases, I didn't try |
943 | | // that). But specifically, this means we can have `999ms 999us |
944 | | // 999ns` as a maximal subsecond value without overflowing |
945 | | // the nanosecond component of a `core::time::Duration`. This lets |
946 | | // us "just do math" without needing to check each result and |
947 | | // handle errors. |
948 | | const LIMIT: u64 = 999; |
949 | | |
950 | 0 | if self.fraction.is_some() |
951 | 0 | || self.values[..Unit::Day.as_usize()] |
952 | 0 | .iter() |
953 | 0 | .any(|&value| value > LIMIT) |
954 | 0 | || self.max.map_or(true, |max| max > Unit::Hour) |
955 | 0 | || self.sign.is_negative() |
956 | | { |
957 | 0 | return self.to_unsigned_duration_general(); |
958 | 0 | } |
959 | | |
960 | 0 | let hours = self.values[Unit::Hour.as_usize()]; |
961 | 0 | let mins = self.values[Unit::Minute.as_usize()]; |
962 | 0 | let secs = self.values[Unit::Second.as_usize()]; |
963 | 0 | let millis = self.values[Unit::Millisecond.as_usize()] as u32; |
964 | 0 | let micros = self.values[Unit::Microsecond.as_usize()] as u32; |
965 | 0 | let nanos = self.values[Unit::Nanosecond.as_usize()] as u32; |
966 | | |
967 | 0 | let total_secs = (hours * 3600) + (mins * 60) + secs; |
968 | 0 | let total_nanos = (millis * 1_000_000) + (micros * 1_000) + nanos; |
969 | 0 | let sdur = core::time::Duration::new(total_secs, total_nanos); |
970 | | |
971 | 0 | Ok(sdur) |
972 | 0 | } |
973 | | |
974 | | /// The "general" implementation of `DurationUnits::to_unsigned_duration`. |
975 | | /// |
976 | | /// This handles all possible cases, including fractional units, with good |
977 | | /// error handling. Basically, we take this path when we think an error |
978 | | /// _could_ occur. But this function is more bloaty and does more work, so |
979 | | /// the more it can be avoided, the better. |
980 | | #[cold] |
981 | | #[inline(never)] |
982 | 0 | fn to_unsigned_duration_general( |
983 | 0 | &self, |
984 | 0 | ) -> Result<core::time::Duration, Error> { |
985 | | #[inline] |
986 | 0 | const fn try_from_hours(hours: u64) -> Option<core::time::Duration> { |
987 | | // OK because (SECS_PER_MINUTE*MINS_PER_HOUR)!={-1,0}. |
988 | | const MAX_HOUR: u64 = u64::MAX / (60 * 60); |
989 | 0 | if hours > MAX_HOUR { |
990 | 0 | return None; |
991 | 0 | } |
992 | 0 | Some(core::time::Duration::from_secs(hours * 60 * 60)) |
993 | 0 | } |
994 | | |
995 | | #[inline] |
996 | 0 | const fn try_from_mins(mins: u64) -> Option<core::time::Duration> { |
997 | | // OK because SECS_PER_MINUTE!={-1,0}. |
998 | | const MAX_MINUTE: u64 = u64::MAX / 60; |
999 | 0 | if mins > MAX_MINUTE { |
1000 | 0 | return None; |
1001 | 0 | } |
1002 | 0 | Some(core::time::Duration::from_secs(mins * 60)) |
1003 | 0 | } |
1004 | | |
1005 | 0 | if self.sign.is_negative() { |
1006 | 0 | return Err(err!( |
1007 | 0 | "cannot parse negative duration into unsigned \ |
1008 | 0 | `std::time::Duration`", |
1009 | 0 | )); |
1010 | 0 | } |
1011 | | |
1012 | 0 | let (min, max) = self.get_min_max_units()?; |
1013 | 0 | if max > Unit::Hour { |
1014 | 0 | return Err(err!( |
1015 | 0 | "parsing {unit} units into a `std::time::Duration` \ |
1016 | 0 | is not supported (perhaps try parsing into a `Span` instead)", |
1017 | 0 | unit = max.singular(), |
1018 | 0 | )); |
1019 | 0 | } |
1020 | | |
1021 | 0 | let mut sdur = core::time::Duration::ZERO; |
1022 | 0 | if self.values[Unit::Hour.as_usize()] != 0 { |
1023 | 0 | let value = self.values[Unit::Hour.as_usize()]; |
1024 | 0 | sdur = try_from_hours(value) |
1025 | 0 | .and_then(|nanos| sdur.checked_add(nanos)) |
1026 | 0 | .ok_or_else(|| { |
1027 | 0 | err!( |
1028 | 0 | "accumulated `std::time::Duration` of `{sdur:?}` \ |
1029 | 0 | overflowed when adding {value} of unit {unit}", |
1030 | 0 | unit = Unit::Hour.singular(), |
1031 | | ) |
1032 | 0 | })?; |
1033 | 0 | } |
1034 | 0 | if self.values[Unit::Minute.as_usize()] != 0 { |
1035 | 0 | let value = self.values[Unit::Minute.as_usize()]; |
1036 | 0 | sdur = try_from_mins(value) |
1037 | 0 | .and_then(|nanos| sdur.checked_add(nanos)) |
1038 | 0 | .ok_or_else(|| { |
1039 | 0 | err!( |
1040 | 0 | "accumulated `std::time::Duration` of `{sdur:?}` \ |
1041 | 0 | overflowed when adding {value} of unit {unit}", |
1042 | 0 | unit = Unit::Minute.singular(), |
1043 | | ) |
1044 | 0 | })?; |
1045 | 0 | } |
1046 | 0 | if self.values[Unit::Second.as_usize()] != 0 { |
1047 | 0 | let value = self.values[Unit::Second.as_usize()]; |
1048 | 0 | sdur = core::time::Duration::from_secs(value) |
1049 | 0 | .checked_add(sdur) |
1050 | 0 | .ok_or_else(|| { |
1051 | 0 | err!( |
1052 | 0 | "accumulated `std::time::Duration` of `{sdur:?}` \ |
1053 | 0 | overflowed when adding {value} of unit {unit}", |
1054 | 0 | unit = Unit::Second.singular(), |
1055 | | ) |
1056 | 0 | })?; |
1057 | 0 | } |
1058 | 0 | if self.values[Unit::Millisecond.as_usize()] != 0 { |
1059 | 0 | let value = self.values[Unit::Millisecond.as_usize()]; |
1060 | 0 | sdur = core::time::Duration::from_millis(value) |
1061 | 0 | .checked_add(sdur) |
1062 | 0 | .ok_or_else(|| { |
1063 | 0 | err!( |
1064 | 0 | "accumulated `std::time::Duration` of `{sdur:?}` \ |
1065 | 0 | overflowed when adding {value} of unit {unit}", |
1066 | 0 | unit = Unit::Millisecond.singular(), |
1067 | | ) |
1068 | 0 | })?; |
1069 | 0 | } |
1070 | 0 | if self.values[Unit::Microsecond.as_usize()] != 0 { |
1071 | 0 | let value = self.values[Unit::Microsecond.as_usize()]; |
1072 | 0 | sdur = core::time::Duration::from_micros(value) |
1073 | 0 | .checked_add(sdur) |
1074 | 0 | .ok_or_else(|| { |
1075 | 0 | err!( |
1076 | 0 | "accumulated `std::time::Duration` of `{sdur:?}` \ |
1077 | 0 | overflowed when adding {value} of unit {unit}", |
1078 | 0 | unit = Unit::Microsecond.singular(), |
1079 | | ) |
1080 | 0 | })?; |
1081 | 0 | } |
1082 | 0 | if self.values[Unit::Nanosecond.as_usize()] != 0 { |
1083 | 0 | let value = self.values[Unit::Nanosecond.as_usize()]; |
1084 | 0 | sdur = core::time::Duration::from_nanos(value) |
1085 | 0 | .checked_add(sdur) |
1086 | 0 | .ok_or_else(|| { |
1087 | 0 | err!( |
1088 | 0 | "accumulated `std::time::Duration` of `{sdur:?}` \ |
1089 | 0 | overflowed when adding {value} of unit {unit}", |
1090 | 0 | unit = Unit::Nanosecond.singular(), |
1091 | | ) |
1092 | 0 | })?; |
1093 | 0 | } |
1094 | | |
1095 | 0 | if let Some(fraction) = self.get_fraction()? { |
1096 | 0 | sdur = sdur |
1097 | 0 | .checked_add( |
1098 | 0 | fractional_duration(min, fraction)?.unsigned_abs(), |
1099 | | ) |
1100 | 0 | .ok_or_else(|| { |
1101 | 0 | err!( |
1102 | 0 | "accumulated `std::time::Duration` of `{sdur:?}` \ |
1103 | 0 | overflowed when adding 0.{fraction} of unit {unit}", |
1104 | 0 | unit = min.singular(), |
1105 | | ) |
1106 | 0 | })?; |
1107 | 0 | } |
1108 | | |
1109 | 0 | Ok(sdur) |
1110 | 0 | } |
1111 | | |
1112 | | /// Returns the minimum unit set. |
1113 | | /// |
1114 | | /// This only returns `None` when no units have been set. |
1115 | 0 | pub(crate) fn get_min(&self) -> Option<Unit> { |
1116 | 0 | self.min |
1117 | 0 | } |
1118 | | |
1119 | | /// Returns the minimum and maximum units set. |
1120 | | /// |
1121 | | /// This returns an error if no units were set. (Since this means there |
1122 | | /// were no parsed duration components.) |
1123 | 0 | fn get_min_max_units(&self) -> Result<(Unit, Unit), Error> { |
1124 | 0 | let (Some(min), Some(max)) = (self.min, self.max) else { |
1125 | 0 | return Err(err!("no parsed duration components")); |
1126 | | }; |
1127 | 0 | Ok((min, max)) |
1128 | 0 | } |
1129 | | |
1130 | | /// Returns the corresponding unit value using the set signed-ness. |
1131 | | #[cfg_attr(feature = "perf-inline", inline(always))] |
1132 | 0 | fn get_unit_value(&self, unit: Unit) -> Result<i64, Error> { |
1133 | | const I64_MIN_ABS: u64 = i64::MIN.unsigned_abs(); |
1134 | | |
1135 | | #[cold] |
1136 | | #[inline(never)] |
1137 | 0 | fn general(unit: Unit, value: u64, sign: Sign) -> Result<i64, Error> { |
1138 | | // As a weird special case, when we need to represent i64::MIN, |
1139 | | // we'll have a unit value of `|i64::MIN|` as a `u64`. We can't |
1140 | | // convert that to a positive `i64` first, since it will overflow. |
1141 | 0 | if sign.is_negative() && value == I64_MIN_ABS { |
1142 | 0 | return Ok(i64::MIN); |
1143 | 0 | } |
1144 | | // Otherwise, if a conversion to `i64` fails, then that failure |
1145 | | // is correct. |
1146 | 0 | let mut value = i64::try_from(value).map_err(|_| { |
1147 | 0 | err!( |
1148 | 0 | "`{sign}{value}` {unit} is too big (or small) \ |
1149 | 0 | to fit into a signed 64-bit integer", |
1150 | 0 | unit = unit.plural() |
1151 | | ) |
1152 | 0 | })?; |
1153 | 0 | if sign.is_negative() { |
1154 | 0 | value = value.checked_neg().ok_or_else(|| { |
1155 | 0 | err!( |
1156 | 0 | "`{sign}{value}` {unit} is too big (or small) \ |
1157 | 0 | to fit into a signed 64-bit integer", |
1158 | 0 | unit = unit.plural() |
1159 | | ) |
1160 | 0 | })?; |
1161 | 0 | } |
1162 | 0 | Ok(value) |
1163 | 0 | } |
1164 | | |
1165 | 0 | let sign = self.get_sign(); |
1166 | 0 | let value = self.values[unit.as_usize()]; |
1167 | 0 | if value >= I64_MIN_ABS { |
1168 | 0 | return general(unit, value, sign); |
1169 | 0 | } |
1170 | 0 | let mut value = value as i64; |
1171 | 0 | if sign.is_negative() { |
1172 | 0 | value = -value; |
1173 | 0 | } |
1174 | 0 | Ok(value) |
1175 | 0 | } |
1176 | | |
1177 | | /// Returns the fraction using the set signed-ness. |
1178 | | /// |
1179 | | /// This returns `None` when no fraction has been set. |
1180 | 0 | fn get_fraction(&self) -> Result<Option<i32>, Error> { |
1181 | 0 | let Some(fraction) = self.fraction else { |
1182 | 0 | return Ok(None); |
1183 | | }; |
1184 | | // OK because `set_fraction` guarantees `0..=999_999_999`. |
1185 | 0 | let mut fraction = fraction as i32; |
1186 | 0 | if self.get_sign().is_negative() { |
1187 | 0 | // OK because `set_fraction` guarantees `0..=999_999_999`. |
1188 | 0 | fraction = -fraction; |
1189 | 0 | } |
1190 | 0 | Ok(Some(fraction)) |
1191 | 0 | } |
1192 | | |
1193 | | /// Returns the sign that should be applied to each individual unit. |
1194 | 0 | fn get_sign(&self) -> Sign { |
1195 | 0 | if self.any_non_zero_units { |
1196 | 0 | self.sign |
1197 | | } else { |
1198 | 0 | Sign::Zero |
1199 | | } |
1200 | 0 | } |
1201 | | } |
1202 | | |
1203 | | /// Parses an optional fractional number from the start of `input`. |
1204 | | /// |
1205 | | /// If `input` does not begin with a `.` (or a `,`), then this returns `None` |
1206 | | /// and no input is consumed. Otherwise, up to 9 ASCII digits are parsed after |
1207 | | /// the decimal separator. |
1208 | | /// |
1209 | | /// While this is most typically used to parse the fractional component of |
1210 | | /// second units, it is also used to parse the fractional component of hours or |
1211 | | /// minutes in ISO 8601 duration parsing, and milliseconds and microseconds in |
1212 | | /// the "friendly" duration format. The return type in that case is obviously a |
1213 | | /// misnomer, but the range of possible values is still correct. (That is, the |
1214 | | /// fractional component of an hour is still limited to 9 decimal places per |
1215 | | /// the Temporal spec.) |
1216 | | /// |
1217 | | /// The number returned is guaranteed to be in the range `0..=999_999_999`. |
1218 | | #[cfg_attr(feature = "perf-inline", inline(always))] |
1219 | 0 | pub(crate) fn parse_temporal_fraction<'i>( |
1220 | 0 | input: &'i [u8], |
1221 | 0 | ) -> Result<Parsed<'i, Option<u32>>, Error> { |
1222 | | // TimeFraction ::: |
1223 | | // TemporalDecimalFraction |
1224 | | // |
1225 | | // TemporalDecimalFraction ::: |
1226 | | // TemporalDecimalSeparator DecimalDigit |
1227 | | // TemporalDecimalSeparator DecimalDigit DecimalDigit |
1228 | | // TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit |
1229 | | // TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit |
1230 | | // DecimalDigit |
1231 | | // TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit |
1232 | | // DecimalDigit DecimalDigit |
1233 | | // TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit |
1234 | | // DecimalDigit DecimalDigit DecimalDigit |
1235 | | // TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit |
1236 | | // DecimalDigit DecimalDigit DecimalDigit |
1237 | | // DecimalDigit |
1238 | | // TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit |
1239 | | // DecimalDigit DecimalDigit DecimalDigit |
1240 | | // DecimalDigit DecimalDigit |
1241 | | // TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit |
1242 | | // DecimalDigit DecimalDigit DecimalDigit |
1243 | | // DecimalDigit DecimalDigit DecimalDigit |
1244 | | // |
1245 | | // TemporalDecimalSeparator ::: one of |
1246 | | // . , |
1247 | | // |
1248 | | // DecimalDigit :: one of |
1249 | | // 0 1 2 3 4 5 6 7 8 9 |
1250 | | |
1251 | | #[inline(never)] |
1252 | 0 | fn imp<'i>(mut input: &'i [u8]) -> Result<Parsed<'i, Option<u32>>, Error> { |
1253 | 0 | let mkdigits = parse::slicer(input); |
1254 | 0 | while mkdigits(input).len() <= 8 |
1255 | 0 | && input.first().map_or(false, u8::is_ascii_digit) |
1256 | 0 | { |
1257 | 0 | input = &input[1..]; |
1258 | 0 | } |
1259 | 0 | let digits = mkdigits(input); |
1260 | 0 | if digits.is_empty() { |
1261 | 0 | return Err(err!( |
1262 | 0 | "found decimal after seconds component, \ |
1263 | 0 | but did not find any decimal digits after decimal", |
1264 | 0 | )); |
1265 | 0 | } |
1266 | | // I believe this error can never happen, since we know we have no more |
1267 | | // than 9 ASCII digits. Any sequence of 9 ASCII digits can be parsed |
1268 | | // into an `i64`. |
1269 | 0 | let nanoseconds = parse::fraction(digits).map_err(|err| { |
1270 | 0 | err!( |
1271 | 0 | "failed to parse {digits:?} as fractional component \ |
1272 | 0 | (up to 9 digits, nanosecond precision): {err}", |
1273 | 0 | digits = escape::Bytes(digits), |
1274 | | ) |
1275 | 0 | })?; |
1276 | | // OK because parsing is forcefully limited to 9 digits, |
1277 | | // which can never be greater than `999_999_99`, |
1278 | | // which is less than `u32::MAX`. |
1279 | 0 | let nanoseconds = nanoseconds as u32; |
1280 | 0 | Ok(Parsed { value: Some(nanoseconds), input }) |
1281 | 0 | } |
1282 | | |
1283 | 0 | if input.is_empty() || (input[0] != b'.' && input[0] != b',') { |
1284 | 0 | return Ok(Parsed { value: None, input }); |
1285 | 0 | } |
1286 | 0 | imp(&input[1..]) |
1287 | 0 | } |
1288 | | |
1289 | | /// This routine returns a span based on the given unit and value with |
1290 | | /// fractional time applied to it. |
1291 | | /// |
1292 | | /// For example, given a span like `P1dT1.5h`, the `unit` would be |
1293 | | /// `Unit::Hour`, the `value` would be `1` and the `fraction` would be |
1294 | | /// `500_000_000`. The span given would just be `1d`. The span returned would |
1295 | | /// be `P1dT1h30m`. |
1296 | | /// |
1297 | | /// Note that `fraction` can be a fractional hour, minute, second, millisecond |
1298 | | /// or microsecond (even though its type suggests its only a fraction of a |
1299 | | /// second). When milliseconds or microseconds, the given fraction has any |
1300 | | /// sub-nanosecond precision truncated. |
1301 | | /// |
1302 | | /// # Errors |
1303 | | /// |
1304 | | /// This can error if the resulting units would be too large for the limits on |
1305 | | /// a `span`. This also errors if `unit` is not `Hour`, `Minute`, `Second`, |
1306 | | /// `Millisecond` or `Microsecond`. |
1307 | | #[inline(never)] |
1308 | 0 | fn fractional_time_to_span( |
1309 | 0 | unit: Unit, |
1310 | 0 | value: i64, |
1311 | 0 | fraction: i32, |
1312 | 0 | mut span: Span, |
1313 | 0 | ) -> Result<Span, Error> { |
1314 | | const MAX_HOURS: i64 = t::SpanHours::MAX_SELF.get_unchecked() as i64; |
1315 | | const MAX_MINS: i64 = t::SpanMinutes::MAX_SELF.get_unchecked() as i64; |
1316 | | const MAX_SECS: i64 = t::SpanSeconds::MAX_SELF.get_unchecked() as i64; |
1317 | | const MAX_MILLIS: i128 = |
1318 | | t::SpanMilliseconds::MAX_SELF.get_unchecked() as i128; |
1319 | | const MAX_MICROS: i128 = |
1320 | | t::SpanMicroseconds::MAX_SELF.get_unchecked() as i128; |
1321 | | const MIN_HOURS: i64 = t::SpanHours::MIN_SELF.get_unchecked() as i64; |
1322 | | const MIN_MINS: i64 = t::SpanMinutes::MIN_SELF.get_unchecked() as i64; |
1323 | | const MIN_SECS: i64 = t::SpanSeconds::MIN_SELF.get_unchecked() as i64; |
1324 | | const MIN_MILLIS: i128 = |
1325 | | t::SpanMilliseconds::MIN_SELF.get_unchecked() as i128; |
1326 | | const MIN_MICROS: i128 = |
1327 | | t::SpanMicroseconds::MIN_SELF.get_unchecked() as i128; |
1328 | | |
1329 | | // We switch everything over to nanoseconds and then divy that up as |
1330 | | // appropriate. In general, we always create a balanced span, but there |
1331 | | // are some cases where we can't. For example, if one serializes a span |
1332 | | // with both the maximum number of seconds and the maximum number of |
1333 | | // milliseconds, then this just can't be balanced due to the limits on |
1334 | | // each of the units. When this kind of span is serialized to a string, |
1335 | | // it results in a second value that is actually bigger than the maximum |
1336 | | // allowed number of seconds in a span. So here, we have to reverse that |
1337 | | // operation and spread the seconds over smaller units. This in turn |
1338 | | // creates an unbalanced span. Annoying. |
1339 | | // |
1340 | | // The above is why we have `if unit_value > MAX { <do adjustments> }` in |
1341 | | // the balancing code below. Basically, if we overshoot our limit, we back |
1342 | | // out anything over the limit and carry it over to the lesser units. If |
1343 | | // our value is truly too big, then the final call to set nanoseconds will |
1344 | | // fail. |
1345 | 0 | let mut sdur = fractional_time_to_duration(unit, value, fraction)?; |
1346 | | |
1347 | 0 | if unit >= Unit::Hour && !sdur.is_zero() { |
1348 | 0 | let (mut hours, rem) = sdur.as_hours_with_remainder(); |
1349 | 0 | sdur = rem; |
1350 | 0 | if hours > MAX_HOURS { |
1351 | 0 | sdur += SignedDuration::from_hours(hours - MAX_HOURS); |
1352 | 0 | hours = MAX_HOURS; |
1353 | 0 | } else if hours < MIN_HOURS { |
1354 | 0 | sdur += SignedDuration::from_hours(hours - MIN_HOURS); |
1355 | 0 | hours = MIN_HOURS; |
1356 | 0 | } |
1357 | | // OK because we just checked that our units are in range. |
1358 | 0 | span = span.hours(hours); |
1359 | 0 | } |
1360 | 0 | if unit >= Unit::Minute && !sdur.is_zero() { |
1361 | 0 | let (mut mins, rem) = sdur.as_mins_with_remainder(); |
1362 | 0 | sdur = rem; |
1363 | 0 | if mins > MAX_MINS { |
1364 | 0 | sdur += SignedDuration::from_mins(mins - MAX_MINS); |
1365 | 0 | mins = MAX_MINS; |
1366 | 0 | } else if mins < MIN_MINS { |
1367 | 0 | sdur += SignedDuration::from_mins(mins - MIN_MINS); |
1368 | 0 | mins = MIN_MINS; |
1369 | 0 | } |
1370 | | // OK because we just checked that our units are in range. |
1371 | 0 | span = span.minutes(mins); |
1372 | 0 | } |
1373 | 0 | if unit >= Unit::Second && !sdur.is_zero() { |
1374 | 0 | let (mut secs, rem) = sdur.as_secs_with_remainder(); |
1375 | 0 | sdur = rem; |
1376 | 0 | if secs > MAX_SECS { |
1377 | 0 | sdur += SignedDuration::from_secs(secs - MAX_SECS); |
1378 | 0 | secs = MAX_SECS; |
1379 | 0 | } else if secs < MIN_SECS { |
1380 | 0 | sdur += SignedDuration::from_secs(secs - MIN_SECS); |
1381 | 0 | secs = MIN_SECS; |
1382 | 0 | } |
1383 | | // OK because we just checked that our units are in range. |
1384 | 0 | span = span.seconds(secs); |
1385 | 0 | } |
1386 | 0 | if unit >= Unit::Millisecond && !sdur.is_zero() { |
1387 | 0 | let (mut millis, rem) = sdur.as_millis_with_remainder(); |
1388 | 0 | sdur = rem; |
1389 | 0 | if millis > MAX_MILLIS { |
1390 | 0 | sdur += SignedDuration::from_millis_i128(millis - MAX_MILLIS); |
1391 | 0 | millis = MAX_MILLIS; |
1392 | 0 | } else if millis < MIN_MILLIS { |
1393 | 0 | sdur += SignedDuration::from_millis_i128(millis - MIN_MILLIS); |
1394 | 0 | millis = MIN_MILLIS; |
1395 | 0 | } |
1396 | | // OK because we just checked that our units are in range. |
1397 | 0 | span = span.milliseconds(i64::try_from(millis).unwrap()); |
1398 | 0 | } |
1399 | 0 | if unit >= Unit::Microsecond && !sdur.is_zero() { |
1400 | 0 | let (mut micros, rem) = sdur.as_micros_with_remainder(); |
1401 | 0 | sdur = rem; |
1402 | 0 | if micros > MAX_MICROS { |
1403 | 0 | sdur += SignedDuration::from_micros_i128(micros - MAX_MICROS); |
1404 | 0 | micros = MAX_MICROS; |
1405 | 0 | } else if micros < MIN_MICROS { |
1406 | 0 | sdur += SignedDuration::from_micros_i128(micros - MIN_MICROS); |
1407 | 0 | micros = MIN_MICROS; |
1408 | 0 | } |
1409 | | // OK because we just checked that our units are in range. |
1410 | 0 | span = span.microseconds(i64::try_from(micros).unwrap()); |
1411 | 0 | } |
1412 | 0 | if !sdur.is_zero() { |
1413 | 0 | let nanos = sdur.as_nanos(); |
1414 | 0 | let nanos64 = i64::try_from(nanos).map_err(|_| { |
1415 | 0 | err!( |
1416 | 0 | "failed to set nanosecond value {nanos} (it overflows \ |
1417 | 0 | `i64`) on span determined from {value}.{fraction}", |
1418 | | ) |
1419 | 0 | })?; |
1420 | 0 | span = span.try_nanoseconds(nanos64).with_context(|| { |
1421 | 0 | err!( |
1422 | 0 | "failed to set nanosecond value {nanos64} on span \ |
1423 | 0 | determined from {value}.{fraction}", |
1424 | | ) |
1425 | 0 | })?; |
1426 | 0 | } |
1427 | | |
1428 | 0 | Ok(span) |
1429 | 0 | } |
1430 | | |
1431 | | /// Like `fractional_time_to_span`, but just converts the fraction of the given |
1432 | | /// unit to a signed duration. |
1433 | | /// |
1434 | | /// Since a signed duration doesn't keep track of individual units, there is |
1435 | | /// no loss of fidelity between it and ISO 8601 durations like there is for |
1436 | | /// `Span`. |
1437 | | /// |
1438 | | /// Note that `fraction` can be a fractional hour, minute, second, millisecond |
1439 | | /// or microsecond (even though its type suggests it's only a fraction of a |
1440 | | /// second). When milliseconds or microseconds, the given fraction has any |
1441 | | /// sub-nanosecond precision truncated. |
1442 | | /// |
1443 | | /// # Errors |
1444 | | /// |
1445 | | /// This returns an error if `unit` is not `Hour`, `Minute`, `Second`, |
1446 | | /// `Millisecond` or `Microsecond`. |
1447 | | #[inline(never)] |
1448 | 0 | fn fractional_time_to_duration( |
1449 | 0 | unit: Unit, |
1450 | 0 | value: i64, |
1451 | 0 | fraction: i32, |
1452 | 0 | ) -> Result<SignedDuration, Error> { |
1453 | 0 | let sdur = duration_unit_value(unit, value)?; |
1454 | 0 | let fraction_dur = fractional_duration(unit, fraction)?; |
1455 | 0 | sdur.checked_add(fraction_dur).ok_or_else(|| { |
1456 | 0 | err!( |
1457 | 0 | "accumulated `SignedDuration` of `{sdur:?}` overflowed \ |
1458 | 0 | when adding `{fraction_dur:?}` (from fractional {unit} units)", |
1459 | 0 | unit = unit.singular(), |
1460 | | ) |
1461 | 0 | }) |
1462 | 0 | } |
1463 | | |
1464 | | /// Converts the fraction of the given unit to a signed duration. |
1465 | | /// |
1466 | | /// Since a signed duration doesn't keep track of individual units, there is |
1467 | | /// no loss of fidelity between it and ISO 8601 durations like there is for |
1468 | | /// `Span`. Thus, we can do something far less complicated. |
1469 | | /// |
1470 | | /// # Panics |
1471 | | /// |
1472 | | /// When `fraction` isn't in the range `-999_999_999..=999_999_999`. |
1473 | | /// |
1474 | | /// # Errors |
1475 | | /// |
1476 | | /// This returns an error if `unit` is not `Hour`, `Minute`, `Second`, |
1477 | | /// `Millisecond` or `Microsecond`. |
1478 | | #[inline(never)] |
1479 | 0 | fn fractional_duration( |
1480 | 0 | unit: Unit, |
1481 | 0 | fraction: i32, |
1482 | 0 | ) -> Result<SignedDuration, Error> { |
1483 | 0 | let fraction = i64::from(fraction); |
1484 | 0 | let nanos = match unit { |
1485 | 0 | Unit::Hour => fraction * t::SECONDS_PER_HOUR.value(), |
1486 | 0 | Unit::Minute => fraction * t::SECONDS_PER_MINUTE.value(), |
1487 | 0 | Unit::Second => fraction, |
1488 | 0 | Unit::Millisecond => fraction / t::NANOS_PER_MICRO.value(), |
1489 | 0 | Unit::Microsecond => fraction / t::NANOS_PER_MILLI.value(), |
1490 | 0 | unit => { |
1491 | 0 | return Err(err!( |
1492 | 0 | "fractional {unit} units are not allowed", |
1493 | 0 | unit = unit.singular(), |
1494 | 0 | )) |
1495 | | } |
1496 | | }; |
1497 | 0 | Ok(SignedDuration::from_nanos(nanos)) |
1498 | 0 | } |
1499 | | |
1500 | | /// Returns the given parsed value, interpreted as the given unit, as a |
1501 | | /// `SignedDuration`. |
1502 | | /// |
1503 | | /// If the given unit is not supported for signed durations (i.e., calendar |
1504 | | /// units), or if converting the given value to a `SignedDuration` for the |
1505 | | /// given units overflows, then an error is returned. |
1506 | | #[cfg_attr(feature = "perf-inline", inline(always))] |
1507 | 0 | fn duration_unit_value( |
1508 | 0 | unit: Unit, |
1509 | 0 | value: i64, |
1510 | 0 | ) -> Result<SignedDuration, Error> { |
1511 | | // Convert our parsed unit into a number of nanoseconds. |
1512 | | // |
1513 | | // Note also that overflow isn't possible here for units less than minutes, |
1514 | | // since a `SignedDuration` supports all `i64` second values. |
1515 | 0 | let sdur = match unit { |
1516 | | Unit::Hour => { |
1517 | 0 | let seconds = value |
1518 | 0 | .checked_mul(t::SECONDS_PER_HOUR.value()) |
1519 | 0 | .ok_or_else(|| { |
1520 | 0 | err!("converting {value} hours to seconds overflows i64") |
1521 | 0 | })?; |
1522 | 0 | SignedDuration::from_secs(seconds) |
1523 | | } |
1524 | | Unit::Minute => { |
1525 | 0 | let seconds = value |
1526 | 0 | .checked_mul(t::SECONDS_PER_MINUTE.value()) |
1527 | 0 | .ok_or_else(|| { |
1528 | 0 | err!("converting {value} minutes to seconds overflows i64") |
1529 | 0 | })?; |
1530 | 0 | SignedDuration::from_secs(seconds) |
1531 | | } |
1532 | 0 | Unit::Second => SignedDuration::from_secs(value), |
1533 | 0 | Unit::Millisecond => SignedDuration::from_millis(value), |
1534 | 0 | Unit::Microsecond => SignedDuration::from_micros(value), |
1535 | 0 | Unit::Nanosecond => SignedDuration::from_nanos(value), |
1536 | 0 | unsupported => { |
1537 | 0 | return Err(err!( |
1538 | 0 | "parsing {unit} units into a `SignedDuration` is not supported \ |
1539 | 0 | (perhaps try parsing into a `Span` instead)", |
1540 | 0 | unit = unsupported.singular(), |
1541 | 0 | )); |
1542 | | } |
1543 | | }; |
1544 | 0 | Ok(sdur) |
1545 | 0 | } |
1546 | | |
1547 | | #[cfg(test)] |
1548 | | mod tests { |
1549 | | use alloc::string::ToString; |
1550 | | |
1551 | | use super::*; |
1552 | | |
1553 | | #[test] |
1554 | | fn decimal() { |
1555 | | let x = DecimalFormatter::new().format_signed(i64::MIN); |
1556 | | assert_eq!(x.as_str(), "-9223372036854775808"); |
1557 | | |
1558 | | let x = DecimalFormatter::new().format_signed(i64::MIN + 1); |
1559 | | assert_eq!(x.as_str(), "-9223372036854775807"); |
1560 | | |
1561 | | let x = DecimalFormatter::new().format_signed(i64::MAX); |
1562 | | assert_eq!(x.as_str(), "9223372036854775807"); |
1563 | | |
1564 | | let x = |
1565 | | DecimalFormatter::new().force_sign(true).format_signed(i64::MAX); |
1566 | | assert_eq!(x.as_str(), "+9223372036854775807"); |
1567 | | |
1568 | | let x = DecimalFormatter::new().format_signed(0); |
1569 | | assert_eq!(x.as_str(), "0"); |
1570 | | |
1571 | | let x = DecimalFormatter::new().force_sign(true).format_signed(0); |
1572 | | assert_eq!(x.as_str(), "+0"); |
1573 | | |
1574 | | let x = DecimalFormatter::new().force_sign(false).format_signed(0); |
1575 | | assert_eq!(x.as_str(), "-0"); |
1576 | | |
1577 | | let x = DecimalFormatter::new().padding(4).format_signed(0); |
1578 | | assert_eq!(x.as_str(), "0000"); |
1579 | | |
1580 | | let x = DecimalFormatter::new().padding(4).format_signed(789); |
1581 | | assert_eq!(x.as_str(), "0789"); |
1582 | | |
1583 | | let x = DecimalFormatter::new().padding(4).format_signed(-789); |
1584 | | assert_eq!(x.as_str(), "-0789"); |
1585 | | |
1586 | | let x = DecimalFormatter::new() |
1587 | | .force_sign(true) |
1588 | | .padding(4) |
1589 | | .format_signed(789); |
1590 | | assert_eq!(x.as_str(), "+0789"); |
1591 | | } |
1592 | | |
1593 | | #[test] |
1594 | | fn fractional_auto() { |
1595 | | let f = |n| FractionalFormatter::new().format(n).as_str().to_string(); |
1596 | | |
1597 | | assert_eq!(f(0), ""); |
1598 | | assert_eq!(f(123_000_000), "123"); |
1599 | | assert_eq!(f(123_456_000), "123456"); |
1600 | | assert_eq!(f(123_456_789), "123456789"); |
1601 | | assert_eq!(f(456_789), "000456789"); |
1602 | | assert_eq!(f(789), "000000789"); |
1603 | | } |
1604 | | |
1605 | | #[test] |
1606 | | fn fractional_precision() { |
1607 | | let f = |precision, n| { |
1608 | | FractionalFormatter::new() |
1609 | | .precision(Some(precision)) |
1610 | | .format(n) |
1611 | | .as_str() |
1612 | | .to_string() |
1613 | | }; |
1614 | | |
1615 | | assert_eq!(f(0, 0), ""); |
1616 | | assert_eq!(f(1, 0), "0"); |
1617 | | assert_eq!(f(9, 0), "000000000"); |
1618 | | |
1619 | | assert_eq!(f(3, 123_000_000), "123"); |
1620 | | assert_eq!(f(6, 123_000_000), "123000"); |
1621 | | assert_eq!(f(9, 123_000_000), "123000000"); |
1622 | | |
1623 | | assert_eq!(f(3, 123_456_000), "123"); |
1624 | | assert_eq!(f(6, 123_456_000), "123456"); |
1625 | | assert_eq!(f(9, 123_456_000), "123456000"); |
1626 | | |
1627 | | assert_eq!(f(3, 123_456_789), "123"); |
1628 | | assert_eq!(f(6, 123_456_789), "123456"); |
1629 | | assert_eq!(f(9, 123_456_789), "123456789"); |
1630 | | |
1631 | | // We use truncation, no rounding. |
1632 | | assert_eq!(f(2, 889_000_000), "88"); |
1633 | | assert_eq!(f(2, 999_000_000), "99"); |
1634 | | } |
1635 | | } |