Skip to main content

rkyv/
time.rs

1//! Archived versions of `time` types.
2
3use crate::{
4    primitive::{ArchivedU32, ArchivedU64},
5    Portable,
6};
7
8/// An archived [`Duration`](core::time::Duration).
9#[derive(
10    Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd, Portable,
11)]
12#[cfg_attr(
13    feature = "bytecheck",
14    derive(bytecheck::CheckBytes),
15    bytecheck(verify)
16)]
17#[rkyv(crate)]
18#[repr(C)]
19pub struct ArchivedDuration {
20    secs: ArchivedU64,
21    nanos: ArchivedU32,
22}
23
24const NANOS_PER_SEC: u32 = 1_000_000_000;
25const NANOS_PER_MILLI: u32 = 1_000_000;
26const NANOS_PER_MICRO: u32 = 1_000;
27const MILLIS_PER_SEC: u64 = 1_000;
28const MICROS_PER_SEC: u64 = 1_000_000;
29
30impl ArchivedDuration {
31    /// Returns the number of _whole_ seconds contained by this
32    /// `ArchivedDuration`.
33    ///
34    /// The returned value does not include the fractional (nanosecond) part of
35    /// the duration, which can be obtained using [`subsec_nanos`].
36    ///
37    /// [`subsec_nanos`]: ArchivedDuration::subsec_nanos
38    #[inline]
39    pub const fn as_secs(&self) -> u64 {
40        self.secs.to_native()
41    }
42
43    /// Returns the fractional part of this `ArchivedDuration`, in whole
44    /// milliseconds.
45    ///
46    /// This method does **not** return the length of the duration when
47    /// represented by milliseconds. The returned number always represents a
48    /// fractional portion of a second (i.e., it is less than one thousand).
49    #[inline]
50    pub const fn subsec_millis(&self) -> u32 {
51        self.nanos.to_native() / NANOS_PER_MILLI
52    }
53
54    /// Returns the fractional part of this `ArchivedDuration`, in whole
55    /// microseconds.
56    ///
57    /// This method does **not** return the length of the duration when
58    /// represented by microseconds. The returned number always represents a
59    /// fractional portion of a second (i.e., it is less than one million).
60    #[inline]
61    pub const fn subsec_micros(&self) -> u32 {
62        self.nanos.to_native() / NANOS_PER_MICRO
63    }
64
65    /// Returns the fractional part of this `Duration`, in nanoseconds.
66    ///
67    /// This method does **not** return the length of the duration when
68    /// represented by nanoseconds. The returned number always represents a
69    /// fractional portion of a second (i.e., it is less than one billion).
70    #[inline]
71    pub const fn subsec_nanos(&self) -> u32 {
72        self.nanos.to_native()
73    }
74
75    /// Returns the total number of whole milliseconds contained by this
76    /// `ArchivedDuration`.
77    #[inline]
78    pub const fn as_millis(&self) -> u128 {
79        self.as_secs() as u128 * MILLIS_PER_SEC as u128
80            + (self.subsec_nanos() / NANOS_PER_MILLI) as u128
81    }
82
83    /// Returns the total number of whole microseconds contained by this
84    /// `ArchivedDuration`.
85    #[inline]
86    pub const fn as_micros(&self) -> u128 {
87        self.as_secs() as u128 * MICROS_PER_SEC as u128
88            + (self.subsec_nanos() / NANOS_PER_MICRO) as u128
89    }
90
91    /// Returns the total number of nanoseconds contained by this
92    /// `ArchivedDuration`.
93    #[inline]
94    pub const fn as_nanos(&self) -> u128 {
95        self.as_secs() as u128 * NANOS_PER_SEC as u128
96            + self.subsec_nanos() as u128
97    }
98
99    /// Returns the number of seconds contained by this `ArchivedDuration` as
100    /// `f64`.
101    ///
102    /// The returned value does include the fractional (nanosecond) part of the
103    /// duration.
104    #[inline]
105    pub fn as_secs_f64(&self) -> f64 {
106        (self.as_secs() as f64)
107            + (self.subsec_nanos() as f64) / (NANOS_PER_SEC as f64)
108    }
109
110    /// Returns the number of seconds contained by this `ArchivedDuration` as
111    /// `f32`.
112    ///
113    /// The returned value does include the fractional (nanosecond) part of the
114    /// duration.
115    #[inline]
116    pub fn as_secs_f32(&self) -> f32 {
117        (self.as_secs() as f32)
118            + (self.subsec_nanos() as f32) / (NANOS_PER_SEC as f32)
119    }
120
121    /// Constructs an archived duration at the given position.
122    ///
123    /// This function is guaranteed not to write any uninitialized bytes to
124    /// `out`.
125    ///
126    /// # Safety
127    ///
128    /// `out` must point to memory suitable for holding an `ArchivedDuration`.
129    #[inline]
130    pub unsafe fn emplace(secs: u64, nanos: u32, out: *mut ArchivedDuration) {
131        use core::ptr::addr_of_mut;
132
133        let out_secs = unsafe { addr_of_mut!((*out).secs) };
134        unsafe {
135            out_secs.write(ArchivedU64::from_native(secs));
136        }
137        let out_nanos = unsafe { addr_of_mut!((*out).nanos) };
138        unsafe {
139            out_nanos.write(ArchivedU32::from_native(nanos));
140        }
141    }
142}
143
144#[cfg(feature = "bytecheck")]
145mod verify {
146    use core::{error::Error, fmt};
147
148    use bytecheck::{
149        rancor::{Fallible, Source},
150        Verify,
151    };
152    use rancor::fail;
153
154    use super::ArchivedDuration;
155
156    /// An error resulting from an invalid duration.
157    ///
158    /// Durations must have a `nanos` field that is less than one billion.
159    #[derive(Debug)]
160    pub struct DurationError {
161        nanos: u32,
162    }
163
164    impl fmt::Display for DurationError {
165        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166            write!(
167                f,
168                "`nanos` field of `Duration` is greater than 1 billion: {}",
169                self.nanos,
170            )
171        }
172    }
173
174    impl Error for DurationError {}
175
176    unsafe impl<C> Verify<C> for ArchivedDuration
177    where
178        C: Fallible + ?Sized,
179        C::Error: Source,
180    {
181        fn verify(&self, _: &mut C) -> Result<(), C::Error> {
182            let nanos = self.nanos.to_native();
183            if nanos >= 1_000_000_000 {
184                fail!(DurationError { nanos });
185            } else {
186                Ok(())
187            }
188        }
189    }
190}