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/10/11 13:28:19 UTC

[commons-rng] 07/16: Created Hex class for encoding/decoding hex bytes.

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 e637b4f300955942ebb875cc53735e73f4b2a952
Author: Alex Herbert <ah...@apache.org>
AuthorDate: Sat Oct 5 00:41:35 2019 +0100

    Created Hex class for encoding/decoding hex bytes.
    
    Added hex seed option to the output command.
---
 .../apache/commons/rng/examples/stress/Hex.java    | 106 +++++++++++++++++++++
 .../commons/rng/examples/stress/OutputCommand.java |  58 +++++++++--
 .../commons/rng/examples/stress/RNGUtils.java      |  32 -------
 .../rng/examples/stress/StressTestCommand.java     |   2 +-
 4 files changed, 157 insertions(+), 41 deletions(-)

diff --git a/commons-rng-examples/examples-stress/src/main/java/org/apache/commons/rng/examples/stress/Hex.java b/commons-rng-examples/examples-stress/src/main/java/org/apache/commons/rng/examples/stress/Hex.java
new file mode 100644
index 0000000..c71532c
--- /dev/null
+++ b/commons-rng-examples/examples-stress/src/main/java/org/apache/commons/rng/examples/stress/Hex.java
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.rng.examples.stress;
+
+/**
+ * Encodes and decodes bytes as hexadecimal characters.
+ *
+ * <p>Adapted from commons-codec.</p>
+ */
+final class Hex {
+    /**
+     * Used to build 4-bit numbers as Hex.
+     */
+    private static final char[] HEX_DIGITS = {
+        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
+    };
+
+    /** No public construction. */
+    private Hex() {}
+
+    /**
+     * Converts an array of bytes into an array of characters representing the hexadecimal
+     * values of each byte in order. The returned array will be double the length of the
+     * passed array, as it takes two characters to represent any given byte.
+     *
+     * <p>This can be used to encode byte array seeds into a text representation.</p>
+     *
+     * @param data A byte[] to convert to Hex characters
+     * @return A char[] containing the lower-case Hex representation
+     */
+    static char[] encodeHex(final byte[] data) {
+        final int l = data.length;
+        final char[] out = new char[l << 1];
+        // Two characters form the hex value
+        for (int i = 0; i < l; i++) {
+            // Upper 4-bits
+            out[2 * i]     = HEX_DIGITS[(0xf0 & data[i]) >>> 4];
+            // Lower 4-bits
+            out[2 * i + 1] = HEX_DIGITS[ 0x0f & data[i]];
+        }
+        return out;
+    }
+
+    /**
+     * Converts an array of characters representing hexadecimal values into an array
+     * of bytes of those same values. The returned array will be half the length of
+     * the passed array, as it takes two characters to represent any given byte. An
+     * exception is thrown if the passed char array has an odd number of elements.
+     *
+     * @param data An array of characters containing hexadecimal digits
+     * @return A byte array containing binary data decoded from the supplied char array.
+     * @throws IllegalArgumentException Thrown if an odd number or illegal of
+     * characters is supplied
+     */
+    public static byte[] decodeHex(final CharSequence data) {
+
+        final int len = data.length();
+
+        if ((len & 0x01) != 0) {
+            throw new IllegalArgumentException("Odd number of characters.");
+        }
+
+        final byte[] out = new byte[len >> 1];
+
+        // Two characters form the hex value
+        for (int j = 0; j < len; j += 2) {
+            final int f = (toDigit(data, j) << 4) |
+                           toDigit(data, j + 1);
+            out[j / 2] = (byte) f;
+        }
+
+        return out;
+    }
+
+    /**
+     * Converts a hexadecimal character to an integer.
+     *
+     * @param data An array of characters containing hexadecimal digits
+     * @param index The index of the character in the source
+     * @return An integer
+     * @throws IllegalArgumentException Thrown if ch is an illegal hex character
+     */
+    protected static int toDigit(final CharSequence data, final int index) {
+        final char ch = data.charAt(index);
+        final int digit = Character.digit(ch, 16);
+        if (digit == -1) {
+            throw new IllegalArgumentException("Illegal hexadecimal character " + ch +
+                                               " at index " + index);
+        }
+        return digit;
+    }
+}
diff --git a/commons-rng-examples/examples-stress/src/main/java/org/apache/commons/rng/examples/stress/OutputCommand.java b/commons-rng-examples/examples-stress/src/main/java/org/apache/commons/rng/examples/stress/OutputCommand.java
index da5646b..b27b197 100644
--- a/commons-rng-examples/examples-stress/src/main/java/org/apache/commons/rng/examples/stress/OutputCommand.java
+++ b/commons-rng-examples/examples-stress/src/main/java/org/apache/commons/rng/examples/stress/OutputCommand.java
@@ -91,9 +91,16 @@ class OutputCommand implements Callable<Void> {
 
     /** The random seed. */
     @Option(names = {"-s", "--seed"},
-            description = {"The random seed (default: auto)."})
+            description = {"The 64-bit number random seed (default: auto)."})
     private Long seed;
 
+    /** The random seed as a byte[]. */
+    @Option(names = {"-x", "--hex-seed"},
+            description = {"The hex-encoded random seed.",
+                           "Bytes for other primitives use little-endian format.",
+                           "Over-rides the --seed parameter."})
+    private String byteSeed;
+
     /** The count of numbers to output. */
     @Option(names = {"-n", "--count"},
             description = {"The count of numbers to output.",
@@ -156,7 +163,8 @@ class OutputCommand implements Callable<Void> {
     @Override
     public Void call() {
         LogUtils.setLogLevel(reusableOptions.logLevel);
-        UniformRandomProvider rng = createRNG();
+        final Object objectSeed = createSeed();
+        UniformRandomProvider rng = createRNG(objectSeed);
 
         // Upper or lower bits from 64-bit generators must be created first.
         // This will throw if not a 64-bit generator.
@@ -183,7 +191,7 @@ class OutputCommand implements Callable<Void> {
                 writeBinaryData(rng, out);
                 break;
             case DIEHARDER:
-                writeDieharder(rng, seed == null ? "auto" : seed, out);
+                writeDieharder(rng, out);
                 break;
             case BITS:
                 writeBitData(rng, out);
@@ -198,12 +206,48 @@ class OutputCommand implements Callable<Void> {
     }
 
     /**
+     * Creates the seed.
+     *
+     * @return the seed
+     */
+    private Object createSeed() {
+        if (byteSeed != null) {
+            try {
+                return Hex.decodeHex(byteSeed);
+            } catch (IllegalArgumentException ex) {
+                throw new ApplicationException("Invalid hex seed: " + ex.getMessage(), ex);
+            }
+        }
+        if (seed != null) {
+            return seed;
+        }
+        // Let the factory constructor create the native seed.
+        return null;
+    }
+
+    /**
+     * Creates the seed.
+     *
+     * @return the seed
+     */
+    private String createSeedString() {
+        if (byteSeed != null) {
+            return byteSeed;
+        }
+        if (seed != null) {
+            return seed.toString();
+        }
+        return "auto";
+    }
+
+    /**
      * Creates the RNG.
      *
+     * @param objectSeed Seed.
      * @return the uniform random provider
      * @throws ApplicationException If the RNG cannot be created
      */
-    private UniformRandomProvider createRNG() {
+    private UniformRandomProvider createRNG(Object objectSeed) {
         if (randomSource == null) {
             throw new ApplicationException("Random source is null");
         }
@@ -216,7 +260,7 @@ class OutputCommand implements Callable<Void> {
             data.add(RNGUtils.parseArgument(argument));
         }
         try {
-            return RandomSource.create(randomSource, seed, data.toArray());
+            return RandomSource.create(randomSource, objectSeed, data.toArray());
         } catch (IllegalStateException | IllegalArgumentException ex) {
             throw new ApplicationException("Failed to create RNG: " + randomSource + ". " + ex.getMessage(), ex);
         }
@@ -310,13 +354,11 @@ class OutputCommand implements Callable<Void> {
      * Write int data to the specified output using the dieharder text format.
      *
      * @param rng The random generator.
-     * @param seedObject The seed using to create the random source.
      * @param out The output.
      * @throws IOException Signals that an I/O exception has occurred.
      * @throws ApplicationException If the count is not positive.
      */
     private void writeDieharder(final UniformRandomProvider rng,
-                                final Object seedObject,
                                 final OutputStream out) throws IOException {
         checkCount(count, OutputFormat.DIEHARDER);
 
@@ -333,7 +375,7 @@ class OutputCommand implements Callable<Void> {
             output.write("# generator ");
             output.write(rng.toString());
             output.write("  seed = ");
-            output.write(String.valueOf(seedObject));
+            output.write(createSeedString());
             output.write(NEW_LINE);
             writeHeaderLine(output);
             output.write("type: d");
diff --git a/commons-rng-examples/examples-stress/src/main/java/org/apache/commons/rng/examples/stress/RNGUtils.java b/commons-rng-examples/examples-stress/src/main/java/org/apache/commons/rng/examples/stress/RNGUtils.java
index 4c3056c..29c09a5 100644
--- a/commons-rng-examples/examples-stress/src/main/java/org/apache/commons/rng/examples/stress/RNGUtils.java
+++ b/commons-rng-examples/examples-stress/src/main/java/org/apache/commons/rng/examples/stress/RNGUtils.java
@@ -46,13 +46,6 @@ final class RNGUtils {
     /** Message when not a RandomLongSource. */
     private static final String NOT_LONG_SOURCE = "Not a 64-bit long generator: ";
 
-    /**
-     * Used to build 4-bit numbers as Hex.
-     */
-    private static final char[] HEX_DIGITS = {
-        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
-    };
-
     /** No public construction. */
     private RNGUtils() {}
 
@@ -419,29 +412,4 @@ final class RNGUtils {
             throw new ApplicationException("Failed to parse RandomSource argument: " + argument, ex);
         }
     }
-
-    /**
-     * Converts an array of bytes into an array of characters representing the hexadecimal
-     * values of each byte in order. The returned array will be double the length of the
-     * passed array, as it takes two characters to represent any given byte.
-     *
-     * <p>This can be used to encode byte array seeds into a text representation.</p>
-     *
-     * <p>Adapted from commons-codec.</p>
-     *
-     * @param data a byte[] to convert to Hex characters
-     * @return A char[] containing the lower-case Hex representation
-     */
-    static char[] encodeHex(final byte[] data) {
-        final int l = data.length;
-        final char[] out = new char[l << 1];
-        // Two characters form the hex value
-        for (int i = 0; i < l; i++) {
-            // Upper 4-bits
-            out[2 * i]     = HEX_DIGITS[(0xf0 & data[i]) >>> 4];
-            // Lower 4-bits
-            out[2 * i + 1] = HEX_DIGITS[ 0x0f & data[i]];
-        }
-        return out;
-    }
 }
diff --git a/commons-rng-examples/examples-stress/src/main/java/org/apache/commons/rng/examples/stress/StressTestCommand.java b/commons-rng-examples/examples-stress/src/main/java/org/apache/commons/rng/examples/stress/StressTestCommand.java
index b8b2c5d..286f825 100644
--- a/commons-rng-examples/examples-stress/src/main/java/org/apache/commons/rng/examples/stress/StressTestCommand.java
+++ b/commons-rng-examples/examples-stress/src/main/java/org/apache/commons/rng/examples/stress/StressTestCommand.java
@@ -690,7 +690,7 @@ class StressTestCommand implements Callable<Void> {
             sb.append(C).append(N)
                 .append(C).append("RandomSource: ").append(randomSource.name()).append(N)
                 .append(C).append("RNG: ").append(rng.toString()).append(N)
-                .append(C).append("Seed: ").append(RNGUtils.encodeHex(seed)).append(N)
+                .append(C).append("Seed: ").append(Hex.encodeHex(seed)).append(N)
                 .append(C).append(N)
 
             // Match the output of 'java -version', e.g.