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
23 import org.apache.commons.codec.BinaryDecoder;
24 import org.apache.commons.codec.BinaryEncoder;
25 import org.apache.commons.codec.CharEncoding;
26 import org.apache.commons.codec.Charsets;
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 * @version $Id: Hex.java 1811344 2017-10-06 15:19:57Z ggregory $
38 */
39 public class Hex implements BinaryEncoder, BinaryDecoder {
40
41 /**
42 * Default charset is {@link Charsets#UTF_8}
43 *
44 * @since 1.7
45 */
46 public static final Charset DEFAULT_CHARSET = Charsets.UTF_8;
47
48 /**
49 * Default charset name is {@link CharEncoding#UTF_8}
50 *
51 * @since 1.4
52 */
53 public static final String DEFAULT_CHARSET_NAME = CharEncoding.UTF_8;
54
55 /**
56 * Used to build output as Hex
57 */
58 private static final char[] DIGITS_LOWER =
59 {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
60
61 /**
62 * Used to build output as Hex
63 */
64 private static final char[] DIGITS_UPPER =
65 {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
66
67 /**
68 * Converts a String representing hexadecimal values into an array of bytes of those same values. The
69 * returned array will be half the length of the passed String, as it takes two characters to represent any given
70 * byte. An exception is thrown if the passed String has an odd number of elements.
71 *
72 * @param data
73 * A String containing hexadecimal digits
74 * @return A byte array containing binary data decoded from the supplied char array.
75 * @throws DecoderException
76 * Thrown if an odd number or illegal of characters is supplied
77 * @since 1.11
78 */
79 public static byte[] decodeHex(final String data) throws DecoderException {
80 return decodeHex(data.toCharArray());
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
89 * An array of characters containing hexadecimal digits
90 * @return A byte array containing binary data decoded from the supplied char array.
91 * @throws DecoderException
92 * Thrown if an odd number or illegal of characters is supplied
93 */
94 public static byte[] decodeHex(final char[] data) throws DecoderException {
95
96 final int len = data.length;
97
98 if ((len & 0x01) != 0) {
99 throw new DecoderException("Odd number of characters.");
100 }
101
102 final byte[] out = new byte[len >> 1];
103
104 // two characters form the hex value.
105 for (int i = 0, j = 0; j < len; i++) {
106 int f = toDigit(data[j], j) << 4;
107 j++;
108 f = f | toDigit(data[j], j);
109 j++;
110 out[i] = (byte) (f & 0xFF);
111 }
112
113 return out;
114 }
115
116 /**
117 * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order.
118 * The returned array will be double the length of the passed array, as it takes two characters to represent any
119 * given byte.
120 *
121 * @param data
122 * a byte[] to convert to Hex characters
123 * @return A char[] containing lower-case hexadecimal characters
124 */
125 public static char[] encodeHex(final byte[] data) {
126 return encodeHex(data, true);
127 }
128
129 /**
130 * Converts a byte buffer into an array of characters representing the hexadecimal values of each byte in order.
131 * The returned array will be double the length of the passed array, as it takes two characters to represent any
132 * given byte.
133 *
134 * @param data
135 * a byte buffer to convert to Hex characters
136 * @return A char[] containing lower-case hexadecimal characters
137 * @since 1.11
138 */
139 public static char[] encodeHex(final ByteBuffer data) {
140 return encodeHex(data, true);
141 }
142
143 /**
144 * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order.
145 * The returned array will be double the length of the passed array, as it takes two characters to represent any
146 * given byte.
147 *
148 * @param data
149 * a byte[] to convert to Hex characters
150 * @param toLowerCase
151 * <code>true</code> converts to lowercase, <code>false</code> 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 a byte buffer 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
165 * a byte buffer to convert to Hex characters
166 * @param toLowerCase
167 * <code>true</code> converts to lowercase, <code>false</code> to uppercase
168 * @return A char[] containing hexadecimal characters in the selected case
169 * @since 1.11
170 */
171 public static char[] encodeHex(final ByteBuffer data, final boolean toLowerCase) {
172 return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
173 }
174
175 /**
176 * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order.
177 * The returned array will be double the length of the passed array, as it takes two characters to represent any
178 * given byte.
179 *
180 * @param data
181 * a byte[] to convert to Hex characters
182 * @param toDigits
183 * the output alphabet (must contain at least 16 chars)
184 * @return A char[] containing the appropriate characters from the alphabet
185 * For best results, this should be either upper- or lower-case hex.
186 * @since 1.4
187 */
188 protected static char[] encodeHex(final byte[] data, final char[] toDigits) {
189 final int l = data.length;
190 final char[] out = new char[l << 1];
191 // two characters form the hex value.
192 for (int i = 0, j = 0; i < l; i++) {
193 out[j++] = toDigits[(0xF0 & data[i]) >>> 4];
194 out[j++] = toDigits[0x0F & data[i]];
195 }
196 return out;
197 }
198
199 /**
200 * Converts a byte buffer into an array of characters representing the hexadecimal values of each byte in order.
201 * The returned array will be double the length of the passed array, as it takes two characters to represent any
202 * given byte.
203 *
204 * @param data
205 * a byte buffer to convert to Hex characters
206 * @param toDigits
207 * the output alphabet (must be at least 16 characters)
208 * @return A char[] containing the appropriate characters from the alphabet
209 * For best results, this should be either upper- or lower-case hex.
210 * @since 1.11
211 */
212 protected static char[] encodeHex(final ByteBuffer data, final char[] toDigits) {
213 return encodeHex(data.array(), toDigits);
214 }
215
216 /**
217 * Converts an array of bytes into a String representing the hexadecimal values of each byte in order. The returned
218 * String will be double the length of the passed array, as it takes two characters to represent any given byte.
219 *
220 * @param data
221 * a byte[] to convert to Hex characters
222 * @return A String containing lower-case hexadecimal characters
223 * @since 1.4
224 */
225 public static String encodeHexString(final byte[] data) {
226 return new String(encodeHex(data));
227 }
228
229 /**
230 * Converts an array of bytes into a String representing the hexadecimal values of each byte in order. The returned
231 * String will be double the length of the passed array, as it takes two characters to represent any given byte.
232 *
233 * @param data
234 * a byte[] to convert to Hex characters
235 * @param toLowerCase
236 * <code>true</code> converts to lowercase, <code>false</code> to uppercase
237 * @return A String containing lower-case hexadecimal characters
238 * @since 1.11
239 */
240 public static String encodeHexString(final byte[] data, final boolean toLowerCase) {
241 return new String(encodeHex(data, toLowerCase));
242 }
243
244 /**
245 * Converts a byte buffer into a String representing the hexadecimal values of each byte in order. The returned
246 * String will be double the length of the passed array, as it takes two characters to represent any given byte.
247 *
248 * @param data
249 * a byte buffer to convert to Hex characters
250 * @return A String containing lower-case hexadecimal characters
251 * @since 1.11
252 */
253 public static String encodeHexString(final ByteBuffer data) {
254 return new String(encodeHex(data));
255 }
256
257 /**
258 * Converts a byte buffer into a String representing the hexadecimal values of each byte in order. The returned
259 * String will be double the length of the passed array, as it takes two characters to represent any given byte.
260 *
261 * @param data
262 * a byte buffer to convert to Hex characters
263 * @param toLowerCase
264 * <code>true</code> converts to lowercase, <code>false</code> to uppercase
265 * @return A String containing lower-case hexadecimal characters
266 * @since 1.11
267 */
268 public static String encodeHexString(final ByteBuffer data, final boolean toLowerCase) {
269 return new String(encodeHex(data, toLowerCase));
270 }
271
272 /**
273 * Converts a hexadecimal character to an integer.
274 *
275 * @param ch
276 * A character to convert to an integer digit
277 * @param index
278 * The index of the character in the source
279 * @return An integer
280 * @throws DecoderException
281 * Thrown if ch is an illegal hex character
282 */
283 protected static int toDigit(final char ch, final int index) throws DecoderException {
284 final int digit = Character.digit(ch, 16);
285 if (digit == -1) {
286 throw new DecoderException("Illegal hexadecimal character " + ch + " at index " + index);
287 }
288 return digit;
289 }
290
291 private final Charset charset;
292
293 /**
294 * Creates a new codec with the default charset name {@link #DEFAULT_CHARSET}
295 */
296 public Hex() {
297 // use default encoding
298 this.charset = DEFAULT_CHARSET;
299 }
300
301 /**
302 * Creates a new codec with the given Charset.
303 *
304 * @param charset
305 * the charset.
306 * @since 1.7
307 */
308 public Hex(final Charset charset) {
309 this.charset = charset;
310 }
311
312 /**
313 * Creates a new codec with the given charset name.
314 *
315 * @param charsetName
316 * the charset name.
317 * @throws java.nio.charset.UnsupportedCharsetException
318 * If the named charset is unavailable
319 * @since 1.4
320 * @since 1.7 throws UnsupportedCharsetException if the named charset is unavailable
321 */
322 public Hex(final String charsetName) {
323 this(Charset.forName(charsetName));
324 }
325
326 /**
327 * Converts an array of character bytes representing hexadecimal values into an array of bytes of those same values.
328 * The returned array will be half the length of the passed array, as it takes two characters to represent any given
329 * byte. An exception is thrown if the passed char array has an odd number of elements.
330 *
331 * @param array
332 * An array of character bytes containing hexadecimal digits
333 * @return A byte array containing binary data decoded from the supplied byte array (representing characters).
334 * @throws DecoderException
335 * Thrown if an odd number of characters is supplied to this function
336 * @see #decodeHex(char[])
337 */
338 @Override
339 public byte[] decode(final byte[] array) throws DecoderException {
340 return decodeHex(new String(array, getCharset()).toCharArray());
341 }
342
343 /**
344 * Converts a buffer of character bytes representing hexadecimal values into an array of bytes of those same values.
345 * The returned array will be half the length of the passed array, as it takes two characters to represent any given
346 * byte. An exception is thrown if the passed char array has an odd number of elements.
347 *
348 * @param buffer
349 * An array of character bytes containing hexadecimal digits
350 * @return A byte array containing binary data decoded from the supplied byte array (representing characters).
351 * @throws DecoderException
352 * Thrown if an odd number of characters is supplied to this function
353 * @see #decodeHex(char[])
354 * @since 1.11
355 */
356 public byte[] decode(final ByteBuffer buffer) throws DecoderException {
357 return decodeHex(new String(buffer.array(), getCharset()).toCharArray());
358 }
359
360 /**
361 * Converts a String or an array of character bytes representing hexadecimal values into an array of bytes of those
362 * same values. The returned array will be half the length of the passed String or array, as it takes two characters
363 * to represent any given byte. An exception is thrown if the passed char array has an odd number of elements.
364 *
365 * @param object
366 * A String, ByteBuffer, byte[], or an array of character bytes containing hexadecimal digits
367 * @return A byte array containing binary data decoded from the supplied byte array (representing characters).
368 * @throws DecoderException
369 * Thrown if an odd number of characters is supplied to this function or the object is not a String or
370 * char[]
371 * @see #decodeHex(char[])
372 */
373 @Override
374 public Object decode(final Object object) throws DecoderException {
375 if (object instanceof String) {
376 return decode(((String) object).toCharArray());
377 } else if (object instanceof byte[]) {
378 return decode((byte[]) object);
379 } else if (object instanceof ByteBuffer) {
380 return decode((ByteBuffer) object);
381 } else {
382 try {
383 return decodeHex((char[]) object);
384 } catch (final ClassCastException e) {
385 throw new DecoderException(e.getMessage(), e);
386 }
387 }
388 }
389
390 /**
391 * Converts an array of bytes into an array of bytes for the characters representing the hexadecimal values of each
392 * byte in order. The returned array will be double the length of the passed array, as it takes two characters to
393 * represent any given byte.
394 * <p>
395 * The conversion from hexadecimal characters to the returned bytes is performed with the charset named by
396 * {@link #getCharset()}.
397 * </p>
398 *
399 * @param array
400 * a byte[] to convert to Hex characters
401 * @return A byte[] containing the bytes of the lower-case hexadecimal characters
402 * @since 1.7 No longer throws IllegalStateException if the charsetName is invalid.
403 * @see #encodeHex(byte[])
404 */
405 @Override
406 public byte[] encode(final byte[] array) {
407 return encodeHexString(array).getBytes(this.getCharset());
408 }
409
410 /**
411 * Converts byte buffer into an array of bytes for the characters representing the hexadecimal values of each
412 * byte in order. The returned array will be double the length of the passed array, as it takes two characters to
413 * represent any given byte.
414 * <p>
415 * The conversion from hexadecimal characters to the returned bytes is performed with the charset named by
416 * {@link #getCharset()}.
417 * </p>
418 *
419 * @param array
420 * a byte buffer to convert to Hex characters
421 * @return A byte[] containing the bytes of the lower-case hexadecimal characters
422 * @see #encodeHex(byte[])
423 * @since 1.11
424 */
425 public byte[] encode(final ByteBuffer array) {
426 return encodeHexString(array).getBytes(this.getCharset());
427 }
428
429 /**
430 * Converts a String or an array of bytes into an array of characters representing the hexadecimal values of each
431 * byte in order. The returned array will be double the length of the passed String or array, as it takes two
432 * characters to represent any given byte.
433 * <p>
434 * The conversion from hexadecimal characters to bytes to be encoded to performed with the charset named by
435 * {@link #getCharset()}.
436 * </p>
437 *
438 * @param object
439 * a String, ByteBuffer, or byte[] to convert to Hex characters
440 * @return A char[] containing lower-case hexadecimal characters
441 * @throws EncoderException
442 * Thrown if the given object is not a String or byte[]
443 * @see #encodeHex(byte[])
444 */
445 @Override
446 public Object encode(final Object object) throws EncoderException {
447 byte[] byteArray;
448 if (object instanceof String) {
449 byteArray = ((String) object).getBytes(this.getCharset());
450 } else if (object instanceof ByteBuffer) {
451 byteArray = ((ByteBuffer) object).array();
452 } else {
453 try {
454 byteArray = (byte[]) object;
455 } catch (final ClassCastException e) {
456 throw new EncoderException(e.getMessage(), e);
457 }
458 }
459 return encodeHex(byteArray);
460 }
461
462 /**
463 * Gets the charset.
464 *
465 * @return the charset.
466 * @since 1.7
467 */
468 public Charset getCharset() {
469 return this.charset;
470 }
471
472 /**
473 * Gets the charset name.
474 *
475 * @return the charset name.
476 * @since 1.4
477 */
478 public String getCharsetName() {
479 return this.charset.name();
480 }
481
482 /**
483 * Returns a string representation of the object, which includes the charset name.
484 *
485 * @return a string representation of the object.
486 */
487 @Override
488 public String toString() {
489 return super.toString() + "[charsetName=" + this.charset + "]";
490 }
491 }