001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.codec.binary; 019 020import java.nio.ByteBuffer; 021import java.nio.charset.Charset; 022import java.nio.charset.StandardCharsets; 023 024import org.apache.commons.codec.BinaryDecoder; 025import org.apache.commons.codec.BinaryEncoder; 026import org.apache.commons.codec.CharEncoding; 027import org.apache.commons.codec.DecoderException; 028import org.apache.commons.codec.EncoderException; 029 030/** 031 * Converts hexadecimal Strings. The Charset used for certain operation can be set, the default is set in 032 * {@link #DEFAULT_CHARSET_NAME} 033 * 034 * This class is thread-safe. 035 * 036 * @since 1.1 037 */ 038public class Hex implements BinaryEncoder, BinaryDecoder { 039 040 /** 041 * Default charset is {@link StandardCharsets#UTF_8}. 042 * 043 * @since 1.7 044 */ 045 public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8; 046 047 /** 048 * Default charset name is {@link CharEncoding#UTF_8}. 049 * 050 * @since 1.4 051 */ 052 public static final String DEFAULT_CHARSET_NAME = CharEncoding.UTF_8; 053 054 /** 055 * Used to build output as hex. 056 */ 057 private static final char[] DIGITS_LOWER = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 058 'e', 'f' }; 059 060 /** 061 * Used to build output as hex. 062 */ 063 private static final char[] DIGITS_UPPER = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 064 'E', 'F' }; 065 066 /** 067 * Converts an array of characters representing hexadecimal values into an array of bytes of those same values. The 068 * returned array will be half the length of the passed array, as it takes two characters to represent any given 069 * byte. An exception is thrown if the passed char array has an odd number of elements. 070 * 071 * @param data An array of characters containing hexadecimal digits 072 * @return A byte array containing binary data decoded from the supplied char array. 073 * @throws DecoderException Thrown if an odd number of characters or illegal characters are supplied 074 */ 075 public static byte[] decodeHex(final char[] data) throws DecoderException { 076 final byte[] out = new byte[data.length >> 1]; 077 decodeHex(data, out, 0); 078 return out; 079 } 080 081 /** 082 * Converts an array of characters representing hexadecimal values into an array of bytes of those same values. The 083 * returned array will be half the length of the passed array, as it takes two characters to represent any given 084 * byte. An exception is thrown if the passed char array has an odd number of elements. 085 * 086 * @param data An array of characters containing hexadecimal digits 087 * @param out A byte array to contain the binary data decoded from the supplied char array. 088 * @param outOffset The position within {@code out} to start writing the decoded bytes. 089 * @return the number of bytes written to {@code out}. 090 * @throws DecoderException Thrown if an odd number of characters or illegal characters are supplied 091 * @since 1.15 092 */ 093 public static int decodeHex(final char[] data, final byte[] out, final int outOffset) throws DecoderException { 094 final int len = data.length; 095 096 if ((len & 0x01) != 0) { 097 throw new DecoderException("Odd number of characters."); 098 } 099 100 final int outLen = len >> 1; 101 if (out.length - outOffset < outLen) { 102 throw new DecoderException("Output array is not large enough to accommodate decoded data."); 103 } 104 105 // two characters form the hex value. 106 for (int i = outOffset, j = 0; j < len; i++) { 107 int f = toDigit(data[j], j) << 4; 108 j++; 109 f |= toDigit(data[j], j); 110 j++; 111 out[i] = (byte) (f & 0xFF); 112 } 113 114 return outLen; 115 } 116 117 /** 118 * Converts a String representing hexadecimal values into an array of bytes of those same values. The returned array 119 * will be half the length of the passed String, as it takes two characters to represent any given byte. An 120 * exception is thrown if the passed String has an odd number of elements. 121 * 122 * @param data A String containing hexadecimal digits 123 * @return A byte array containing binary data decoded from the supplied char array. 124 * @throws DecoderException Thrown if an odd number of characters or illegal characters are supplied 125 * @since 1.11 126 */ 127 public static byte[] decodeHex(final String data) throws DecoderException { 128 return decodeHex(data.toCharArray()); 129 } 130 131 /** 132 * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order. 133 * The returned array will be double the length of the passed array, as it takes two characters to represent any 134 * given byte. 135 * 136 * @param data a byte[] to convert to hexadecimal characters 137 * @return A char[] containing lower-case hexadecimal characters 138 */ 139 public static char[] encodeHex(final byte[] 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 a byte[] to convert to Hex characters 149 * @param toLowerCase {@code true} converts to lowercase, {@code false} to uppercase 150 * @return A char[] containing hexadecimal characters in the selected case 151 * @since 1.4 152 */ 153 public static char[] encodeHex(final byte[] data, final boolean toLowerCase) { 154 return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER); 155 } 156 157 /** 158 * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order. 159 * The returned array will be double the length of the passed array, as it takes two characters to represent any 160 * given byte. 161 * 162 * @param data a byte[] to convert to hexadecimal characters 163 * @param toDigits the output alphabet (must contain at least 16 chars) 164 * @return A char[] containing the appropriate characters from the alphabet For best results, this should be either 165 * upper- or lower-case hex. 166 * @since 1.4 167 */ 168 protected static char[] encodeHex(final byte[] data, final char[] toDigits) { 169 final int dataLength = data.length; 170 final char[] out = new char[dataLength << 1]; 171 encodeHex(data, 0, dataLength, toDigits, out, 0); 172 return out; 173 } 174 175 /** 176 * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order. 177 * 178 * @param data a byte[] to convert to hexadecimal characters 179 * @param dataOffset the position in {@code data} to start encoding from 180 * @param dataLen the number of bytes from {@code dataOffset} to encode 181 * @param toLowerCase {@code true} converts to lowercase, {@code false} to uppercase 182 * @return A char[] containing the appropriate characters from the alphabet For best results, this should be either 183 * upper- or lower-case hex. 184 * @since 1.15 185 */ 186 public static char[] encodeHex(final byte[] data, final int dataOffset, final int dataLen, 187 final boolean toLowerCase) { 188 final char[] out = new char[dataLen << 1]; 189 encodeHex(data, dataOffset, dataLen, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER, out, 0); 190 return out; 191 } 192 193 /** 194 * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order. 195 * 196 * @param data a byte[] to convert to hexadecimal characters 197 * @param dataOffset the position in {@code data} to start encoding from 198 * @param dataLen the number of bytes from {@code dataOffset} to encode 199 * @param toLowerCase {@code true} converts to lowercase, {@code false} to uppercase 200 * @param out a char[] which will hold the resultant appropriate characters from the alphabet. 201 * @param outOffset the position within {@code out} at which to start writing the encoded characters. 202 * @since 1.15 203 */ 204 public static void encodeHex(final byte[] data, final int dataOffset, final int dataLen, 205 final boolean toLowerCase, final char[] out, final int outOffset) { 206 encodeHex(data, dataOffset, dataLen, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER, out, outOffset); 207 } 208 209 /** 210 * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order. 211 * 212 * @param data a byte[] to convert to hexadecimal characters 213 * @param dataOffset the position in {@code data} to start encoding from 214 * @param dataLen the number of bytes from {@code dataOffset} to encode 215 * @param toDigits the output alphabet (must contain at least 16 chars) 216 * @param out a char[] which will hold the resultant appropriate characters from the alphabet. 217 * @param outOffset the position within {@code out} at which to start writing the encoded characters. 218 */ 219 private static void encodeHex(final byte[] data, final int dataOffset, final int dataLen, final char[] toDigits, 220 final char[] out, final int outOffset) { 221 // two characters form the hex value. 222 for (int i = dataOffset, j = outOffset; i < dataOffset + dataLen; i++) { 223 out[j++] = toDigits[(0xF0 & data[i]) >>> 4]; 224 out[j++] = toDigits[0x0F & data[i]]; 225 } 226 } 227 228 /** 229 * Converts a byte buffer into an array of characters representing the hexadecimal values of each byte in order. The 230 * returned array will be double the length of the passed array, as it takes two characters to represent any given 231 * byte. 232 * 233 * <p>All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method 234 * the value {@link ByteBuffer#remaining() remaining()} will be zero.</p> 235 * 236 * @param data a byte buffer to convert to hexadecimal characters 237 * @return A char[] containing lower-case hexadecimal characters 238 * @since 1.11 239 */ 240 public static char[] encodeHex(final ByteBuffer data) { 241 return encodeHex(data, true); 242 } 243 244 /** 245 * Converts a byte buffer into an array of characters representing the hexadecimal values of each byte in order. The 246 * returned array will be double the length of the passed array, as it takes two characters to represent any given 247 * byte. 248 * 249 * <p>All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method 250 * the value {@link ByteBuffer#remaining() remaining()} will be zero.</p> 251 * 252 * @param data a byte buffer to convert to hexadecimal characters 253 * @param toLowerCase {@code true} converts to lowercase, {@code false} to uppercase 254 * @return A char[] containing hexadecimal characters in the selected case 255 * @since 1.11 256 */ 257 public static char[] encodeHex(final ByteBuffer data, final boolean toLowerCase) { 258 return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER); 259 } 260 261 /** 262 * Converts a byte buffer into an array of characters representing the hexadecimal values of each byte in order. The 263 * returned array will be double the length of the passed array, as it takes two characters to represent any given 264 * byte. 265 * 266 * <p>All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method 267 * the value {@link ByteBuffer#remaining() remaining()} will be zero.</p> 268 * 269 * @param byteBuffer a byte buffer to convert to hexadecimal characters 270 * @param toDigits the output alphabet (must be at least 16 characters) 271 * @return A char[] containing the appropriate characters from the alphabet For best results, this should be either 272 * upper- or lower-case hex. 273 * @since 1.11 274 */ 275 protected static char[] encodeHex(final ByteBuffer byteBuffer, final char[] toDigits) { 276 return encodeHex(toByteArray(byteBuffer), toDigits); 277 } 278 279 /** 280 * Converts an array of bytes into a String representing the hexadecimal values of each byte in order. The returned 281 * String will be double the length of the passed array, as it takes two characters to represent any given byte. 282 * 283 * @param data a byte[] to convert to hexadecimal characters 284 * @return A String containing lower-case hexadecimal characters 285 * @since 1.4 286 */ 287 public static String encodeHexString(final byte[] data) { 288 return new String(encodeHex(data)); 289 } 290 291 /** 292 * Converts an array of bytes into a String representing the hexadecimal values of each byte in order. The returned 293 * String will be double the length of the passed array, as it takes two characters to represent any given byte. 294 * 295 * @param data a byte[] to convert to hexadecimal characters 296 * @param toLowerCase {@code true} converts to lowercase, {@code false} to uppercase 297 * @return A String containing lower-case hexadecimal characters 298 * @since 1.11 299 */ 300 public static String encodeHexString(final byte[] data, final boolean toLowerCase) { 301 return new String(encodeHex(data, toLowerCase)); 302 } 303 304 /** 305 * Converts a byte buffer into a String representing the hexadecimal values of each byte in order. The returned 306 * String will be double the length of the passed array, as it takes two characters to represent any given byte. 307 * 308 * <p>All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method 309 * the value {@link ByteBuffer#remaining() remaining()} will be zero.</p> 310 * 311 * @param data a byte buffer to convert to hexadecimal characters 312 * @return A String containing lower-case hexadecimal characters 313 * @since 1.11 314 */ 315 public static String encodeHexString(final ByteBuffer data) { 316 return new String(encodeHex(data)); 317 } 318 319 /** 320 * Converts a byte buffer into a String representing the hexadecimal values of each byte in order. The returned 321 * String will be double the length of the passed array, as it takes two characters to represent any given byte. 322 * 323 * <p>All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method 324 * the value {@link ByteBuffer#remaining() remaining()} will be zero.</p> 325 * 326 * @param data a byte buffer to convert to hexadecimal characters 327 * @param toLowerCase {@code true} converts to lowercase, {@code false} to uppercase 328 * @return A String containing lower-case hexadecimal characters 329 * @since 1.11 330 */ 331 public static String encodeHexString(final ByteBuffer data, final boolean toLowerCase) { 332 return new String(encodeHex(data, toLowerCase)); 333 } 334 335 /** 336 * Convert the byte buffer to a byte array. All bytes identified by 337 * {@link ByteBuffer#remaining()} will be used. 338 * 339 * @param byteBuffer the byte buffer 340 * @return the byte[] 341 */ 342 private static byte[] toByteArray(final ByteBuffer byteBuffer) { 343 final int remaining = byteBuffer.remaining(); 344 // Use the underlying buffer if possible 345 if (byteBuffer.hasArray()) { 346 final byte[] byteArray = byteBuffer.array(); 347 if (remaining == byteArray.length) { 348 byteBuffer.position(remaining); 349 return byteArray; 350 } 351 } 352 // Copy the bytes 353 final byte[] byteArray = new byte[remaining]; 354 byteBuffer.get(byteArray); 355 return byteArray; 356 } 357 358 /** 359 * Converts a hexadecimal character to an integer. 360 * 361 * @param ch A character to convert to an integer digit 362 * @param index The index of the character in the source 363 * @return An integer 364 * @throws DecoderException Thrown if ch is an illegal hexadecimal character 365 */ 366 protected static int toDigit(final char ch, final int index) throws DecoderException { 367 final int digit = Character.digit(ch, 16); 368 if (digit == -1) { 369 throw new DecoderException("Illegal hexadecimal character " + ch + " at index " + index); 370 } 371 return digit; 372 } 373 374 private final Charset charset; 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 hexadecimal 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 hexadecimal 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 hexadecimal 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}