You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by ah...@apache.org on 2019/09/03 08:43:36 UTC

[commons-rng] 01/04: RNG-115: Fix JDKRandom to restore state to a new instance.

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 <ah...@apache.org>
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());
+        }
+    }
 }