--- a/src/java.base/share/classes/java/util/SplittableRandom.java Fri Oct 13 18:12:54 2017 -0700
+++ b/src/java.base/share/classes/java/util/SplittableRandom.java Fri Oct 13 18:19:18 2017 -0700
@@ -399,6 +399,26 @@
}
/**
+ * 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
--- a/src/java.base/share/classes/java/util/concurrent/ThreadLocalRandom.java Fri Oct 13 18:12:54 2017 -0700
+++ b/src/java.base/share/classes/java/util/concurrent/ThreadLocalRandom.java Fri Oct 13 18:19:18 2017 -0700
@@ -1039,7 +1039,10 @@
*/
private static final long SEEDER_INCREMENT = 0xbb67ae8584caa73bL;
- // Constants from 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_UNIT = 0x1.0p-53; // 1.0 / (1L << 53)
private static final float FLOAT_UNIT = 0x1.0p-24f; // 1.0f / (1 << 24)
--- a/test/jdk/java/util/concurrent/tck/SplittableRandomTest.java Fri Oct 13 18:12:54 2017 -0700
+++ b/test/jdk/java/util/concurrent/tck/SplittableRandomTest.java Fri Oct 13 18:19:18 2017 -0700
@@ -31,9 +31,14 @@
* http://creativecommons.org/publicdomain/zero/1.0/
*/
+import java.util.Arrays;
+import java.util.List;
import java.util.SplittableRandom;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.LongAdder;
+import java.lang.reflect.Method;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
import junit.framework.Test;
import junit.framework.TestSuite;
@@ -552,4 +557,64 @@
assertEquals(size, counter.sum());
}
+ /**
+ * SplittableRandom should implement most of Random's public methods
+ */
+ public void testShouldImplementMostRandomMethods() throws Throwable {
+ Predicate<Method> wasForgotten = method -> {
+ String name = method.getName();
+ // some methods deliberately not implemented
+ if (name.equals("setSeed")) return false;
+ if (name.equals("nextFloat")) return false;
+ if (name.equals("nextGaussian")) return false;
+ try {
+ SplittableRandom.class.getMethod(
+ method.getName(), method.getParameterTypes());
+ } catch (ReflectiveOperationException ex) {
+ return true;
+ }
+ return false;
+ };
+ List<Method> forgotten =
+ Arrays.stream(java.util.Random.class.getMethods())
+ .filter(wasForgotten)
+ .collect(Collectors.toList());
+ if (!forgotten.isEmpty())
+ throw new AssertionError("Please implement: " + forgotten);
+ }
+
+ /**
+ * Repeated calls to nextBytes produce at least values of different signs for every byte
+ */
+ public void testNextBytes() {
+ SplittableRandom sr = new SplittableRandom();
+ int n = sr.nextInt(1, 20);
+ byte[] bytes = new byte[n];
+ outer:
+ for (int i = 0; i < n; i++) {
+ for (int tries = NCALLS; tries-->0; ) {
+ byte before = bytes[i];
+ sr.nextBytes(bytes);
+ byte after = bytes[i];
+ if (after * before < 0)
+ continue outer;
+ }
+ fail("not enough variation in random bytes");
+ }
+ }
+
+ /**
+ * Filling an empty array with random bytes succeeds without effect.
+ */
+ public void testNextBytes_emptyArray() {
+ new SplittableRandom().nextBytes(new byte[0]);
+ }
+
+ public void testNextBytes_nullArray() {
+ try {
+ new SplittableRandom().nextBytes(null);
+ shouldThrow();
+ } catch (NullPointerException success) {}
+ }
+
}
--- a/test/jdk/java/util/concurrent/tck/ThreadLocalRandomTest.java Fri Oct 13 18:12:54 2017 -0700
+++ b/test/jdk/java/util/concurrent/tck/ThreadLocalRandomTest.java Fri Oct 13 18:19:18 2017 -0700
@@ -412,4 +412,38 @@
fail("all threads generate the same pseudo-random sequence");
}
+ /**
+ * Repeated calls to nextBytes produce at least values of different signs for every byte
+ */
+ public void testNextBytes() {
+ ThreadLocalRandom rnd = ThreadLocalRandom.current();
+ int n = rnd.nextInt(1, 20);
+ byte[] bytes = new byte[n];
+ outer:
+ for (int i = 0; i < n; i++) {
+ for (int tries = NCALLS; tries-->0; ) {
+ byte before = bytes[i];
+ rnd.nextBytes(bytes);
+ byte after = bytes[i];
+ if (after * before < 0)
+ continue outer;
+ }
+ fail("not enough variation in random bytes");
+ }
+ }
+
+ /**
+ * Filling an empty array with random bytes succeeds without effect.
+ */
+ public void testNextBytes_emptyArray() {
+ ThreadLocalRandom.current().nextBytes(new byte[0]);
+ }
+
+ public void testNextBytes_nullArray() {
+ try {
+ ThreadLocalRandom.current().nextBytes(null);
+ shouldThrow();
+ } catch (NullPointerException success) {}
+ }
+
}