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