summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJosh Suereth <Joshua.Suereth@gmail.com>2012-09-20 10:22:01 -0700
committerJosh Suereth <Joshua.Suereth@gmail.com>2012-09-20 10:22:01 -0700
commit45c0b2428219b6a00514e21529650b32631880b6 (patch)
tree88eeb6ce02a1ec9dda21e77f7add4a995a8b9879
parent1fc89420688d2123615e89a15e515c801729be5a (diff)
parent05367235ef4c519521cb28073f951e3d55fe3aad (diff)
downloadscala-45c0b2428219b6a00514e21529650b32631880b6.tar.gz
scala-45c0b2428219b6a00514e21529650b32631880b6.tar.bz2
scala-45c0b2428219b6a00514e21529650b32631880b6.zip
Merge pull request #1339 from rkuhn/wip-6389-finite-duration-mult
enable integer multiplication/divison on FiniteDuration, see SI-6389
-rw-r--r--src/actors-migration/scala/actors/Pattern.scala2
-rw-r--r--src/actors-migration/scala/actors/StashingActor.scala2
-rw-r--r--src/actors-migration/scala/actors/Timeout.scala2
-rw-r--r--src/actors/scala/actors/ActorRef.scala2
-rw-r--r--src/library/scala/concurrent/Awaitable.scala2
-rw-r--r--src/library/scala/concurrent/duration/Deadline.scala81
-rw-r--r--src/library/scala/concurrent/duration/Duration.scala (renamed from src/library/scala/concurrent/util/Duration.scala)253
-rw-r--r--src/library/scala/concurrent/duration/DurationConversions.scala92
-rw-r--r--src/library/scala/concurrent/duration/package.scala75
-rw-r--r--src/library/scala/concurrent/impl/Promise.scala12
-rw-r--r--src/library/scala/concurrent/package.scala2
-rw-r--r--src/library/scala/concurrent/util/duration/Classifier.scala9
-rw-r--r--src/library/scala/concurrent/util/duration/IntMult.scala18
-rw-r--r--src/library/scala/concurrent/util/duration/package.scala31
-rw-r--r--test/files/jvm/actmig-PinS.scala2
-rw-r--r--test/files/jvm/actmig-PinS_1.scala2
-rw-r--r--test/files/jvm/actmig-PinS_2.scala2
-rw-r--r--test/files/jvm/actmig-PinS_3.scala2
-rw-r--r--test/files/jvm/actmig-public-methods_1.scala2
-rw-r--r--test/files/jvm/actmig-react-receive.scala2
-rw-r--r--test/files/jvm/duration-java/Test.java2
-rw-r--r--test/files/jvm/duration-tck.scala34
-rw-r--r--test/files/jvm/future-spec/FutureTests.scala4
-rw-r--r--test/files/jvm/future-spec/PromiseTests.scala4
-rw-r--r--test/files/jvm/future-spec/main.scala2
-rw-r--r--test/files/jvm/scala-concurrent-tck.scala2
-rw-r--r--test/files/neg/deadline-inf-illegal.check12
-rw-r--r--test/files/neg/deadline-inf-illegal.scala2
-rw-r--r--test/files/scalacheck/duration.scala69
29 files changed, 443 insertions, 283 deletions
diff --git a/src/actors-migration/scala/actors/Pattern.scala b/src/actors-migration/scala/actors/Pattern.scala
index fa2db79152..a97ee3ba07 100644
--- a/src/actors-migration/scala/actors/Pattern.scala
+++ b/src/actors-migration/scala/actors/Pattern.scala
@@ -1,6 +1,6 @@
package scala.actors
-import scala.concurrent.util.Duration
+import scala.concurrent.duration.Duration
import scala.language.implicitConversions
object pattern {
diff --git a/src/actors-migration/scala/actors/StashingActor.scala b/src/actors-migration/scala/actors/StashingActor.scala
index 29f370a3e2..775d115d0b 100644
--- a/src/actors-migration/scala/actors/StashingActor.scala
+++ b/src/actors-migration/scala/actors/StashingActor.scala
@@ -1,7 +1,7 @@
package scala.actors
import scala.collection._
-import scala.concurrent.util.Duration
+import scala.concurrent.duration.Duration
import java.util.concurrent.TimeUnit
import scala.language.implicitConversions
diff --git a/src/actors-migration/scala/actors/Timeout.scala b/src/actors-migration/scala/actors/Timeout.scala
index 5540d2880e..c3017d8569 100644
--- a/src/actors-migration/scala/actors/Timeout.scala
+++ b/src/actors-migration/scala/actors/Timeout.scala
@@ -8,7 +8,7 @@
package scala.actors
-import scala.concurrent.util.Duration
+import scala.concurrent.duration.Duration
import java.util.concurrent.TimeUnit
import scala.language.implicitConversions
diff --git a/src/actors/scala/actors/ActorRef.scala b/src/actors/scala/actors/ActorRef.scala
index 8f70b13e01..7768f04c2b 100644
--- a/src/actors/scala/actors/ActorRef.scala
+++ b/src/actors/scala/actors/ActorRef.scala
@@ -1,7 +1,7 @@
package scala.actors
import java.util.concurrent.TimeoutException
-import scala.concurrent.util.Duration
+import scala.concurrent.duration.Duration
/**
* Trait used for migration of Scala actors to Akka.
diff --git a/src/library/scala/concurrent/Awaitable.scala b/src/library/scala/concurrent/Awaitable.scala
index 655115349a..3bd7617bce 100644
--- a/src/library/scala/concurrent/Awaitable.scala
+++ b/src/library/scala/concurrent/Awaitable.scala
@@ -10,7 +10,7 @@ package scala.concurrent
-import scala.concurrent.util.Duration
+import scala.concurrent.duration.Duration
diff --git a/src/library/scala/concurrent/duration/Deadline.scala b/src/library/scala/concurrent/duration/Deadline.scala
new file mode 100644
index 0000000000..50e9a75ff7
--- /dev/null
+++ b/src/library/scala/concurrent/duration/Deadline.scala
@@ -0,0 +1,81 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003-2012, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+package scala.concurrent.duration
+
+/**
+ * This class stores a deadline, as obtained via `Deadline.now` or the
+ * duration DSL:
+ *
+ * {{{
+ * import scala.concurrent.duration._
+ * 3.seconds.fromNow
+ * }}}
+ *
+ * Its main purpose is to manage repeated attempts to achieve something (like
+ * awaiting a condition) by offering the methods `hasTimeLeft` and `timeLeft`. All
+ * durations are measured according to `System.nanoTime` aka wall-time; this
+ * does not take into account changes to the system clock (such as leap
+ * seconds).
+ */
+case class Deadline private (time: FiniteDuration) extends Ordered[Deadline] {
+ /**
+ * Return a deadline advanced (i.e. moved into the future) by the given duration.
+ */
+ def +(other: FiniteDuration): Deadline = copy(time = time + other)
+ /**
+ * Return a deadline moved backwards (i.e. towards the past) by the given duration.
+ */
+ def -(other: FiniteDuration): Deadline = copy(time = time - other)
+ /**
+ * Calculate time difference between this and the other deadline, where the result is directed (i.e. may be negative).
+ */
+ def -(other: Deadline): FiniteDuration = time - other.time
+ /**
+ * Calculate time difference between this duration and now; the result is negative if the deadline has passed.
+ *
+ * '''''Note that on some systems this operation is costly because it entails a system call.'''''
+ * Check `System.nanoTime` for your platform.
+ */
+ def timeLeft: FiniteDuration = this - Deadline.now
+ /**
+ * Determine whether the deadline still lies in the future at the point where this method is called.
+ *
+ * '''''Note that on some systems this operation is costly because it entails a system call.'''''
+ * Check `System.nanoTime` for your platform.
+ */
+ def hasTimeLeft(): Boolean = !isOverdue()
+ /**
+ * Determine whether the deadline lies in the past at the point where this method is called.
+ *
+ * '''''Note that on some systems this operation is costly because it entails a system call.'''''
+ * Check `System.nanoTime` for your platform.
+ */
+ def isOverdue(): Boolean = (time.toNanos - System.nanoTime()) < 0
+ /**
+ * The natural ordering for deadline is determined by the natural order of the underlying (finite) duration.
+ */
+ def compare(other: Deadline) = time compare other.time
+}
+
+object Deadline {
+ /**
+ * Construct a deadline due exactly at the point where this method is called. Useful for then
+ * advancing it to obtain a future deadline, or for sampling the current time exactly once and
+ * then comparing it to multiple deadlines (using subtraction).
+ */
+ def now: Deadline = Deadline(Duration(System.nanoTime, NANOSECONDS))
+
+ /**
+ * The natural ordering for deadline is determined by the natural order of the underlying (finite) duration.
+ */
+ implicit object DeadlineIsOrdered extends Ordering[Deadline] {
+ def compare(a: Deadline, b: Deadline) = a compare b
+ }
+
+}
diff --git a/src/library/scala/concurrent/util/Duration.scala b/src/library/scala/concurrent/duration/Duration.scala
index 3f8b98831e..79f9b4db86 100644
--- a/src/library/scala/concurrent/util/Duration.scala
+++ b/src/library/scala/concurrent/duration/Duration.scala
@@ -6,90 +6,12 @@
** |/ **
\* */
-package scala.concurrent.util
+package scala.concurrent.duration
-import java.util.concurrent.TimeUnit
-import TimeUnit._
import java.lang.{ Double => JDouble, Long => JLong }
import scala.language.implicitConversions
-/**
- * This class stores a deadline, as obtained via `Deadline.now` or the
- * duration DSL:
- *
- * {{{
- * import scala.concurrent.util.duration._
- * 3.seconds.fromNow
- * }}}
- *
- * Its main purpose is to manage repeated attempts to achieve something (like
- * awaiting a condition) by offering the methods `hasTimeLeft` and `timeLeft`. All
- * durations are measured according to `System.nanoTime` aka wall-time; this
- * does not take into account changes to the system clock (such as leap
- * seconds).
- */
-case class Deadline private (time: FiniteDuration) extends Ordered[Deadline] {
- /**
- * Return a deadline advanced (i.e. moved into the future) by the given duration.
- */
- def +(other: FiniteDuration): Deadline = copy(time = time + other)
- /**
- * Return a deadline moved backwards (i.e. towards the past) by the given duration.
- */
- def -(other: FiniteDuration): Deadline = copy(time = time - other)
- /**
- * Calculate time difference between this and the other deadline, where the result is directed (i.e. may be negative).
- */
- def -(other: Deadline): FiniteDuration = time - other.time
- /**
- * Calculate time difference between this duration and now; the result is negative if the deadline has passed.
- *
- * '''''Note that on some systems this operation is costly because it entails a system call.'''''
- * Check `System.nanoTime` for your platform.
- */
- def timeLeft: FiniteDuration = this - Deadline.now
- /**
- * Determine whether the deadline still lies in the future at the point where this method is called.
- *
- * '''''Note that on some systems this operation is costly because it entails a system call.'''''
- * Check `System.nanoTime` for your platform.
- */
- def hasTimeLeft(): Boolean = !isOverdue()
- /**
- * Determine whether the deadline lies in the past at the point where this method is called.
- *
- * '''''Note that on some systems this operation is costly because it entails a system call.'''''
- * Check `System.nanoTime` for your platform.
- */
- def isOverdue(): Boolean = (time.toNanos - System.nanoTime()) < 0
- /**
- * The natural ordering for deadline is determined by the natural order of the underlying (finite) duration.
- */
- def compare(other: Deadline) = time compare other.time
-}
-
-object Deadline {
- /**
- * Construct a deadline due exactly at the point where this method is called. Useful for then
- * advancing it to obtain a future deadline, or for sampling the current time exactly once and
- * then comparing it to multiple deadlines (using subtraction).
- */
- def now: Deadline = Deadline(Duration(System.nanoTime, NANOSECONDS))
-
- /**
- * The natural ordering for deadline is determined by the natural order of the underlying (finite) duration.
- */
- implicit object DeadlineIsOrdered extends Ordering[Deadline] {
- def compare(a: Deadline, b: Deadline) = a compare b
- }
-}
-
object Duration {
- /**
- * This implicit conversion allows the use of a Deadline in place of a Duration, which will
- * insert the time left until the deadline in its place.
- */
- implicit def timeLeft(implicit d: Deadline): Duration = d.timeLeft
/**
* Construct a Duration from the given length and unit. Observe that nanosecond precision may be lost if
@@ -102,11 +24,13 @@ object Duration {
* @throws IllegalArgumentException if the length was finite but the resulting duration cannot be expressed as a [[FiniteDuration]]
*/
def apply(length: Double, unit: TimeUnit): Duration = fromNanos(unit.toNanos(1) * length)
+
/**
* Construct a finite duration from the given length and time unit. The unit given is retained
* throughout calculations as long as possible, so that it can be retrieved later.
*/
def apply(length: Long, unit: TimeUnit): FiniteDuration = new FiniteDuration(length, unit)
+
/**
* Construct a finite duration from the given length and time unit, where the latter is
* looked up in a list of string representation. Valid choices are:
@@ -117,7 +41,7 @@ object Duration {
def apply(length: Long, unit: String): FiniteDuration = new FiniteDuration(length, Duration.timeUnit(unit))
// Double stores 52 bits mantissa, but there is an implied '1' in front, making the limit 2^53
- final val maxPreciseDouble = 9007199254740992d
+ private[this] final val maxPreciseDouble = 9007199254740992d
/**
* Parse String into Duration. Format is `"<length><unit>"`, where
@@ -161,11 +85,11 @@ object Duration {
)
// TimeUnit => standard label
- protected[util] val timeUnitName: Map[TimeUnit, String] =
+ protected[duration] val timeUnitName: Map[TimeUnit, String] =
timeUnitLabels.toMap mapValues (s => words(s).last) toMap
// Label => TimeUnit
- protected[util] val timeUnit: Map[String, TimeUnit] =
+ protected[duration] val timeUnit: Map[String, TimeUnit] =
timeUnitLabels flatMap { case (unit, names) => expandLabels(names) map (_ -> unit) } toMap
/**
@@ -275,13 +199,13 @@ object Duration {
if (factor == 0d || factor.isNaN) Undefined
else if (factor < 0d) -this
else this
- def /(factor: Double): Duration =
- if (factor.isNaN || factor.isInfinite) Undefined
- else if ((factor compare 0d) < 0) -this
+ def /(divisor: Double): Duration =
+ if (divisor.isNaN || divisor.isInfinite) Undefined
+ else if ((divisor compare 0d) < 0) -this
else this
- def /(other: Duration): Double = other match {
+ def /(divisor: Duration): Double = divisor match {
case _: Infinite => Double.NaN
- case x => Double.PositiveInfinity * (if ((this > Zero) ^ (other >= Zero)) -1 else 1)
+ case x => Double.PositiveInfinity * (if ((this > Zero) ^ (divisor >= Zero)) -1 else 1)
}
final def isFinite() = false
@@ -380,8 +304,7 @@ object Duration {
* <p/>
* Examples:
* {{{
- * import scala.concurrent.util.Duration
- * import java.util.concurrent.TimeUnit
+ * import scala.concurrent.duration._
*
* val duration = Duration(100, MILLISECONDS)
* val duration = Duration(100, "millis")
@@ -396,7 +319,7 @@ object Duration {
* <p/>
* Implicits are also provided for Int, Long and Double. Example usage:
* {{{
- * import scala.concurrent.util.Duration._
+ * import scala.concurrent.duration._
*
* val duration = 100 millis
* }}}
@@ -529,12 +452,12 @@ sealed abstract class Duration extends Serializable with Ordered[Duration] {
*
* $ovf
*/
- def /(factor: Double): Duration
+ def /(divisor: Double): Duration
/**
* Return the quotient of this and that duration as floating-point number. The semantics are
* determined by Double as if calculating the quotient of the nanosecond lengths of both factors.
*/
- def /(other: Duration): Double
+ def /(divisor: Duration): Double
/**
* Negate this duration. The only two values which are mapped to themselves are [[Duration.Zero]] and [[Duration.Undefined]].
*/
@@ -561,7 +484,7 @@ sealed abstract class Duration extends Serializable with Ordered[Duration] {
*
* $ovf
*/
- def div(factor: Double) = this / factor
+ def div(divisor: Double) = this / divisor
/**
* Return the quotient of this and that duration as floating-point number. The semantics are
* determined by Double as if calculating the quotient of the nanosecond lengths of both factors.
@@ -599,6 +522,7 @@ sealed abstract class Duration extends Serializable with Ordered[Duration] {
}
object FiniteDuration {
+
implicit object FiniteDurationIsOrdered extends Ordering[FiniteDuration] {
def compare(a: FiniteDuration, b: FiniteDuration) = a compare b
}
@@ -691,29 +615,77 @@ final class FiniteDuration(val length: Long, val unit: TimeUnit) extends Duratio
else if ((factor > 0) ^ (this < Zero)) Inf
else MinusInf
- def /(factor: Double) =
- if (!factor.isInfinite) fromNanos(toNanos / factor)
- else if (factor.isNaN) Undefined
+ def /(divisor: Double) =
+ if (!divisor.isInfinite) fromNanos(toNanos / divisor)
+ else if (divisor.isNaN) Undefined
else Zero
// if this is made a constant, then scalac will elide the conditional and always return +0.0, SI-6331
private[this] def minusZero = -0d
- def /(other: Duration): Double =
- if (other.isFinite) toNanos.toDouble / other.toNanos
- else if (other eq Undefined) Double.NaN
- else if ((length < 0) ^ (other > Zero)) 0d
+ def /(divisor: Duration): Double =
+ if (divisor.isFinite) toNanos.toDouble / divisor.toNanos
+ else if (divisor eq Undefined) Double.NaN
+ else if ((length < 0) ^ (divisor > Zero)) 0d
else minusZero
- // overridden methods taking FiniteDurations, so that you can calculate while statically staying finite
+ // overloaded methods taking FiniteDurations, so that you can calculate while statically staying finite
def +(other: FiniteDuration) = add(other.length, other.unit)
def -(other: FiniteDuration) = add(-other.length, other.unit)
def plus(other: FiniteDuration) = this + other
def minus(other: FiniteDuration) = this - other
- override def div(factor: Double) = this / factor
- override def mul(factor: Double) = this * factor
def min(other: FiniteDuration) = if (this < other) this else other
def max(other: FiniteDuration) = if (this > other) this else other
+ // overloaded methods taking Long so that you can calculate while statically staying finite
+
+ /**
+ * Return the quotient of this duration and the given integer factor.
+ *
+ * @throws ArithmeticException if the factor is 0
+ */
+ def /(divisor: Long) = fromNanos(toNanos / divisor)
+
+ /**
+ * Return the product of this duration and the given integer factor.
+ *
+ * @throws IllegalArgumentException if the result would overflow the range of FiniteDuration
+ */
+ def *(factor: Long) = new FiniteDuration(safeMul(length, factor), unit)
+
+ /*
+ * This method avoids the use of Long division, which saves 95% of the time spent,
+ * by checking that there are enough leading zeros so that the result has a chance
+ * to fit into a Long again; the remaining edge cases are caught by using the sign
+ * of the product for overflow detection.
+ *
+ * This method is not general purpose because it disallows the (otherwise legal)
+ * case of Long.MinValue * 1, but that is okay for use in FiniteDuration, since
+ * Long.MinValue is not a legal `length` anyway.
+ */
+ private def safeMul(_a: Long, _b: Long): Long = {
+ val a = math.abs(_a)
+ val b = math.abs(_b)
+ import java.lang.Long.{ numberOfLeadingZeros => leading }
+ if (leading(a) + leading(b) < 64) throw new IllegalArgumentException("multiplication overflow")
+ val product = a * b
+ if (product < 0) throw new IllegalArgumentException("multiplication overflow")
+ if (a == _a ^ b == _b) -product else product
+ }
+
+ /**
+ * Return the quotient of this duration and the given integer factor.
+ *
+ * @throws ArithmeticException if the factor is 0
+ */
+ def div(divisor: Long) = this / divisor
+
+ /**
+ * Return the product of this duration and the given integer factor.
+ *
+ * @throws IllegalArgumentException if the result would overflow the range of FiniteDuration
+ */
+ def mul(factor: Long) = this * factor
+
def unary_- = Duration(-length, unit)
final def isFinite() = true
@@ -724,78 +696,3 @@ final class FiniteDuration(val length: Long, val unit: TimeUnit) extends Duratio
}
override def hashCode = toNanos.toInt
}
-
-trait DurationConversions extends Any {
- import duration.Classifier
- protected def durationIn(unit: TimeUnit): FiniteDuration
-
- def nanoseconds = durationIn(NANOSECONDS)
- def nanos = nanoseconds
- def nanosecond = nanoseconds
- def nano = nanoseconds
-
- def microseconds = durationIn(MICROSECONDS)
- def micros = microseconds
- def microsecond = microseconds
- def micro = microseconds
-
- def milliseconds = durationIn(MILLISECONDS)
- def millis = milliseconds
- def millisecond = milliseconds
- def milli = milliseconds
-
- def seconds = durationIn(SECONDS)
- def second = seconds
-
- def minutes = durationIn(MINUTES)
- def minute = minutes
-
- def hours = durationIn(HOURS)
- def hour = hours
-
- def days = durationIn(DAYS)
- def day = days
-
- def nanoseconds[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(nanoseconds)
- def nanos[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = nanoseconds(c)
- def nanosecond[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = nanoseconds(c)
- def nano[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = nanoseconds(c)
-
- def microseconds[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(microseconds)
- def micros[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = microseconds(c)
- def microsecond[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = microseconds(c)
- def micro[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = microseconds(c)
-
- def milliseconds[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(milliseconds)
- def millis[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = milliseconds(c)
- def millisecond[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = milliseconds(c)
- def milli[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = milliseconds(c)
-
- def seconds[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(seconds)
- def second[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = seconds(c)
-
- def minutes[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(minutes)
- def minute[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = minutes(c)
-
- def hours[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(hours)
- def hour[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = hours(c)
-
- def days[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(days)
- def day[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = days(c)
-}
-
-final class DurationInt(val n: Int) extends AnyVal with DurationConversions {
- override protected def durationIn(unit: TimeUnit): FiniteDuration = Duration(n, unit)
-}
-
-final class DurationLong(val n: Long) extends AnyVal with DurationConversions {
- override protected def durationIn(unit: TimeUnit): FiniteDuration = Duration(n, unit)
-}
-
-final class DurationDouble(val d: Double) extends AnyVal with DurationConversions {
- override protected def durationIn(unit: TimeUnit): FiniteDuration =
- Duration(d, unit) match {
- case f: FiniteDuration => f
- case _ => throw new IllegalArgumentException("Duration DSL not applicable to " + d)
- }
-}
diff --git a/src/library/scala/concurrent/duration/DurationConversions.scala b/src/library/scala/concurrent/duration/DurationConversions.scala
new file mode 100644
index 0000000000..2c7e192a0e
--- /dev/null
+++ b/src/library/scala/concurrent/duration/DurationConversions.scala
@@ -0,0 +1,92 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003-2012, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+package scala.concurrent.duration
+
+import DurationConversions._
+
+// Would be nice to limit the visibility of this trait a little bit,
+// but it crashes scalac to do so.
+trait DurationConversions extends Any {
+ protected def durationIn(unit: TimeUnit): FiniteDuration
+
+ def nanoseconds = durationIn(NANOSECONDS)
+ def nanos = nanoseconds
+ def nanosecond = nanoseconds
+ def nano = nanoseconds
+
+ def microseconds = durationIn(MICROSECONDS)
+ def micros = microseconds
+ def microsecond = microseconds
+ def micro = microseconds
+
+ def milliseconds = durationIn(MILLISECONDS)
+ def millis = milliseconds
+ def millisecond = milliseconds
+ def milli = milliseconds
+
+ def seconds = durationIn(SECONDS)
+ def second = seconds
+
+ def minutes = durationIn(MINUTES)
+ def minute = minutes
+
+ def hours = durationIn(HOURS)
+ def hour = hours
+
+ def days = durationIn(DAYS)
+ def day = days
+
+ def nanoseconds[C](c: C)(implicit ev: Classifier[C]): ev.R = ev.convert(nanoseconds)
+ def nanos[C](c: C)(implicit ev: Classifier[C]): ev.R = nanoseconds(c)
+ def nanosecond[C](c: C)(implicit ev: Classifier[C]): ev.R = nanoseconds(c)
+ def nano[C](c: C)(implicit ev: Classifier[C]): ev.R = nanoseconds(c)
+
+ def microseconds[C](c: C)(implicit ev: Classifier[C]): ev.R = ev.convert(microseconds)
+ def micros[C](c: C)(implicit ev: Classifier[C]): ev.R = microseconds(c)
+ def microsecond[C](c: C)(implicit ev: Classifier[C]): ev.R = microseconds(c)
+ def micro[C](c: C)(implicit ev: Classifier[C]): ev.R = microseconds(c)
+
+ def milliseconds[C](c: C)(implicit ev: Classifier[C]): ev.R = ev.convert(milliseconds)
+ def millis[C](c: C)(implicit ev: Classifier[C]): ev.R = milliseconds(c)
+ def millisecond[C](c: C)(implicit ev: Classifier[C]): ev.R = milliseconds(c)
+ def milli[C](c: C)(implicit ev: Classifier[C]): ev.R = milliseconds(c)
+
+ def seconds[C](c: C)(implicit ev: Classifier[C]): ev.R = ev.convert(seconds)
+ def second[C](c: C)(implicit ev: Classifier[C]): ev.R = seconds(c)
+
+ def minutes[C](c: C)(implicit ev: Classifier[C]): ev.R = ev.convert(minutes)
+ def minute[C](c: C)(implicit ev: Classifier[C]): ev.R = minutes(c)
+
+ def hours[C](c: C)(implicit ev: Classifier[C]): ev.R = ev.convert(hours)
+ def hour[C](c: C)(implicit ev: Classifier[C]): ev.R = hours(c)
+
+ def days[C](c: C)(implicit ev: Classifier[C]): ev.R = ev.convert(days)
+ def day[C](c: C)(implicit ev: Classifier[C]): ev.R = days(c)
+}
+
+/**
+ * This object just holds some cogs which make the DSL machine work, not for direct consumption.
+ */
+object DurationConversions {
+ trait Classifier[C] {
+ type R
+ def convert(d: FiniteDuration): R
+ }
+
+ implicit object spanConvert extends Classifier[span.type] {
+ type R = FiniteDuration
+ def convert(d: FiniteDuration) = d
+ }
+
+ implicit object fromNowConvert extends Classifier[fromNow.type] {
+ type R = Deadline
+ def convert(d: FiniteDuration) = Deadline.now + d
+ }
+
+}
diff --git a/src/library/scala/concurrent/duration/package.scala b/src/library/scala/concurrent/duration/package.scala
new file mode 100644
index 0000000000..2fd735f19e
--- /dev/null
+++ b/src/library/scala/concurrent/duration/package.scala
@@ -0,0 +1,75 @@
+package scala.concurrent
+
+import scala.language.implicitConversions
+
+package object duration {
+ /**
+ * This object can be used as closing token if you prefer dot-less style but do not want
+ * to enable language.postfixOps:
+ *
+ * {{{
+ * import scala.concurrent.duration._
+ *
+ * val duration = 2 seconds span
+ * }}}
+ */
+ object span
+
+ /**
+ * This object can be used as closing token for declaring a deadline at some future point
+ * in time:
+ *
+ * {{{
+ * import scala.concurrent.duration._
+ *
+ * val deadline = 3 seconds fromNow
+ * }}}
+ */
+ object fromNow
+
+ type TimeUnit = java.util.concurrent.TimeUnit
+ final val DAYS = java.util.concurrent.TimeUnit.DAYS
+ final val HOURS = java.util.concurrent.TimeUnit.HOURS
+ final val MICROSECONDS = java.util.concurrent.TimeUnit.MICROSECONDS
+ final val MILLISECONDS = java.util.concurrent.TimeUnit.MILLISECONDS
+ final val MINUTES = java.util.concurrent.TimeUnit.MINUTES
+ final val NANOSECONDS = java.util.concurrent.TimeUnit.NANOSECONDS
+ final val SECONDS = java.util.concurrent.TimeUnit.SECONDS
+
+ implicit def pairIntToDuration(p: (Int, TimeUnit)): Duration = Duration(p._1, p._2)
+ implicit def pairLongToDuration(p: (Long, TimeUnit)): FiniteDuration = Duration(p._1, p._2)
+ implicit def durationToPair(d: Duration): (Long, TimeUnit) = (d.length, d.unit)
+
+ implicit final class DurationInt(val n: Int) extends AnyVal with DurationConversions {
+ override protected def durationIn(unit: TimeUnit): FiniteDuration = Duration(n, unit)
+ }
+
+ implicit final class DurationLong(val n: Long) extends AnyVal with DurationConversions {
+ override protected def durationIn(unit: TimeUnit): FiniteDuration = Duration(n, unit)
+ }
+
+ implicit final class DurationDouble(val d: Double) extends AnyVal with DurationConversions {
+ override protected def durationIn(unit: TimeUnit): FiniteDuration =
+ Duration(d, unit) match {
+ case f: FiniteDuration => f
+ case _ => throw new IllegalArgumentException("Duration DSL not applicable to " + d)
+ }
+ }
+
+ /*
+ * Avoid reflection based invocation by using non-duck type
+ */
+ implicit final class IntMult(val i: Int) extends AnyVal {
+ def *(d: Duration) = d * i
+ def *(d: FiniteDuration) = d * i
+ }
+
+ implicit final class LongMult(val i: Long) extends AnyVal {
+ def *(d: Duration) = d * i
+ def *(d: FiniteDuration) = d * i
+ }
+
+ implicit final class DoubleMult(val f: Double) extends AnyVal {
+ def *(d: Duration) = d * f
+ }
+}
diff --git a/src/library/scala/concurrent/impl/Promise.scala b/src/library/scala/concurrent/impl/Promise.scala
index 9228872f2b..a1a3305db0 100644
--- a/src/library/scala/concurrent/impl/Promise.scala
+++ b/src/library/scala/concurrent/impl/Promise.scala
@@ -8,16 +8,12 @@
package scala.concurrent.impl
-
-
-import java.util.concurrent.TimeUnit.NANOSECONDS
import scala.concurrent.{ ExecutionContext, CanAwait, OnCompleteRunnable, TimeoutException, ExecutionException }
-import scala.concurrent.util.{ Duration, Deadline, FiniteDuration }
+import scala.concurrent.duration.{ Duration, Deadline, FiniteDuration, NANOSECONDS }
import scala.annotation.tailrec
import scala.util.control.NonFatal
import scala.util.{ Try, Success, Failure }
-
private[concurrent] trait Promise[T] extends scala.concurrent.Promise[T] with scala.concurrent.Future[T] {
def future: this.type = this
}
@@ -48,7 +44,7 @@ private[concurrent] object Promise {
case Failure(t) => resolver(t)
case _ => source
}
-
+
private def resolver[T](throwable: Throwable): Try[T] = throwable match {
case t: scala.runtime.NonLocalReturnControl[_] => Success(t.value.asInstanceOf[T])
case t: scala.util.control.ControlThrowable => Failure(new ExecutionException("Boxed ControlThrowable", t))
@@ -56,12 +52,12 @@ private[concurrent] object Promise {
case e: Error => Failure(new ExecutionException("Boxed Error", e))
case t => Failure(t)
}
-
+
/** Default promise implementation.
*/
class DefaultPromise[T] extends AbstractPromise with Promise[T] { self =>
updateState(null, Nil) // Start at "No callbacks"
-
+
protected final def tryAwait(atMost: Duration): Boolean = {
@tailrec
def awaitUnsafe(deadline: Deadline, nextWait: FiniteDuration): Boolean = {
diff --git a/src/library/scala/concurrent/package.scala b/src/library/scala/concurrent/package.scala
index 1d06341d4d..e683732e41 100644
--- a/src/library/scala/concurrent/package.scala
+++ b/src/library/scala/concurrent/package.scala
@@ -8,7 +8,7 @@
package scala
-import scala.concurrent.util.Duration
+import scala.concurrent.duration.Duration
import scala.annotation.implicitNotFound
/** This package object contains primitives for concurrent and parallel programming.
diff --git a/src/library/scala/concurrent/util/duration/Classifier.scala b/src/library/scala/concurrent/util/duration/Classifier.scala
deleted file mode 100644
index 10faf0a5ce..0000000000
--- a/src/library/scala/concurrent/util/duration/Classifier.scala
+++ /dev/null
@@ -1,9 +0,0 @@
-package scala.concurrent.util.duration
-
-import scala.concurrent.util.{ FiniteDuration }
-
-trait Classifier[C] {
- type R
- def convert(d: FiniteDuration): R
-}
-
diff --git a/src/library/scala/concurrent/util/duration/IntMult.scala b/src/library/scala/concurrent/util/duration/IntMult.scala
deleted file mode 100644
index 94c58fb8c2..0000000000
--- a/src/library/scala/concurrent/util/duration/IntMult.scala
+++ /dev/null
@@ -1,18 +0,0 @@
-package scala.concurrent.util.duration
-
-import scala.concurrent.util.{ Duration }
-
-/*
- * Avoid reflection based invocation by using non-duck type
- */
-protected[duration] class IntMult(i: Int) {
- def *(d: Duration) = d * i
-}
-
-protected[duration] class LongMult(i: Long) {
- def *(d: Duration) = d * i
-}
-
-protected[duration] class DoubleMult(f: Double) {
- def *(d: Duration) = d * f
-}
diff --git a/src/library/scala/concurrent/util/duration/package.scala b/src/library/scala/concurrent/util/duration/package.scala
deleted file mode 100644
index d5ae3f1544..0000000000
--- a/src/library/scala/concurrent/util/duration/package.scala
+++ /dev/null
@@ -1,31 +0,0 @@
-package scala.concurrent.util
-
-import java.util.concurrent.TimeUnit
-import scala.language.implicitConversions
-
-package object duration {
-
- object span
- implicit object spanConvert extends Classifier[span.type] {
- type R = FiniteDuration
- def convert(d: FiniteDuration) = d
- }
-
- object fromNow
- implicit object fromNowConvert extends Classifier[fromNow.type] {
- type R = Deadline
- def convert(d: FiniteDuration) = Deadline.now + d
- }
-
- implicit def intToDurationInt(n: Int) = new DurationInt(n)
- implicit def longToDurationLong(n: Long) = new DurationLong(n)
- implicit def doubleToDurationDouble(d: Double) = new DurationDouble(d)
-
- implicit def pairIntToDuration(p: (Int, TimeUnit)) = Duration(p._1, p._2)
- implicit def pairLongToDuration(p: (Long, TimeUnit)) = Duration(p._1, p._2)
- implicit def durationToPair(d: Duration) = (d.length, d.unit)
-
- implicit def intMult(i: Int) = new IntMult(i)
- implicit def longMult(l: Long) = new LongMult(l)
- implicit def doubleMult(f: Double) = new DoubleMult(f)
-}
diff --git a/test/files/jvm/actmig-PinS.scala b/test/files/jvm/actmig-PinS.scala
index 39f8f04b3b..30307f3737 100644
--- a/test/files/jvm/actmig-PinS.scala
+++ b/test/files/jvm/actmig-PinS.scala
@@ -1,5 +1,5 @@
import scala.actors._
-import scala.concurrent.util.duration._
+import scala.concurrent.duration._
import scala.concurrent.{ Promise, Await }
import scala.actors.Actor._
diff --git a/test/files/jvm/actmig-PinS_1.scala b/test/files/jvm/actmig-PinS_1.scala
index 1fb50567b9..640684f728 100644
--- a/test/files/jvm/actmig-PinS_1.scala
+++ b/test/files/jvm/actmig-PinS_1.scala
@@ -1,5 +1,5 @@
import scala.actors._
-import scala.concurrent.util.duration._
+import scala.concurrent.duration._
import scala.concurrent.{ Promise, Await }
object SillyActor {
diff --git a/test/files/jvm/actmig-PinS_2.scala b/test/files/jvm/actmig-PinS_2.scala
index 46277efd43..761df6b5a7 100644
--- a/test/files/jvm/actmig-PinS_2.scala
+++ b/test/files/jvm/actmig-PinS_2.scala
@@ -1,5 +1,5 @@
import scala.actors.{ MigrationSystem, StashingActor, ActorRef, Props, Exit }
-import scala.concurrent.util.duration._
+import scala.concurrent.duration._
import scala.concurrent.{ Promise, Await }
object SillyActor {
diff --git a/test/files/jvm/actmig-PinS_3.scala b/test/files/jvm/actmig-PinS_3.scala
index 321e99b1c2..de9788724d 100644
--- a/test/files/jvm/actmig-PinS_3.scala
+++ b/test/files/jvm/actmig-PinS_3.scala
@@ -1,5 +1,5 @@
import scala.actors.{ MigrationSystem, StashingActor, ActorRef, Terminated, Props }
-import scala.concurrent.util.duration._
+import scala.concurrent.duration._
import scala.concurrent.{ Promise, Await }
diff --git a/test/files/jvm/actmig-public-methods_1.scala b/test/files/jvm/actmig-public-methods_1.scala
index 7e5bc24210..4bbdc9a71f 100644
--- a/test/files/jvm/actmig-public-methods_1.scala
+++ b/test/files/jvm/actmig-public-methods_1.scala
@@ -3,7 +3,7 @@ import scala.actors.Actor._
import scala.actors._
import scala.util._
import java.util.concurrent.{ TimeUnit, CountDownLatch }
-import scala.concurrent.util.Duration
+import scala.concurrent.duration.Duration
import scala.actors.pattern._
object Test {
diff --git a/test/files/jvm/actmig-react-receive.scala b/test/files/jvm/actmig-react-receive.scala
index 8464a2af79..280582511f 100644
--- a/test/files/jvm/actmig-react-receive.scala
+++ b/test/files/jvm/actmig-react-receive.scala
@@ -3,7 +3,7 @@ import scala.actors.Actor._
import scala.actors.{ Actor, StashingActor, ActorRef, Props, MigrationSystem, PoisonPill }
import java.util.concurrent.{ TimeUnit, CountDownLatch }
import scala.collection.mutable.ArrayBuffer
-import scala.concurrent.util.duration._
+import scala.concurrent.duration._
import scala.concurrent.{ Promise, Await }
object Test {
diff --git a/test/files/jvm/duration-java/Test.java b/test/files/jvm/duration-java/Test.java
index 1c53ccb266..94f3f83db8 100644
--- a/test/files/jvm/duration-java/Test.java
+++ b/test/files/jvm/duration-java/Test.java
@@ -1,4 +1,4 @@
-import scala.concurrent.util.Duration;
+import scala.concurrent.duration.Duration;
import java.util.*;
import java.util.concurrent.TimeUnit;
import static java.util.concurrent.TimeUnit.*;
diff --git a/test/files/jvm/duration-tck.scala b/test/files/jvm/duration-tck.scala
index 0947e84004..df1052fed3 100644
--- a/test/files/jvm/duration-tck.scala
+++ b/test/files/jvm/duration-tck.scala
@@ -1,11 +1,9 @@
/**
* Copyright (C) 2012 Typesafe Inc. <http://www.typesafe.com>
*/
-
-import scala.concurrent.util._
-import duration._
+
+import scala.concurrent.duration._
import scala.reflect._
-import java.util.concurrent.TimeUnit._
import scala.tools.partest.TestUtil.intercept
object Test extends App {
@@ -39,7 +37,7 @@ object Test extends App {
two / one mustBe 2
one + zero mustBe one
one / 1000000 mustBe 1.micro
-
+
// test infinities
@@ -91,7 +89,7 @@ object Test extends App {
minf.toUnit(MINUTES) mustBe Double.NegativeInfinity
Duration.fromNanos(Double.PositiveInfinity) mustBe inf
Duration.fromNanos(Double.NegativeInfinity) mustBe minf
-
+
// test undefined & NaN
@@ -122,7 +120,7 @@ object Test extends App {
undef.toUnit(DAYS) mustBe nan
Duration.fromNanos(nan) mustBe undef
-
+
// test overflow protection
for (unit ← Seq(DAYS, HOURS, MINUTES, SECONDS, MILLISECONDS, MICROSECONDS, NANOSECONDS)) {
@@ -153,21 +151,21 @@ object Test extends App {
}
intercept[IllegalArgumentException] { Duration.fromNanos(1e20) }
intercept[IllegalArgumentException] { Duration.fromNanos(-1e20) }
-
+
// test precision
1.second + 1.millisecond mustBe 1001.milliseconds
100000.days + 1.nanosecond mustBe 8640000000000000001L.nanoseconds
1.5.seconds.toSeconds mustBe 1
(-1.5).seconds.toSeconds mustBe -1
-
+
// test unit stability
1000.millis.unit mustBe MILLISECONDS
(1000.millis + 0.days).unit mustBe MILLISECONDS
1.second.unit mustBe SECONDS
(1.second + 1.millisecond).unit mustBe MILLISECONDS
-
+
// test Deadline
val dead = 2.seconds.fromNow
@@ -177,10 +175,20 @@ object Test extends App {
Thread.sleep(1.second.toMillis)
assert(dead.timeLeft < 1.second)
assert(dead2.timeLeft < 1.second)
-
+
+
+ // test integer mul/div
+ 500.millis * 2 mustBe 1.second
+ (500.millis * 2).unit mustBe MILLISECONDS
+ 1.second / 2 mustBe 500.millis
+ (1.second / 2).unit mustBe MILLISECONDS
+
// check statically retaining finite-ness
- val finiteDuration: FiniteDuration = 1.second plus 3.seconds minus 1.millisecond min 1.second max 1.second
-
+ val finiteDuration: FiniteDuration = 1.second * 2 / 3 mul 5 div 4 plus 3.seconds minus 1.millisecond min 1.second max 1.second
+ val finite2: FiniteDuration = 2 * 1.second + 3L * 2.seconds
+ finite2 mustBe 8.seconds
+ ((2 seconds fromNow).timeLeft: FiniteDuration) < 4.seconds mustBe true
+ val finite3: FiniteDuration = 3.5 seconds span
}
diff --git a/test/files/jvm/future-spec/FutureTests.scala b/test/files/jvm/future-spec/FutureTests.scala
index 31bb8c4e44..8674be168c 100644
--- a/test/files/jvm/future-spec/FutureTests.scala
+++ b/test/files/jvm/future-spec/FutureTests.scala
@@ -2,8 +2,8 @@
import scala.concurrent._
-import scala.concurrent.util.duration._
-import scala.concurrent.util.Duration.Inf
+import scala.concurrent.duration._
+import scala.concurrent.duration.Duration.Inf
import scala.collection._
import scala.runtime.NonLocalReturnControl
import scala.util.{Try,Success,Failure}
diff --git a/test/files/jvm/future-spec/PromiseTests.scala b/test/files/jvm/future-spec/PromiseTests.scala
index d9aaa1d5ed..8e07393900 100644
--- a/test/files/jvm/future-spec/PromiseTests.scala
+++ b/test/files/jvm/future-spec/PromiseTests.scala
@@ -2,8 +2,8 @@
import scala.concurrent._
-import scala.concurrent.util.duration._
-import scala.concurrent.util.Duration.Inf
+import scala.concurrent.duration._
+import scala.concurrent.duration.Duration.Inf
import scala.collection._
import scala.runtime.NonLocalReturnControl
import scala.util.{Try,Success,Failure}
diff --git a/test/files/jvm/future-spec/main.scala b/test/files/jvm/future-spec/main.scala
index 57183d8cea..90048ccda0 100644
--- a/test/files/jvm/future-spec/main.scala
+++ b/test/files/jvm/future-spec/main.scala
@@ -3,7 +3,7 @@
import scala.collection._
import scala.concurrent._
-import scala.concurrent.util.Duration
+import scala.concurrent.duration.Duration
import java.util.concurrent.{ TimeoutException, CountDownLatch, TimeUnit }
diff --git a/test/files/jvm/scala-concurrent-tck.scala b/test/files/jvm/scala-concurrent-tck.scala
index 0e76b711de..b529bca38a 100644
--- a/test/files/jvm/scala-concurrent-tck.scala
+++ b/test/files/jvm/scala-concurrent-tck.scala
@@ -10,7 +10,7 @@ import scala.concurrent.{
}
import scala.concurrent.{ future, promise, blocking }
import scala.util.{ Try, Success, Failure }
-import scala.concurrent.util.Duration
+import scala.concurrent.duration.Duration
import scala.reflect.{ classTag, ClassTag }
import scala.tools.partest.TestUtil.intercept
diff --git a/test/files/neg/deadline-inf-illegal.check b/test/files/neg/deadline-inf-illegal.check
index 2b9b25e48e..530d2b2443 100644
--- a/test/files/neg/deadline-inf-illegal.check
+++ b/test/files/neg/deadline-inf-illegal.check
@@ -1,15 +1,15 @@
-deadline-inf-illegal.scala:5: error: value fromNow is not a member of scala.concurrent.util.Duration
+deadline-inf-illegal.scala:5: error: value fromNow is not a member of scala.concurrent.duration.Duration
d.fromNow
^
deadline-inf-illegal.scala:6: error: type mismatch;
- found : scala.concurrent.util.Duration
- required: scala.concurrent.util.FiniteDuration
+ found : scala.concurrent.duration.Duration
+ required: scala.concurrent.duration.FiniteDuration
Deadline.now + d
^
deadline-inf-illegal.scala:7: error: overloaded method value - with alternatives:
- (other: scala.concurrent.util.Deadline)scala.concurrent.util.FiniteDuration <and>
- (other: scala.concurrent.util.FiniteDuration)scala.concurrent.util.Deadline
- cannot be applied to (scala.concurrent.util.Duration)
+ (other: scala.concurrent.duration.Deadline)scala.concurrent.duration.FiniteDuration <and>
+ (other: scala.concurrent.duration.FiniteDuration)scala.concurrent.duration.Deadline
+ cannot be applied to (scala.concurrent.duration.Duration)
Deadline.now - d
^
three errors found
diff --git a/test/files/neg/deadline-inf-illegal.scala b/test/files/neg/deadline-inf-illegal.scala
index 161089bfee..942cea7014 100644
--- a/test/files/neg/deadline-inf-illegal.scala
+++ b/test/files/neg/deadline-inf-illegal.scala
@@ -1,4 +1,4 @@
-import concurrent.util.{ Deadline, Duration }
+import concurrent.duration.{ Deadline, Duration }
class T {
val d: Duration = Duration.Zero
diff --git a/test/files/scalacheck/duration.scala b/test/files/scalacheck/duration.scala
new file mode 100644
index 0000000000..5e93638614
--- /dev/null
+++ b/test/files/scalacheck/duration.scala
@@ -0,0 +1,69 @@
+import org.scalacheck._
+import Prop._
+import Gen._
+import Arbitrary._
+import math._
+import concurrent.duration.Duration.fromNanos
+
+object Test extends Properties("Division of Duration by Long") {
+
+ val weightedLong =
+ frequency(
+ 1 -> choose(-128L, 127L),
+ 1 -> (arbitrary[Byte] map (_.toLong << 8)),
+ 1 -> (arbitrary[Byte] map (_.toLong << 16)),
+ 1 -> (arbitrary[Byte] map (_.toLong << 24)),
+ 1 -> (arbitrary[Byte] map (_.toLong << 32)),
+ 1 -> (arbitrary[Byte] map (_.toLong << 40)),
+ 1 -> (arbitrary[Byte] map (_.toLong << 48)),
+ 1 -> (choose(-127L, 127L) map (_ << 56))
+ )
+
+ val genTwoSmall = for {
+ a <- weightedLong
+ b <- choose(-(Long.MaxValue / max(1, abs(a))), Long.MaxValue / max(1, abs(a)))
+ } yield (a, b)
+
+ val genTwoLarge = for {
+ a <- weightedLong
+ b <- arbitrary[Long] suchThat (b => (abs(b) > Long.MaxValue / max(1, abs(a))))
+ } yield (a, b)
+
+ val genClose = for {
+ a <- weightedLong
+ if a != 0
+ b <- choose(Long.MaxValue / a - 10, Long.MaxValue / a + 10)
+ } yield (a, b)
+
+ val genBorderline =
+ frequency(
+ 1 -> (Long.MinValue, 0L),
+ 1 -> (Long.MinValue, 1L),
+ 1 -> (Long.MinValue, -1L),
+ 1 -> (0L, Long.MinValue),
+ 1 -> (1L, Long.MinValue),
+ 1 -> (-1L, Long.MinValue),
+ 90 -> genClose
+ )
+
+ def mul(a: Long, b: Long): Long = {
+ (fromNanos(a) * b).toNanos
+ }
+
+ property("without overflow") = forAll(genTwoSmall) { case (a, b) =>
+ a * b == mul(a, b)
+ }
+
+ property("with overflow") = forAll(genTwoLarge) { case (a, b) =>
+ try { mul(a, b); false } catch { case _: IllegalArgumentException => true }
+ }
+
+ property("on overflow edge cases") = forAll(genBorderline) { case (a, b) =>
+ val shouldFit =
+ a != Long.MinValue && // must fail due to illegal duration length
+ (b != Long.MinValue || a == 0) && // Long factor may only be MinValue if the duration is zero, otherwise the result will be illegal
+ (abs(b) <= Long.MaxValue / max(1, abs(a))) // check the rest against the “safe” division method
+ try { mul(a, b); shouldFit }
+ catch { case _: IllegalArgumentException => !shouldFit }
+ }
+}