8188047: Add SplittableRandom.nextBytes
authordl
Fri, 13 Oct 2017 18:19:18 -0700
changeset 47342 bffcbf07ea88
parent 47341 ed1fd45b6eb5
child 47343 75ee0b48ea63
8188047: Add SplittableRandom.nextBytes Reviewed-by: martin, psandoz
src/java.base/share/classes/java/util/SplittableRandom.java
src/java.base/share/classes/java/util/concurrent/ThreadLocalRandom.java
test/jdk/java/util/concurrent/tck/SplittableRandomTest.java
test/jdk/java/util/concurrent/tck/ThreadLocalRandomTest.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
--- 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) {}
+    }
+
 }