8010325: Remove hash32() method and hash32 int field from java.lang.String
Reviewed-by: alanb, mduigou
This commit is contained in:
parent
d233c11556
commit
e19945c794
@ -3156,101 +3156,4 @@ public final class String
|
||||
* guaranteed to be from a pool of unique strings.
|
||||
*/
|
||||
public native String intern();
|
||||
|
||||
/**
|
||||
* Seed value used for each alternative hash calculated.
|
||||
*/
|
||||
private static final int HASHING_SEED;
|
||||
|
||||
static {
|
||||
long nanos = System.nanoTime();
|
||||
long now = System.currentTimeMillis();
|
||||
int SEED_MATERIAL[] = {
|
||||
System.identityHashCode(String.class),
|
||||
System.identityHashCode(System.class),
|
||||
(int) (nanos >>> 32),
|
||||
(int) nanos,
|
||||
(int) (now >>> 32),
|
||||
(int) now,
|
||||
(int) (System.nanoTime() >>> 2)
|
||||
};
|
||||
|
||||
// Use murmur3 to scramble the seeding material.
|
||||
// Inline implementation to avoid loading classes
|
||||
int h1 = 0;
|
||||
|
||||
// body
|
||||
for(int k1 : SEED_MATERIAL) {
|
||||
k1 *= 0xcc9e2d51;
|
||||
k1 = (k1 << 15) | (k1 >>> 17);
|
||||
k1 *= 0x1b873593;
|
||||
|
||||
h1 ^= k1;
|
||||
h1 = (h1 << 13) | (h1 >>> 19);
|
||||
h1 = h1 * 5 + 0xe6546b64;
|
||||
}
|
||||
|
||||
// tail (always empty, as body is always 32-bit chunks)
|
||||
|
||||
// finalization
|
||||
|
||||
h1 ^= SEED_MATERIAL.length * 4;
|
||||
|
||||
// finalization mix force all bits of a hash block to avalanche
|
||||
h1 ^= h1 >>> 16;
|
||||
h1 *= 0x85ebca6b;
|
||||
h1 ^= h1 >>> 13;
|
||||
h1 *= 0xc2b2ae35;
|
||||
h1 ^= h1 >>> 16;
|
||||
|
||||
HASHING_SEED = h1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cached value of the hashing algorithm result
|
||||
*/
|
||||
private transient int hash32 = 0;
|
||||
|
||||
/**
|
||||
* Return a 32-bit hash code value for this object.
|
||||
* <p>
|
||||
* The general contract of {@code hash32} is:
|
||||
* <ul>
|
||||
* <li>Whenever it is invoked on the same object more than once during
|
||||
* an execution of a Java application, the {@code hash32} method
|
||||
* must consistently return the same integer, provided no information
|
||||
* used in {@code equals} comparisons on the object is modified.
|
||||
* This integer need not remain consistent from one execution of an
|
||||
* application to another execution of the same application.
|
||||
* <li>If two objects are equal according to the {@code equals(Object)}
|
||||
* method, then calling the {@code hash32} method on each of
|
||||
* the two objects must produce the same integer result.
|
||||
* <li>It is <em>not</em> required that if two objects are unequal
|
||||
* according to the {@link java.lang.Object#equals(java.lang.Object)}
|
||||
* method, then calling the {@code hash32} method on each of the
|
||||
* two objects must produce distinct integer results. However, the
|
||||
* programmer should be aware that producing distinct integer results
|
||||
* for unequal objects may improve the performance of hash tables.
|
||||
* </ul>
|
||||
*
|
||||
* The hash value will never be zero.
|
||||
*
|
||||
* @return a hash code value for this object.
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
public int hash32() {
|
||||
int h = hash32;
|
||||
if (0 == h) {
|
||||
// harmless data race on hash32 here.
|
||||
h = sun.misc.Hashing.murmur3_32(HASHING_SEED, value, 0, value.length);
|
||||
|
||||
// ensure result is not zero to avoid recalcing
|
||||
h = (0 != h) ? h : 1;
|
||||
|
||||
hash32 = h;
|
||||
}
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ package java.util;
|
||||
import java.io.*;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
@ -912,7 +913,8 @@ public class HashMap<K,V>
|
||||
*/
|
||||
final int initHashSeed() {
|
||||
if (sun.misc.VM.isBooted() && Holder.USE_HASHSEED) {
|
||||
return sun.misc.Hashing.randomHashSeed(this);
|
||||
int seed = ThreadLocalRandom.current().nextInt();
|
||||
return (seed != 0) ? seed : 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -2572,8 +2574,9 @@ public class HashMap<K,V>
|
||||
|
||||
// set other fields that need values
|
||||
if (Holder.USE_HASHSEED) {
|
||||
int seed = ThreadLocalRandom.current().nextInt();
|
||||
Holder.UNSAFE.putIntVolatile(this, Holder.HASHSEED_OFFSET,
|
||||
sun.misc.Hashing.randomHashSeed(this));
|
||||
(seed != 0) ? seed : 1);
|
||||
}
|
||||
table = EMPTY_TABLE;
|
||||
|
||||
|
@ -26,6 +26,7 @@
|
||||
package java.util;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.BiFunction;
|
||||
@ -219,7 +220,8 @@ public class Hashtable<K,V>
|
||||
*/
|
||||
final int initHashSeed() {
|
||||
if (sun.misc.VM.isBooted() && Holder.USE_HASHSEED) {
|
||||
return sun.misc.Hashing.randomHashSeed(this);
|
||||
int seed = ThreadLocalRandom.current().nextInt();
|
||||
return (seed != 0) ? seed : 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -1206,8 +1208,9 @@ public class Hashtable<K,V>
|
||||
|
||||
// set hashMask
|
||||
if (Holder.USE_HASHSEED) {
|
||||
int seed = ThreadLocalRandom.current().nextInt();
|
||||
Holder.UNSAFE.putIntVolatile(this, Holder.HASHSEED_OFFSET,
|
||||
sun.misc.Hashing.randomHashSeed(this));
|
||||
(seed != 0) ? seed : 1);
|
||||
}
|
||||
|
||||
// Read the original length of the array and number of elements
|
||||
|
@ -27,6 +27,7 @@ package java.util;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
|
||||
@ -215,7 +216,8 @@ public class WeakHashMap<K,V>
|
||||
if (sun.misc.VM.isBooted() && Holder.USE_HASHSEED) {
|
||||
// Do not set hashSeed more than once!
|
||||
// assert hashSeed == 0;
|
||||
hashSeed = sun.misc.Hashing.randomHashSeed(this);
|
||||
int seed = ThreadLocalRandom.current().nextInt();
|
||||
hashSeed = (seed != 0) ? seed : 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,239 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package sun.misc;
|
||||
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
/**
|
||||
* Hashing utilities.
|
||||
*
|
||||
* Little endian implementations of Murmur3 hashing.
|
||||
*/
|
||||
public class Hashing {
|
||||
|
||||
/**
|
||||
* Static utility methods only.
|
||||
*/
|
||||
private Hashing() {
|
||||
throw new Error("No instances");
|
||||
}
|
||||
|
||||
public static int murmur3_32(byte[] data) {
|
||||
return murmur3_32(0, data, 0, data.length);
|
||||
}
|
||||
|
||||
public static int murmur3_32(int seed, byte[] data) {
|
||||
return murmur3_32(seed, data, 0, data.length);
|
||||
}
|
||||
|
||||
@SuppressWarnings("fallthrough")
|
||||
public static int murmur3_32(int seed, byte[] data, int offset, int len) {
|
||||
int h1 = seed;
|
||||
int count = len;
|
||||
|
||||
// body
|
||||
while (count >= 4) {
|
||||
int k1 = (data[offset] & 0x0FF)
|
||||
| (data[offset + 1] & 0x0FF) << 8
|
||||
| (data[offset + 2] & 0x0FF) << 16
|
||||
| data[offset + 3] << 24;
|
||||
|
||||
count -= 4;
|
||||
offset += 4;
|
||||
|
||||
k1 *= 0xcc9e2d51;
|
||||
k1 = Integer.rotateLeft(k1, 15);
|
||||
k1 *= 0x1b873593;
|
||||
|
||||
h1 ^= k1;
|
||||
h1 = Integer.rotateLeft(h1, 13);
|
||||
h1 = h1 * 5 + 0xe6546b64;
|
||||
}
|
||||
|
||||
// tail
|
||||
|
||||
if (count > 0) {
|
||||
int k1 = 0;
|
||||
|
||||
switch (count) {
|
||||
case 3:
|
||||
k1 ^= (data[offset + 2] & 0xff) << 16;
|
||||
// fall through
|
||||
case 2:
|
||||
k1 ^= (data[offset + 1] & 0xff) << 8;
|
||||
// fall through
|
||||
case 1:
|
||||
k1 ^= (data[offset] & 0xff);
|
||||
// fall through
|
||||
default:
|
||||
k1 *= 0xcc9e2d51;
|
||||
k1 = Integer.rotateLeft(k1, 15);
|
||||
k1 *= 0x1b873593;
|
||||
h1 ^= k1;
|
||||
}
|
||||
}
|
||||
|
||||
// finalization
|
||||
|
||||
h1 ^= len;
|
||||
|
||||
// finalization mix force all bits of a hash block to avalanche
|
||||
h1 ^= h1 >>> 16;
|
||||
h1 *= 0x85ebca6b;
|
||||
h1 ^= h1 >>> 13;
|
||||
h1 *= 0xc2b2ae35;
|
||||
h1 ^= h1 >>> 16;
|
||||
|
||||
return h1;
|
||||
}
|
||||
|
||||
public static int murmur3_32(char[] data) {
|
||||
return murmur3_32(0, data, 0, data.length);
|
||||
}
|
||||
|
||||
public static int murmur3_32(int seed, char[] data) {
|
||||
return murmur3_32(seed, data, 0, data.length);
|
||||
}
|
||||
|
||||
public static int murmur3_32(int seed, char[] data, int offset, int len) {
|
||||
int h1 = seed;
|
||||
|
||||
int off = offset;
|
||||
int count = len;
|
||||
|
||||
// body
|
||||
while (count >= 2) {
|
||||
int k1 = (data[off++] & 0xFFFF) | (data[off++] << 16);
|
||||
|
||||
count -= 2;
|
||||
|
||||
k1 *= 0xcc9e2d51;
|
||||
k1 = Integer.rotateLeft(k1, 15);
|
||||
k1 *= 0x1b873593;
|
||||
|
||||
h1 ^= k1;
|
||||
h1 = Integer.rotateLeft(h1, 13);
|
||||
h1 = h1 * 5 + 0xe6546b64;
|
||||
}
|
||||
|
||||
// tail
|
||||
|
||||
if (count > 0) {
|
||||
int k1 = data[off];
|
||||
|
||||
k1 *= 0xcc9e2d51;
|
||||
k1 = Integer.rotateLeft(k1, 15);
|
||||
k1 *= 0x1b873593;
|
||||
h1 ^= k1;
|
||||
}
|
||||
|
||||
// finalization
|
||||
|
||||
h1 ^= len * (Character.SIZE / Byte.SIZE);
|
||||
|
||||
// finalization mix force all bits of a hash block to avalanche
|
||||
h1 ^= h1 >>> 16;
|
||||
h1 *= 0x85ebca6b;
|
||||
h1 ^= h1 >>> 13;
|
||||
h1 *= 0xc2b2ae35;
|
||||
h1 ^= h1 >>> 16;
|
||||
|
||||
return h1;
|
||||
}
|
||||
|
||||
public static int murmur3_32(int[] data) {
|
||||
return murmur3_32(0, data, 0, data.length);
|
||||
}
|
||||
|
||||
public static int murmur3_32(int seed, int[] data) {
|
||||
return murmur3_32(seed, data, 0, data.length);
|
||||
}
|
||||
|
||||
public static int murmur3_32(int seed, int[] data, int offset, int len) {
|
||||
int h1 = seed;
|
||||
|
||||
int off = offset;
|
||||
int end = offset + len;
|
||||
|
||||
// body
|
||||
while (off < end) {
|
||||
int k1 = data[off++];
|
||||
|
||||
k1 *= 0xcc9e2d51;
|
||||
k1 = Integer.rotateLeft(k1, 15);
|
||||
k1 *= 0x1b873593;
|
||||
|
||||
h1 ^= k1;
|
||||
h1 = Integer.rotateLeft(h1, 13);
|
||||
h1 = h1 * 5 + 0xe6546b64;
|
||||
}
|
||||
|
||||
// tail (always empty, as body is always 32-bit chunks)
|
||||
|
||||
// finalization
|
||||
|
||||
h1 ^= len * (Integer.SIZE / Byte.SIZE);
|
||||
|
||||
// finalization mix force all bits of a hash block to avalanche
|
||||
h1 ^= h1 >>> 16;
|
||||
h1 *= 0x85ebca6b;
|
||||
h1 ^= h1 >>> 13;
|
||||
h1 *= 0xc2b2ae35;
|
||||
h1 ^= h1 >>> 16;
|
||||
|
||||
return h1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a non-zero 32-bit pseudo random value. The {@code instance} object
|
||||
* may be used as part of the value.
|
||||
*
|
||||
* @param instance an object to use if desired in choosing value.
|
||||
* @return a non-zero 32-bit pseudo random value.
|
||||
*/
|
||||
public static int randomHashSeed(Object instance) {
|
||||
int seed;
|
||||
if (sun.misc.VM.isBooted()) {
|
||||
seed = ThreadLocalRandom.current().nextInt();
|
||||
} else {
|
||||
// lower quality "random" seed value--still better than zero and not
|
||||
// not practically reversible.
|
||||
int hashing_seed[] = {
|
||||
System.identityHashCode(Hashing.class),
|
||||
System.identityHashCode(instance),
|
||||
System.identityHashCode(Thread.currentThread()),
|
||||
(int) Thread.currentThread().getId(),
|
||||
(int) (System.currentTimeMillis() >>> 2), // resolution is poor
|
||||
(int) (System.nanoTime() >>> 5), // resolution is poor
|
||||
(int) (Runtime.getRuntime().freeMemory() >>> 4) // alloc min
|
||||
};
|
||||
|
||||
seed = murmur3_32(hashing_seed);
|
||||
}
|
||||
|
||||
// force to non-zero.
|
||||
return (0 != seed) ? seed : 1;
|
||||
}
|
||||
}
|
@ -1,127 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test @summary Ensure that Murmur3 hash performs according to specification.
|
||||
* @compile -XDignore.symbol.file Hashing.java
|
||||
*/
|
||||
public class Hashing {
|
||||
|
||||
static final byte ONE_BYTE[] = {
|
||||
(byte) 0x80};
|
||||
static final byte TWO_BYTE[] = {
|
||||
(byte) 0x80, (byte) 0x81};
|
||||
static final char ONE_CHAR[] = {
|
||||
(char) 0x8180};
|
||||
static final byte THREE_BYTE[] = {
|
||||
(byte) 0x80, (byte) 0x81, (byte) 0x82};
|
||||
static final byte FOUR_BYTE[] = {
|
||||
(byte) 0x80, (byte) 0x81, (byte) 0x82, (byte) 0x83};
|
||||
static final char TWO_CHAR[] = {
|
||||
(char) 0x8180, (char) 0x8382};
|
||||
static final int ONE_INT[] = {
|
||||
0x83828180};
|
||||
static final byte SIX_BYTE[] = {
|
||||
(byte) 0x80, (byte) 0x81, (byte) 0x82,
|
||||
(byte) 0x83, (byte) 0x84, (byte) 0x85};
|
||||
static final char THREE_CHAR[] = {
|
||||
(char) 0x8180, (char) 0x8382, (char) 0x8584};
|
||||
static final byte EIGHT_BYTE[] = {
|
||||
(byte) 0x80, (byte) 0x81, (byte) 0x82,
|
||||
(byte) 0x83, (byte) 0x84, (byte) 0x85,
|
||||
(byte) 0x86, (byte) 0x87};
|
||||
static final char FOUR_CHAR[] = {
|
||||
(char) 0x8180, (char) 0x8382,
|
||||
(char) 0x8584, (char) 0x8786};
|
||||
static final int TWO_INT[] = {
|
||||
0x83828180, 0x87868584};
|
||||
// per http://code.google.com/p/smhasher/source/browse/trunk/main.cpp, line:72
|
||||
static final int MURMUR3_32_X86_CHECK_VALUE = 0xB0F57EE3;
|
||||
|
||||
public static void testMurmur3_32_ByteArray() {
|
||||
System.out.println("testMurmur3_32_ByteArray");
|
||||
|
||||
byte[] vector = new byte[256];
|
||||
byte[] hashes = new byte[4 * 256];
|
||||
|
||||
for (int i = 0; i < 256; i++) {
|
||||
vector[i] = (byte) i;
|
||||
}
|
||||
|
||||
// Hash subranges {}, {0}, {0,1}, {0,1,2}, ..., {0,...,255}
|
||||
for (int i = 0; i < 256; i++) {
|
||||
int hash = sun.misc.Hashing.murmur3_32(256 - i, vector, 0, i);
|
||||
|
||||
hashes[i * 4] = (byte) hash;
|
||||
hashes[i * 4 + 1] = (byte) (hash >>> 8);
|
||||
hashes[i * 4 + 2] = (byte) (hash >>> 16);
|
||||
hashes[i * 4 + 3] = (byte) (hash >>> 24);
|
||||
}
|
||||
|
||||
// hash to get final result.
|
||||
int final_hash = sun.misc.Hashing.murmur3_32(0, hashes);
|
||||
|
||||
if (MURMUR3_32_X86_CHECK_VALUE != final_hash) {
|
||||
throw new RuntimeException(
|
||||
String.format("Calculated hash result not as expected. Expected %08X got %08X",
|
||||
MURMUR3_32_X86_CHECK_VALUE,
|
||||
final_hash));
|
||||
}
|
||||
}
|
||||
|
||||
public static void testEquivalentHashes() {
|
||||
int bytes, chars, ints;
|
||||
|
||||
System.out.println("testEquivalentHashes");
|
||||
|
||||
bytes = sun.misc.Hashing.murmur3_32(TWO_BYTE);
|
||||
chars = sun.misc.Hashing.murmur3_32(ONE_CHAR);
|
||||
if (bytes != chars) {
|
||||
throw new RuntimeException(String.format("Hashes did not match. b:%08x != c:%08x", bytes, chars));
|
||||
}
|
||||
|
||||
bytes = sun.misc.Hashing.murmur3_32(FOUR_BYTE);
|
||||
chars = sun.misc.Hashing.murmur3_32(TWO_CHAR);
|
||||
ints = sun.misc.Hashing.murmur3_32(ONE_INT);
|
||||
if ((bytes != chars) || (bytes != ints)) {
|
||||
throw new RuntimeException(String.format("Hashes did not match. b:%08x != c:%08x != i:%08x", bytes, chars, ints));
|
||||
}
|
||||
bytes = sun.misc.Hashing.murmur3_32(SIX_BYTE);
|
||||
chars = sun.misc.Hashing.murmur3_32(THREE_CHAR);
|
||||
if (bytes != chars) {
|
||||
throw new RuntimeException(String.format("Hashes did not match. b:%08x != c:%08x", bytes, chars));
|
||||
}
|
||||
|
||||
bytes = sun.misc.Hashing.murmur3_32(EIGHT_BYTE);
|
||||
chars = sun.misc.Hashing.murmur3_32(FOUR_CHAR);
|
||||
ints = sun.misc.Hashing.murmur3_32(TWO_INT);
|
||||
if ((bytes != chars) || (bytes != ints)) {
|
||||
throw new RuntimeException(String.format("Hashes did not match. b:%08x != c:%08x != i:%08x", bytes, chars, ints));
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
testMurmur3_32_ByteArray();
|
||||
testEquivalentHashes();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user