# HG changeset patch # User dl # Date 1507943958 25200 # Node ID bffcbf07ea88f01dc58229f8de05476fe7affb05 # Parent ed1fd45b6eb5c6a19e11eba4acfc079e20443ed0 8188047: Add SplittableRandom.nextBytes Reviewed-by: martin, psandoz diff -r ed1fd45b6eb5 -r bffcbf07ea88 src/java.base/share/classes/java/util/SplittableRandom.java --- 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 diff -r ed1fd45b6eb5 -r bffcbf07ea88 src/java.base/share/classes/java/util/concurrent/ThreadLocalRandom.java --- 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) diff -r ed1fd45b6eb5 -r bffcbf07ea88 test/jdk/java/util/concurrent/tck/SplittableRandomTest.java --- 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 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 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) {} + } + } diff -r ed1fd45b6eb5 -r bffcbf07ea88 test/jdk/java/util/concurrent/tck/ThreadLocalRandomTest.java --- 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) {} + } + }