001// Generated by delombok at Mon Jan 02 16:27:27 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 int len = data.length;
067        if ((len & 1) != 0) {
068            throw new DecoderException("Odd number of characters.");
069        }
070        final byte[] out = new byte[len >> 1];
071        // two characters form the hex value.
072        for (int i = 0, j = 0; j < len; i++) {
073            int 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 int l = data.length;
167        final char[] 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            out[j++] = toDigits[15 & data[i]];
172        }
173        return out;
174    }
175
176    /**
177     * Converts a byte buffer into an array of characters representing the hexadecimal values of
178     * each byte in order. The
179     * returned array will be double the length of the passed array, as it takes two characters to
180     * represent any given
181     * byte.
182     *
183     * <p>
184     * All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method
185     * the value {@link ByteBuffer#remaining() remaining()} will be zero.
186     * </p>
187     *
188     * @param byteBuffer a byte buffer to convert to Hex characters
189     * @param toDigits the output alphabet (must be at least 16 characters)
190     * @return A char[] containing the appropriate characters from the alphabet For best results,
191     *         this should be either
192     *         upper- or lower-case hex.
193     */
194    protected static char[] encodeHex(final ByteBuffer byteBuffer, final char[] toDigits) {
195        return encodeHex(toByteArray(byteBuffer), toDigits);
196    }
197
198    /**
199     * Converts an array of bytes into a String representing the hexadecimal values of each byte in
200     * order. The returned
201     * String will be double the length of the passed array, as it takes two characters to represent
202     * any given byte.
203     *
204     * @param data a byte[] to convert to Hex characters
205     * @return A String containing lower-case hexadecimal characters
206     */
207    public static String encodeHexString(final byte[] data) {
208        return new String(encodeHex(data));
209    }
210
211    /**
212     * Converts an array of bytes into a String representing the hexadecimal values of each byte in
213     * order. The returned
214     * String will be double the length of the passed array, as it takes two characters to represent
215     * any given byte.
216     *
217     * @param data a byte[] to convert to Hex characters
218     * @param toLowerCase {@code true} converts to lowercase, {@code false} to uppercase
219     * @return A String containing lower-case hexadecimal characters
220     */
221    public static String encodeHexString(final byte[] data, final boolean toLowerCase) {
222        return new String(encodeHex(data, toLowerCase));
223    }
224
225    /**
226     * Converts a byte buffer into a String representing the hexadecimal values of each byte in
227     * order. The returned
228     * String will be double the length of the passed array, as it takes two characters to represent
229     * any given byte.
230     *
231     * <p>
232     * All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method
233     * the value {@link ByteBuffer#remaining() remaining()} will be zero.
234     * </p>
235     *
236     * @param data a byte buffer to convert to Hex characters
237     * @return A String containing lower-case hexadecimal characters
238     */
239    public static String encodeHexString(final ByteBuffer data) {
240        return new String(encodeHex(data));
241    }
242
243    /**
244     * Converts a byte buffer into a String representing the hexadecimal values of each byte in
245     * order. The returned
246     * String will be double the length of the passed array, as it takes two characters to represent
247     * any given byte.
248     *
249     * <p>
250     * All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method
251     * the value {@link ByteBuffer#remaining() remaining()} will be zero.
252     * </p>
253     *
254     * @param data a byte buffer to convert to Hex characters
255     * @param toLowerCase {@code true} converts to lowercase, {@code false} to uppercase
256     * @return A String containing lower-case hexadecimal characters
257     */
258    public static String encodeHexString(final ByteBuffer data, final boolean toLowerCase) {
259        return new String(encodeHex(data, toLowerCase));
260    }
261
262    /**
263     * Convert the byte buffer to a byte array. All bytes identified by
264     * {@link ByteBuffer#remaining()} will be used.
265     *
266     * @param byteBuffer the byte buffer
267     * @return the byte[]
268     */
269    private static byte[] toByteArray(final ByteBuffer byteBuffer) {
270        final int remaining = byteBuffer.remaining();
271        // Use the underlying buffer if possible
272        if (byteBuffer.hasArray()) {
273            final byte[] byteArray = byteBuffer.array();
274            if (remaining == byteArray.length) {
275                byteBuffer.position(remaining);
276                return byteArray;
277            }
278        }
279        // Copy the bytes
280        final byte[] byteArray = new byte[remaining];
281        byteBuffer.get(byteArray);
282        return byteArray;
283    }
284
285    /**
286     * Converts a hexadecimal character to an integer.
287     *
288     * @param ch A character to convert to an integer digit
289     * @param index The index of the character in the source
290     * @return An integer
291     * @throws DecoderException Thrown if ch is an illegal hex character
292     */
293    protected static int toDigit(final char ch, final int index) throws DecoderException {
294        final int digit = Character.digit(ch, 16);
295        if (digit == -1) {
296            throw new DecoderException("Illegal hexadecimal character " + ch + " at index " + index);
297        }
298        return digit;
299    }
300
301    private final Charset charset;
302
303    /**
304     * Creates a new codec with the default charset name {@link #DEFAULT_CHARSET}
305     */
306    public Hex() {
307        // use default encoding
308        charset = DEFAULT_CHARSET;
309    }
310
311    /**
312     * Creates a new codec with the given Charset.
313     *
314     * @param charset the charset.
315     */
316    public Hex(final Charset charset) {
317        this.charset = charset;
318    }
319
320    /**
321     * Creates a new codec with the given charset name.
322     *
323     * @param charsetName the charset name.
324     * @throws java.nio.charset.UnsupportedCharsetException If the named charset is unavailable
325     *
326     *             throws UnsupportedCharsetException if the named charset is unavailable
327     */
328    public Hex(final String charsetName) {
329        this(Charset.forName(charsetName));
330    }
331
332    /**
333     * Converts an array of character bytes representing hexadecimal values into an array of bytes
334     * of those same values.
335     * The returned array will be half the length of the passed array, as it takes two characters to
336     * represent any given
337     * byte. An exception is thrown if the passed char array has an odd number of elements.
338     *
339     * @param array An array of character bytes containing hexadecimal digits
340     * @return A byte array containing binary data decoded from the supplied byte array
341     *         (representing characters).
342     * @throws DecoderException Thrown if an odd number of characters is supplied to this function
343     * @see #decodeHex(char[])
344     */
345    public byte[] decode(final byte[] array) throws DecoderException {
346        return decodeHex(new String(array, getCharset()).toCharArray());
347    }
348
349    /**
350     * Converts a buffer of character bytes representing hexadecimal values into an array of bytes
351     * of those same values.
352     * The returned array will be half the length of the passed array, as it takes two characters to
353     * represent any given
354     * byte. An exception is thrown if the passed char array has an odd number of elements.
355     *
356     * <p>
357     * All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method
358     * the value {@link ByteBuffer#remaining() remaining()} will be zero.
359     * </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
363     *         (representing characters).
364     * @throws DecoderException Thrown if an odd number of characters is supplied to this function
365     * @see #decodeHex(char[])
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
373     * array of bytes of those
374     * same values. The returned array will be half the length of the passed String or array, as it
375     * takes two characters
376     * to represent any given byte. An exception is thrown if the passed char array has an odd
377     * number of elements.
378     *
379     * @param object A String, ByteBuffer, byte[], or an array of character bytes containing
380     *            hexadecimal digits
381     * @return A byte array containing binary data decoded from the supplied byte array
382     *         (representing characters).
383     * @throws DecoderException Thrown if an odd number of characters is supplied to this function
384     *             or the object is not
385     *             a String or char[]
386     * @see #decodeHex(char[])
387     */
388    public Object decode(final Object object) throws DecoderException {
389        if (object instanceof String) {
390            return decode(((String) object).toCharArray());
391        } else if (object instanceof byte[]) {
392            return decode((byte[]) object);
393        } else if (object instanceof ByteBuffer) {
394            return decode((ByteBuffer) object);
395        } else {
396            try {
397                return decodeHex((char[]) object);
398            } catch (final ClassCastException e) {
399                throw new DecoderException(e.getMessage(), e);
400            }
401        }
402    }
403
404    /**
405     * Converts an array of bytes into an array of bytes for the characters representing the
406     * hexadecimal values of each
407     * byte in order. The returned array will be double the length of the passed array, as it takes
408     * two characters to
409     * represent any given byte.
410     * <p>
411     * The conversion from hexadecimal characters to the returned bytes is performed with the
412     * charset named by {@link #getCharset()}.
413     * </p>
414     *
415     * @param array a byte[] to convert to Hex characters
416     * @return A byte[] containing the bytes of the lower-case hexadecimal characters
417     *         No longer throws IllegalStateException if the charsetName is invalid.
418     * @see #encodeHex(byte[])
419     */
420    public byte[] encode(final byte[] array) {
421        return encodeHexString(array).getBytes(getCharset());
422    }
423
424    /**
425     * Converts byte buffer into an array of bytes for the characters representing the hexadecimal
426     * values of each byte
427     * in order. The returned array will be double the length of the passed array, as it takes two
428     * characters to
429     * represent any given byte.
430     *
431     * <p>
432     * The conversion from hexadecimal characters to the returned bytes is performed with the
433     * charset named by
434     * {@link #getCharset()}.
435     * </p>
436     *
437     * <p>
438     * All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method
439     * the value {@link ByteBuffer#remaining() remaining()} will be zero.
440     * </p>
441     *
442     * @param array a byte buffer to convert to Hex characters
443     * @return A byte[] containing the bytes of the lower-case hexadecimal characters
444     * @see #encodeHex(byte[])
445     */
446    public byte[] encode(final ByteBuffer array) {
447        return encodeHexString(array).getBytes(getCharset());
448    }
449
450    /**
451     * Converts a String or an array of bytes into an array of characters representing the
452     * hexadecimal values of each
453     * byte in order. The returned array will be double the length of the passed String or array, as
454     * it takes two
455     * characters to represent any given byte.
456     * <p>
457     * The conversion from hexadecimal characters to bytes to be encoded to performed with the
458     * charset named by {@link #getCharset()}.
459     * </p>
460     *
461     * @param object a String, ByteBuffer, or byte[] to convert to Hex characters
462     * @return A char[] containing lower-case hexadecimal characters
463     * @throws EncoderException Thrown if the given object is not a String or byte[]
464     * @see #encodeHex(byte[])
465     */
466    public Object encode(final Object object) throws EncoderException {
467        byte[] byteArray;
468        if (object instanceof String) {
469            byteArray = ((String) object).getBytes(getCharset());
470        } else if (object instanceof ByteBuffer) {
471            byteArray = toByteArray((ByteBuffer) object);
472        } else {
473            try {
474                byteArray = (byte[]) object;
475            } catch (final ClassCastException e) {
476                throw new EncoderException(e.getMessage(), e);
477            }
478        }
479        return encodeHex(byteArray);
480    }
481
482    @java.lang.Override
483    @java.lang.SuppressWarnings("all")
484    @lombok.Generated
485    public java.lang.String toString() {
486        return "Hex(charset=" + this.getCharset() + ")";
487    }
488
489    @java.lang.SuppressWarnings("all")
490    @lombok.Generated
491    public Charset getCharset() {
492        return this.charset;
493    }
494}