--- jsr166/src/main/java/util/SplittableRandom.java 2013/09/19 23:19:43 1.21
+++ jsr166/src/main/java/util/SplittableRandom.java 2018/12/02 23:06:16 1.41
@@ -25,17 +25,14 @@
package java.util;
-import java.security.SecureRandom;
-import java.net.NetworkInterface;
-import java.util.Enumeration;
import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.DoubleConsumer;
import java.util.function.IntConsumer;
import java.util.function.LongConsumer;
-import java.util.function.DoubleConsumer;
-import java.util.stream.StreamSupport;
+import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
-import java.util.stream.DoubleStream;
+import java.util.stream.StreamSupport;
/**
* A generator of uniform pseudorandom values applicable for use in
@@ -55,15 +52,15 @@ import java.util.stream.DoubleStream;
* types and ranges, but similar properties are expected to hold, at
* least approximately, for others as well. The period
* (length of any series of generated values before it repeats) is at
- * least 264.
+ * least 264.
*
- *
Method {@link #split} constructs and returns a new
+ * Method {@link #split} constructs and returns a new
* SplittableRandom instance that shares no mutable state with the
* current instance. However, with very high probability, the
* values collectively generated by the two objects have the same
* statistical properties as if the same quantity of values were
* generated by a single thread using a single {@code
- * SplittableRandom} object.
+ * SplittableRandom} object.
*
* Instances of SplittableRandom are not thread-safe.
* They are designed to be split, not shared, across threads. For
@@ -74,7 +71,7 @@ import java.util.stream.DoubleStream;
*
* This class provides additional methods for generating random
* streams, that employ the above techniques when used in {@code
- * stream.parallel()} mode.
+ * stream.parallel()} mode.
*
*
*
@@ -83,13 +80,13 @@ import java.util.stream.DoubleStream;
* in security-sensitive applications. Additionally,
* default-constructed instances do not use a cryptographically random
* seed unless the {@linkplain System#getProperty system property}
- * {@code java.util.secureRandomSeed} is set to {@code true}.
+ * {@systemProperty java.util.secureRandomSeed} is set to {@code true}.
*
* @author Guy Steele
* @author Doug Lea
* @since 1.8
*/
-public class SplittableRandom {
+public final class SplittableRandom {
/*
* Implementation Overview.
@@ -112,8 +109,8 @@ public class SplittableRandom {
* For nextLong, the mix64 function is based on David Stafford's
* (http://zimbry.blogspot.com/2011/09/better-bit-mixing-improving-on.html)
* "Mix13" variant of the "64-bit finalizer" function in Austin
- * Appleby's MurmurHash3 algorithm See
- * http://code.google.com/p/smhasher/wiki/MurmurHash3 . The mix32
+ * Appleby's MurmurHash3 algorithm (see
+ * http://code.google.com/p/smhasher/wiki/MurmurHash3). The mix32
* function is based on Stafford's Mix04 mix function, but returns
* the upper 32 bits cast as int.
*
@@ -142,11 +139,10 @@ public class SplittableRandom {
* other cases, this split must be performed in a thread-safe
* manner, so we use an AtomicLong to represent the seed rather
* than use an explicit SplittableRandom. To bootstrap the
- * defaultGen, we start off using a seed based on current time and
- * network interface address unless the java.util.secureRandomSeed
- * property is set. This serves as a slimmed-down (and insecure)
- * variant of SecureRandom that also avoids stalls that may occur
- * when using /dev/random.
+ * defaultGen, we start off using a seed based on current time
+ * unless the java.util.secureRandomSeed property is set. This
+ * serves as a slimmed-down (and insecure) variant of SecureRandom
+ * that also avoids stalls that may occur when using /dev/random.
*
* It is a relatively simple matter to apply the basic design here
* to use 128 bit seeds. However, emulating 128bit arithmetic and
@@ -168,7 +164,7 @@ public class SplittableRandom {
* The least non-zero value returned by nextDouble(). This value
* is scaled by a random value of 53 bits to produce a result.
*/
- private static final double DOUBLE_ULP = 1.0 / (1L << 53);
+ private static final double DOUBLE_UNIT = 0x1.0p-53; // 1.0 / (1L << 53);
/**
* The seed. Updated only via method nextSeed.
@@ -192,13 +188,7 @@ public class SplittableRandom {
* Computes Stafford variant 13 of 64bit mix function.
*/
private static long mix64(long z) {
- z *= 0xbf58476d1ce4e5b9L;
- z = (z ^ (z >>> 32)) * 0x94d049bb133111ebL;
- return z ^ (z >>> 32);
- }
-
- private static long xmix64(long z) {
- z *= 0xbf58476d1ce4e5b9L;
+ z = (z ^ (z >>> 30)) * 0xbf58476d1ce4e5b9L;
z = (z ^ (z >>> 27)) * 0x94d049bb133111ebL;
return z ^ (z >>> 31);
}
@@ -207,7 +197,7 @@ public class SplittableRandom {
* Returns the 32 high bits of Stafford variant 4 mix64 function as int.
*/
private static int mix32(long z) {
- z *= 0x62a9d9ed799705f5L;
+ z = (z ^ (z >>> 33)) * 0x62a9d9ed799705f5L;
return (int)(((z ^ (z >>> 28)) * 0xcb24d0a5c88c35b3L) >>> 32);
}
@@ -215,7 +205,7 @@ public class SplittableRandom {
* Returns the gamma value to use for a new split instance.
*/
private static long mixGamma(long z) {
- z *= 0xff51afd7ed558ccdL; // MurmurHash3 mix constants
+ z = (z ^ (z >>> 33)) * 0xff51afd7ed558ccdL; // MurmurHash3 mix constants
z = (z ^ (z >>> 33)) * 0xc4ceb9fe1a85ec53L;
z = (z ^ (z >>> 33)) | 1L; // force to be odd
int n = Long.bitCount(z ^ (z >>> 1)); // ensure enough transitions
@@ -229,58 +219,33 @@ public class SplittableRandom {
return seed += gamma;
}
+ // IllegalArgumentException messages
+ static final String BAD_BOUND = "bound must be positive";
+ static final String BAD_RANGE = "bound must be greater than origin";
+ static final String BAD_SIZE = "size must be non-negative";
+
/**
* The seed generator for default constructors.
*/
- private static final AtomicLong defaultGen = new AtomicLong(initialSeed());
-
- private static long initialSeed() {
- String pp = java.security.AccessController.doPrivileged(
- new sun.security.action.GetPropertyAction(
- "java.util.secureRandomSeed"));
- if (pp != null && pp.equalsIgnoreCase("true")) {
+ private static final AtomicLong defaultGen
+ = new AtomicLong(mix64(System.currentTimeMillis()) ^
+ mix64(System.nanoTime()));
+
+ // at end of to survive static initialization circularity
+ static {
+ if (java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Boolean run() {
+ return Boolean.getBoolean("java.util.secureRandomSeed");
+ }})) {
byte[] seedBytes = java.security.SecureRandom.getSeed(8);
- long s = (long)(seedBytes[0]) & 0xffL;
+ long s = (long)seedBytes[0] & 0xffL;
for (int i = 1; i < 8; ++i)
- s = (s << 8) | ((long)(seedBytes[i]) & 0xffL);
- return s;
+ s = (s << 8) | ((long)seedBytes[i] & 0xffL);
+ defaultGen.set(s);
}
- long h = 0L;
- try {
- Enumeration ifcs =
- NetworkInterface.getNetworkInterfaces();
- boolean retry = false; // retry once if getHardwareAddress is null
- while (ifcs.hasMoreElements()) {
- NetworkInterface ifc = ifcs.nextElement();
- if (!ifc.isVirtual()) { // skip fake addresses
- byte[] bs = ifc.getHardwareAddress();
- if (bs != null) {
- int n = bs.length;
- int m = Math.min(n >>> 1, 4);
- for (int i = 0; i < m; ++i)
- h = (h << 16) ^ (bs[i] << 8) ^ bs[n-1-i];
- if (m < 4)
- h = (h << 8) ^ bs[n-1-m];
- h = mix64(h);
- break;
- }
- else if (!retry)
- retry = true;
- else
- break;
- }
- }
- } catch (Exception ignore) {
- }
- return (h ^ mix64(System.currentTimeMillis()) ^
- mix64(System.nanoTime()));
}
- // IllegalArgumentException messages
- static final String BadBound = "bound must be positive";
- static final String BadRange = "bound must be greater than origin";
- static final String BadSize = "size must be non-negative";
-
/*
* Internal versions of nextX methods used by streams, as well as
* the public nextX(origin, bound) methods. These exist mainly to
@@ -381,7 +346,7 @@ public class SplittableRandom {
* @return a pseudorandom value
*/
final double internalNextDouble(double origin, double bound) {
- double r = (nextLong() >>> 11) * DOUBLE_ULP;
+ double r = (nextLong() >>> 11) * DOUBLE_UNIT;
if (origin < bound) {
r = r * (bound - origin) + origin;
if (r >= bound) // correct for rounding
@@ -410,7 +375,7 @@ public class SplittableRandom {
* may, and typically does, vary across program invocations.
*/
public SplittableRandom() { // emulate defaultGen.split()
- long s = defaultGen.getAndAdd(2*GOLDEN_GAMMA);
+ long s = defaultGen.getAndAdd(GOLDEN_GAMMA << 1);
this.seed = mix64(s);
this.gamma = mixGamma(s + GOLDEN_GAMMA);
}
@@ -434,6 +399,26 @@ public class SplittableRandom {
}
/**
+ * Fills a user-supplied byte array with generated pseudorandom bytes.
+ *
+ * @param bytes the byte array to fill with pseudorandom bytes
+ * @throws NullPointerException if bytes is null
+ * @since 10
+ */
+ public void nextBytes(byte[] bytes) {
+ int i = 0;
+ int len = bytes.length;
+ for (int words = len >> 3; words--> 0; ) {
+ long rnd = nextLong();
+ for (int n = 8; n--> 0; rnd >>>= Byte.SIZE)
+ bytes[i++] = (byte)rnd;
+ }
+ if (i < len)
+ for (long rnd = nextLong(); i < len; rnd >>>= Byte.SIZE)
+ bytes[i++] = (byte)rnd;
+ }
+
+ /**
* Returns a pseudorandom {@code int} value.
*
* @return a pseudorandom {@code int} value
@@ -453,7 +438,7 @@ public class SplittableRandom {
*/
public int nextInt(int bound) {
if (bound <= 0)
- throw new IllegalArgumentException(BadBound);
+ throw new IllegalArgumentException(BAD_BOUND);
// Specialize internalNextInt for origin 0
int r = mix32(nextSeed());
int m = bound - 1;
@@ -481,7 +466,7 @@ public class SplittableRandom {
*/
public int nextInt(int origin, int bound) {
if (origin >= bound)
- throw new IllegalArgumentException(BadRange);
+ throw new IllegalArgumentException(BAD_RANGE);
return internalNextInt(origin, bound);
}
@@ -505,7 +490,7 @@ public class SplittableRandom {
*/
public long nextLong(long bound) {
if (bound <= 0)
- throw new IllegalArgumentException(BadBound);
+ throw new IllegalArgumentException(BAD_BOUND);
// Specialize internalNextLong for origin 0
long r = mix64(nextSeed());
long m = bound - 1;
@@ -533,7 +518,7 @@ public class SplittableRandom {
*/
public long nextLong(long origin, long bound) {
if (origin >= bound)
- throw new IllegalArgumentException(BadRange);
+ throw new IllegalArgumentException(BAD_RANGE);
return internalNextLong(origin, bound);
}
@@ -545,7 +530,7 @@ public class SplittableRandom {
* (inclusive) and one (exclusive)
*/
public double nextDouble() {
- return (mix64(nextSeed()) >>> 11) * DOUBLE_ULP;
+ return (mix64(nextSeed()) >>> 11) * DOUBLE_UNIT;
}
/**
@@ -559,8 +544,8 @@ public class SplittableRandom {
*/
public double nextDouble(double bound) {
if (!(bound > 0.0))
- throw new IllegalArgumentException(BadBound);
- double result = (mix64(nextSeed()) >>> 11) * DOUBLE_ULP * bound;
+ throw new IllegalArgumentException(BAD_BOUND);
+ double result = (mix64(nextSeed()) >>> 11) * DOUBLE_UNIT * bound;
return (result < bound) ? result : // correct for rounding
Double.longBitsToDouble(Double.doubleToLongBits(bound) - 1);
}
@@ -578,7 +563,7 @@ public class SplittableRandom {
*/
public double nextDouble(double origin, double bound) {
if (!(origin < bound))
- throw new IllegalArgumentException(BadRange);
+ throw new IllegalArgumentException(BAD_RANGE);
return internalNextDouble(origin, bound);
}
@@ -606,7 +591,7 @@ public class SplittableRandom {
*/
public IntStream ints(long streamSize) {
if (streamSize < 0L)
- throw new IllegalArgumentException(BadSize);
+ throw new IllegalArgumentException(BAD_SIZE);
return StreamSupport.intStream
(new RandomIntsSpliterator
(this, 0L, streamSize, Integer.MAX_VALUE, 0),
@@ -647,9 +632,9 @@ public class SplittableRandom {
public IntStream ints(long streamSize, int randomNumberOrigin,
int randomNumberBound) {
if (streamSize < 0L)
- throw new IllegalArgumentException(BadSize);
+ throw new IllegalArgumentException(BAD_SIZE);
if (randomNumberOrigin >= randomNumberBound)
- throw new IllegalArgumentException(BadRange);
+ throw new IllegalArgumentException(BAD_RANGE);
return StreamSupport.intStream
(new RandomIntsSpliterator
(this, 0L, streamSize, randomNumberOrigin, randomNumberBound),
@@ -673,7 +658,7 @@ public class SplittableRandom {
*/
public IntStream ints(int randomNumberOrigin, int randomNumberBound) {
if (randomNumberOrigin >= randomNumberBound)
- throw new IllegalArgumentException(BadRange);
+ throw new IllegalArgumentException(BAD_RANGE);
return StreamSupport.intStream
(new RandomIntsSpliterator
(this, 0L, Long.MAX_VALUE, randomNumberOrigin, randomNumberBound),
@@ -692,7 +677,7 @@ public class SplittableRandom {
*/
public LongStream longs(long streamSize) {
if (streamSize < 0L)
- throw new IllegalArgumentException(BadSize);
+ throw new IllegalArgumentException(BAD_SIZE);
return StreamSupport.longStream
(new RandomLongsSpliterator
(this, 0L, streamSize, Long.MAX_VALUE, 0L),
@@ -733,9 +718,9 @@ public class SplittableRandom {
public LongStream longs(long streamSize, long randomNumberOrigin,
long randomNumberBound) {
if (streamSize < 0L)
- throw new IllegalArgumentException(BadSize);
+ throw new IllegalArgumentException(BAD_SIZE);
if (randomNumberOrigin >= randomNumberBound)
- throw new IllegalArgumentException(BadRange);
+ throw new IllegalArgumentException(BAD_RANGE);
return StreamSupport.longStream
(new RandomLongsSpliterator
(this, 0L, streamSize, randomNumberOrigin, randomNumberBound),
@@ -759,7 +744,7 @@ public class SplittableRandom {
*/
public LongStream longs(long randomNumberOrigin, long randomNumberBound) {
if (randomNumberOrigin >= randomNumberBound)
- throw new IllegalArgumentException(BadRange);
+ throw new IllegalArgumentException(BAD_RANGE);
return StreamSupport.longStream
(new RandomLongsSpliterator
(this, 0L, Long.MAX_VALUE, randomNumberOrigin, randomNumberBound),
@@ -778,7 +763,7 @@ public class SplittableRandom {
*/
public DoubleStream doubles(long streamSize) {
if (streamSize < 0L)
- throw new IllegalArgumentException(BadSize);
+ throw new IllegalArgumentException(BAD_SIZE);
return StreamSupport.doubleStream
(new RandomDoublesSpliterator
(this, 0L, streamSize, Double.MAX_VALUE, 0.0),
@@ -814,16 +799,15 @@ public class SplittableRandom {
* @return a stream of pseudorandom {@code double} values,
* each with the given origin (inclusive) and bound (exclusive)
* @throws IllegalArgumentException if {@code streamSize} is
- * less than zero
- * @throws IllegalArgumentException if {@code randomNumberOrigin}
+ * less than zero, or {@code randomNumberOrigin}
* is greater than or equal to {@code randomNumberBound}
*/
public DoubleStream doubles(long streamSize, double randomNumberOrigin,
double randomNumberBound) {
if (streamSize < 0L)
- throw new IllegalArgumentException(BadSize);
+ throw new IllegalArgumentException(BAD_SIZE);
if (!(randomNumberOrigin < randomNumberBound))
- throw new IllegalArgumentException(BadRange);
+ throw new IllegalArgumentException(BAD_RANGE);
return StreamSupport.doubleStream
(new RandomDoublesSpliterator
(this, 0L, streamSize, randomNumberOrigin, randomNumberBound),
@@ -847,7 +831,7 @@ public class SplittableRandom {
*/
public DoubleStream doubles(double randomNumberOrigin, double randomNumberBound) {
if (!(randomNumberOrigin < randomNumberBound))
- throw new IllegalArgumentException(BadRange);
+ throw new IllegalArgumentException(BAD_RANGE);
return StreamSupport.doubleStream
(new RandomDoublesSpliterator
(this, 0L, Long.MAX_VALUE, randomNumberOrigin, randomNumberBound),
@@ -862,7 +846,8 @@ public class SplittableRandom {
* approach. The long and double versions of this class are
* identical except for types.
*/
- static final class RandomIntsSpliterator implements Spliterator.OfInt {
+ private static final class RandomIntsSpliterator
+ implements Spliterator.OfInt {
final SplittableRandom rng;
long index;
final long fence;
@@ -917,7 +902,8 @@ public class SplittableRandom {
/**
* Spliterator for long streams.
*/
- static final class RandomLongsSpliterator implements Spliterator.OfLong {
+ private static final class RandomLongsSpliterator
+ implements Spliterator.OfLong {
final SplittableRandom rng;
long index;
final long fence;
@@ -973,7 +959,8 @@ public class SplittableRandom {
/**
* Spliterator for double streams.
*/
- static final class RandomDoublesSpliterator implements Spliterator.OfDouble {
+ private static final class RandomDoublesSpliterator
+ implements Spliterator.OfDouble {
final SplittableRandom rng;
long index;
final long fence;