summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2012-05-09 15:52:44 -0700
committerPaul Phillips <paulp@improving.org>2012-05-09 15:53:08 -0700
commit1cd498f9091504b42030d4b81c6f659bc386115f (patch)
treedd9757785890a2098620893f1884ed85c63cd801
parentd459104447df2132fc510c7d3cb096ec3092b070 (diff)
downloadscala-1cd498f9091504b42030d4b81c6f659bc386115f.tar.gz
scala-1cd498f9091504b42030d4b81c6f659bc386115f.tar.bz2
scala-1cd498f9091504b42030d4b81c6f659bc386115f.zip
Revert recent commits.
This reverts commit 9b6f51d3ae6ddc6571d3101ea715e25a05aa8adb. This reverts commit b5919100e785df58bde35bb24abe9d60b4da08a2.
-rw-r--r--src/library/scala/runtime/BoxesRunTime.java61
-rw-r--r--src/library/scala/runtime/ScalaRunTime.scala30
-rw-r--r--test/files/run/hashCodeBoxesRunTime.scala (renamed from test/files/run/hashCodeScalaRunTime.scala)10
-rw-r--r--test/files/run/hashhash.scala8
4 files changed, 68 insertions, 41 deletions
diff --git a/src/library/scala/runtime/BoxesRunTime.java b/src/library/scala/runtime/BoxesRunTime.java
index 0df196b2a6..258a176671 100644
--- a/src/library/scala/runtime/BoxesRunTime.java
+++ b/src/library/scala/runtime/BoxesRunTime.java
@@ -203,6 +203,67 @@ public final class BoxesRunTime
}
}
+ /** Hashcode algorithm is driven by the requirements imposed
+ * by primitive equality semantics, namely that equal objects
+ * have equal hashCodes. The first priority are the integral/char
+ * types, which already have the same hashCodes for the same
+ * values except for Long. So Long's hashCode is altered to
+ * conform to Int's for all values in Int's range.
+ *
+ * Float is problematic because it's far too small to hold
+ * all the Ints, so for instance Int.MaxValue.toFloat claims
+ * to be == to each of the largest 64 Ints. There is no way
+ * to preserve equals/hashCode alignment without compromising
+ * the hashCode distribution, so Floats are only guaranteed
+ * to have the same hashCode for whole Floats in the range
+ * Short.MinValue to Short.MaxValue (2^16 total.)
+ *
+ * Double has its hashCode altered to match the entire Int range,
+ * but is not guaranteed beyond that. (But could/should it be?
+ * The hashCode is only 32 bits so this is a more tractable
+ * issue than Float's, but it might be better simply to exclude it.)
+ *
+ * Note: BigInt and BigDecimal, being arbitrary precision, could
+ * be made consistent with all other types for the Int range, but
+ * as yet have not.
+ *
+ * Note: Among primitives, Float.NaN != Float.NaN, but the boxed
+ * verisons are equal. This still needs reconciliation.
+ */
+ public static int hashFromLong(java.lang.Long n) {
+ int iv = n.intValue();
+ if (iv == n.longValue()) return iv;
+ else return n.hashCode();
+ }
+ public static int hashFromDouble(java.lang.Double n) {
+ int iv = n.intValue();
+ double dv = n.doubleValue();
+ if (iv == dv) return iv;
+
+ long lv = n.longValue();
+ if (lv == dv) return java.lang.Long.valueOf(lv).hashCode();
+ else return n.hashCode();
+ }
+ public static int hashFromFloat(java.lang.Float n) {
+ int iv = n.intValue();
+ float fv = n.floatValue();
+ if (iv == fv) return iv;
+
+ long lv = n.longValue();
+ if (lv == fv) return java.lang.Long.valueOf(lv).hashCode();
+ else return n.hashCode();
+ }
+ public static int hashFromNumber(java.lang.Number n) {
+ if (n instanceof java.lang.Long) return hashFromLong((java.lang.Long)n);
+ else if (n instanceof java.lang.Double) return hashFromDouble((java.lang.Double)n);
+ else if (n instanceof java.lang.Float) return hashFromFloat((java.lang.Float)n);
+ else return n.hashCode();
+ }
+ public static int hashFromObject(Object a) {
+ if (a instanceof Number) return hashFromNumber((Number)a);
+ else return a.hashCode();
+ }
+
private static int unboxCharOrInt(Object arg1, int code) {
if (code == CHAR)
return ((java.lang.Character) arg1).charValue();
diff --git a/src/library/scala/runtime/ScalaRunTime.scala b/src/library/scala/runtime/ScalaRunTime.scala
index 6bf25b8464..a04fd23710 100644
--- a/src/library/scala/runtime/ScalaRunTime.scala
+++ b/src/library/scala/runtime/ScalaRunTime.scala
@@ -233,39 +233,12 @@ object ScalaRunTime {
//
// Note that these are the implementations called by ##, so they
// must not call ## themselves.
- //
- // Hashcode algorithm is driven by the requirements imposed
- // by primitive equality semantics, namely that equal objects
- // have equal hashCodes. The first priority are the integral/char
- // types, which already have the same hashCodes for the same
- // values except for Long. So Long's hashCode is altered to
- // conform to Int's for all values in Int's range.
- //
- // Float is problematic because it's far too small to hold
- // all the Ints, so for instance Int.MaxValue.toFloat claims
- // to be == to each of the largest 64 Ints. There is no way
- // to preserve equals/hashCode alignment without compromising
- // the hashCode distribution, so Floats are only guaranteed
- // to have the same hashCode for whole Floats in the range
- // Short.MinValue to Short.MaxValue (2^16 total.)
- //
- // Double has its hashCode altered to match the entire Int range,
- // but is not guaranteed beyond that. (But could/should it be?
- // The hashCode is only 32 bits so this is a more tractable
- // issue than Float's, but it might be better simply to exclude it.)
- //
- // Note: BigInt and BigDecimal, being arbitrary precision, could
- // be made consistent with all other types for the Int range, but
- // as yet have not.
- //
- // Note: Among primitives, Float.NaN != Float.NaN, but the boxed
- // versions are equal. This still needs reconciliation.
@inline def hash(x: Any): Int = x match {
case null => 0
- case x: Long => hash(x)
case x: Double => hash(x)
case x: Float => hash(x)
+ case x: java.lang.Number => hash(x)
case _ => x.hashCode
}
@@ -293,6 +266,7 @@ object ScalaRunTime {
val high = (lv >>> 32).toInt
low ^ (high + lowSign)
}
+ @inline def hash(x: Number): Int = runtime.BoxesRunTime.hashFromNumber(x)
// The remaining overloads are here for completeness, but the compiler
// inlines these definitions directly so they're not generally used.
diff --git a/test/files/run/hashCodeScalaRunTime.scala b/test/files/run/hashCodeBoxesRunTime.scala
index e352af95f1..081a73376e 100644
--- a/test/files/run/hashCodeScalaRunTime.scala
+++ b/test/files/run/hashCodeBoxesRunTime.scala
@@ -1,23 +1,23 @@
-// This only tests direct access to the methods in ScalaRunTime,
+// This only tests direct access to the methods in BoxesRunTime,
// not the whole scheme.
object Test
{
import java.{ lang => jl }
- import scala.runtime.ScalaRunTime.{ hash }
+ import scala.runtime.BoxesRunTime.{ hashFromNumber, hashFromObject }
def allSame[T](xs: List[T]) = assert(xs.distinct.size == 1, "failed: " + xs)
def mkNumbers(x: Int): List[Number] =
List(x.toByte, x.toShort, x, x.toLong, x.toFloat, x.toDouble)
- def testLDF(x: Long) = allSame(List[Number](x, x.toDouble, x.toFloat) map hash)
+ def testLDF(x: Long) = allSame(List[Number](x, x.toDouble, x.toFloat) map hashFromNumber)
def main(args: Array[String]): Unit = {
List(Byte.MinValue, -1, 0, 1, Byte.MaxValue) foreach { n =>
- val hashes = mkNumbers(n) map hash
+ val hashes = mkNumbers(n) map hashFromNumber
allSame(hashes)
if (n >= 0) {
- val charCode = hash(n.toChar: Character)
+ val charCode = hashFromObject(n.toChar: Character)
assert(charCode == hashes.head)
}
}
diff --git a/test/files/run/hashhash.scala b/test/files/run/hashhash.scala
index f9fc067398..dc31df8cfa 100644
--- a/test/files/run/hashhash.scala
+++ b/test/files/run/hashhash.scala
@@ -9,15 +9,7 @@ object Test {
val x = (BigInt(1) << 64).toDouble
val y: Any = x
- val f: Float = x.toFloat
- val jn: java.lang.Number = x
- val jf: java.lang.Float = x.toFloat
- val jd: java.lang.Double = x
assert(x.## == y.##, ((x, y)))
- assert(x.## == f.##, ((x, f)))
- assert(x.## == jn.##, ((x, jn)))
- assert(x.## == jf.##, ((x, jf)))
- assert(x.## == jd.##, ((x, jd)))
}
}