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