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