Skip to content

Commit b591910

Browse files
committed
removes redundant hash implementation from BoxesRunTime.java
1 parent 58bb2d1 commit b591910

File tree

4 files changed

+41
-68
lines changed

4 files changed

+41
-68
lines changed

src/library/scala/runtime/BoxesRunTime.java

Lines changed: 0 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -203,67 +203,6 @@ private static boolean equalsNumChar(java.lang.Number xn, java.lang.Character yc
203203
}
204204
}
205205

206-
/** Hashcode algorithm is driven by the requirements imposed
207-
* by primitive equality semantics, namely that equal objects
208-
* have equal hashCodes. The first priority are the integral/char
209-
* types, which already have the same hashCodes for the same
210-
* values except for Long. So Long's hashCode is altered to
211-
* conform to Int's for all values in Int's range.
212-
*
213-
* Float is problematic because it's far too small to hold
214-
* all the Ints, so for instance Int.MaxValue.toFloat claims
215-
* to be == to each of the largest 64 Ints. There is no way
216-
* to preserve equals/hashCode alignment without compromising
217-
* the hashCode distribution, so Floats are only guaranteed
218-
* to have the same hashCode for whole Floats in the range
219-
* Short.MinValue to Short.MaxValue (2^16 total.)
220-
*
221-
* Double has its hashCode altered to match the entire Int range,
222-
* but is not guaranteed beyond that. (But could/should it be?
223-
* The hashCode is only 32 bits so this is a more tractable
224-
* issue than Float's, but it might be better simply to exclude it.)
225-
*
226-
* Note: BigInt and BigDecimal, being arbitrary precision, could
227-
* be made consistent with all other types for the Int range, but
228-
* as yet have not.
229-
*
230-
* Note: Among primitives, Float.NaN != Float.NaN, but the boxed
231-
* verisons are equal. This still needs reconciliation.
232-
*/
233-
public static int hashFromLong(java.lang.Long n) {
234-
int iv = n.intValue();
235-
if (iv == n.longValue()) return iv;
236-
else return n.hashCode();
237-
}
238-
public static int hashFromDouble(java.lang.Double n) {
239-
int iv = n.intValue();
240-
double dv = n.doubleValue();
241-
if (iv == dv) return iv;
242-
243-
long lv = n.longValue();
244-
if (lv == dv) return java.lang.Long.valueOf(lv).hashCode();
245-
else return n.hashCode();
246-
}
247-
public static int hashFromFloat(java.lang.Float n) {
248-
int iv = n.intValue();
249-
float fv = n.floatValue();
250-
if (iv == fv) return iv;
251-
252-
long lv = n.longValue();
253-
if (lv == fv) return java.lang.Long.valueOf(lv).hashCode();
254-
else return n.hashCode();
255-
}
256-
public static int hashFromNumber(java.lang.Number n) {
257-
if (n instanceof java.lang.Long) return hashFromLong((java.lang.Long)n);
258-
else if (n instanceof java.lang.Double) return hashFromDouble((java.lang.Double)n);
259-
else if (n instanceof java.lang.Float) return hashFromFloat((java.lang.Float)n);
260-
else return n.hashCode();
261-
}
262-
public static int hashFromObject(Object a) {
263-
if (a instanceof Number) return hashFromNumber((Number)a);
264-
else return a.hashCode();
265-
}
266-
267206
private static int unboxCharOrInt(Object arg1, int code) {
268207
if (code == CHAR)
269208
return ((java.lang.Character) arg1).charValue();

src/library/scala/runtime/ScalaRunTime.scala

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -233,12 +233,39 @@ object ScalaRunTime {
233233
//
234234
// Note that these are the implementations called by ##, so they
235235
// must not call ## themselves.
236+
//
237+
// Hashcode algorithm is driven by the requirements imposed
238+
// by primitive equality semantics, namely that equal objects
239+
// have equal hashCodes. The first priority are the integral/char
240+
// types, which already have the same hashCodes for the same
241+
// values except for Long. So Long's hashCode is altered to
242+
// conform to Int's for all values in Int's range.
243+
//
244+
// Float is problematic because it's far too small to hold
245+
// all the Ints, so for instance Int.MaxValue.toFloat claims
246+
// to be == to each of the largest 64 Ints. There is no way
247+
// to preserve equals/hashCode alignment without compromising
248+
// the hashCode distribution, so Floats are only guaranteed
249+
// to have the same hashCode for whole Floats in the range
250+
// Short.MinValue to Short.MaxValue (2^16 total.)
251+
//
252+
// Double has its hashCode altered to match the entire Int range,
253+
// but is not guaranteed beyond that. (But could/should it be?
254+
// The hashCode is only 32 bits so this is a more tractable
255+
// issue than Float's, but it might be better simply to exclude it.)
256+
//
257+
// Note: BigInt and BigDecimal, being arbitrary precision, could
258+
// be made consistent with all other types for the Int range, but
259+
// as yet have not.
260+
//
261+
// Note: Among primitives, Float.NaN != Float.NaN, but the boxed
262+
// verisons are equal. This still needs reconciliation.
236263

237264
@inline def hash(x: Any): Int = x match {
238265
case null => 0
266+
case x: Long => hash(x)
239267
case x: Double => hash(x)
240268
case x: Float => hash(x)
241-
case x: java.lang.Number => hash(x)
242269
case _ => x.hashCode
243270
}
244271

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

271297
// The remaining overloads are here for completeness, but the compiler
272298
// inlines these definitions directly so they're not generally used.

test/files/run/hashCodeBoxesRunTime.scala renamed to test/files/run/hashCodeScalaRunTime.scala

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
1-
// This only tests direct access to the methods in BoxesRunTime,
1+
// This only tests direct access to the methods in ScalaRunTime,
22
// not the whole scheme.
33
object Test
44
{
55
import java.{ lang => jl }
6-
import scala.runtime.BoxesRunTime.{ hashFromNumber, hashFromObject }
6+
import scala.runtime.ScalaRunTime.{ hash }
77

88
def allSame[T](xs: List[T]) = assert(xs.distinct.size == 1, "failed: " + xs)
99

1010
def mkNumbers(x: Int): List[Number] =
1111
List(x.toByte, x.toShort, x, x.toLong, x.toFloat, x.toDouble)
1212

13-
def testLDF(x: Long) = allSame(List[Number](x, x.toDouble, x.toFloat) map hashFromNumber)
13+
def testLDF(x: Long) = allSame(List[Number](x, x.toDouble, x.toFloat) map hash)
1414

1515
def main(args: Array[String]): Unit = {
1616
List(Byte.MinValue, -1, 0, 1, Byte.MaxValue) foreach { n =>
17-
val hashes = mkNumbers(n) map hashFromNumber
17+
val hashes = mkNumbers(n) map hash
1818
allSame(hashes)
1919
if (n >= 0) {
20-
val charCode = hashFromObject(n.toChar: Character)
20+
val charCode = hash(n.toChar: Character)
2121
assert(charCode == hashes.head)
2222
}
2323
}

test/files/run/hashhash.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,15 @@ object Test {
99

1010
val x = (BigInt(1) << 64).toDouble
1111
val y: Any = x
12+
val f: Float = x.toFloat
13+
val jn: java.lang.Number = x
14+
val jf: java.lang.Float = x.toFloat
15+
val jd: java.lang.Double = x
1216

1317
assert(x.## == y.##, ((x, y)))
18+
assert(x.## == f.##, ((x, f)))
19+
assert(x.## == jn.##, ((x, jn)))
20+
assert(x.## == jf.##, ((x, jf)))
21+
assert(x.## == jd.##, ((x, jd)))
1422
}
1523
}

0 commit comments

Comments
 (0)