diff --git a/src/swift/Time.swift b/src/swift/Time.swift index d7d49c96b..f044d592e 100644 --- a/src/swift/Time.swift +++ b/src/swift/Time.swift @@ -119,6 +119,40 @@ public func ==(a: DispatchWallTime, b: DispatchWallTime) -> Bool { return a.rawValue == b.rawValue } +// Returns m1 * m2, clamped to the range [Int64.min, Int64.max]. +// Because of the way this function is used, we can always assume +// that m2 > 0. +private func clampedInt64Product(_ m1: Int64, _ m2: Int64) -> Int64 { + assert(m2 > 0, "multiplier must be positive") + let (result, overflow) = m1.multipliedReportingOverflow(by: m2) + if overflow { + return m1 > 0 ? Int64.max : Int64.min + } + return result +} + +// Returns its argument clamped to the range [Int64.min, Int64.max]. +private func toInt64Clamped(_ value: Double) -> Int64 { + if value.isNaN { return Int64.max } + if value >= Double(Int64.max) { return Int64.max } + if value <= Double(Int64.min) { return Int64.min } + return Int64(value) +} + +/// Represents a time interval that can be used as an offset from a `DispatchTime` +/// or `DispatchWallTime`. +/// +/// For example: +/// let inOneSecond = DispatchTime.now() + DispatchTimeInterval.seconds(1) +/// +/// If the requested time interval is larger then the internal representation +/// permits, the result of adding it to a `DispatchTime` or `DispatchWallTime` +/// is `DispatchTime.distantFuture` and `DispatchWallTime.distantFuture` +/// respectively. Such time intervals compare as equal: +/// +/// let t1 = DispatchTimeInterval.seconds(Int.max) +/// let t2 = DispatchTimeInterval.milliseconds(Int.max) +/// let result = t1 == t2 // true public enum DispatchTimeInterval { case seconds(Int) case milliseconds(Int) @@ -129,9 +163,9 @@ public enum DispatchTimeInterval { internal var rawValue: Int64 { switch self { - case .seconds(let s): return Int64(s) * Int64(NSEC_PER_SEC) - case .milliseconds(let ms): return Int64(ms) * Int64(NSEC_PER_MSEC) - case .microseconds(let us): return Int64(us) * Int64(NSEC_PER_USEC) + case .seconds(let s): return clampedInt64Product(Int64(s), Int64(NSEC_PER_SEC)) + case .milliseconds(let ms): return clampedInt64Product(Int64(ms), Int64(NSEC_PER_MSEC)) + case .microseconds(let us): return clampedInt64Product(Int64(us), Int64(NSEC_PER_USEC)) case .nanoseconds(let ns): return Int64(ns) case .never: return Int64.max } @@ -158,16 +192,12 @@ public func -(time: DispatchTime, interval: DispatchTimeInterval) -> DispatchTim } public func +(time: DispatchTime, seconds: Double) -> DispatchTime { - let interval = seconds * Double(NSEC_PER_SEC) - let t = CDispatch.dispatch_time(time.rawValue, - interval.isInfinite || interval.isNaN ? Int64.max : Int64(interval)) + let t = CDispatch.dispatch_time(time.rawValue, toInt64Clamped(seconds * Double(NSEC_PER_SEC))); return DispatchTime(rawValue: t) } public func -(time: DispatchTime, seconds: Double) -> DispatchTime { - let interval = -seconds * Double(NSEC_PER_SEC) - let t = CDispatch.dispatch_time(time.rawValue, - interval.isInfinite || interval.isNaN ? Int64.min : Int64(interval)) + let t = CDispatch.dispatch_time(time.rawValue, toInt64Clamped(-seconds * Double(NSEC_PER_SEC))); return DispatchTime(rawValue: t) } @@ -182,15 +212,11 @@ public func -(time: DispatchWallTime, interval: DispatchTimeInterval) -> Dispatc } public func +(time: DispatchWallTime, seconds: Double) -> DispatchWallTime { - let interval = seconds * Double(NSEC_PER_SEC) - let t = CDispatch.dispatch_time(time.rawValue, - interval.isInfinite || interval.isNaN ? Int64.max : Int64(interval)) + let t = CDispatch.dispatch_time(time.rawValue, toInt64Clamped(seconds * Double(NSEC_PER_SEC))); return DispatchWallTime(rawValue: t) } public func -(time: DispatchWallTime, seconds: Double) -> DispatchWallTime { - let interval = -seconds * Double(NSEC_PER_SEC) - let t = CDispatch.dispatch_time(time.rawValue, - interval.isInfinite || interval.isNaN ? Int64.min : Int64(interval)) + let t = CDispatch.dispatch_time(time.rawValue, toInt64Clamped(-seconds * Double(NSEC_PER_SEC))); return DispatchWallTime(rawValue: t) }