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.
45//! Utilities useful when parsing and serializing wire formats.
67use core::num::{NonZeroU32, NonZeroU64};
8use core::time::Duration;
910/// 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);
1415impl 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.
22pub const unsafe fn new_unchecked(d: Duration) -> NonZeroDuration {
23 NonZeroDuration(d)
24 }
2526/// Creates a new `NonZeroDuration` from the specified number of whole
27 /// seconds if that number is non-zero.
28pub const fn from_secs(secs: u64) -> Option<NonZeroDuration> {
29 NonZeroDuration::new(Duration::from_secs(secs))
30 }
3132/// Creates a new `NonZeroDuration` from the specified non-zero number of
33 /// whole seconds.
34pub const fn from_nonzero_secs(secs: NonZeroU64) -> NonZeroDuration {
35 NonZeroDuration(Duration::from_secs(secs.get()))
36 }
3738/// 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.
50pub const fn from_secs_nanos(secs: u64, nanos: u32) -> Option<NonZeroDuration> {
51 NonZeroDuration::new(Duration::new(secs, nanos))
52 }
5354/// 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.
65pub const fn from_nonzero_secs_nanos(secs: NonZeroU64, nanos: NonZeroU32) -> NonZeroDuration {
66 NonZeroDuration(Duration::new(secs.get(), nanos.get()))
67 }
6869/// Creates a new `NonZeroDuration` from the specified number of
70 /// milliseconds if that number is non-zero.
71pub const fn from_millis(millis: u64) -> Option<NonZeroDuration> {
72 NonZeroDuration::new(Duration::from_millis(millis))
73 }
7475/// Creates a new `NonZeroDuration` from the specified non-zero number of
76 /// milliseconds.
77pub const fn from_nonzero_millis(millis: NonZeroU64) -> NonZeroDuration {
78 NonZeroDuration(Duration::from_millis(millis.get()))
79 }
8081/// Creates a new `NonZeroDuration` from the specified number of
82 /// microseconds if that number is non-zero.
83pub const fn from_micros(micros: u64) -> Option<NonZeroDuration> {
84 NonZeroDuration::new(Duration::from_micros(micros))
85 }
8687/// Creates a new `NonZeroDuration` from the specified non-zero number of
88 /// microseconds.
89pub const fn from_nonzero_micros(micros: NonZeroU64) -> NonZeroDuration {
90 NonZeroDuration(Duration::from_micros(micros.get()))
91 }
9293/// Creates a new `NonZeroDuration` from the specified number of nanoseconds
94 /// if that number is non-zero.
95pub const fn from_nanos(nanos: u64) -> Option<NonZeroDuration> {
96 NonZeroDuration::new(Duration::from_nanos(nanos))
97 }
9899/// Creates a new `NonZeroDuration` from the specified non-zero number of
100 /// nanoseconds.
101pub const fn from_nonzero_nanos(nanos: NonZeroU64) -> NonZeroDuration {
102 NonZeroDuration(Duration::from_nanos(nanos.get()))
103 }
104105/// Creates a non-zero if the given value is not zero.
106pub 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.
109if d.as_nanos() == 0 {
110return None;
111 }
112113Some(NonZeroDuration(d))
114 }
115116/// Like [`Duration::saturating_add`].
117pub fn saturating_add(self, d: Duration) -> Self {
118// NB: Duration is strictly positive so we're not violating the
119 // invariant.
120Self(self.0.saturating_add(d))
121 }
122123/// Like [`Duration::saturating_mul`].
124pub fn saturating_mul(self, m: NonZeroU32) -> Self {
125// NB: multiplier is nonzero so we're not violating the invariant.
126Self(self.0.saturating_mul(m.into()))
127 }
128129/// Returns the value as a [`Duration`].
130pub const fn get(self) -> Duration {
131self.0
132}
133}
134135impl From<NonZeroDuration> for Duration {
136fn from(NonZeroDuration(d): NonZeroDuration) -> Duration {
137 d
138 }
139}
140141impl core::ops::Add<Duration> for NonZeroDuration {
142type Output = Self;
143144fn add(self, rhs: Duration) -> Self::Output {
145let Self(d) = self;
146Self(d + rhs)
147 }
148}
149150impl core::ops::Add<NonZeroDuration> for NonZeroDuration {
151type Output = Self;
152153fn add(self, rhs: NonZeroDuration) -> Self::Output {
154self + rhs.get()
155 }
156}
157158impl core::ops::Mul<NonZeroU32> for NonZeroDuration {
159type Output = Self;
160161fn mul(self, rhs: NonZeroU32) -> Self::Output {
162let Self(d) = self;
163Self(d * rhs.get())
164 }
165}
166167/// 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}
172173#[cfg(test)]
174mod tests {
175use super::*;
176177#[test]
178fn non_zero_duration() {
179// `NonZeroDuration` should not hold a zero duration.
180assert_eq!(NonZeroDuration::new(Duration::from_secs(0)), None);
181182// `new_unchecked` should hold the duration as is.
183let d = Duration::from_secs(1);
184assert_eq!(unsafe { NonZeroDuration::new_unchecked(d) }, NonZeroDuration(d));
185186// `NonZeroDuration` should hold a non-zero duration.
187let non_zero = NonZeroDuration::new(d);
188assert_eq!(non_zero, Some(NonZeroDuration(d)));
189190let one_u64 = NonZeroU64::new(1).unwrap();
191let expect = NonZeroDuration(Duration::from_secs(1));
192assert_eq!(NonZeroDuration::from_secs(1), Some(expect));
193assert_eq!(NonZeroDuration::from_nonzero_secs(one_u64), expect);
194195let expect = NonZeroDuration(Duration::new(1, 1));
196assert_eq!(NonZeroDuration::from_secs_nanos(1, 1), Some(expect));
197assert_eq!(
198 NonZeroDuration::from_nonzero_secs_nanos(one_u64, NonZeroU32::new(1).unwrap()),
199 expect
200 );
201202let expect = NonZeroDuration(Duration::from_millis(1));
203assert_eq!(NonZeroDuration::from_millis(1), Some(expect));
204assert_eq!(NonZeroDuration::from_nonzero_millis(one_u64), expect);
205206let expect = NonZeroDuration(Duration::from_micros(1));
207assert_eq!(NonZeroDuration::from_micros(1), Some(expect));
208assert_eq!(NonZeroDuration::from_nonzero_micros(one_u64), expect);
209210let expect = NonZeroDuration(Duration::from_nanos(1));
211assert_eq!(NonZeroDuration::from_nanos(1), Some(expect));
212assert_eq!(NonZeroDuration::from_nonzero_nanos(one_u64), expect);
213214// `get` and `Into::into` should return the underlying duration.
215let non_zero = non_zero.unwrap();
216assert_eq!(d, non_zero.get());
217assert_eq!(d, non_zero.into());
218 }
219220#[test]
221fn test_next_multiple_of_four() {
222for x in 0usize..=(core::u16::MAX - 3) as usize {
223let y = round_to_next_multiple_of_four(x);
224assert_eq!(y % 4, 0);
225assert!(y >= x);
226if x % 4 == 0 {
227assert_eq!(x, y);
228 } else {
229assert_eq!(x + (4 - x % 4), y);
230 }
231 }
232 }
233234#[test]
235fn add_duration() {
236let a = NonZeroDuration::new(Duration::from_secs(48291)).unwrap();
237let b = Duration::from_secs(195811);
238assert_eq!(Some(a + b), NonZeroDuration::new(a.get() + b));
239240assert_eq!(a + Duration::from_secs(0), a);
241 }
242243#[test]
244fn add_nonzero_duration() {
245let a = NonZeroDuration::new(Duration::from_secs(48291)).unwrap();
246let b = NonZeroDuration::new(Duration::from_secs(195811)).unwrap();
247assert_eq!(Some(a + b), NonZeroDuration::new(a.get() + b.get()));
248 }
249}