summaryrefslogtreecommitdiff
path: root/src/sys/time.rs
diff options
context:
space:
mode:
authorAlan Somers <asomers@gmail.com>2016-11-19 19:06:13 -0700
committerAlan Somers <asomers@gmail.com>2016-11-19 20:37:08 -0700
commitb983240ef137c5c38a5554413381fb26d1c2e0f9 (patch)
tree6bad2efc345394567a0f0c7ace7dedd0c5f7986c /src/sys/time.rs
parente983d20bd6be3dde43ed4a7f9f6e63eda19872b3 (diff)
downloadnix-b983240ef137c5c38a5554413381fb26d1c2e0f9.zip
Add TimeSpec, a Newtype around libc::timespec
Also, add trait TimeValLike, so some code can be shared between TimeSpec and TimeVal.
Diffstat (limited to 'src/sys/time.rs')
-rw-r--r--src/sys/time.rs356
1 files changed, 310 insertions, 46 deletions
diff --git a/src/sys/time.rs b/src/sys/time.rs
index 8340c9d0..21eb87bb 100644
--- a/src/sys/time.rs
+++ b/src/sys/time.rs
@@ -1,21 +1,270 @@
use std::{cmp, fmt, ops};
-use libc::{time_t, suseconds_t, timeval};
+use libc::{c_long, time_t, suseconds_t, timespec, timeval};
+
+pub trait TimeValLike: Sized {
+ #[inline]
+ fn zero() -> Self {
+ Self::seconds(0)
+ }
+
+ #[inline]
+ fn hours(hours: i64) -> Self {
+ let secs = hours.checked_mul(SECS_PER_HOUR)
+ .expect("TimeVal::hours ouf of bounds");
+ Self::seconds(secs)
+ }
+
+ #[inline]
+ fn minutes(minutes: i64) -> Self {
+ let secs = minutes.checked_mul(SECS_PER_MINUTE)
+ .expect("TimeVal::minutes out of bounds");
+ Self::seconds(secs)
+ }
+
+ fn seconds(seconds: i64) -> Self;
+ fn milliseconds(milliseconds: i64) -> Self;
+ fn microseconds(microseconds: i64) -> Self;
+ fn nanoseconds(nanoseconds: i64) -> Self;
+
+ #[inline]
+ fn num_hours(&self) -> i64 {
+ self.num_seconds() / 3600
+ }
+
+ #[inline]
+ fn num_minutes(&self) -> i64 {
+ self.num_seconds() / 60
+ }
+
+ fn num_seconds(&self) -> i64;
+ fn num_milliseconds(&self) -> i64;
+ fn num_microseconds(&self) -> i64;
+ fn num_nanoseconds(&self) -> i64;
+}
#[repr(C)]
#[derive(Clone, Copy)]
-pub struct TimeVal(timeval);
+pub struct TimeSpec(timespec);
-const MICROS_PER_SEC: i64 = 1_000_000;
+const NANOS_PER_SEC: i64 = 1_000_000_000;
const SECS_PER_MINUTE: i64 = 60;
const SECS_PER_HOUR: i64 = 3600;
#[cfg(target_pointer_width = "64")]
-const MAX_SECONDS: i64 = (::std::i64::MAX / MICROS_PER_SEC) - 1;
+const TS_MAX_SECONDS: i64 = (::std::i64::MAX / NANOS_PER_SEC) - 1;
#[cfg(target_pointer_width = "32")]
-const MAX_SECONDS: i64 = ::std::isize::MAX as i64;
+const TS_MAX_SECONDS: i64 = ::std::isize::MAX as i64;
+
+const TS_MIN_SECONDS: i64 = -TS_MAX_SECONDS;
+
+
+impl fmt::Debug for TimeSpec {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ fmt.debug_struct("TimeSpec")
+ .field("tv_sec", &self.tv_sec())
+ .field("tv_nsec", &self.tv_nsec())
+ .finish()
+ }
+}
+
+impl cmp::PartialEq for TimeSpec {
+ // The implementation of cmp is simplified by assuming that the struct is
+ // normalized. That is, tv_nsec must always be within [0, 1_000_000_000)
+ fn eq(&self, other: &TimeSpec) -> bool {
+ self.tv_sec() == other.tv_sec() && self.tv_nsec() == other.tv_nsec()
+ }
+}
+
+impl cmp::Eq for TimeSpec {}
+
+impl cmp::Ord for TimeSpec {
+ // The implementation of cmp is simplified by assuming that the struct is
+ // normalized. That is, tv_nsec must always be within [0, 1_000_000_000)
+ fn cmp(&self, other: &TimeSpec) -> cmp::Ordering {
+ if self.tv_sec() == other.tv_sec() {
+ self.tv_sec().cmp(&other.tv_sec())
+ } else {
+ self.tv_nsec().cmp(&other.tv_nsec())
+ }
+ }
+}
-const MIN_SECONDS: i64 = -MAX_SECONDS;
+impl cmp::PartialOrd for TimeSpec {
+ fn partial_cmp(&self, other: &TimeSpec) -> Option<cmp::Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl TimeValLike for TimeSpec {
+ #[inline]
+ fn seconds(seconds: i64) -> TimeSpec {
+ assert!(seconds >= TS_MIN_SECONDS && seconds <= TS_MAX_SECONDS, "TimeSpec out of bounds; seconds={}", seconds);
+ TimeSpec(timespec {tv_sec: seconds as time_t, tv_nsec: 0 })
+ }
+
+ #[inline]
+ fn milliseconds(milliseconds: i64) -> TimeSpec {
+ let nanoseconds = milliseconds.checked_mul(1_000_000)
+ .expect("TimeSpec::milliseconds out of bounds");
+
+ TimeSpec::nanoseconds(nanoseconds)
+ }
+
+ /// Makes a new `TimeSpec` with given number of microseconds.
+ #[inline]
+ fn microseconds(microseconds: i64) -> TimeSpec {
+ let nanoseconds = microseconds.checked_mul(1_000)
+ .expect("TimeSpec::milliseconds out of bounds");
+
+ TimeSpec::nanoseconds(nanoseconds)
+ }
+
+ /// Makes a new `TimeSpec` with given number of nanoseconds.
+ #[inline]
+ fn nanoseconds(nanoseconds: i64) -> TimeSpec {
+ let (secs, nanos) = div_mod_floor_64(nanoseconds, NANOS_PER_SEC);
+ assert!(secs >= TS_MIN_SECONDS && secs <= TS_MAX_SECONDS, "TimeSpec out of bounds");
+ TimeSpec(timespec {tv_sec: secs as time_t,
+ tv_nsec: nanos as c_long })
+ }
+
+ fn num_seconds(&self) -> i64 {
+ if self.tv_sec() < 0 && self.tv_nsec() > 0 {
+ (self.tv_sec() + 1) as i64
+ } else {
+ self.tv_sec() as i64
+ }
+ }
+
+ fn num_milliseconds(&self) -> i64 {
+ self.num_nanoseconds() / 1_000_000
+ }
+
+ fn num_microseconds(&self) -> i64 {
+ self.num_nanoseconds() / 1_000_000_000
+ }
+
+ fn num_nanoseconds(&self) -> i64 {
+ let secs = self.num_seconds() * 1_000_000_000;
+ let nsec = self.nanos_mod_sec();
+ secs + nsec as i64
+ }
+}
+
+impl TimeSpec {
+ fn nanos_mod_sec(&self) -> c_long {
+ if self.tv_sec() < 0 && self.tv_nsec() > 0 {
+ self.tv_nsec() - NANOS_PER_SEC as c_long
+ } else {
+ self.tv_nsec()
+ }
+ }
+
+ pub fn timespec(&self) -> timespec{
+ self.0
+ }
+
+ pub fn tv_sec(&self) -> time_t {
+ self.0.tv_sec
+ }
+
+ pub fn tv_nsec(&self) -> c_long {
+ self.0.tv_nsec
+ }
+}
+
+impl ops::Neg for TimeSpec {
+ type Output = TimeSpec;
+
+ fn neg(self) -> TimeSpec {
+ TimeSpec::nanoseconds(-self.num_nanoseconds())
+ }
+}
+
+impl ops::Add for TimeSpec {
+ type Output = TimeSpec;
+
+ fn add(self, rhs: TimeSpec) -> TimeSpec {
+ TimeSpec::nanoseconds(
+ self.num_nanoseconds() + rhs.num_nanoseconds())
+ }
+}
+
+impl ops::Sub for TimeSpec {
+ type Output = TimeSpec;
+
+ fn sub(self, rhs: TimeSpec) -> TimeSpec {
+ TimeSpec::nanoseconds(
+ self.num_nanoseconds() - rhs.num_nanoseconds())
+ }
+}
+
+impl ops::Mul<i32> for TimeSpec {
+ type Output = TimeSpec;
+
+ fn mul(self, rhs: i32) -> TimeSpec {
+ let usec = self.num_nanoseconds().checked_mul(rhs as i64)
+ .expect("TimeSpec multiply out of bounds");
+
+ TimeSpec::nanoseconds(usec)
+ }
+}
+
+impl ops::Div<i32> for TimeSpec {
+ type Output = TimeSpec;
+
+ fn div(self, rhs: i32) -> TimeSpec {
+ let usec = self.num_nanoseconds() / rhs as i64;
+ TimeSpec::nanoseconds(usec)
+ }
+}
+
+impl fmt::Display for TimeSpec {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let (abs, sign) = if self.tv_sec() < 0 {
+ (-*self, "-")
+ } else {
+ (*self, "")
+ };
+
+ let sec = abs.tv_sec();
+
+ try!(write!(f, "{}", sign));
+
+ if abs.tv_nsec() == 0 {
+ if abs.tv_sec() == 1 {
+ try!(write!(f, "{} second", sec));
+ } else {
+ try!(write!(f, "{} seconds", sec));
+ }
+ } else if abs.tv_nsec() % 1_000_000 == 0 {
+ try!(write!(f, "{}.{:03} seconds", sec, abs.tv_nsec() / 1_000_000));
+ } else if abs.tv_nsec() % 1_000 == 0 {
+ try!(write!(f, "{}.{:06} seconds", sec, abs.tv_nsec() / 1_000));
+ } else {
+ try!(write!(f, "{}.{:09} seconds", sec, abs.tv_nsec()));
+ }
+
+ Ok(())
+ }
+}
+
+
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct TimeVal(timeval);
+
+const MICROS_PER_SEC: i64 = 1_000_000;
+
+#[cfg(target_pointer_width = "64")]
+const TV_MAX_SECONDS: i64 = (::std::i64::MAX / MICROS_PER_SEC) - 1;
+
+#[cfg(target_pointer_width = "32")]
+const TV_MAX_SECONDS: i64 = ::std::isize::MAX as i64;
+
+const TV_MIN_SECONDS: i64 = -TV_MAX_SECONDS;
impl fmt::Debug for TimeVal {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
@@ -54,36 +303,15 @@ impl cmp::PartialOrd for TimeVal {
}
}
-impl TimeVal {
- #[inline]
- pub fn zero() -> TimeVal {
- TimeVal::microseconds(0)
- }
-
- #[inline]
- pub fn hours(hours: i64) -> TimeVal {
- let secs = hours.checked_mul(SECS_PER_HOUR)
- .expect("TimeVal::hours ouf of bounds");
-
- TimeVal::seconds(secs)
- }
-
- #[inline]
- pub fn minutes(minutes: i64) -> TimeVal {
- let secs = minutes.checked_mul(SECS_PER_MINUTE)
- .expect("TimeVal::minutes out of bounds");
-
- TimeVal::seconds(secs)
- }
-
+impl TimeValLike for TimeVal {
#[inline]
- pub fn seconds(seconds: i64) -> TimeVal {
- assert!(seconds >= MIN_SECONDS && seconds <= MAX_SECONDS, "TimeVal out of bounds; seconds={}", seconds);
+ fn seconds(seconds: i64) -> TimeVal {
+ assert!(seconds >= TV_MIN_SECONDS && seconds <= TV_MAX_SECONDS, "TimeVal out of bounds; seconds={}", seconds);
TimeVal(timeval {tv_sec: seconds as time_t, tv_usec: 0 })
}
#[inline]
- pub fn milliseconds(milliseconds: i64) -> TimeVal {
+ fn milliseconds(milliseconds: i64) -> TimeVal {
let microseconds = milliseconds.checked_mul(1_000)
.expect("TimeVal::milliseconds out of bounds");
@@ -92,22 +320,25 @@ impl TimeVal {
/// Makes a new `TimeVal` with given number of microseconds.
#[inline]
- pub fn microseconds(microseconds: i64) -> TimeVal {
+ fn microseconds(microseconds: i64) -> TimeVal {
let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC);
- assert!(secs >= MIN_SECONDS && secs <= MAX_SECONDS, "TimeVal out of bounds");
+ assert!(secs >= TV_MIN_SECONDS && secs <= TV_MAX_SECONDS, "TimeVal out of bounds");
TimeVal(timeval {tv_sec: secs as time_t,
tv_usec: micros as suseconds_t })
}
- pub fn num_hours(&self) -> i64 {
- self.num_seconds() / 3600
- }
-
- pub fn num_minutes(&self) -> i64 {
- self.num_seconds() / 60
+ /// Makes a new `TimeVal` with given number of nanoseconds. Some precision
+ /// will be lost
+ #[inline]
+ fn nanoseconds(nanoseconds: i64) -> TimeVal {
+ let microseconds = nanoseconds / 1000;
+ let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC);
+ assert!(secs >= TV_MIN_SECONDS && secs <= TV_MAX_SECONDS, "TimeVal out of bounds");
+ TimeVal(timeval {tv_sec: secs as time_t,
+ tv_usec: micros as suseconds_t })
}
- pub fn num_seconds(&self) -> i64 {
+ fn num_seconds(&self) -> i64 {
if self.tv_sec() < 0 && self.tv_usec() > 0 {
(self.tv_sec() + 1) as i64
} else {
@@ -115,16 +346,22 @@ impl TimeVal {
}
}
- pub fn num_milliseconds(&self) -> i64 {
+ fn num_milliseconds(&self) -> i64 {
self.num_microseconds() / 1_000
}
- pub fn num_microseconds(&self) -> i64 {
+ fn num_microseconds(&self) -> i64 {
let secs = self.num_seconds() * 1_000_000;
let usec = self.micros_mod_sec();
secs + usec as i64
}
+ fn num_nanoseconds(&self) -> i64 {
+ self.num_microseconds() * 1_000
+ }
+}
+
+impl TimeVal {
fn micros_mod_sec(&self) -> suseconds_t {
if self.tv_sec() < 0 && self.tv_usec() > 0 {
self.tv_usec() - MICROS_PER_SEC as suseconds_t
@@ -250,10 +487,36 @@ fn div_rem_64(this: i64, other: i64) -> (i64, i64) {
#[cfg(test)]
mod test {
- use super::TimeVal;
+ use super::{TimeSpec, TimeVal, TimeValLike};
+
+ #[test]
+ pub fn test_timespec() {
+ assert!(TimeSpec::seconds(1) != TimeSpec::zero());
+ assert_eq!(TimeSpec::seconds(1) + TimeSpec::seconds(2), TimeSpec::seconds(3));
+ assert_eq!(TimeSpec::minutes(3) + TimeSpec::seconds(2),
+ TimeSpec::seconds(182));
+ }
+
+ #[test]
+ pub fn test_timespec_neg() {
+ let a = TimeSpec::seconds(1) + TimeSpec::nanoseconds(123);
+ let b = TimeSpec::seconds(-1) + TimeSpec::nanoseconds(-123);
+
+ assert_eq!(a, -b);
+ }
+
+ #[test]
+ pub fn test_timespec_fmt() {
+ assert_eq!(TimeSpec::zero().to_string(), "0 seconds");
+ assert_eq!(TimeSpec::seconds(42).to_string(), "42 seconds");
+ assert_eq!(TimeSpec::milliseconds(42).to_string(), "0.042 seconds");
+ assert_eq!(TimeSpec::microseconds(42).to_string(), "0.000042 seconds");
+ assert_eq!(TimeSpec::nanoseconds(42).to_string(), "0.000000042 seconds");
+ assert_eq!(TimeSpec::seconds(-86401).to_string(), "-86401 seconds");
+ }
#[test]
- pub fn test_time_val() {
+ pub fn test_timeval() {
assert!(TimeVal::seconds(1) != TimeVal::zero());
assert_eq!(TimeVal::seconds(1) + TimeVal::seconds(2), TimeVal::seconds(3));
assert_eq!(TimeVal::minutes(3) + TimeVal::seconds(2),
@@ -261,7 +524,7 @@ mod test {
}
#[test]
- pub fn test_time_val_neg() {
+ pub fn test_timeval_neg() {
let a = TimeVal::seconds(1) + TimeVal::microseconds(123);
let b = TimeVal::seconds(-1) + TimeVal::microseconds(-123);
@@ -269,11 +532,12 @@ mod test {
}
#[test]
- pub fn test_time_val_fmt() {
+ pub fn test_timeval_fmt() {
assert_eq!(TimeVal::zero().to_string(), "0 seconds");
assert_eq!(TimeVal::seconds(42).to_string(), "42 seconds");
assert_eq!(TimeVal::milliseconds(42).to_string(), "0.042 seconds");
assert_eq!(TimeVal::microseconds(42).to_string(), "0.000042 seconds");
+ assert_eq!(TimeVal::nanoseconds(1402).to_string(), "0.000001 seconds");
assert_eq!(TimeVal::seconds(-86401).to_string(), "-86401 seconds");
}
}