packet_formats/
utils.rs

1// Copyright 2020 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Utilities useful when parsing and serializing wire formats.
6
7use core::num::{NonZeroU32, NonZeroU64};
8use core::time::Duration;
9
10/// A thin wrapper over a [`Duration`] that guarantees that the underlying
11/// `Duration` is non-zero.
12#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
13pub struct NonZeroDuration(Duration);
14
15impl NonZeroDuration {
16    /// Creates a non-zero without checking the value.
17    ///
18    /// # Safety
19    ///
20    /// If `d` is zero, unsafe code which relies on the invariant that
21    /// `NonZeroDuration` values are always zero may cause undefined behavior.
22    pub const unsafe fn new_unchecked(d: Duration) -> NonZeroDuration {
23        NonZeroDuration(d)
24    }
25
26    /// Creates a new `NonZeroDuration` from the specified number of whole
27    /// seconds if that number is non-zero.
28    pub const fn from_secs(secs: u64) -> Option<NonZeroDuration> {
29        NonZeroDuration::new(Duration::from_secs(secs))
30    }
31
32    /// Creates a new `NonZeroDuration` from the specified non-zero number of
33    /// whole seconds.
34    pub const fn from_nonzero_secs(secs: NonZeroU64) -> NonZeroDuration {
35        NonZeroDuration(Duration::from_secs(secs.get()))
36    }
37
38    /// Creates a new `NonZeroDuration` from the specified number of whole
39    /// seconds and additional nanoseconds if the resulting duration is
40    /// non-zero.
41    ///
42    /// If the number of nanoseconds is greater than 1 billion (the number of
43    /// nanoseconds in a second), then it will carry over into the seconds
44    /// provided.
45    ///
46    /// # Panics
47    ///
48    /// This constructor will panic if the carry from the nanoseconds overflows
49    /// the seconds counter.
50    pub const fn from_secs_nanos(secs: u64, nanos: u32) -> Option<NonZeroDuration> {
51        NonZeroDuration::new(Duration::new(secs, nanos))
52    }
53
54    /// Creates a new `NonZeroDuration` from the specified non-zero number of
55    /// whole seconds and additional nanoseconds.
56    ///
57    /// If the number of nanoseconds is greater than 1 billion (the number of
58    /// nanoseconds in a second), then it will carry over into the seconds
59    /// provided.
60    ///
61    /// # Panics
62    ///
63    /// This constructor will panic if the carry from the nanoseconds overflows
64    /// the seconds counter.
65    pub const fn from_nonzero_secs_nanos(secs: NonZeroU64, nanos: NonZeroU32) -> NonZeroDuration {
66        NonZeroDuration(Duration::new(secs.get(), nanos.get()))
67    }
68
69    /// Creates a new `NonZeroDuration` from the specified number of
70    /// milliseconds if that number is non-zero.
71    pub const fn from_millis(millis: u64) -> Option<NonZeroDuration> {
72        NonZeroDuration::new(Duration::from_millis(millis))
73    }
74
75    /// Creates a new `NonZeroDuration` from the specified non-zero number of
76    /// milliseconds.
77    pub const fn from_nonzero_millis(millis: NonZeroU64) -> NonZeroDuration {
78        NonZeroDuration(Duration::from_millis(millis.get()))
79    }
80
81    /// Creates a new `NonZeroDuration` from the specified number of
82    /// microseconds if that number is non-zero.
83    pub const fn from_micros(micros: u64) -> Option<NonZeroDuration> {
84        NonZeroDuration::new(Duration::from_micros(micros))
85    }
86
87    /// Creates a new `NonZeroDuration` from the specified non-zero number of
88    /// microseconds.
89    pub const fn from_nonzero_micros(micros: NonZeroU64) -> NonZeroDuration {
90        NonZeroDuration(Duration::from_micros(micros.get()))
91    }
92
93    /// Creates a new `NonZeroDuration` from the specified number of nanoseconds
94    /// if that number is non-zero.
95    pub const fn from_nanos(nanos: u64) -> Option<NonZeroDuration> {
96        NonZeroDuration::new(Duration::from_nanos(nanos))
97    }
98
99    /// Creates a new `NonZeroDuration` from the specified non-zero number of
100    /// nanoseconds.
101    pub const fn from_nonzero_nanos(nanos: NonZeroU64) -> NonZeroDuration {
102        NonZeroDuration(Duration::from_nanos(nanos.get()))
103    }
104
105    /// Creates a non-zero if the given value is not zero.
106    pub const fn new(d: Duration) -> Option<NonZeroDuration> {
107        // Can't do this comparison as `d == Duration::from_secs(0)` because
108        // equality checking is not const.
109        if d.as_nanos() == 0 {
110            return None;
111        }
112
113        Some(NonZeroDuration(d))
114    }
115
116    /// Like [`Duration::saturating_add`].
117    pub fn saturating_add(self, d: Duration) -> Self {
118        // NB: Duration is strictly positive so we're not violating the
119        // invariant.
120        Self(self.0.saturating_add(d))
121    }
122
123    /// Like [`Duration::saturating_mul`].
124    pub fn saturating_mul(self, m: NonZeroU32) -> Self {
125        // NB: multiplier is nonzero so we're not violating the invariant.
126        Self(self.0.saturating_mul(m.into()))
127    }
128
129    /// Returns the value as a [`Duration`].
130    pub const fn get(self) -> Duration {
131        self.0
132    }
133}
134
135impl From<NonZeroDuration> for Duration {
136    fn from(NonZeroDuration(d): NonZeroDuration) -> Duration {
137        d
138    }
139}
140
141impl core::ops::Add<Duration> for NonZeroDuration {
142    type Output = Self;
143
144    fn add(self, rhs: Duration) -> Self::Output {
145        let Self(d) = self;
146        Self(d + rhs)
147    }
148}
149
150impl core::ops::Add<NonZeroDuration> for NonZeroDuration {
151    type Output = Self;
152
153    fn add(self, rhs: NonZeroDuration) -> Self::Output {
154        self + rhs.get()
155    }
156}
157
158impl core::ops::Mul<NonZeroU32> for NonZeroDuration {
159    type Output = Self;
160
161    fn mul(self, rhs: NonZeroU32) -> Self::Output {
162        let Self(d) = self;
163        Self(d * rhs.get())
164    }
165}
166
167/// Rounds `x` up to the next multiple of 4 unless `x` is already a multiple of
168/// 4.
169pub(crate) fn round_to_next_multiple_of_four(x: usize) -> usize {
170    (x + 3) & !3
171}
172
173#[cfg(test)]
174mod tests {
175    use super::*;
176
177    #[test]
178    fn non_zero_duration() {
179        // `NonZeroDuration` should not hold a zero duration.
180        assert_eq!(NonZeroDuration::new(Duration::from_secs(0)), None);
181
182        // `new_unchecked` should hold the duration as is.
183        let d = Duration::from_secs(1);
184        assert_eq!(unsafe { NonZeroDuration::new_unchecked(d) }, NonZeroDuration(d));
185
186        // `NonZeroDuration` should hold a non-zero duration.
187        let non_zero = NonZeroDuration::new(d);
188        assert_eq!(non_zero, Some(NonZeroDuration(d)));
189
190        let one_u64 = NonZeroU64::new(1).unwrap();
191        let expect = NonZeroDuration(Duration::from_secs(1));
192        assert_eq!(NonZeroDuration::from_secs(1), Some(expect));
193        assert_eq!(NonZeroDuration::from_nonzero_secs(one_u64), expect);
194
195        let expect = NonZeroDuration(Duration::new(1, 1));
196        assert_eq!(NonZeroDuration::from_secs_nanos(1, 1), Some(expect));
197        assert_eq!(
198            NonZeroDuration::from_nonzero_secs_nanos(one_u64, NonZeroU32::new(1).unwrap()),
199            expect
200        );
201
202        let expect = NonZeroDuration(Duration::from_millis(1));
203        assert_eq!(NonZeroDuration::from_millis(1), Some(expect));
204        assert_eq!(NonZeroDuration::from_nonzero_millis(one_u64), expect);
205
206        let expect = NonZeroDuration(Duration::from_micros(1));
207        assert_eq!(NonZeroDuration::from_micros(1), Some(expect));
208        assert_eq!(NonZeroDuration::from_nonzero_micros(one_u64), expect);
209
210        let expect = NonZeroDuration(Duration::from_nanos(1));
211        assert_eq!(NonZeroDuration::from_nanos(1), Some(expect));
212        assert_eq!(NonZeroDuration::from_nonzero_nanos(one_u64), expect);
213
214        // `get` and `Into::into` should return the underlying duration.
215        let non_zero = non_zero.unwrap();
216        assert_eq!(d, non_zero.get());
217        assert_eq!(d, non_zero.into());
218    }
219
220    #[test]
221    fn test_next_multiple_of_four() {
222        for x in 0usize..=(core::u16::MAX - 3) as usize {
223            let y = round_to_next_multiple_of_four(x);
224            assert_eq!(y % 4, 0);
225            assert!(y >= x);
226            if x % 4 == 0 {
227                assert_eq!(x, y);
228            } else {
229                assert_eq!(x + (4 - x % 4), y);
230            }
231        }
232    }
233
234    #[test]
235    fn add_duration() {
236        let a = NonZeroDuration::new(Duration::from_secs(48291)).unwrap();
237        let b = Duration::from_secs(195811);
238        assert_eq!(Some(a + b), NonZeroDuration::new(a.get() + b));
239
240        assert_eq!(a + Duration::from_secs(0), a);
241    }
242
243    #[test]
244    fn add_nonzero_duration() {
245        let a = NonZeroDuration::new(Duration::from_secs(48291)).unwrap();
246        let b = NonZeroDuration::new(Duration::from_secs(195811)).unwrap();
247        assert_eq!(Some(a + b), NonZeroDuration::new(a.get() + b.get()));
248    }
249}