Skip to content

Commit b710cf2

Browse files
authored
Add addition, subtraction and conversion to/from core::time::Duration for Timespec (#1311)
1 parent 9d292d2 commit b710cf2

1 file changed

Lines changed: 147 additions & 0 deletions

File tree

src/timespec.rs

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
44
#![allow(dead_code)]
55

6+
use core::num::TryFromIntError;
7+
use core::ops::{Add, AddAssign, Neg, Sub, SubAssign};
8+
use core::time::Duration;
9+
610
#[cfg(not(fix_y2038))]
711
use crate::backend::c;
812
#[allow(unused)]
@@ -40,6 +44,149 @@ pub type Nsecs = i64;
4044
))]
4145
pub type Nsecs = ffi::c_long;
4246

47+
impl Timespec {
48+
/// Checked `Timespec` addition. Returns `None` if overflow occurred.
49+
///
50+
/// # Panics
51+
///
52+
/// If `0 <= .tv_nsec < 1_000_000_000` doesn't hold, this function may panic or return
53+
/// unexpected results.
54+
///
55+
/// # Example
56+
///
57+
/// ```
58+
/// use rustix::event::Timespec;
59+
///
60+
/// assert_eq!(
61+
/// Timespec { tv_sec: 1, tv_nsec: 2 }.checked_add(Timespec { tv_sec: 30, tv_nsec: 40 }),
62+
/// Some(Timespec { tv_sec: 31, tv_nsec: 42 })
63+
/// );
64+
/// assert_eq!(
65+
/// Timespec { tv_sec: 0, tv_nsec: 999_999_999 }.checked_add(Timespec { tv_sec: 0, tv_nsec: 2 }),
66+
/// Some(Timespec { tv_sec: 1, tv_nsec: 1 })
67+
/// );
68+
/// assert_eq!(
69+
/// Timespec { tv_sec: i64::MAX, tv_nsec: 999_999_999 }.checked_add(Timespec { tv_sec: 0, tv_nsec: 1 }),
70+
/// None
71+
/// );
72+
/// ```
73+
pub const fn checked_add(self, rhs: Self) -> Option<Self> {
74+
if let Some(mut tv_sec) = self.tv_sec.checked_add(rhs.tv_sec) {
75+
let mut tv_nsec = self.tv_nsec + rhs.tv_nsec;
76+
if tv_nsec >= 1_000_000_000 {
77+
tv_nsec -= 1_000_000_000;
78+
if let Some(carried_sec) = tv_sec.checked_add(1) {
79+
tv_sec = carried_sec;
80+
} else {
81+
return None;
82+
}
83+
}
84+
Some(Timespec { tv_sec, tv_nsec })
85+
} else {
86+
None
87+
}
88+
}
89+
90+
/// Checked `Timespec` subtraction. Returns `None` if overflow occurred.
91+
///
92+
/// # Panics
93+
///
94+
/// If `0 <= .tv_nsec < 1_000_000_000` doesn't hold, this function may panic or return
95+
/// unexpected results.
96+
///
97+
/// # Example
98+
///
99+
/// ```
100+
/// use rustix::event::Timespec;
101+
///
102+
/// assert_eq!(
103+
/// Timespec { tv_sec: 31, tv_nsec: 42 }.checked_sub(Timespec { tv_sec: 30, tv_nsec: 40 }),
104+
/// Some(Timespec { tv_sec: 1, tv_nsec: 2 })
105+
/// );
106+
/// assert_eq!(
107+
/// Timespec { tv_sec: 1, tv_nsec: 1 }.checked_sub(Timespec { tv_sec: 0, tv_nsec: 2 }),
108+
/// Some(Timespec { tv_sec: 0, tv_nsec: 999_999_999 })
109+
/// );
110+
/// assert_eq!(
111+
/// Timespec { tv_sec: i64::MIN, tv_nsec: 0 }.checked_sub(Timespec { tv_sec: 0, tv_nsec: 1 }),
112+
/// None
113+
/// );
114+
/// ```
115+
pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
116+
if let Some(mut tv_sec) = self.tv_sec.checked_sub(rhs.tv_sec) {
117+
let mut tv_nsec = self.tv_nsec - rhs.tv_nsec;
118+
if tv_nsec < 0 {
119+
tv_nsec += 1_000_000_000;
120+
if let Some(borrowed_sec) = tv_sec.checked_sub(1) {
121+
tv_sec = borrowed_sec;
122+
} else {
123+
return None;
124+
}
125+
}
126+
Some(Timespec { tv_sec, tv_nsec })
127+
} else {
128+
None
129+
}
130+
}
131+
}
132+
133+
impl TryFrom<Timespec> for Duration {
134+
type Error = TryFromIntError;
135+
136+
fn try_from(ts: Timespec) -> Result<Duration, Self::Error> {
137+
Ok(Duration::new(ts.tv_sec.try_into()?, ts.tv_nsec as _))
138+
}
139+
}
140+
141+
impl TryFrom<Duration> for Timespec {
142+
type Error = TryFromIntError;
143+
144+
fn try_from(dur: Duration) -> Result<Timespec, Self::Error> {
145+
Ok(Timespec {
146+
tv_sec: dur.as_secs().try_into()?,
147+
tv_nsec: dur.subsec_nanos() as _,
148+
})
149+
}
150+
}
151+
152+
impl Add for Timespec {
153+
type Output = Self;
154+
155+
fn add(self, rhs: Self) -> Self {
156+
self.checked_add(rhs)
157+
.expect("overflow when adding timespecs")
158+
}
159+
}
160+
161+
impl AddAssign for Timespec {
162+
fn add_assign(&mut self, rhs: Self) {
163+
*self = *self + rhs;
164+
}
165+
}
166+
167+
impl Sub for Timespec {
168+
type Output = Self;
169+
170+
fn sub(self, rhs: Self) -> Self {
171+
self.checked_sub(rhs)
172+
.expect("overflow when subtracting timespecs")
173+
}
174+
}
175+
176+
impl SubAssign for Timespec {
177+
fn sub_assign(&mut self, rhs: Self) {
178+
*self = *self - rhs;
179+
}
180+
}
181+
182+
impl Neg for Timespec {
183+
type Output = Self;
184+
185+
fn neg(self) -> Self {
186+
Self::default() - self
187+
}
188+
}
189+
43190
/// On 32-bit glibc platforms, `timespec` has anonymous padding fields, which
44191
/// Rust doesn't support yet (see `unnamed_fields`), so we define our own
45192
/// struct with explicit padding, with bidirectional `From` impls.

0 commit comments

Comments
 (0)