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