Skip to content

Commit 5b3635e

Browse files
committed
Add addition, subtraction and conversion to/from core::time::Duration for Timespec
1 parent 9d292d2 commit 5b3635e

File tree

1 file changed

+145
-0
lines changed

1 file changed

+145
-0
lines changed

src/timespec.rs

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

0 commit comments

Comments
 (0)