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-codec.git

commit 2c969a83454605e204e3e6977f563961f227f185
Author: Alex Herbert <aherb...@apache.org>
AuthorDate: Wed Jul 15 12:52:49 2020 +0100

    CODEC-291: Hex Encode/Decode with existing arrays
    
    Squashed commit of the following:
    
    commit 878d96cec17ce487775306d40171922383d1210a
    Author: Adam Retter <adam.ret...@googlemail.com>
    Date:   Fri Jul 3 17:53:41 2020 +0200
    
        Add missing tests
    
    commit c1bcf38f3cbf03979bc3de09916a53194156da8c
    Author: Adam Retter <adam.ret...@googlemail.com>
    Date:   Tue Jun 30 19:08:20 2020 +0200
    
        Make public methods
    
    commit 45533a1876f3a68f5040e6645d3db2f6c6f6e875
    Author: Adam Retter <adam.ret...@googlemail.com>
    Date:   Mon Jun 29 22:26:15 2020 +0200
    
        Add additional encode/decode functions for writing data to existing 
arrays
---
 .../java/org/apache/commons/codec/binary/Hex.java  | 81 +++++++++++++++++++--
 .../org/apache/commons/codec/binary/HexTest.java   | 83 +++++++++++++++++++++-
 2 files changed, 156 insertions(+), 8 deletions(-)

diff --git a/src/main/java/org/apache/commons/codec/binary/Hex.java 
b/src/main/java/org/apache/commons/codec/binary/Hex.java
index 57c05bd..1cdabbc 100644
--- a/src/main/java/org/apache/commons/codec/binary/Hex.java
+++ b/src/main/java/org/apache/commons/codec/binary/Hex.java
@@ -73,17 +73,38 @@ public class Hex implements BinaryEncoder, BinaryDecoder {
      * @throws DecoderException Thrown if an odd number or illegal of 
characters is supplied
      */
     public static byte[] decodeHex(final char[] data) throws DecoderException {
+        final byte[] out = new byte[data.length >> 1];
+        decodeHex(data, out, 0);
+        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
+     * @param out A byte array to contain the binary data decoded from the 
supplied char array.
+     * @param outOffset The position within {@code out} to start writing the 
decoded bytes.
+     * @return the number of bytes written to {@code out}.
+     * @throws DecoderException Thrown if an odd number or illegal of 
characters is supplied
+     *
+     * @since 1.15
+     */
+    public static int decodeHex(final char[] data, final byte[] out, final int 
outOffset) throws DecoderException {
         final int len = data.length;
 
         if ((len & 0x01) != 0) {
             throw new DecoderException("Odd number of characters.");
         }
 
-        final byte[] out = new byte[len >> 1];
+        final int outLen = len >> 1;
+        if (out.length - outOffset < outLen) {
+            throw new DecoderException("out is not large enough to accommodate 
decoded data.");
+        }
 
         // two characters form the hex value.
-        for (int i = 0, j = 0; j < len; i++) {
+        for (int i = outOffset, j = 0; j < len; i++) {
             int f = toDigit(data[j], j) << 4;
             j++;
             f = f | toDigit(data[j], j);
@@ -91,7 +112,7 @@ public class Hex implements BinaryEncoder, BinaryDecoder {
             out[i] = (byte) (f & 0xFF);
         }
 
-        return out;
+        return outLen;
     }
 
     /**
@@ -148,12 +169,62 @@ public class Hex implements BinaryEncoder, BinaryDecoder {
     protected static char[] encodeHex(final byte[] data, final char[] 
toDigits) {
         final int l = data.length;
         final char[] out = new char[l << 1];
+        encodeHex(data, 0, data.length, toDigits, out, 0);
+        return out;
+    }
+
+    /**
+     * Converts an array of bytes into an array of characters representing the 
hexadecimal values of each byte in order.
+     *
+     * @param data a byte[] to convert to Hex characters
+     * @param dataOffset the position in {@code data} to start encoding from
+     * @param dataLen the number of bytes from {@code dataOffset} to encode
+     * @param toLowerCase {@code true} converts to lowercase, {@code false} to 
uppercase
+     * @return A char[] containing the appropriate characters from the 
alphabet For best results, this should be either
+     *         upper- or lower-case hex.
+     * @since 1.15
+     */
+    public static char[] encodeHex(final byte[] data, final int dataOffset, 
final int dataLen,
+            final boolean toLowerCase) {
+        final char[] out = new char[dataLen << 1];
+        encodeHex(data, dataOffset, dataLen, toLowerCase ? DIGITS_LOWER : 
DIGITS_UPPER, out, 0);
+        return out;
+    }
+
+    /**
+     * Converts an array of bytes into an array of characters representing the 
hexadecimal values of each byte in order.
+     *
+     * @param data a byte[] to convert to Hex characters
+     * @param dataOffset the position in {@code data} to start encoding from
+     * @param dataLen the number of bytes from {@code dataOffset} to encode
+     * @param toLowerCase {@code true} converts to lowercase, {@code false} to 
uppercase
+     * @param out a char[] which will hold the resultant appropriate 
characters from the alphabet.
+     * @param outOffset the position within {@code out} at which to start 
writing the encoded characters.
+     * @since 1.15
+     */
+    public static void encodeHex(final byte[] data, final int dataOffset, 
final int dataLen,
+            final boolean toLowerCase, final char[] out, final int outOffset) {
+        encodeHex(data, dataOffset, dataLen, toLowerCase ? DIGITS_LOWER : 
DIGITS_UPPER, out, outOffset);
+    }
+
+    /**
+     * Converts an array of bytes into an array of characters representing the 
hexadecimal values of each byte in order.
+     *
+     * @param data a byte[] to convert to Hex characters
+     * @param dataOffset the position in {@code data} to start encoding from
+     * @param dataLen the number of bytes from {@code dataOffset} to encode
+     * @param toDigits the output alphabet (must contain at least 16 chars)
+     * @param out a char[] which will hold the resultant appropriate 
characters from the alphabet.
+     * @param outOffset the position within {@code out} at which to start 
writing the encoded characters.
+     * @since 1.15
+     */
+    protected static void encodeHex(final byte[] data, final int dataOffset, 
final int dataLen, final char[] toDigits,
+            final char[] out, final int outOffset) {
         // two characters form the hex value.
-        for (int i = 0, j = 0; i < l; i++) {
+        for (int i = dataOffset, j = outOffset; i < dataOffset + dataLen; i++) 
{
             out[j++] = toDigits[(0xF0 & data[i]) >>> 4];
             out[j++] = toDigits[0x0F & data[i]];
         }
-        return out;
     }
 
     /**
diff --git a/src/test/java/org/apache/commons/codec/binary/HexTest.java 
b/src/test/java/org/apache/commons/codec/binary/HexTest.java
index 4c29548..99e5013 100644
--- a/src/test/java/org/apache/commons/codec/binary/HexTest.java
+++ b/src/test/java/org/apache/commons/codec/binary/HexTest.java
@@ -17,6 +17,8 @@
 
 package org.apache.commons.codec.binary;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -68,7 +70,7 @@ public class HexTest {
      * @return the byte buffer
      */
     private ByteBuffer getByteBufferUtf8(final String string) {
-        final byte[] bytes = string.getBytes(StandardCharsets.UTF_8);
+        final byte[] bytes = string.getBytes(UTF_8);
         final ByteBuffer bb = allocate(bytes.length);
         bb.put(bytes);
         bb.flip();
@@ -340,6 +342,18 @@ public class HexTest {
         checkDecodeHexCharArrayOddCharacters(new char[] { 'A', 'B', 'C', 'D', 
'E' });
     }
 
+    @Test(expected = DecoderException.class)
+    public void testDecodeHexCharArrayOutBufferUnderSized() throws 
DecoderException {
+        final byte[] out = new byte[4];
+        Hex.decodeHex("aabbccddeeff".toCharArray(), out, 0);
+    }
+
+    @Test(expected = DecoderException.class)
+    public void testDecodeHexCharArrayOutBufferUnderSizedByOffset() throws 
DecoderException {
+        final byte[] out = new byte[6];
+        Hex.decodeHex("aabbccddeeff".toCharArray(), out, 1);
+    }
+
     @Test
     public void testDecodeHexStringOddCharacters() {
         try {
@@ -441,6 +455,27 @@ public class HexTest {
     }
 
     @Test
+    public void testEncodeDecodeHexCharArrayRandomToOutput() throws 
DecoderException, EncoderException {
+        final Random random = new Random();
+        for (int i = 5; i > 0; i--) {
+            final byte[] data = new byte[random.nextInt(10000) + 1];
+            random.nextBytes(data);
+
+            // lower-case
+            final char[] lowerEncodedChars = new char[data.length * 2];
+            Hex.encodeHex(data, 0, data.length, true, lowerEncodedChars, 0);
+            final byte[] decodedLowerCaseBytes = 
Hex.decodeHex(lowerEncodedChars);
+            assertArrayEquals(data, decodedLowerCaseBytes);
+
+            // upper-case
+            final char[] upperEncodedChars = new char[data.length * 2];
+            Hex.encodeHex(data, 0, data.length, false, upperEncodedChars, 0);
+            final byte[] decodedUpperCaseBytes = 
Hex.decodeHex(upperEncodedChars);
+            assertArrayEquals(data, decodedUpperCaseBytes);
+        }
+    }
+
+    @Test
     public void testEncodeHexByteArrayEmpty() {
         assertTrue(Arrays.equals(new char[0], Hex.encodeHex(new byte[0])));
         assertTrue(Arrays.equals(new byte[0], new Hex().encode(new byte[0])));
@@ -550,6 +585,48 @@ public class HexTest {
     }
 
     @Test
+    public void testEncodeHexPartialInput() {
+        final byte data[] = "hello world".getBytes(UTF_8);
+
+        char[] hex = Hex.encodeHex(data, 0, 0, true);
+        assertArrayEquals(new char[0], hex);
+
+        hex = Hex.encodeHex(data, 0, 1, true);
+        assertArrayEquals("68".toCharArray(), hex);
+
+        hex = Hex.encodeHex(data, 0, 1, false);
+        assertArrayEquals("68".toCharArray(), hex);
+
+        hex = Hex.encodeHex(data, 2, 4, true);
+        assertArrayEquals("6c6c6f20".toCharArray(), hex);
+
+        hex = Hex.encodeHex(data, 2, 4, false);
+        assertArrayEquals("6C6C6F20".toCharArray(), hex);
+
+        hex = Hex.encodeHex(data, 10, 1, true);
+        assertArrayEquals("64".toCharArray(), hex);
+
+        hex = Hex.encodeHex(data, 10, 1, false);
+        assertArrayEquals("64".toCharArray(), hex);
+    }
+
+    @Test(expected=ArrayIndexOutOfBoundsException.class)
+    public void testEncodeHexPartialInputUnderbounds() {
+        final byte data[] = "hello world".getBytes(UTF_8);
+
+        final char[] hex = Hex.encodeHex(data, -2, 10, true);
+        assertArrayEquals("64".toCharArray(), hex);
+    }
+
+    @Test(expected=ArrayIndexOutOfBoundsException.class)
+    public void testEncodeHexPartialInputOverbounds() {
+        final byte data[] = "hello world".getBytes(UTF_8);
+
+        final char[] hex = Hex.encodeHex(data, 9, 10, true);
+        assertArrayEquals("64".toCharArray(), hex);
+    }
+
+    @Test
     public void testEncodeHexByteString_ByteBufferOfZeroes() {
         final String c = Hex.encodeHexString(allocate(36));
         
assertEquals("000000000000000000000000000000000000000000000000000000000000000000000000",
 c);
@@ -636,12 +713,12 @@ public class HexTest {
 
     @Test
     public void testGetCharset() {
-        Assert.assertEquals(StandardCharsets.UTF_8, new 
Hex(StandardCharsets.UTF_8).getCharset());
+        Assert.assertEquals(UTF_8, new Hex(UTF_8).getCharset());
     }
 
     @Test
     public void testGetCharsetName() {
-        Assert.assertEquals(StandardCharsets.UTF_8.name(), new 
Hex(StandardCharsets.UTF_8).getCharsetName());
+        Assert.assertEquals(UTF_8.name(), new Hex(UTF_8).getCharsetName());
     }
 
     @Test

Reply via email to