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