This is an automated email from the ASF dual-hosted git repository. aherbert pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-rng.git
commit 592ad0be4fe4fa912b5cdaf5f83b006eb8a02f77 Author: Alex Herbert <[email protected]> AuthorDate: Mon Sep 2 21:42:12 2019 +0100 RNG-115: Fix JDKRandom to restore state to a new instance. Save the state size to the byte array representation. This can be read by any instance when restoring. --- .../commons/rng/core/source32/JDKRandom.java | 19 ++++++++++---- .../commons/rng/core/source32/JDKRandomTest.java | 29 ++++++++++++++++++++++ 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/commons-rng-core/src/main/java/org/apache/commons/rng/core/source32/JDKRandom.java b/commons-rng-core/src/main/java/org/apache/commons/rng/core/source32/JDKRandom.java index 94ba0b4..e9245b1 100644 --- a/commons-rng-core/src/main/java/org/apache/commons/rng/core/source32/JDKRandom.java +++ b/commons-rng-core/src/main/java/org/apache/commons/rng/core/source32/JDKRandom.java @@ -21,6 +21,9 @@ import java.io.IOException; import java.io.ObjectOutputStream; import java.io.ObjectInputStream; import java.io.ByteArrayOutputStream; + +import org.apache.commons.rng.core.util.NumberFactory; + import java.io.ByteArrayInputStream; /** @@ -42,8 +45,6 @@ import java.io.ByteArrayInputStream; public class JDKRandom extends IntProvider { /** Delegate. Cannot be "final" (to allow serialization). */ private Random delegate; - /** Size of the byte representation of the state (of the delegate). */ - private int stateSize; /** * Creates an instance with the given seed. @@ -75,8 +76,11 @@ public class JDKRandom extends IntProvider { oos.writeObject(delegate); final byte[] state = bos.toByteArray(); - stateSize = state.length; // To allow state recovery. - return composeStateInternal(state, + final int stateSize = state.length; // To allow state recovery. + // Compose the size with the state + final byte[] sizeAndState = composeStateInternal(NumberFactory.makeByteArray(stateSize), + state); + return composeStateInternal(sizeAndState, super.getStateInternal()); } catch (IOException e) { // Workaround checked exception. @@ -87,7 +91,12 @@ public class JDKRandom extends IntProvider { /** {@inheritDoc} */ @Override protected void setStateInternal(byte[] s) { - final byte[][] c = splitStateInternal(s, stateSize); + // First obtain the state size + final byte[][] s2 = splitStateInternal(s, 4); + final int stateSize = NumberFactory.makeInt(s2[0]); + + // Second obtain the state + final byte[][] c = splitStateInternal(s2[1], stateSize); try { final ByteArrayInputStream bis = new ByteArrayInputStream(c[0]); diff --git a/commons-rng-core/src/test/java/org/apache/commons/rng/core/source32/JDKRandomTest.java b/commons-rng-core/src/test/java/org/apache/commons/rng/core/source32/JDKRandomTest.java index 73be52e..0e86baf 100644 --- a/commons-rng-core/src/test/java/org/apache/commons/rng/core/source32/JDKRandomTest.java +++ b/commons-rng-core/src/test/java/org/apache/commons/rng/core/source32/JDKRandomTest.java @@ -18,6 +18,7 @@ package org.apache.commons.rng.core.source32; import java.util.Random; +import org.apache.commons.rng.RandomProviderState; import org.junit.Assert; import org.junit.Test; @@ -35,4 +36,32 @@ public class JDKRandomTest { Assert.assertEquals(r + " nextInt", jdk.nextInt(), rng.nextInt()); } } + + /** + * Test the state can be used to restore a new instance that has not previously had a call + * to save the state. + */ + @Test + public void testRestoreToNewInstance() { + final long seed = 8796746234L; + final JDKRandom rng1 = new JDKRandom(seed); + final JDKRandom rng2 = new JDKRandom(seed + 1); + + // Ensure different + final int numRepeats = 10; + for (int r = 0; r < numRepeats; r++) { + Assert.assertNotEquals(r + " nextInt", rng1.nextInt(), rng2.nextInt()); + } + + final RandomProviderState state = rng1.saveState(); + // This second instance will not know the state size required to write + // java.util.Random to serialized form. This is only known when the saveState + // method is called. + rng2.restoreState(state); + + // Ensure the same + for (int r = 0; r < numRepeats; r++) { + Assert.assertEquals(r + " nextInt", rng1.nextInt(), rng2.nextInt()); + } + } }
