/src/gitoxide/gix-date/src/time/write.rs
Line | Count | Source |
1 | | use crate::{SecondsSinceUnixEpoch, Time}; |
2 | | |
3 | | /// Serialize this instance as string, similar to what [`write_to()`](Self::write_to()) would do. |
4 | | impl std::fmt::Display for Time { |
5 | 0 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
6 | 0 | let mut buf = Vec::with_capacity(Time::MIN.size()); |
7 | 0 | self.write_to(&mut buf).expect("write to memory cannot fail"); |
8 | | // Time serializes as ASCII, which is a subset of UTF-8. |
9 | | // Use `from_utf8()` (validated) instead of `from_utf8_unchecked()` for safety, |
10 | | // with the option to switch to an unsafe version if 30% performance boost are needed here. |
11 | 0 | let raw = std::str::from_utf8(&buf).expect("time serializes as valid UTF-8"); |
12 | 0 | f.write_str(raw) |
13 | 0 | } Unexecuted instantiation: <gix_date::Time as core::fmt::Display>::fmt Unexecuted instantiation: <gix_date::Time as core::fmt::Display>::fmt Unexecuted instantiation: <gix_date::Time as core::fmt::Display>::fmt |
14 | | } |
15 | | |
16 | | /// Serialization with standard `git` format |
17 | | impl Time { |
18 | | /// Serialize this instance to `out` in a format suitable for use in header fields of serialized git commits or tags. |
19 | 0 | pub fn write_to(&self, out: &mut dyn std::io::Write) -> std::io::Result<()> { |
20 | | const SECONDS_PER_HOUR: u32 = 60 * 60; |
21 | 0 | let offset = self.offset.unsigned_abs(); |
22 | 0 | let hours = offset / SECONDS_PER_HOUR; |
23 | 0 | let minutes = (offset - (hours * SECONDS_PER_HOUR)) / 60; |
24 | | |
25 | 0 | if hours > 99 { |
26 | 0 | return Err(std::io::Error::other("Cannot represent offsets larger than +-9900")); |
27 | 0 | } |
28 | | |
29 | 0 | let mut itoa = itoa::Buffer::new(); |
30 | 0 | out.write_all(itoa.format(self.seconds).as_bytes())?; |
31 | 0 | out.write_all(b" ")?; |
32 | 0 | out.write_all(if self.offset < 0 { b"-" } else { b"+" })?; |
33 | | |
34 | | const ZERO: &[u8; 1] = b"0"; |
35 | | |
36 | 0 | if hours < 10 { |
37 | 0 | out.write_all(ZERO)?; |
38 | 0 | } |
39 | 0 | out.write_all(itoa.format(hours).as_bytes())?; |
40 | | |
41 | 0 | if minutes < 10 { |
42 | 0 | out.write_all(ZERO)?; |
43 | 0 | } |
44 | 0 | out.write_all(itoa.format(minutes).as_bytes()).map(|_| ()) |
45 | 0 | } Unexecuted instantiation: <gix_date::Time>::write_to Unexecuted instantiation: <gix_date::Time>::write_to Unexecuted instantiation: <gix_date::Time>::write_to |
46 | | |
47 | | /// Computes the number of bytes necessary to write it using [`Time::write_to()`]. |
48 | 0 | pub const fn size(&self) -> usize { |
49 | 0 | let is_negative = self.seconds < 0; |
50 | 0 | Self::count_positive_digits(self.seconds.unsigned_abs()) + is_negative as usize + 6 |
51 | | // space + offset sign + hours (2) + minutes (2) |
52 | 0 | } Unexecuted instantiation: <gix_date::Time>::size Unexecuted instantiation: <gix_date::Time>::size Unexecuted instantiation: <gix_date::Time>::size |
53 | | |
54 | | /// Count the number of decimal digits in a positive integer. |
55 | 0 | const fn count_positive_digits(n: u64) -> usize { |
56 | | // Powers of 10 for comparison |
57 | | const POW10: [u64; 20] = [ |
58 | | 1, |
59 | | 10, |
60 | | 100, |
61 | | 1_000, |
62 | | 10_000, |
63 | | 100_000, |
64 | | 1_000_000, |
65 | | 10_000_000, |
66 | | 100_000_000, |
67 | | 1_000_000_000, |
68 | | 10_000_000_000, |
69 | | 100_000_000_000, |
70 | | 1_000_000_000_000, |
71 | | 10_000_000_000_000, |
72 | | 100_000_000_000_000, |
73 | | 1_000_000_000_000_000, |
74 | | 10_000_000_000_000_000, |
75 | | 100_000_000_000_000_000, |
76 | | 1_000_000_000_000_000_000, |
77 | | 10_000_000_000_000_000_000, |
78 | | ]; |
79 | | |
80 | | // Binary search would be nice but not const-fn friendly, so use simple loop |
81 | 0 | let mut digits = 1; |
82 | 0 | while digits < 20 && n >= POW10[digits] { |
83 | 0 | digits += 1; |
84 | 0 | } |
85 | 0 | digits |
86 | 0 | } Unexecuted instantiation: <gix_date::Time>::count_positive_digits Unexecuted instantiation: <gix_date::Time>::count_positive_digits Unexecuted instantiation: <gix_date::Time>::count_positive_digits |
87 | | |
88 | | /// The numerically largest possible time instance. |
89 | | pub const MAX: Time = Time { |
90 | | seconds: SecondsSinceUnixEpoch::MAX, |
91 | | offset: 99 * 60 * 60 + 59 * 60 + 59, |
92 | | }; |
93 | | |
94 | | /// The numerically smallest possible time instance. |
95 | | pub const MIN: Time = Time { |
96 | | seconds: SecondsSinceUnixEpoch::MIN, |
97 | | offset: -(99 * 60 * 60 + 59 * 60 + 59), |
98 | | }; |
99 | | } |