From 39133efebf49823002977f0ad0ac12edf27b4d06 Mon Sep 17 00:00:00 2001 From: Ruud van Asseldonk Date: Tue, 19 Aug 2014 14:32:20 +0200 Subject: [PATCH 1/3] libstd: Refactor Duration. This changes the internal representation of `Duration` from days: i32, secs: i32, nanos: u32 to secs: i64, nanos: i32 This resolves #16466. Some methods now take `i64` instead of `i32` due to the increased range. Some methods, like `num_milliseconds`, now return an `Option` instead of `i64`, because the range of `Duration` is now larger than e.g. 2^63 milliseconds. --- src/libstd/io/net/tcp.rs | 12 +- src/libstd/io/net/unix.rs | 10 +- src/libstd/io/timer.rs | 2 +- src/libstd/time/duration.rs | 472 +++++++++++++++--------------------- 4 files changed, 208 insertions(+), 288 deletions(-) diff --git a/src/libstd/io/net/tcp.rs b/src/libstd/io/net/tcp.rs index 7055b9d7a4738..52d3a04432a35 100644 --- a/src/libstd/io/net/tcp.rs +++ b/src/libstd/io/net/tcp.rs @@ -97,25 +97,31 @@ impl TcpStream { /// the specified duration. /// /// This is the same as the `connect` method, except that if the timeout - /// specified (in milliseconds) elapses before a connection is made an error - /// will be returned. The error's kind will be `TimedOut`. + /// specified elapses before a connection is made an error will be + /// returned. The error's kind will be `TimedOut`. /// /// Note that the `addr` argument may one day be split into a separate host /// and port, similar to the API seen in `connect`. /// /// If a `timeout` with zero or negative duration is specified then /// the function returns `Err`, with the error kind set to `TimedOut`. + /// If the timeout is larger than 2^63 milliseconds, the function also + /// returns `Err` with the error kind set to `TimedOut`. #[experimental = "the timeout argument may eventually change types"] pub fn connect_timeout(addr: SocketAddr, timeout: Duration) -> IoResult { if timeout <= Duration::milliseconds(0) { return Err(standard_error(TimedOut)); } + let timeout_ms = timeout.num_milliseconds().map(|x| { x as u64 }); + if timeout_ms.is_none() { + return Err(standard_error(TimedOut)); + } let SocketAddr { ip, port } = addr; let addr = rtio::SocketAddr { ip: super::to_rtio(ip), port: port }; LocalIo::maybe_raise(|io| { - io.tcp_connect(addr, Some(timeout.num_milliseconds() as u64)).map(TcpStream::new) + io.tcp_connect(addr, timeout_ms).map(TcpStream::new) }).map_err(IoError::from_rtio_error) } diff --git a/src/libstd/io/net/unix.rs b/src/libstd/io/net/unix.rs index eb25107541839..179855003f9bd 100644 --- a/src/libstd/io/net/unix.rs +++ b/src/libstd/io/net/unix.rs @@ -61,20 +61,26 @@ impl UnixStream { /// Connect to a pipe named by `path`, timing out if the specified number of /// milliseconds. /// - /// This function is similar to `connect`, except that if `timeout_ms` + /// This function is similar to `connect`, except that if `timeout` /// elapses the function will return an error of kind `TimedOut`. /// /// If a `timeout` with zero or negative duration is specified then /// the function returns `Err`, with the error kind set to `TimedOut`. + /// If the timeout is larger than 2^63 milliseconds, the function also + /// returns `Err` with the error kind set to `TimedOut`. #[experimental = "the timeout argument is likely to change types"] pub fn connect_timeout(path: &P, timeout: Duration) -> IoResult { if timeout <= Duration::milliseconds(0) { return Err(standard_error(TimedOut)); } + let timeout_ms = timeout.num_milliseconds().map(|x| { x as u64 }); + if timeout_ms.is_none() { + return Err(standard_error(TimedOut)); + } LocalIo::maybe_raise(|io| { - let s = io.unix_connect(&path.to_c_str(), Some(timeout.num_milliseconds() as u64)); + let s = io.unix_connect(&path.to_c_str(), timeout_ms); s.map(|p| UnixStream { obj: p }) }).map_err(IoError::from_rtio_error) } diff --git a/src/libstd/io/timer.rs b/src/libstd/io/timer.rs index 39c6c74e45eef..205132aca1d35 100644 --- a/src/libstd/io/timer.rs +++ b/src/libstd/io/timer.rs @@ -225,7 +225,7 @@ impl Callback for TimerCallback { } fn in_ms_u64(d: Duration) -> u64 { - let ms = d.num_milliseconds(); + let ms = d.num_milliseconds().unwrap_or(0); if ms < 0 { return 0 }; return ms as u64; } diff --git a/src/libstd/time/duration.rs b/src/libstd/time/duration.rs index 155dfbc66c008..c2030ccceb06d 100644 --- a/src/libstd/time/duration.rs +++ b/src/libstd/time/duration.rs @@ -12,227 +12,182 @@ #![experimental] -use {fmt, i32}; +use {fmt, i64}; use ops::{Add, Sub, Mul, Div, Neg}; use option::{Option, Some, None}; use num; use num::{CheckedAdd, CheckedMul}; use result::{Result, Ok, Err}; - -/// `Duration`'s `days` component should have no more than this value. -static MIN_DAYS: i32 = i32::MIN; -/// `Duration`'s `days` component should have no less than this value. -static MAX_DAYS: i32 = i32::MAX; - +/// The number of nanoseconds in a microsecond. +static NANOS_PER_MICRO: i32 = 1000; +/// The number of nanosecdons in a millisecond. +static NANOS_PER_MILLI: i32 = 1000_000; /// The number of nanoseconds in seconds. static NANOS_PER_SEC: i32 = 1_000_000_000; +/// The number of microseconds per second. +static MICROS_PER_SEC: i64 = 1000_000; +/// The number of milliseconds per second. +static MILLIS_PER_SEC: i64 = 1000; +/// The number of seconds in a minute. +static SECS_PER_MINUTE: i64 = 60; +/// The number of seconds in an hour. +static SECS_PER_HOUR: i64 = 3600; /// The number of (non-leap) seconds in days. -static SECS_PER_DAY: i32 = 86400; +static SECS_PER_DAY: i64 = 86400; +/// The number of (non-leap) seconds in a week. +static SECS_PER_WEEK: i64 = 604800; macro_rules! try_opt( ($e:expr) => (match $e { Some(v) => v, None => return None }) ) -// FIXME #16466: This could be represented as (i64 seconds, u32 nanos) /// ISO 8601 time duration with nanosecond precision. /// This also allows for the negative duration; see individual methods for details. #[deriving(Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct Duration { - days: i32, - secs: u32, // Always < SECS_PER_DAY - nanos: u32, // Always < NANOS_PR_SECOND + secs: i64, + nanos: i32, // Always 0 <= nanos < NANOS_PER_SEC } /// The minimum possible `Duration`. -pub static MIN: Duration = Duration { days: MIN_DAYS, secs: 0, nanos: 0 }; +pub static MIN: Duration = Duration { secs: i64::MIN, nanos: 0 }; /// The maximum possible `Duration`. -pub static MAX: Duration = Duration { days: MAX_DAYS, secs: SECS_PER_DAY as u32 - 1, - nanos: NANOS_PER_SEC as u32 - 1 }; +pub static MAX: Duration = Duration { secs: i64::MAX, nanos: NANOS_PER_SEC - 1 }; impl Duration { /// Makes a new `Duration` with given number of weeks. - /// Equivalent to `Duration::new(weeks * 7, 0, 0)` with overflow checks. - /// + /// Equivalent to `Duration::seconds(weeks * 7 * 24 * 60 * 60), with overflow checks. /// Fails when the duration is out of bounds. #[inline] - pub fn weeks(weeks: i32) -> Duration { - let days = weeks.checked_mul(&7).expect("Duration::weeks out of bounds"); - Duration::days(days) + pub fn weeks(weeks: i64) -> Duration { + let secs = weeks.checked_mul(&SECS_PER_WEEK).expect("Duration::weeks out of bounds"); + Duration::seconds(secs) } /// Makes a new `Duration` with given number of days. - /// Equivalent to `Duration::new(days, 0, 0)`. + /// Equivalent to `Duration::seconds(days * 24 * 60 * 60)` with overflow checks. + /// Fails when the duration is out of bounds. #[inline] - pub fn days(days: i32) -> Duration { - Duration { days: days, secs: 0, nanos: 0 } + pub fn days(days: i64) -> Duration { + let secs = days.checked_mul(&SECS_PER_DAY).expect("Duration::days out of bounds"); + Duration::seconds(secs) } /// Makes a new `Duration` with given number of hours. - /// Equivalent to `Duration::new(0, hours * 3600, 0)` with overflow checks. + /// Equivalent to `Duration::seconds(hours * 60 * 60)` with overflow checks. + /// Fails when the duration is out of bounds. #[inline] - pub fn hours(hours: i32) -> Duration { - let (days, hours) = div_mod_floor(hours, (SECS_PER_DAY / 3600)); - let secs = hours * 3600; - Duration { secs: secs as u32, ..Duration::days(days) } + pub fn hours(hours: i64) -> Duration { + let secs = hours.checked_mul(&SECS_PER_HOUR).expect("Duration::hours ouf of bounds"); + Duration::seconds(secs) } /// Makes a new `Duration` with given number of minutes. - /// Equivalent to `Duration::new(0, mins * 60, 0)` with overflow checks. + /// Equivalent to `Duration::seconds(minutes * 60)` with overflow checks. + /// Fails when the duration is out of bounds. #[inline] - pub fn minutes(mins: i32) -> Duration { - let (days, mins) = div_mod_floor(mins, (SECS_PER_DAY / 60)); - let secs = mins * 60; - Duration { secs: secs as u32, ..Duration::days(days) } + pub fn minutes(minutes: i64) -> Duration { + let secs = minutes.checked_mul(&SECS_PER_MINUTE).expect("Duration::minutes out of bounds"); + Duration::seconds(secs) } /// Makes a new `Duration` with given number of seconds. - /// Equivalent to `Duration::new(0, secs, 0)`. #[inline] - pub fn seconds(secs: i32) -> Duration { - let (days, secs) = div_mod_floor(secs, SECS_PER_DAY); - Duration { secs: secs as u32, ..Duration::days(days) } + pub fn seconds(seconds: i64) -> Duration { + Duration { secs: seconds, nanos: 0 } } /// Makes a new `Duration` with given number of milliseconds. - /// Equivalent to `Duration::new(0, 0, millis * 1_000_000)` with overflow checks. #[inline] - pub fn milliseconds(millis: i32) -> Duration { - let (secs, millis) = div_mod_floor(millis, (NANOS_PER_SEC / 1_000_000)); - let nanos = millis * 1_000_000; - Duration { nanos: nanos as u32, ..Duration::seconds(secs) } + pub fn milliseconds(milliseconds: i64) -> Duration { + let (secs, millis) = div_mod_floor_64(milliseconds, MILLIS_PER_SEC); + let nanos = millis as i32 * NANOS_PER_MILLI; + Duration { secs: secs, nanos: nanos } } /// Makes a new `Duration` with given number of microseconds. - /// Equivalent to `Duration::new(0, 0, micros * 1_000)` with overflow checks. #[inline] - pub fn microseconds(micros: i32) -> Duration { - let (secs, micros) = div_mod_floor(micros, (NANOS_PER_SEC / 1_000)); - let nanos = micros * 1_000; - Duration { nanos: nanos as u32, ..Duration::seconds(secs) } + pub fn microseconds(microseconds: i64) -> Duration { + let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC); + let nanos = micros as i32 * NANOS_PER_MICRO; + Duration { secs: secs, nanos: nanos } } /// Makes a new `Duration` with given number of nanoseconds. - /// Equivalent to `Duration::new(0, 0, nanos)`. #[inline] - pub fn nanoseconds(nanos: i32) -> Duration { - let (secs, nanos) = div_mod_floor(nanos, NANOS_PER_SEC); - Duration { nanos: nanos as u32, ..Duration::seconds(secs) } - } - - /// Returns a tuple of the number of days, (non-leap) seconds and - /// nanoseconds in the duration. Note that the number of seconds - /// and nanoseconds are always positive, so that for example - /// `-Duration::seconds(3)` has -1 days and 86,397 seconds. - #[inline] - fn to_tuple_64(&self) -> (i64, u32, u32) { - (self.days as i64, self.secs, self.nanos) - } - - /// Negates the duration and returns a tuple like `to_tuple`. - /// This does not overflow and thus is internally used for several methods. - fn to_negated_tuple_64(&self) -> (i64, u32, u32) { - let mut days = -(self.days as i64); - let mut secs = -(self.secs as i32); - let mut nanos = -(self.nanos as i32); - if nanos < 0 { - nanos += NANOS_PER_SEC; - secs -= 1; - } - if secs < 0 { - secs += SECS_PER_DAY; - days -= 1; - } - (days, secs as u32, nanos as u32) + pub fn nanoseconds(nanos: i64) -> Duration { + let (secs, nanos) = div_mod_floor_64(nanos, NANOS_PER_SEC as i64); + Duration { secs: secs, nanos: nanos as i32 } } /// Returns the total number of whole weeks in the duration. #[inline] - pub fn num_weeks(&self) -> i32 { + pub fn num_weeks(&self) -> i64 { self.num_days() / 7 } /// Returns the total number of whole days in the duration. - pub fn num_days(&self) -> i32 { - if self.days < 0 { - let negated = -*self; - -negated.days - } else { - self.days - } + pub fn num_days(&self) -> i64 { + self.num_seconds() / SECS_PER_DAY } /// Returns the total number of whole hours in the duration. #[inline] pub fn num_hours(&self) -> i64 { - self.num_seconds() / 3600 + self.num_seconds() / SECS_PER_HOUR } /// Returns the total number of whole minutes in the duration. #[inline] pub fn num_minutes(&self) -> i64 { - self.num_seconds() / 60 + self.num_seconds() / SECS_PER_MINUTE } /// Returns the total number of whole seconds in the duration. pub fn num_seconds(&self) -> i64 { - // cannot overflow, 2^32 * 86400 < 2^64 - fn secs((days, secs, _): (i64, u32, u32)) -> i64 { - days as i64 * SECS_PER_DAY as i64 + secs as i64 + // If secs is negative, nanos should be subtracted from the duration. + if self.secs < 0 && self.nanos > 0 { + self.secs + 1 + } else { + self.secs } - if self.days < 0 {-secs(self.to_negated_tuple_64())} else {secs(self.to_tuple_64())} - } - - /// Returns the total number of whole milliseconds in the duration. - pub fn num_milliseconds(&self) -> i64 { - // cannot overflow, 2^32 * 86400 * 1000 < 2^64 - fn millis((days, secs, nanos): (i64, u32, u32)) -> i64 { - static MILLIS_PER_SEC: i64 = 1_000; - static NANOS_PER_MILLI: i64 = 1_000_000; - (days as i64 * MILLIS_PER_SEC * SECS_PER_DAY as i64 + - secs as i64 * MILLIS_PER_SEC + - nanos as i64 / NANOS_PER_MILLI) + } + + /// Returns the number of nanoseconds such that + /// `nanos_mod_sec() + num_seconds() * NANOS_PER_SEC` is the total number of + /// nanoseconds in the duration. + fn nanos_mod_sec(&self) -> i32 { + if self.secs < 0 && self.nanos > 0 { + self.nanos - NANOS_PER_SEC + } else { + self.nanos } - if self.days < 0 {-millis(self.to_negated_tuple_64())} else {millis(self.to_tuple_64())} + } + + /// Returns the total number of whole milliseconds in the duration, + /// or `None` on overflow (exceeding 2^63 milliseconds in either direction). + pub fn num_milliseconds(&self) -> Option { + let secs_part = try_opt!(self.num_seconds().checked_mul(&MILLIS_PER_SEC)); + let nanos_part = self.nanos_mod_sec() / NANOS_PER_MILLI; + secs_part.checked_add(&(nanos_part as i64)) } /// Returns the total number of whole microseconds in the duration, - /// or `None` on the overflow (exceeding 2^63 microseconds in either directions). + /// or `None` on overflow (exceeding 2^63 microseconds in either direction). pub fn num_microseconds(&self) -> Option { - fn micros((days, secs, nanos): (i64, u32, u32)) -> Option { - static MICROS_PER_SEC: i64 = 1_000_000; - static MICROS_PER_DAY: i64 = MICROS_PER_SEC * SECS_PER_DAY as i64; - static NANOS_PER_MICRO: i64 = 1_000; - let nmicros = try_opt!((days as i64).checked_mul(&MICROS_PER_DAY)); - let nmicros = try_opt!(nmicros.checked_add(&(secs as i64 * MICROS_PER_SEC))); - let nmicros = try_opt!(nmicros.checked_add(&(nanos as i64 / NANOS_PER_MICRO as i64))); - Some(nmicros) - } - if self.days < 0 { - // the final negation won't overflow since we start with positive numbers. - micros(self.to_negated_tuple_64()).map(|micros| -micros) - } else { - micros(self.to_tuple_64()) - } + let secs_part = try_opt!(self.num_seconds().checked_mul(&MICROS_PER_SEC)); + let nanos_part = self.nanos_mod_sec() / NANOS_PER_MICRO; + secs_part.checked_add(&(nanos_part as i64)) } /// Returns the total number of whole nanoseconds in the duration, - /// or `None` on the overflow (exceeding 2^63 nanoseconds in either directions). + /// or `None` on overflow (exceeding 2^63 nanoseconds in either direction). pub fn num_nanoseconds(&self) -> Option { - fn nanos((days, secs, nanos): (i64, u32, u32)) -> Option { - static NANOS_PER_DAY: i64 = NANOS_PER_SEC as i64 * SECS_PER_DAY as i64; - let nnanos = try_opt!((days as i64).checked_mul(&NANOS_PER_DAY)); - let nnanos = try_opt!(nnanos.checked_add(&(secs as i64 * NANOS_PER_SEC as i64))); - let nnanos = try_opt!(nnanos.checked_add(&(nanos as i64))); - Some(nnanos) - } - if self.days < 0 { - // the final negation won't overflow since we start with positive numbers. - nanos(self.to_negated_tuple_64()).map(|micros| -micros) - } else { - nanos(self.to_tuple_64()) - } + let secs_part = try_opt!(self.num_seconds().checked_mul(&(NANOS_PER_SEC as i64))); + let nanos_part = self.nanos_mod_sec(); + secs_part.checked_add(&(nanos_part as i64)) } } @@ -244,156 +199,130 @@ impl num::Bounded for Duration { impl num::Zero for Duration { #[inline] fn zero() -> Duration { - Duration { days: 0, secs: 0, nanos: 0 } + Duration { secs: 0, nanos: 0 } } #[inline] fn is_zero(&self) -> bool { - self.days == 0 && self.secs == 0 && self.nanos == 0 + self.secs == 0 && self.nanos == 0 } } impl Neg for Duration { #[inline] fn neg(&self) -> Duration { - let (days, secs, nanos) = self.to_negated_tuple_64(); - Duration { days: days as i32, secs: secs, nanos: nanos } // FIXME can overflow + if self.secs == i64::MIN && self.nanos == 0 { + // The minimum value cannot be negated due to overflow. Use the + // maximum value, which is one nanosecond less than the negated minimum. + MAX + } else if self.secs == i64::MIN { + Duration { secs: i64::MAX, nanos: NANOS_PER_SEC - self.nanos } + } else if self.nanos == 0 { + Duration { secs: -self.secs, nanos: 0 } + } else { + Duration { secs: -self.secs - 1, nanos: NANOS_PER_SEC - self.nanos } + } } } impl Add for Duration { fn add(&self, rhs: &Duration) -> Duration { - let mut days = self.days + rhs.days; let mut secs = self.secs + rhs.secs; let mut nanos = self.nanos + rhs.nanos; - if nanos >= NANOS_PER_SEC as u32 { - nanos -= NANOS_PER_SEC as u32; + if nanos >= NANOS_PER_SEC { + nanos -= NANOS_PER_SEC; secs += 1; } - if secs >= SECS_PER_DAY as u32 { - secs -= SECS_PER_DAY as u32; - days += 1; - } - Duration { days: days, secs: secs, nanos: nanos } + Duration { secs: secs, nanos: nanos } } } impl num::CheckedAdd for Duration { fn checked_add(&self, rhs: &Duration) -> Option { - let mut days = try_opt!(self.days.checked_add(&rhs.days)); - let mut secs = self.secs + rhs.secs; + let mut secs = try_opt!(self.secs.checked_add(&rhs.secs)); let mut nanos = self.nanos + rhs.nanos; - if nanos >= NANOS_PER_SEC as u32 { - nanos -= NANOS_PER_SEC as u32; - secs += 1; + if nanos >= NANOS_PER_SEC { + nanos -= NANOS_PER_SEC; + secs = try_opt!(secs.checked_add(&1)); } - if secs >= SECS_PER_DAY as u32 { - secs -= SECS_PER_DAY as u32; - days = try_opt!(days.checked_add(&1)); - } - Some(Duration { days: days, secs: secs, nanos: nanos }) + Some(Duration { secs: secs, nanos: nanos }) } } impl Sub for Duration { fn sub(&self, rhs: &Duration) -> Duration { - let mut days = self.days - rhs.days; - let mut secs = self.secs as i32 - rhs.secs as i32; - let mut nanos = self.nanos as i32 - rhs.nanos as i32; + let mut secs = self.secs - rhs.secs; + let mut nanos = self.nanos - rhs.nanos; if nanos < 0 { nanos += NANOS_PER_SEC; secs -= 1; } - if secs < 0 { - secs += SECS_PER_DAY; - days -= 1; - } - Duration { days: days, secs: secs as u32, nanos: nanos as u32 } + Duration { secs: secs, nanos: nanos } } } impl num::CheckedSub for Duration { fn checked_sub(&self, rhs: &Duration) -> Option { - let mut days = try_opt!(self.days.checked_sub(&rhs.days)); - let mut secs = self.secs as i32 - rhs.secs as i32; - let mut nanos = self.nanos as i32 - rhs.nanos as i32; + let mut secs = try_opt!(self.secs.checked_sub(&rhs.secs)); + let mut nanos = self.nanos - rhs.nanos; if nanos < 0 { nanos += NANOS_PER_SEC; - secs -= 1; - } - if secs < 0 { - secs += SECS_PER_DAY; - days = try_opt!(days.checked_sub(&1)); + secs = try_opt!(secs.checked_sub(&1)); } - Some(Duration { days: days, secs: secs as u32, nanos: nanos as u32 }) + Some(Duration { secs: secs, nanos: nanos }) } } impl Mul for Duration { fn mul(&self, rhs: &i32) -> Duration { - /// Given `0 <= y < limit <= 2^30`, - /// returns `(h,l)` such that `x * y = h * limit + l` where `0 <= l < limit`. - fn mul_i64_u32_limit(x: i64, y: u32, limit: u32) -> (i64,u32) { - let y = y as i64; - let limit = limit as i64; - let (xh, xl) = div_mod_floor_64(x, limit); - let (h, l) = (xh * y, xl * y); - let (h_, l) = div_rem_64(l, limit); - (h + h_, l as u32) - } - - let rhs = *rhs as i64; - let (secs1, nanos) = mul_i64_u32_limit(rhs, self.nanos, NANOS_PER_SEC as u32); - let (days1, secs1) = div_mod_floor_64(secs1, (SECS_PER_DAY as i64)); - let (days2, secs2) = mul_i64_u32_limit(rhs, self.secs, SECS_PER_DAY as u32); - let mut days = self.days as i64 * rhs + days1 + days2; - let mut secs = secs1 as u32 + secs2; - if secs >= SECS_PER_DAY as u32 { - secs -= 1; - days += 1; - } - Duration { days: days as i32, secs: secs, nanos: nanos } + // Multiply nanoseconds as i64, because it cannot overflow that way. + let total_nanos = self.nanos as i64 * *rhs as i64; + let (extra_secs, nanos) = div_mod_floor_64(total_nanos, NANOS_PER_SEC as i64); + let secs = self.secs * *rhs as i64 + extra_secs; + Duration { secs: secs, nanos: nanos as i32 } } } impl Div for Duration { fn div(&self, rhs: &i32) -> Duration { - let (rhs, days, secs, nanos) = if *rhs < 0 { - let (days, secs, nanos) = self.to_negated_tuple_64(); - (-(*rhs as i64), days, secs as i64, nanos as i64) - } else { - (*rhs as i64, self.days as i64, self.secs as i64, self.nanos as i64) - }; - - let (days, carry) = div_mod_floor_64(days, rhs); - let secs = secs + carry * SECS_PER_DAY as i64; - let (secs, carry) = div_mod_floor_64(secs, rhs); - let nanos = nanos + carry * NANOS_PER_SEC as i64; - let nanos = nanos / rhs; - Duration { days: days as i32, secs: secs as u32, nanos: nanos as u32 } + let mut secs = self.secs / *rhs as i64; + let carry = self.secs - secs * *rhs as i64; + let extra_nanos = carry * NANOS_PER_SEC as i64 / *rhs as i64; + let mut nanos = self.nanos / *rhs + extra_nanos as i32; + if nanos >= NANOS_PER_SEC { + nanos -= NANOS_PER_SEC; + secs += 1; + } + if nanos < 0 { + nanos += NANOS_PER_SEC; + secs -= 1; + } + Duration { secs: secs, nanos: nanos } } } impl fmt::Show for Duration { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let hasdate = self.days != 0; - let hastime = (self.secs != 0 || self.nanos != 0) || !hasdate; + let days = self.num_days(); + let secs = self.secs - days * SECS_PER_DAY; + let hasdate = days != 0; + let hastime = (secs != 0 || self.nanos != 0) || !hasdate; try!(write!(f, "P")); if hasdate { // technically speaking the negative part is not the valid ISO 8601, // but we need to print it anyway. - try!(write!(f, "{}D", self.days)); + try!(write!(f, "{}D", days)); } if hastime { if self.nanos == 0 { - try!(write!(f, "T{}S", self.secs)); - } else if self.nanos % 1_000_000 == 0 { - try!(write!(f, "T{}.{:03}S", self.secs, self.nanos / 1_000_000)); - } else if self.nanos % 1_000 == 0 { - try!(write!(f, "T{}.{:06}S", self.secs, self.nanos / 1_000)); + try!(write!(f, "T{}S", secs)); + } else if self.nanos % NANOS_PER_MILLI == 0 { + try!(write!(f, "T{}.{:03}S", secs, self.nanos / NANOS_PER_MILLI)); + } else if self.nanos % NANOS_PER_MICRO == 0 { + try!(write!(f, "T{}.{:06}S", secs, self.nanos / NANOS_PER_MICRO)); } else { - try!(write!(f, "T{}.{:09}S", self.secs, self.nanos)); + try!(write!(f, "T{}.{:09}S", secs, self.nanos)); } } Ok(()) @@ -401,34 +330,6 @@ impl fmt::Show for Duration { } // Copied from libnum -#[inline] -fn div_mod_floor(this: i32, other: i32) -> (i32, i32) { - (div_floor(this, other), mod_floor(this, other)) -} - -#[inline] -fn div_floor(this: i32, other: i32) -> i32 { - match div_rem(this, other) { - (d, r) if (r > 0 && other < 0) - || (r < 0 && other > 0) => d - 1, - (d, _) => d, - } -} - -#[inline] -fn mod_floor(this: i32, other: i32) -> i32 { - match this % other { - r if (r > 0 && other < 0) - || (r < 0 && other > 0) => r + other, - r => r, - } -} - -#[inline] -fn div_rem(this: i32, other: i32) -> (i32, i32) { - (this / other, this % other) -} - #[inline] fn div_mod_floor_64(this: i64, other: i64) -> (i64, i64) { (div_floor_64(this, other), mod_floor_64(this, other)) @@ -459,7 +360,7 @@ fn div_rem_64(this: i64, other: i64) -> (i64, i64) { #[cfg(test)] mod tests { - use super::{Duration, MIN_DAYS, MAX_DAYS, MIN, MAX}; + use super::{Duration, MIN, MAX}; use {i32, i64}; use num::{Zero, CheckedAdd, CheckedSub}; use option::{Some, None}; @@ -492,10 +393,8 @@ mod tests { assert_eq!(Duration::seconds(86401).num_days(), 1); assert_eq!(Duration::seconds(-86399).num_days(), 0); assert_eq!(Duration::seconds(-86401).num_days(), -1); - assert_eq!(Duration::days(i32::MAX).num_days(), i32::MAX); - assert_eq!(Duration::days(i32::MIN).num_days(), i32::MIN); - assert_eq!(MAX.num_days(), MAX_DAYS); - assert_eq!(MIN.num_days(), MIN_DAYS); + assert_eq!(Duration::days(i32::MAX as i64).num_days(), i32::MAX as i64); + assert_eq!(Duration::days(i32::MIN as i64).num_days(), i32::MIN as i64); } #[test] @@ -508,26 +407,26 @@ mod tests { assert_eq!(Duration::milliseconds(1001).num_seconds(), 1); assert_eq!(Duration::milliseconds(-999).num_seconds(), 0); assert_eq!(Duration::milliseconds(-1001).num_seconds(), -1); - assert_eq!(Duration::seconds(i32::MAX).num_seconds(), i32::MAX as i64); - assert_eq!(Duration::seconds(i32::MIN).num_seconds(), i32::MIN as i64); - assert_eq!(MAX.num_seconds(), (MAX_DAYS as i64 + 1) * 86400 - 1); - assert_eq!(MIN.num_seconds(), MIN_DAYS as i64 * 86400); + assert_eq!(Duration::seconds(i64::MAX).num_seconds(), i64::MAX); + assert_eq!(Duration::seconds(i64::MIN).num_seconds(), i64::MIN); + assert_eq!(MAX.num_seconds(), i64::MAX); + assert_eq!(MIN.num_seconds(), i64::MIN); } #[test] fn test_duration_num_milliseconds() { let d: Duration = Zero::zero(); - assert_eq!(d.num_milliseconds(), 0); - assert_eq!(Duration::milliseconds(1).num_milliseconds(), 1); - assert_eq!(Duration::milliseconds(-1).num_milliseconds(), -1); - assert_eq!(Duration::microseconds(999).num_milliseconds(), 0); - assert_eq!(Duration::microseconds(1001).num_milliseconds(), 1); - assert_eq!(Duration::microseconds(-999).num_milliseconds(), 0); - assert_eq!(Duration::microseconds(-1001).num_milliseconds(), -1); - assert_eq!(Duration::milliseconds(i32::MAX).num_milliseconds(), i32::MAX as i64); - assert_eq!(Duration::milliseconds(i32::MIN).num_milliseconds(), i32::MIN as i64); - assert_eq!(MAX.num_milliseconds(), (MAX_DAYS as i64 + 1) * 86400_000 - 1); - assert_eq!(MIN.num_milliseconds(), MIN_DAYS as i64 * 86400_000); + assert_eq!(d.num_milliseconds(), Some(0)); + assert_eq!(Duration::milliseconds(1).num_milliseconds(), Some(1)); + assert_eq!(Duration::milliseconds(-1).num_milliseconds(), Some(-1)); + assert_eq!(Duration::microseconds(999).num_milliseconds(), Some(0)); + assert_eq!(Duration::microseconds(1001).num_milliseconds(), Some(1)); + assert_eq!(Duration::microseconds(-999).num_milliseconds(), Some(0)); + assert_eq!(Duration::microseconds(-1001).num_milliseconds(), Some(-1)); + assert_eq!(Duration::milliseconds(i64::MAX).num_milliseconds(), Some(i64::MAX)); + assert_eq!(Duration::milliseconds(i64::MIN).num_milliseconds(), Some(i64::MIN)); + assert_eq!(MAX.num_milliseconds(), None); + assert_eq!(MIN.num_milliseconds(), None); } #[test] @@ -540,19 +439,19 @@ mod tests { assert_eq!(Duration::nanoseconds(1001).num_microseconds(), Some(1)); assert_eq!(Duration::nanoseconds(-999).num_microseconds(), Some(0)); assert_eq!(Duration::nanoseconds(-1001).num_microseconds(), Some(-1)); - assert_eq!(Duration::microseconds(i32::MAX).num_microseconds(), Some(i32::MAX as i64)); - assert_eq!(Duration::microseconds(i32::MIN).num_microseconds(), Some(i32::MIN as i64)); + assert_eq!(Duration::microseconds(i64::MAX).num_microseconds(), Some(i64::MAX)); + assert_eq!(Duration::microseconds(i64::MIN).num_microseconds(), Some(i64::MIN)); assert_eq!(MAX.num_microseconds(), None); assert_eq!(MIN.num_microseconds(), None); // overflow checks static MICROS_PER_DAY: i64 = 86400_000_000; - assert_eq!(Duration::days((i64::MAX / MICROS_PER_DAY) as i32).num_microseconds(), + assert_eq!(Duration::days(i64::MAX / MICROS_PER_DAY).num_microseconds(), Some(i64::MAX / MICROS_PER_DAY * MICROS_PER_DAY)); - assert_eq!(Duration::days((i64::MIN / MICROS_PER_DAY) as i32).num_microseconds(), + assert_eq!(Duration::days(i64::MIN / MICROS_PER_DAY).num_microseconds(), Some(i64::MIN / MICROS_PER_DAY * MICROS_PER_DAY)); - assert_eq!(Duration::days((i64::MAX / MICROS_PER_DAY + 1) as i32).num_microseconds(), None); - assert_eq!(Duration::days((i64::MIN / MICROS_PER_DAY - 1) as i32).num_microseconds(), None); + assert_eq!(Duration::days(i64::MAX / MICROS_PER_DAY + 1).num_microseconds(), None); + assert_eq!(Duration::days(i64::MIN / MICROS_PER_DAY - 1).num_microseconds(), None); } #[test] @@ -561,30 +460,30 @@ mod tests { assert_eq!(d.num_nanoseconds(), Some(0)); assert_eq!(Duration::nanoseconds(1).num_nanoseconds(), Some(1)); assert_eq!(Duration::nanoseconds(-1).num_nanoseconds(), Some(-1)); - assert_eq!(Duration::nanoseconds(i32::MAX).num_nanoseconds(), Some(i32::MAX as i64)); - assert_eq!(Duration::nanoseconds(i32::MIN).num_nanoseconds(), Some(i32::MIN as i64)); + assert_eq!(Duration::nanoseconds(i64::MAX).num_nanoseconds(), Some(i64::MAX)); + assert_eq!(Duration::nanoseconds(i64::MIN).num_nanoseconds(), Some(i64::MIN)); assert_eq!(MAX.num_nanoseconds(), None); assert_eq!(MIN.num_nanoseconds(), None); // overflow checks static NANOS_PER_DAY: i64 = 86400_000_000_000; - assert_eq!(Duration::days((i64::MAX / NANOS_PER_DAY) as i32).num_nanoseconds(), + assert_eq!(Duration::days(i64::MAX / NANOS_PER_DAY).num_nanoseconds(), Some(i64::MAX / NANOS_PER_DAY * NANOS_PER_DAY)); - assert_eq!(Duration::days((i64::MIN / NANOS_PER_DAY) as i32).num_nanoseconds(), + assert_eq!(Duration::days(i64::MIN / NANOS_PER_DAY).num_nanoseconds(), Some(i64::MIN / NANOS_PER_DAY * NANOS_PER_DAY)); - assert_eq!(Duration::days((i64::MAX / NANOS_PER_DAY + 1) as i32).num_nanoseconds(), None); - assert_eq!(Duration::days((i64::MIN / NANOS_PER_DAY - 1) as i32).num_nanoseconds(), None); + assert_eq!(Duration::days(i64::MAX / NANOS_PER_DAY + 1).num_nanoseconds(), None); + assert_eq!(Duration::days(i64::MIN / NANOS_PER_DAY - 1).num_nanoseconds(), None); } #[test] fn test_duration_checked_ops() { - assert_eq!(Duration::days(MAX_DAYS).checked_add(&Duration::seconds(86399)), - Some(Duration::days(MAX_DAYS - 1) + Duration::seconds(86400+86399))); - assert!(Duration::days(MAX_DAYS).checked_add(&Duration::seconds(86400)).is_none()); + assert_eq!(Duration::seconds(i64::MAX).checked_add(&Duration::milliseconds(999)), + Some(Duration::seconds(i64::MAX - 1) + Duration::milliseconds(1999))); + assert!(Duration::seconds(i64::MAX).checked_add(&Duration::milliseconds(1000)).is_none()); - assert_eq!(Duration::days(MIN_DAYS).checked_sub(&Duration::seconds(0)), - Some(Duration::days(MIN_DAYS))); - assert!(Duration::days(MIN_DAYS).checked_sub(&Duration::seconds(1)).is_none()); + assert_eq!(Duration::seconds(i64::MIN).checked_sub(&Duration::seconds(0)), + Some(Duration::seconds(i64::MIN))); + assert!(Duration::seconds(i64::MIN).checked_sub(&Duration::seconds(1)).is_none()); } #[test] @@ -601,6 +500,8 @@ mod tests { Duration::seconds(10) - Duration::nanoseconds(10)); assert_eq!((Duration::nanoseconds(1) + Duration::seconds(1) + Duration::days(1)) * 3, Duration::nanoseconds(3) + Duration::seconds(3) + Duration::days(3)); + assert_eq!(Duration::milliseconds(1500) * -2, Duration::seconds(-3)); + assert_eq!(Duration::milliseconds(-1500) * 2, Duration::seconds(-3)); } #[test] @@ -612,6 +513,13 @@ mod tests { assert_eq!(Duration::nanoseconds(123_456_789) / -1, -Duration::nanoseconds(123_456_789)); assert_eq!(-Duration::nanoseconds(123_456_789) / -1, Duration::nanoseconds(123_456_789)); assert_eq!(-Duration::nanoseconds(123_456_789) / 1, -Duration::nanoseconds(123_456_789)); + assert_eq!(Duration::seconds(1) / 3, Duration::nanoseconds(333_333_333)); + assert_eq!(Duration::seconds(4) / 3, Duration::nanoseconds(1_333_333_333)); + assert_eq!(Duration::seconds(-1) / 2, Duration::milliseconds(-500)); + assert_eq!(Duration::seconds(1) / -2, Duration::milliseconds(-500)); + assert_eq!(Duration::seconds(-1) / -2, Duration::milliseconds(500)); + assert_eq!(Duration::seconds(-4) / 3, Duration::nanoseconds(-1_333_333_333)); + assert_eq!(Duration::seconds(-4) / -3, Duration::nanoseconds(1_333_333_333)); } #[test] From 26af5da6d4ca7ee3e7cec0333b2465a1c65e1171 Mon Sep 17 00:00:00 2001 From: Ruud van Asseldonk Date: Thu, 21 Aug 2014 09:37:30 +0200 Subject: [PATCH 2/3] libstd: Limit Duration range to i64 milliseconds. This enables `num_milliseconds` to return an `i64` again instead of `Option`, because it is guaranteed not to overflow. The Duration range is now rougly 300e6 years (positive and negative), whereas it was 300e9 years previously. To put these numbers in perspective, 300e9 years is about 21 times the age of the universe (according to Wolfram|Alpha). 300e6 years is about 1/15 of the age of the earth (according to Wolfram|Alpha). --- src/libstd/io/net/tcp.rs | 8 +--- src/libstd/io/net/unix.rs | 8 +--- src/libstd/io/timer.rs | 2 +- src/libstd/time/duration.rs | 88 +++++++++++++++++++++---------------- 4 files changed, 52 insertions(+), 54 deletions(-) diff --git a/src/libstd/io/net/tcp.rs b/src/libstd/io/net/tcp.rs index 52d3a04432a35..8128c4466ce5a 100644 --- a/src/libstd/io/net/tcp.rs +++ b/src/libstd/io/net/tcp.rs @@ -105,23 +105,17 @@ impl TcpStream { /// /// If a `timeout` with zero or negative duration is specified then /// the function returns `Err`, with the error kind set to `TimedOut`. - /// If the timeout is larger than 2^63 milliseconds, the function also - /// returns `Err` with the error kind set to `TimedOut`. #[experimental = "the timeout argument may eventually change types"] pub fn connect_timeout(addr: SocketAddr, timeout: Duration) -> IoResult { if timeout <= Duration::milliseconds(0) { return Err(standard_error(TimedOut)); } - let timeout_ms = timeout.num_milliseconds().map(|x| { x as u64 }); - if timeout_ms.is_none() { - return Err(standard_error(TimedOut)); - } let SocketAddr { ip, port } = addr; let addr = rtio::SocketAddr { ip: super::to_rtio(ip), port: port }; LocalIo::maybe_raise(|io| { - io.tcp_connect(addr, timeout_ms).map(TcpStream::new) + io.tcp_connect(addr, Some(timeout.num_milliseconds() as u64)).map(TcpStream::new) }).map_err(IoError::from_rtio_error) } diff --git a/src/libstd/io/net/unix.rs b/src/libstd/io/net/unix.rs index 179855003f9bd..33235bd6b03d6 100644 --- a/src/libstd/io/net/unix.rs +++ b/src/libstd/io/net/unix.rs @@ -66,21 +66,15 @@ impl UnixStream { /// /// If a `timeout` with zero or negative duration is specified then /// the function returns `Err`, with the error kind set to `TimedOut`. - /// If the timeout is larger than 2^63 milliseconds, the function also - /// returns `Err` with the error kind set to `TimedOut`. #[experimental = "the timeout argument is likely to change types"] pub fn connect_timeout(path: &P, timeout: Duration) -> IoResult { if timeout <= Duration::milliseconds(0) { return Err(standard_error(TimedOut)); } - let timeout_ms = timeout.num_milliseconds().map(|x| { x as u64 }); - if timeout_ms.is_none() { - return Err(standard_error(TimedOut)); - } LocalIo::maybe_raise(|io| { - let s = io.unix_connect(&path.to_c_str(), timeout_ms); + let s = io.unix_connect(&path.to_c_str(), Some(timeout.num_milliseconds() as u64)); s.map(|p| UnixStream { obj: p }) }).map_err(IoError::from_rtio_error) } diff --git a/src/libstd/io/timer.rs b/src/libstd/io/timer.rs index 205132aca1d35..39c6c74e45eef 100644 --- a/src/libstd/io/timer.rs +++ b/src/libstd/io/timer.rs @@ -225,7 +225,7 @@ impl Callback for TimerCallback { } fn in_ms_u64(d: Duration) -> u64 { - let ms = d.num_milliseconds().unwrap_or(0); + let ms = d.num_milliseconds(); if ms < 0 { return 0 }; return ms as u64; } diff --git a/src/libstd/time/duration.rs b/src/libstd/time/duration.rs index c2030ccceb06d..e8fefebc963d8 100644 --- a/src/libstd/time/duration.rs +++ b/src/libstd/time/duration.rs @@ -51,10 +51,17 @@ pub struct Duration { nanos: i32, // Always 0 <= nanos < NANOS_PER_SEC } -/// The minimum possible `Duration`. -pub static MIN: Duration = Duration { secs: i64::MIN, nanos: 0 }; -/// The maximum possible `Duration`. -pub static MAX: Duration = Duration { secs: i64::MAX, nanos: NANOS_PER_SEC - 1 }; +/// The minimum possible `Duration`: `i64::MIN` milliseconds. +pub static MIN: Duration = Duration { + secs: i64::MIN / MILLIS_PER_SEC - 1, + nanos: NANOS_PER_SEC + (i64::MIN % MILLIS_PER_SEC) as i32 * NANOS_PER_MILLI +}; + +/// The maximum possible `Duration`: `i64::MAX` milliseconds. +pub static MAX: Duration = Duration { + secs: i64::MAX / MILLIS_PER_SEC, + nanos: (i64::MAX % MILLIS_PER_SEC) as i32 * NANOS_PER_MILLI +}; impl Duration { /// Makes a new `Duration` with given number of weeks. @@ -94,9 +101,15 @@ impl Duration { } /// Makes a new `Duration` with given number of seconds. + /// Fails when the duration is more than `i64::MAX` milliseconds + /// or less than `i64::MIN` milliseconds. #[inline] pub fn seconds(seconds: i64) -> Duration { - Duration { secs: seconds, nanos: 0 } + let d = Duration { secs: seconds, nanos: 0 }; + if d < MIN || d > MAX { + fail!("Duration::seconds out of bounds"); + } + d } /// Makes a new `Duration` with given number of milliseconds. @@ -167,11 +180,12 @@ impl Duration { } /// Returns the total number of whole milliseconds in the duration, - /// or `None` on overflow (exceeding 2^63 milliseconds in either direction). - pub fn num_milliseconds(&self) -> Option { - let secs_part = try_opt!(self.num_seconds().checked_mul(&MILLIS_PER_SEC)); + pub fn num_milliseconds(&self) -> i64 { + // A proper Duration will not overflow, because MIN and MAX are defined + // such that the range is exactly i64 milliseconds. + let secs_part = self.num_seconds() * MILLIS_PER_SEC; let nanos_part = self.nanos_mod_sec() / NANOS_PER_MILLI; - secs_part.checked_add(&(nanos_part as i64)) + secs_part + nanos_part as i64 } /// Returns the total number of whole microseconds in the duration, @@ -211,13 +225,7 @@ impl num::Zero for Duration { impl Neg for Duration { #[inline] fn neg(&self) -> Duration { - if self.secs == i64::MIN && self.nanos == 0 { - // The minimum value cannot be negated due to overflow. Use the - // maximum value, which is one nanosecond less than the negated minimum. - MAX - } else if self.secs == i64::MIN { - Duration { secs: i64::MAX, nanos: NANOS_PER_SEC - self.nanos } - } else if self.nanos == 0 { + if self.nanos == 0 { Duration { secs: -self.secs, nanos: 0 } } else { Duration { secs: -self.secs - 1, nanos: NANOS_PER_SEC - self.nanos } @@ -245,7 +253,10 @@ impl num::CheckedAdd for Duration { nanos -= NANOS_PER_SEC; secs = try_opt!(secs.checked_add(&1)); } - Some(Duration { secs: secs, nanos: nanos }) + let d = Duration { secs: secs, nanos: nanos }; + // Even if d is within the bounds of i64 seconds, + // it might still overflow i64 milliseconds. + if d < MIN || d > MAX { None } else { Some(d) } } } @@ -269,7 +280,10 @@ impl num::CheckedSub for Duration { nanos += NANOS_PER_SEC; secs = try_opt!(secs.checked_sub(&1)); } - Some(Duration { secs: secs, nanos: nanos }) + let d = Duration { secs: secs, nanos: nanos }; + // Even if d is within the bounds of i64 seconds, + // it might still overflow i64 milliseconds. + if d < MIN || d > MAX { None } else { Some(d) } } } @@ -407,26 +421,22 @@ mod tests { assert_eq!(Duration::milliseconds(1001).num_seconds(), 1); assert_eq!(Duration::milliseconds(-999).num_seconds(), 0); assert_eq!(Duration::milliseconds(-1001).num_seconds(), -1); - assert_eq!(Duration::seconds(i64::MAX).num_seconds(), i64::MAX); - assert_eq!(Duration::seconds(i64::MIN).num_seconds(), i64::MIN); - assert_eq!(MAX.num_seconds(), i64::MAX); - assert_eq!(MIN.num_seconds(), i64::MIN); } #[test] fn test_duration_num_milliseconds() { let d: Duration = Zero::zero(); - assert_eq!(d.num_milliseconds(), Some(0)); - assert_eq!(Duration::milliseconds(1).num_milliseconds(), Some(1)); - assert_eq!(Duration::milliseconds(-1).num_milliseconds(), Some(-1)); - assert_eq!(Duration::microseconds(999).num_milliseconds(), Some(0)); - assert_eq!(Duration::microseconds(1001).num_milliseconds(), Some(1)); - assert_eq!(Duration::microseconds(-999).num_milliseconds(), Some(0)); - assert_eq!(Duration::microseconds(-1001).num_milliseconds(), Some(-1)); - assert_eq!(Duration::milliseconds(i64::MAX).num_milliseconds(), Some(i64::MAX)); - assert_eq!(Duration::milliseconds(i64::MIN).num_milliseconds(), Some(i64::MIN)); - assert_eq!(MAX.num_milliseconds(), None); - assert_eq!(MIN.num_milliseconds(), None); + assert_eq!(d.num_milliseconds(), 0); + assert_eq!(Duration::milliseconds(1).num_milliseconds(), 1); + assert_eq!(Duration::milliseconds(-1).num_milliseconds(), -1); + assert_eq!(Duration::microseconds(999).num_milliseconds(), 0); + assert_eq!(Duration::microseconds(1001).num_milliseconds(), 1); + assert_eq!(Duration::microseconds(-999).num_milliseconds(), 0); + assert_eq!(Duration::microseconds(-1001).num_milliseconds(), -1); + assert_eq!(Duration::milliseconds(i64::MAX).num_milliseconds(), i64::MAX); + assert_eq!(Duration::milliseconds(i64::MIN).num_milliseconds(), i64::MIN); + assert_eq!(MAX.num_milliseconds(), i64::MAX); + assert_eq!(MIN.num_milliseconds(), i64::MIN); } #[test] @@ -477,13 +487,13 @@ mod tests { #[test] fn test_duration_checked_ops() { - assert_eq!(Duration::seconds(i64::MAX).checked_add(&Duration::milliseconds(999)), - Some(Duration::seconds(i64::MAX - 1) + Duration::milliseconds(1999))); - assert!(Duration::seconds(i64::MAX).checked_add(&Duration::milliseconds(1000)).is_none()); + assert_eq!(Duration::milliseconds(i64::MAX - 1).checked_add(&Duration::microseconds(999)), + Some(Duration::milliseconds(i64::MAX - 2) + Duration::microseconds(1999))); + assert!(Duration::milliseconds(i64::MAX).checked_add(&Duration::microseconds(1000)).is_none()); - assert_eq!(Duration::seconds(i64::MIN).checked_sub(&Duration::seconds(0)), - Some(Duration::seconds(i64::MIN))); - assert!(Duration::seconds(i64::MIN).checked_sub(&Duration::seconds(1)).is_none()); + assert_eq!(Duration::milliseconds(i64::MIN).checked_sub(&Duration::milliseconds(0)), + Some(Duration::milliseconds(i64::MIN))); + assert!(Duration::milliseconds(i64::MIN).checked_sub(&Duration::milliseconds(1)).is_none()); } #[test] From 447b64ebc28b0104a8121b4318bbd2970de2cc56 Mon Sep 17 00:00:00 2001 From: Ruud van Asseldonk Date: Thu, 28 Aug 2014 21:56:27 +0200 Subject: [PATCH 3/3] libstd: Wrap duration.rs at 100 characters. --- src/libstd/time/duration.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/libstd/time/duration.rs b/src/libstd/time/duration.rs index e8fefebc963d8..17dba9af9e7b3 100644 --- a/src/libstd/time/duration.rs +++ b/src/libstd/time/duration.rs @@ -390,7 +390,8 @@ mod tests { Duration::days(1) + Duration::seconds(3)); assert_eq!(Duration::days(10) - Duration::seconds(1000), Duration::seconds(863000)); assert_eq!(Duration::days(10) - Duration::seconds(1000000), Duration::seconds(-136000)); - assert_eq!(Duration::days(2) + Duration::seconds(86399) + Duration::nanoseconds(1234567890), + assert_eq!(Duration::days(2) + Duration::seconds(86399) + + Duration::nanoseconds(1234567890), Duration::days(3) + Duration::nanoseconds(234567890)); assert_eq!(-Duration::days(3), Duration::days(-3)); assert_eq!(-(Duration::days(3) + Duration::seconds(70)), @@ -489,11 +490,13 @@ mod tests { fn test_duration_checked_ops() { assert_eq!(Duration::milliseconds(i64::MAX - 1).checked_add(&Duration::microseconds(999)), Some(Duration::milliseconds(i64::MAX - 2) + Duration::microseconds(1999))); - assert!(Duration::milliseconds(i64::MAX).checked_add(&Duration::microseconds(1000)).is_none()); + assert!(Duration::milliseconds(i64::MAX).checked_add(&Duration::microseconds(1000)) + .is_none()); assert_eq!(Duration::milliseconds(i64::MIN).checked_sub(&Duration::milliseconds(0)), Some(Duration::milliseconds(i64::MIN))); - assert!(Duration::milliseconds(i64::MIN).checked_sub(&Duration::milliseconds(1)).is_none()); + assert!(Duration::milliseconds(i64::MIN).checked_sub(&Duration::milliseconds(1)) + .is_none()); } #[test]