diff --git a/src/main/java/org/apache/commons/math3/random/BitsStreamGenerator.java b/src/main/java/org/apache/commons/math3/random/BitsStreamGenerator.java index dcb000f69..9f7cb56d9 100644 --- a/src/main/java/org/apache/commons/math3/random/BitsStreamGenerator.java +++ b/src/main/java/org/apache/commons/math3/random/BitsStreamGenerator.java @@ -19,6 +19,7 @@ package org.apache.commons.math3.random; import java.io.Serializable; import org.apache.commons.math3.exception.NotStrictlyPositiveException; +import org.apache.commons.math3.exception.OutOfRangeException; import org.apache.commons.math3.util.FastMath; /** Base class for random number generators that generates bits streams. @@ -65,25 +66,6 @@ public abstract class BitsStreamGenerator return next(1) != 0; } - /** {@inheritDoc} */ - public void nextBytes(byte[] bytes) { - int i = 0; - final int iEnd = bytes.length - 3; - while (i < iEnd) { - final int random = next(32); - bytes[i] = (byte) (random & 0xff); - bytes[i + 1] = (byte) ((random >> 8) & 0xff); - bytes[i + 2] = (byte) ((random >> 16) & 0xff); - bytes[i + 3] = (byte) ((random >> 24) & 0xff); - i += 4; - } - int random = next(32); - while (i < bytes.length) { - bytes[i++] = (byte) (random & 0xff); - random >>= 8; - } - } - /** {@inheritDoc} */ public double nextDouble() { final long high = ((long) next(26)) << 26; @@ -194,4 +176,96 @@ public abstract class BitsStreamGenerator nextGaussian = Double.NaN; } + /** + * Generates random bytes and places them into a user-supplied array. + * + *

+ * The array is filled with bytes extracted from random integers. + * This implies that the number of random bytes generated may be larger than + * the length of the byte array. + *

+ * + * @param bytes Array in which to put the generated bytes. Cannot be {@code null}. + */ + @Override + public void nextBytes(byte[] bytes) { + nextBytesFill(bytes, 0, bytes.length); + } + + /** + * Generates random bytes and places them into a user-supplied array. + * + *

+ * The array is filled with bytes extracted from random integers. + * This implies that the number of random bytes generated may be larger than + * the length of the byte array. + *

+ * + * @param bytes Array in which to put the generated bytes. Cannot be {@code null}. + * @param start Index at which to start inserting the generated bytes. + * @param len Number of bytes to insert. + * @throws OutOfRangeException if {@code start < 0} or {@code start >= bytes.length}. + * @throws OutOfRangeException if {@code len < 0} or {@code len > bytes.length - start}. + */ + public void nextBytes(byte[] bytes, + int start, + int len) { + if (start < 0 || + start >= bytes.length) { + throw new OutOfRangeException(start, 0, bytes.length); + } + if (len < 0 || + len > bytes.length - start) { + throw new OutOfRangeException(len, 0, bytes.length - start); + } + + nextBytesFill(bytes, start, len); + } + + /** + * Generates random bytes and places them into a user-supplied array. + * + *

+ * The array is filled with bytes extracted from random integers. + * This implies that the number of random bytes generated may be larger than + * the length of the byte array. + *

+ * + * @param bytes Array in which to put the generated bytes. Cannot be {@code null}. + * @param start Index at which to start inserting the generated bytes. + * @param len Number of bytes to insert. + */ + private void nextBytesFill(byte[] bytes, + int start, + int len) { + int index = start; // Index of first insertion. + + // Index of first insertion plus multiple 4 part of length (i.e. length + // with two least significant bits unset). + final int indexLoopLimit = index + (len & 0x7ffffffc); + + // Start filling in the byte array, 4 bytes at a time. + while (index < indexLoopLimit) { + final int random = next(32); + bytes[index++] = (byte) random; + bytes[index++] = (byte) (random >>> 8); + bytes[index++] = (byte) (random >>> 16); + bytes[index++] = (byte) (random >>> 24); + } + + final int indexLimit = start + len; // Index of last insertion + 1. + + // Fill in the remaining bytes. + if (index < indexLimit) { + int random = next(32); + while (true) { + bytes[index++] = (byte) random; + if (index < indexLimit) { + random >>>= 8; + } else { + break; + } + } + } + } }