Skip to content

Commit

Permalink
Revert recent commits.
Browse files Browse the repository at this point in the history
This reverts commit 9b6f51d.
This reverts commit b591910.
  • Loading branch information
paulp committed May 9, 2012
1 parent d459104 commit 1cd498f
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 41 deletions.
61 changes: 61 additions & 0 deletions src/library/scala/runtime/BoxesRunTime.java
Expand Up @@ -203,6 +203,67 @@ private static boolean equalsNumChar(java.lang.Number xn, java.lang.Character yc
} }
} }


/** 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) { private static int unboxCharOrInt(Object arg1, int code) {
if (code == CHAR) if (code == CHAR)
return ((java.lang.Character) arg1).charValue(); return ((java.lang.Character) arg1).charValue();
Expand Down
30 changes: 2 additions & 28 deletions src/library/scala/runtime/ScalaRunTime.scala
Expand Up @@ -233,39 +233,12 @@ object ScalaRunTime {
// //
// Note that these are the implementations called by ##, so they // Note that these are the implementations called by ##, so they
// must not call ## themselves. // 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 { @inline def hash(x: Any): Int = x match {
case null => 0 case null => 0
case x: Long => hash(x)
case x: Double => hash(x) case x: Double => hash(x)
case x: Float => hash(x) case x: Float => hash(x)
case x: java.lang.Number => hash(x)
case _ => x.hashCode case _ => x.hashCode
} }


Expand Down Expand Up @@ -293,6 +266,7 @@ object ScalaRunTime {
val high = (lv >>> 32).toInt val high = (lv >>> 32).toInt
low ^ (high + lowSign) low ^ (high + lowSign)
} }
@inline def hash(x: Number): Int = runtime.BoxesRunTime.hashFromNumber(x)


// The remaining overloads are here for completeness, but the compiler // The remaining overloads are here for completeness, but the compiler
// inlines these definitions directly so they're not generally used. // inlines these definitions directly so they're not generally used.
Expand Down
@@ -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. // not the whole scheme.
object Test object Test
{ {
import java.{ lang => jl } 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 allSame[T](xs: List[T]) = assert(xs.distinct.size == 1, "failed: " + xs)


def mkNumbers(x: Int): List[Number] = def mkNumbers(x: Int): List[Number] =
List(x.toByte, x.toShort, x, x.toLong, x.toFloat, x.toDouble) 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 = { def main(args: Array[String]): Unit = {
List(Byte.MinValue, -1, 0, 1, Byte.MaxValue) foreach { n => List(Byte.MinValue, -1, 0, 1, Byte.MaxValue) foreach { n =>
val hashes = mkNumbers(n) map hash val hashes = mkNumbers(n) map hashFromNumber
allSame(hashes) allSame(hashes)
if (n >= 0) { if (n >= 0) {
val charCode = hash(n.toChar: Character) val charCode = hashFromObject(n.toChar: Character)
assert(charCode == hashes.head) assert(charCode == hashes.head)
} }
} }
Expand Down
8 changes: 0 additions & 8 deletions test/files/run/hashhash.scala
Expand Up @@ -9,15 +9,7 @@ object Test {


val x = (BigInt(1) << 64).toDouble val x = (BigInt(1) << 64).toDouble
val y: Any = x 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.## == y.##, ((x, y)))
assert(x.## == f.##, ((x, f)))
assert(x.## == jn.##, ((x, jn)))
assert(x.## == jf.##, ((x, jf)))
assert(x.## == jd.##, ((x, jd)))
} }
} }

0 comments on commit 1cd498f

Please sign in to comment.