    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017
    018package org.apache.commons.codec.binary;
    019
    020import java.nio.ByteBuffer;
    021import java.nio.charset.Charset;
    022import java.nio.charset.StandardCharsets;
    023
    024import org.apache.commons.codec.BinaryDecoder;
    025import org.apache.commons.codec.BinaryEncoder;
    026import org.apache.commons.codec.CharEncoding;
    027import org.apache.commons.codec.DecoderException;
    028import org.apache.commons.codec.EncoderException;
    029
    030/**
    031 * Converts hexadecimal Strings. The Charset used for certain operation can be set, the default is set in
    032 * {@link #DEFAULT_CHARSET_NAME}
    033 *
    034 * This class is thread-safe.
    035 *
    036 * @since 1.1
    037 */
    038public class Hex implements BinaryEncoder, BinaryDecoder {
    039
    040    /**
    041     * Default charset is {@link StandardCharsets#UTF_8}.
    042     *
    043     * @since 1.7
    044     */
    045    public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
    046
    047    /**
    048     * Default charset name is {@link CharEncoding#UTF_8}.
    049     *
    050     * @since 1.4
    051     */
    052    public static final String DEFAULT_CHARSET_NAME = CharEncoding.UTF_8;
    053
    054    /**
    055     * Used to build output as hex.
    056     */
    057    private static final char[] DIGITS_LOWER = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
    058            'e', 'f' };
    059
    060    /**
    061     * Used to build output as hex.
    062     */
    063    private static final char[] DIGITS_UPPER = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
    064            'E', 'F' };
    065
    066    /**
    067     * Converts an array of characters representing hexadecimal values into an array of bytes of those same values. The
    068     * returned array will be half the length of the passed array, as it takes two characters to represent any given
    069     * byte. An exception is thrown if the passed char array has an odd number of elements.
    070     *
    071     * @param data An array of characters containing hexadecimal digits
    072     * @return A byte array containing binary data decoded from the supplied char array.
    073     * @throws DecoderException Thrown if an odd number of characters or illegal characters are supplied
    074     */
    075    public static byte[] decodeHex(final char[] data) throws DecoderException {
    076        final byte[] out = new byte[data.length >> 1];
    077        decodeHex(data, out, 0);
    078        return out;
    079    }
    080
    081    /**
    082     * Converts an array of characters representing hexadecimal values into an array of bytes of those same values. The
    083     * returned array will be half the length of the passed array, as it takes two characters to represent any given
    084     * byte. An exception is thrown if the passed char array has an odd number of elements.
    085     *
    086     * @param data An array of characters containing hexadecimal digits
    087     * @param out A byte array to contain the binary data decoded from the supplied char array.
    088     * @param outOffset The position within {@code out} to start writing the decoded bytes.
    089     * @return the number of bytes written to {@code out}.
    090     * @throws DecoderException Thrown if an odd number of characters or illegal characters are supplied
    091     * @since 1.15
    092     */
    093    public static int decodeHex(final char[] data, final byte[] out, final int outOffset) throws DecoderException {
    094        final int len = data.length;
    095
    096        if ((len & 0x01) != 0) {
    097            throw new DecoderException("Odd number of characters.");
    098        }
    099
    100        final int outLen = len >> 1;
    101        if (out.length - outOffset < outLen) {
    102            throw new DecoderException("Output array is not large enough to accommodate decoded data.");
    103        }
    104
    105        // two characters form the hex value.
    106        for (int i = outOffset, j = 0; j < len; i++) {
    107            int f = toDigit(data[j], j) << 4;
    108            j++;
    109            f |= toDigit(data[j], j);
    110            j++;
    111            out[i] = (byte) (f & 0xFF);
    112        }
    113
    114        return outLen;
    115    }
    116
    117    /**
    118     * Converts a String representing hexadecimal values into an array of bytes of those same values. The returned array
    119     * will be half the length of the passed String, as it takes two characters to represent any given byte. An
    120     * exception is thrown if the passed String has an odd number of elements.
    121     *
    122     * @param data A String containing hexadecimal digits
    123     * @return A byte array containing binary data decoded from the supplied char array.
    124     * @throws DecoderException Thrown if an odd number of characters or illegal characters are supplied
    125     * @since 1.11
    126     */
    127    public static byte[] decodeHex(final String data) throws DecoderException {
    128        return decodeHex(data.toCharArray());
    129    }
    130
    131    /**
    132     * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order.
    133     * The returned array will be double the length of the passed array, as it takes two characters to represent any
    134     * given byte.
    135     *
    136     * @param data a byte[] to convert to hexadecimal characters
    137     * @return A char[] containing lower-case hexadecimal characters
    138     */
    139    public static char[] encodeHex(final byte[] data) {
    140        return encodeHex(data, true);
    141    }
    142
    143    /**
    144     * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order.
    145     * The returned array will be double the length of the passed array, as it takes two characters to represent any
    146     * given byte.
    147     *
    148     * @param data        a byte[] to convert to Hex characters
    149     * @param toLowerCase {@code true} converts to lowercase, {@code false} to uppercase
    150     * @return A char[] containing hexadecimal characters in the selected case
    151     * @since 1.4
    152     */
    153    public static char[] encodeHex(final byte[] data, final boolean toLowerCase) {
    154        return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
    155    }
    156
    157    /**
    158     * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order.
    159     * The returned array will be double the length of the passed array, as it takes two characters to represent any
    160     * given byte.
    161     *
    162     * @param data     a byte[] to convert to hexadecimal characters
    163     * @param toDigits the output alphabet (must contain at least 16 chars)
    164     * @return A char[] containing the appropriate characters from the alphabet For best results, this should be either
    165     *         upper- or lower-case hex.
    166     * @since 1.4
    167     */
    168    protected static char[] encodeHex(final byte[] data, final char[] toDigits) {
    169        final int dataLength = data.length;
    170        final char[] out = new char[dataLength << 1];
    171        encodeHex(data, 0, dataLength, toDigits, out, 0);
    172        return out;
    173    }
    174
    175    /**
    176     * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order.
    177     *
    178     * @param data a byte[] to convert to hexadecimal characters
    179     * @param dataOffset the position in {@code data} to start encoding from
    180     * @param dataLen the number of bytes from {@code dataOffset} to encode
    181     * @param toLowerCase {@code true} converts to lowercase, {@code false} to uppercase
    182     * @return A char[] containing the appropriate characters from the alphabet For best results, this should be either
    183     *         upper- or lower-case hex.
    184     * @since 1.15
    185     */
    186    public static char[] encodeHex(final byte[] data, final int dataOffset, final int dataLen,
    187            final boolean toLowerCase) {
    188        final char[] out = new char[dataLen << 1];
    189        encodeHex(data, dataOffset, dataLen, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER, out, 0);
    190        return out;
    191    }
    192
    193    /**
    194     * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order.
    195     *
    196     * @param data a byte[] to convert to hexadecimal characters
    197     * @param dataOffset the position in {@code data} to start encoding from
    198     * @param dataLen the number of bytes from {@code dataOffset} to encode
    199     * @param toLowerCase {@code true} converts to lowercase, {@code false} to uppercase
    200     * @param out a char[] which will hold the resultant appropriate characters from the alphabet.
    201     * @param outOffset the position within {@code out} at which to start writing the encoded characters.
    202     * @since 1.15
    203     */
    204    public static void encodeHex(final byte[] data, final int dataOffset, final int dataLen,
    205            final boolean toLowerCase, final char[] out, final int outOffset) {
    206        encodeHex(data, dataOffset, dataLen, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER, out, outOffset);
    207    }
    208
    209    /**
    210     * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order.
    211     *
    212     * @param data a byte[] to convert to hexadecimal characters
    213     * @param dataOffset the position in {@code data} to start encoding from
    214     * @param dataLen the number of bytes from {@code dataOffset} to encode
    215     * @param toDigits the output alphabet (must contain at least 16 chars)
    216     * @param out a char[] which will hold the resultant appropriate characters from the alphabet.
    217     * @param outOffset the position within {@code out} at which to start writing the encoded characters.
    218     */
    219    private static void encodeHex(final byte[] data, final int dataOffset, final int dataLen, final char[] toDigits,
    220            final char[] out, final int outOffset) {
    221        // two characters form the hex value.
    222        for (int i = dataOffset, j = outOffset; i < dataOffset + dataLen; i++) {
    223            out[j++] = toDigits[(0xF0 & data[i]) >>> 4];
    224            out[j++] = toDigits[0x0F & data[i]];
    225        }
    226    }
    227
    228    /**
    229     * Converts a byte buffer into an array of characters representing the hexadecimal values of each byte in order. The
    230     * returned array will be double the length of the passed array, as it takes two characters to represent any given
    231     * byte.
    232     *
    233     * <p>All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method
    234     * the value {@link ByteBuffer#remaining() remaining()} will be zero.</p>
    235     *
    236     * @param data a byte buffer to convert to hexadecimal characters
    237     * @return A char[] containing lower-case hexadecimal characters
    238     * @since 1.11
    239     */
    240    public static char[] encodeHex(final ByteBuffer data) {
    241        return encodeHex(data, true);
    242    }
    243
    244    /**
    245     * Converts a byte buffer into an array of characters representing the hexadecimal values of each byte in order. The
    246     * returned array will be double the length of the passed array, as it takes two characters to represent any given
    247     * byte.
    248     *
    249     * <p>All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method
    250     * the value {@link ByteBuffer#remaining() remaining()} will be zero.</p>
    251     *
    252     * @param data        a byte buffer to convert to hexadecimal characters
    253     * @param toLowerCase {@code true} converts to lowercase, {@code false} to uppercase
    254     * @return A char[] containing hexadecimal characters in the selected case
    255     * @since 1.11
    256     */
    257    public static char[] encodeHex(final ByteBuffer data, final boolean toLowerCase) {
    258        return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
    259    }
    260
    261    /**
    262     * Converts a byte buffer into an array of characters representing the hexadecimal values of each byte in order. The
    263     * returned array will be double the length of the passed array, as it takes two characters to represent any given
    264     * byte.
    265     *
    266     * <p>All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method
    267     * the value {@link ByteBuffer#remaining() remaining()} will be zero.</p>
    268     *
    269     * @param byteBuffer a byte buffer to convert to hexadecimal characters
    270     * @param toDigits   the output alphabet (must be at least 16 characters)
    271     * @return A char[] containing the appropriate characters from the alphabet For best results, this should be either
    272     *         upper- or lower-case hex.
    273     * @since 1.11
    274     */
    275    protected static char[] encodeHex(final ByteBuffer byteBuffer, final char[] toDigits) {
    276        return encodeHex(toByteArray(byteBuffer), toDigits);
    277    }
    278
    279    /**
    280     * Converts an array of bytes into a String representing the hexadecimal values of each byte in order. The returned
    281     * String will be double the length of the passed array, as it takes two characters to represent any given byte.
    282     *
    283     * @param data a byte[] to convert to hexadecimal characters
    284     * @return A String containing lower-case hexadecimal characters
    285     * @since 1.4
    286     */
    287    public static String encodeHexString(final byte[] data) {
    288        return new String(encodeHex(data));
    289    }
    290
    291    /**
    292     * Converts an array of bytes into a String representing the hexadecimal values of each byte in order. The returned
    293     * String will be double the length of the passed array, as it takes two characters to represent any given byte.
    294     *
    295     * @param data        a byte[] to convert to hexadecimal characters
    296     * @param toLowerCase {@code true} converts to lowercase, {@code false} to uppercase
    297     * @return A String containing lower-case hexadecimal characters
    298     * @since 1.11
    299     */
    300    public static String encodeHexString(final byte[] data, final boolean toLowerCase) {
    301        return new String(encodeHex(data, toLowerCase));
    302    }
    303
    304    /**
    305     * Converts a byte buffer into a String representing the hexadecimal values of each byte in order. The returned
    306     * String will be double the length of the passed array, as it takes two characters to represent any given byte.
    307     *
    308     * <p>All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method
    309     * the value {@link ByteBuffer#remaining() remaining()} will be zero.</p>
    310     *
    311     * @param data a byte buffer to convert to hexadecimal characters
    312     * @return A String containing lower-case hexadecimal characters
    313     * @since 1.11
    314     */
    315    public static String encodeHexString(final ByteBuffer data) {
    316        return new String(encodeHex(data));
    317    }
    318
    319    /**
    320     * Converts a byte buffer into a String representing the hexadecimal values of each byte in order. The returned
    321     * String will be double the length of the passed array, as it takes two characters to represent any given byte.
    322     *
    323     * <p>All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method
    324     * the value {@link ByteBuffer#remaining() remaining()} will be zero.</p>
    325     *
    326     * @param data        a byte buffer to convert to hexadecimal characters
    327     * @param toLowerCase {@code true} converts to lowercase, {@code false} to uppercase
    328     * @return A String containing lower-case hexadecimal characters
    329     * @since 1.11
    330     */
    331    public static String encodeHexString(final ByteBuffer data, final boolean toLowerCase) {
    332        return new String(encodeHex(data, toLowerCase));
    333    }
    334
    335    /**
    336     * Convert the byte buffer to a byte array. All bytes identified by
    337     * {@link ByteBuffer#remaining()} will be used.
    338     *
    339     * @param byteBuffer the byte buffer
    340     * @return the byte[]
    341     */
    342    private static byte[] toByteArray(final ByteBuffer byteBuffer) {
    343        final int remaining = byteBuffer.remaining();
    344        // Use the underlying buffer if possible
    345        if (byteBuffer.hasArray()) {
    346            final byte[] byteArray = byteBuffer.array();
    347            if (remaining == byteArray.length) {
    348                byteBuffer.position(remaining);
    349                return byteArray;
    350            }
    351        }
    352        // Copy the bytes
    353        final byte[] byteArray = new byte[remaining];
    354        byteBuffer.get(byteArray);
    355        return byteArray;
    356    }
    357
    358    /**
    359     * Converts a hexadecimal character to an integer.
    360     *
    361     * @param ch    A character to convert to an integer digit
    362     * @param index The index of the character in the source
    363     * @return An integer
    364     * @throws DecoderException Thrown if ch is an illegal hexadecimal character
    365     */
    366    protected static int toDigit(final char ch, final int index) throws DecoderException {
    367        final int digit = Character.digit(ch, 16);
    368        if (digit == -1) {
    369            throw new DecoderException("Illegal hexadecimal character " + ch + " at index " + index);
    370        }
    371        return digit;
    372    }
    373
    374    private final Charset charset;
    375
    376    /**
    377     * Creates a new codec with the default charset name {@link #DEFAULT_CHARSET}
    378     */
    379    public Hex() {
    380        // use default encoding
    381        this.charset = DEFAULT_CHARSET;
    382    }
    383
    384    /**
    385     * Creates a new codec with the given Charset.
    386     *
    387     * @param charset the charset.
    388     * @since 1.7
    389     */
    390    public Hex(final Charset charset) {
    391        this.charset = charset;
    392    }
    393
    394    /**
    395     * Creates a new codec with the given charset name.
    396     *
    397     * @param charsetName the charset name.
    398     * @throws java.nio.charset.UnsupportedCharsetException If the named charset is unavailable
    399     * @since 1.4
    400     * @since 1.7 throws UnsupportedCharsetException if the named charset is unavailable
    401     */
    402    public Hex(final String charsetName) {
    403        this(Charset.forName(charsetName));
    404    }
    405
    406    /**
    407     * Converts an array of character bytes representing hexadecimal values into an array of bytes of those same values.
    408     * The returned array will be half the length of the passed array, as it takes two characters to represent any given
    409     * byte. An exception is thrown if the passed char array has an odd number of elements.
    410     *
    411     * @param array An array of character bytes containing hexadecimal digits
    412     * @return A byte array containing binary data decoded from the supplied byte array (representing characters).
    413     * @throws DecoderException Thrown if an odd number of characters is supplied to this function
    414     * @see #decodeHex(char[])
    415     */
    416    @Override
    417    public byte[] decode(final byte[] array) throws DecoderException {
    418        return decodeHex(new String(array, getCharset()).toCharArray());
    419    }
    420
    421    /**
    422     * Converts a buffer of character bytes representing hexadecimal values into an array of bytes of those same values.
    423     * The returned array will be half the length of the passed array, as it takes two characters to represent any given
    424     * byte. An exception is thrown if the passed char array has an odd number of elements.
    425     *
    426     * <p>All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method
    427     * the value {@link ByteBuffer#remaining() remaining()} will be zero.</p>
    428     *
    429     * @param buffer An array of character bytes containing hexadecimal digits
    430     * @return A byte array containing binary data decoded from the supplied byte array (representing characters).
    431     * @throws DecoderException Thrown if an odd number of characters is supplied to this function
    432     * @see #decodeHex(char[])
    433     * @since 1.11
    434     */
    435    public byte[] decode(final ByteBuffer buffer) throws DecoderException {
    436        return decodeHex(new String(toByteArray(buffer), getCharset()).toCharArray());
    437    }
    438
    439    /**
    440     * Converts a String or an array of character bytes representing hexadecimal values into an array of bytes of those
    441     * same values. The returned array will be half the length of the passed String or array, as it takes two characters
    442     * to represent any given byte. An exception is thrown if the passed char array has an odd number of elements.
    443     *
    444     * @param object A String, ByteBuffer, byte[], or an array of character bytes containing hexadecimal digits
    445     * @return A byte array containing binary data decoded from the supplied byte array (representing characters).
    446     * @throws DecoderException Thrown if an odd number of characters is supplied to this function or the object is not
    447     *                          a String or char[]
    448     * @see #decodeHex(char[])
    449     */
    450    @Override
    451    public Object decode(final Object object) throws DecoderException {
    452        if (object instanceof String) {
    453            return decode(((String) object).toCharArray());
    454        }
    455        if (object instanceof byte[]) {
    456            return decode((byte[]) object);
    457        }
    458        if (object instanceof ByteBuffer) {
    459            return decode((ByteBuffer) object);
    460        }
    461        try {
    462            return decodeHex((char[]) object);
    463        } catch (final ClassCastException e) {
    464            throw new DecoderException(e.getMessage(), e);
    465        }
    466    }
    467
    468    /**
    469     * Converts an array of bytes into an array of bytes for the characters representing the hexadecimal values of each
    470     * byte in order. The returned array will be double the length of the passed array, as it takes two characters to
    471     * represent any given byte.
    472     * <p>
    473     * The conversion from hexadecimal characters to the returned bytes is performed with the charset named by
    474     * {@link #getCharset()}.
    475     * </p>
    476     *
    477     * @param array a byte[] to convert to hexadecimal characters
    478     * @return A byte[] containing the bytes of the lower-case hexadecimal characters
    479     * @since 1.7 No longer throws IllegalStateException if the charsetName is invalid.
    480     * @see #encodeHex(byte[])
    481     */
    482    @Override
    483    public byte[] encode(final byte[] array) {
    484        return encodeHexString(array).getBytes(this.getCharset());
    485    }
    486
    487    /**
    488     * Converts byte buffer into an array of bytes for the characters representing the hexadecimal values of each byte
    489     * in order. The returned array will be double the length of the passed array, as it takes two characters to
    490     * represent any given byte.
    491     *
    492     * <p>The conversion from hexadecimal characters to the returned bytes is performed with the charset named by
    493     * {@link #getCharset()}.</p>
    494     *
    495     * <p>All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method
    496     * the value {@link ByteBuffer#remaining() remaining()} will be zero.</p>
    497     *
    498     * @param array a byte buffer to convert to hexadecimal characters
    499     * @return A byte[] containing the bytes of the lower-case hexadecimal characters
    500     * @see #encodeHex(byte[])
    501     * @since 1.11
    502     */
    503    public byte[] encode(final ByteBuffer array) {
    504        return encodeHexString(array).getBytes(this.getCharset());
    505    }
    506
    507    /**
    508     * Converts a String or an array of bytes into an array of characters representing the hexadecimal values of each
    509     * byte in order. The returned array will be double the length of the passed String or array, as it takes two
    510     * characters to represent any given byte.
    511     * <p>
    512     * The conversion from hexadecimal characters to bytes to be encoded to performed with the charset named by
    513     * {@link #getCharset()}.
    514     * </p>
    515     *
    516     * @param object a String, ByteBuffer, or byte[] to convert to hexadecimal characters
    517     * @return A char[] containing lower-case hexadecimal characters
    518     * @throws EncoderException Thrown if the given object is not a String or byte[]
    519     * @see #encodeHex(byte[])
    520     */
    521    @Override
    522    public Object encode(final Object object) throws EncoderException {
    523        final byte[] byteArray;
    524        if (object instanceof String) {
    525            byteArray = ((String) object).getBytes(this.getCharset());
    526        } else if (object instanceof ByteBuffer) {
    527            byteArray = toByteArray((ByteBuffer) object);
    528        } else {
    529            try {
    530                byteArray = (byte[]) object;
    531            } catch (final ClassCastException e) {
    532                throw new EncoderException(e.getMessage(), e);
    533            }
    534        }
    535        return encodeHex(byteArray);
    536    }
    537
    538    /**
    539     * Gets the charset.
    540     *
    541     * @return the charset.
    542     * @since 1.7
    543     */
    544    public Charset getCharset() {
    545        return this.charset;
    546    }
    547
    548    /**
    549     * Gets the charset name.
    550     *
    551     * @return the charset name.
    552     * @since 1.4
    553     */
    554    public String getCharsetName() {
    555        return this.charset.name();
    556    }
    557
    558    /**
    559     * Returns a string representation of the object, which includes the charset name.
    560     *
    561     * @return a string representation of the object.
    562     */
    563    @Override
    564    public String toString() {
    565        return super.toString() + "[charsetName=" + this.charset + "]";
    566    }
    567}