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.Charsets; 028import org.apache.commons.codec.DecoderException; 029import org.apache.commons.codec.EncoderException; 030 031/** 032 * Converts hexadecimal Strings. The Charset used for certain operation can be set, the default is set in 033 * {@link #DEFAULT_CHARSET_NAME} 034 * 035 * This class is thread-safe. 036 * 037 * @since 1.1 038 */ 039public class Hex implements BinaryEncoder, BinaryDecoder { 040 041 /** 042 * Default charset is {@link StandardCharsets#UTF_8} 043 * 044 * @since 1.7 045 */ 046 public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8; 047 048 /** 049 * Default charset name is {@link CharEncoding#UTF_8} 050 * 051 * @since 1.4 052 */ 053 public static final String DEFAULT_CHARSET_NAME = CharEncoding.UTF_8; 054 055 /** 056 * Used to build output as Hex 057 */ 058 private static final char[] DIGITS_LOWER = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 059 'e', 'f' }; 060 061 /** 062 * Used to build output as Hex 063 */ 064 private static final char[] DIGITS_UPPER = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 065 'E', 'F' }; 066 067 /** 068 * Converts a String representing hexadecimal values into an array of bytes of those same values. The returned array 069 * will be half the length of the passed String, as it takes two characters to represent any given byte. An 070 * exception is thrown if the passed String has an odd number of elements. 071 * 072 * @param data A String containing hexadecimal digits 073 * @return A byte array containing binary data decoded from the supplied char array. 074 * @throws DecoderException Thrown if an odd number or illegal of characters is supplied 075 * @since 1.11 076 */ 077 public static byte[] decodeHex(final String data) throws DecoderException { 078 return decodeHex(data.toCharArray()); 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 * @return A byte array containing binary data decoded from the supplied char array. 088 * @throws DecoderException Thrown if an odd number or illegal of characters is supplied 089 */ 090 public static byte[] decodeHex(final char[] data) throws DecoderException { 091 092 final int len = data.length; 093 094 if ((len & 0x01) != 0) { 095 throw new DecoderException("Odd number of characters."); 096 } 097 098 final byte[] out = new byte[len >> 1]; 099 100 // two characters form the hex value. 101 for (int i = 0, j = 0; j < len; i++) { 102 int f = toDigit(data[j], j) << 4; 103 j++; 104 f = f | toDigit(data[j], j); 105 j++; 106 out[i] = (byte) (f & 0xFF); 107 } 108 109 return out; 110 } 111 112 /** 113 * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order. 114 * The returned array will be double the length of the passed array, as it takes two characters to represent any 115 * given byte. 116 * 117 * @param data a byte[] to convert to Hex characters 118 * @return A char[] containing lower-case hexadecimal characters 119 */ 120 public static char[] encodeHex(final byte[] data) { 121 return encodeHex(data, true); 122 } 123 124 /** 125 * Converts a byte buffer into an array of characters representing the hexadecimal values of each byte in order. The 126 * returned array will be double the length of the passed array, as it takes two characters to represent any given 127 * byte. 128 * 129 * <p>All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method 130 * the value {@link ByteBuffer#remaining() remaining()} will be zero.</p> 131 * 132 * @param data a byte buffer to convert to Hex characters 133 * @return A char[] containing lower-case hexadecimal characters 134 * @since 1.11 135 */ 136 public static char[] encodeHex(final ByteBuffer data) { 137 return encodeHex(data, true); 138 } 139 140 /** 141 * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order. 142 * The returned array will be double the length of the passed array, as it takes two characters to represent any 143 * given byte. 144 * 145 * @param data a byte[] to convert to Hex characters 146 * @param toLowerCase {@code true} converts to lowercase, {@code false} to uppercase 147 * @return A char[] containing hexadecimal characters in the selected case 148 * @since 1.4 149 */ 150 public static char[] encodeHex(final byte[] data, final boolean toLowerCase) { 151 return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER); 152 } 153 154 /** 155 * Converts a byte buffer into an array of characters representing the hexadecimal values of each byte in order. The 156 * returned array will be double the length of the passed array, as it takes two characters to represent any given 157 * byte. 158 * 159 * <p>All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method 160 * the value {@link ByteBuffer#remaining() remaining()} will be zero.</p> 161 * 162 * @param data a byte buffer to convert to Hex characters 163 * @param toLowerCase {@code true} converts to lowercase, {@code false} to uppercase 164 * @return A char[] containing hexadecimal characters in the selected case 165 * @since 1.11 166 */ 167 public static char[] encodeHex(final ByteBuffer data, final boolean toLowerCase) { 168 return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER); 169 } 170 171 /** 172 * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order. 173 * The returned array will be double the length of the passed array, as it takes two characters to represent any 174 * given byte. 175 * 176 * @param data a byte[] to convert to Hex characters 177 * @param toDigits the output alphabet (must contain at least 16 chars) 178 * @return A char[] containing the appropriate characters from the alphabet For best results, this should be either 179 * upper- or lower-case hex. 180 * @since 1.4 181 */ 182 protected static char[] encodeHex(final byte[] data, final char[] toDigits) { 183 final int l = data.length; 184 final char[] out = new char[l << 1]; 185 // two characters form the hex value. 186 for (int i = 0, j = 0; i < l; i++) { 187 out[j++] = toDigits[(0xF0 & data[i]) >>> 4]; 188 out[j++] = toDigits[0x0F & data[i]]; 189 } 190 return out; 191 } 192 193 /** 194 * Converts a byte buffer into an array of characters representing the hexadecimal values of each byte in order. The 195 * returned array will be double the length of the passed array, as it takes two characters to represent any given 196 * byte. 197 * 198 * <p>All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method 199 * the value {@link ByteBuffer#remaining() remaining()} will be zero.</p> 200 * 201 * @param byteBuffer a byte buffer to convert to Hex characters 202 * @param toDigits the output alphabet (must be at least 16 characters) 203 * @return A char[] containing the appropriate characters from the alphabet For best results, this should be either 204 * upper- or lower-case hex. 205 * @since 1.11 206 */ 207 protected static char[] encodeHex(final ByteBuffer byteBuffer, final char[] toDigits) { 208 return encodeHex(toByteArray(byteBuffer), toDigits); 209 } 210 211 /** 212 * Converts an array of bytes into a String representing the hexadecimal values of each byte in order. The returned 213 * String will be double the length of the passed array, as it takes two characters to represent any given byte. 214 * 215 * @param data a byte[] to convert to Hex characters 216 * @return A String containing lower-case hexadecimal characters 217 * @since 1.4 218 */ 219 public static String encodeHexString(final byte[] data) { 220 return new String(encodeHex(data)); 221 } 222 223 /** 224 * Converts an array of bytes into a String representing the hexadecimal values of each byte in order. The returned 225 * String will be double the length of the passed array, as it takes two characters to represent any given byte. 226 * 227 * @param data a byte[] to convert to Hex characters 228 * @param toLowerCase {@code true} converts to lowercase, {@code false} to uppercase 229 * @return A String containing lower-case hexadecimal characters 230 * @since 1.11 231 */ 232 public static String encodeHexString(final byte[] data, final boolean toLowerCase) { 233 return new String(encodeHex(data, toLowerCase)); 234 } 235 236 /** 237 * Converts a byte buffer into a String representing the hexadecimal values of each byte in order. The returned 238 * String will be double the length of the passed array, as it takes two characters to represent any given byte. 239 * 240 * <p>All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method 241 * the value {@link ByteBuffer#remaining() remaining()} will be zero.</p> 242 * 243 * @param data a byte buffer to convert to Hex characters 244 * @return A String containing lower-case hexadecimal characters 245 * @since 1.11 246 */ 247 public static String encodeHexString(final ByteBuffer data) { 248 return new String(encodeHex(data)); 249 } 250 251 /** 252 * Converts a byte buffer into a String representing the hexadecimal values of each byte in order. The returned 253 * String will be double the length of the passed array, as it takes two characters to represent any given 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 data a byte buffer to convert to Hex characters 259 * @param toLowerCase {@code true} converts to lowercase, {@code false} to uppercase 260 * @return A String containing lower-case hexadecimal characters 261 * @since 1.11 262 */ 263 public static String encodeHexString(final ByteBuffer data, final boolean toLowerCase) { 264 return new String(encodeHex(data, toLowerCase)); 265 } 266 267 /** 268 * Convert the byte buffer to a a byte array. All bytes identified by 269 * {@link ByteBuffer#remaining()} will be used. 270 * 271 * @param byteBuffer the byte buffer 272 * @return the byte[] 273 */ 274 private static byte[] toByteArray(final ByteBuffer byteBuffer) { 275 final int remaining = byteBuffer.remaining(); 276 // Use the underlying buffer if possible 277 if (byteBuffer.hasArray()) { 278 final byte[] byteArray = byteBuffer.array(); 279 if (remaining == byteArray.length) { 280 byteBuffer.position(remaining); 281 return byteArray; 282 } 283 } 284 // Copy the bytes 285 final byte[] byteArray = new byte[remaining]; 286 byteBuffer.get(byteArray); 287 return byteArray; 288 } 289 290 /** 291 * Converts a hexadecimal character to an integer. 292 * 293 * @param ch A character to convert to an integer digit 294 * @param index The index of the character in the source 295 * @return An integer 296 * @throws DecoderException Thrown if ch is an illegal hex character 297 */ 298 protected static int toDigit(final char ch, final int index) throws DecoderException { 299 final int digit = Character.digit(ch, 16); 300 if (digit == -1) { 301 throw new DecoderException("Illegal hexadecimal character " + ch + " at index " + index); 302 } 303 return digit; 304 } 305 306 private final Charset charset; 307 308 /** 309 * Creates a new codec with the default charset name {@link #DEFAULT_CHARSET} 310 */ 311 public Hex() { 312 // use default encoding 313 this.charset = DEFAULT_CHARSET; 314 } 315 316 /** 317 * Creates a new codec with the given Charset. 318 * 319 * @param charset the charset. 320 * @since 1.7 321 */ 322 public Hex(final Charset charset) { 323 this.charset = charset; 324 } 325 326 /** 327 * Creates a new codec with the given charset name. 328 * 329 * @param charsetName the charset name. 330 * @throws java.nio.charset.UnsupportedCharsetException If the named charset is unavailable 331 * @since 1.4 332 * @since 1.7 throws UnsupportedCharsetException if the named charset is unavailable 333 */ 334 public Hex(final String charsetName) { 335 this(Charset.forName(charsetName)); 336 } 337 338 /** 339 * Converts an array of character bytes representing hexadecimal values into an array of bytes of those same values. 340 * The returned array will be half the length of the passed array, as it takes two characters to represent any given 341 * byte. An exception is thrown if the passed char array has an odd number of elements. 342 * 343 * @param array An array of character bytes containing hexadecimal digits 344 * @return A byte array containing binary data decoded from the supplied byte array (representing characters). 345 * @throws DecoderException Thrown if an odd number of characters is supplied to this function 346 * @see #decodeHex(char[]) 347 */ 348 @Override 349 public byte[] decode(final byte[] array) throws DecoderException { 350 return decodeHex(new String(array, getCharset()).toCharArray()); 351 } 352 353 /** 354 * Converts a buffer of character bytes representing hexadecimal values into an array of bytes of those same values. 355 * The returned array will be half the length of the passed array, as it takes two characters to represent any given 356 * byte. An exception is thrown if the passed char array has an odd number of elements. 357 * 358 * <p>All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method 359 * the value {@link ByteBuffer#remaining() remaining()} will be zero.</p> 360 * 361 * @param buffer An array of character bytes containing hexadecimal digits 362 * @return A byte array containing binary data decoded from the supplied byte array (representing characters). 363 * @throws DecoderException Thrown if an odd number of characters is supplied to this function 364 * @see #decodeHex(char[]) 365 * @since 1.11 366 */ 367 public byte[] decode(final ByteBuffer buffer) throws DecoderException { 368 return decodeHex(new String(toByteArray(buffer), getCharset()).toCharArray()); 369 } 370 371 /** 372 * Converts a String or an array of character bytes representing hexadecimal values into an array of bytes of those 373 * same values. The returned array will be half the length of the passed String or array, as it takes two characters 374 * to represent any given byte. An exception is thrown if the passed char array has an odd number of elements. 375 * 376 * @param object A String, ByteBuffer, byte[], or an array of character bytes containing hexadecimal digits 377 * @return A byte array containing binary data decoded from the supplied byte array (representing characters). 378 * @throws DecoderException Thrown if an odd number of characters is supplied to this function or the object is not 379 * a String or char[] 380 * @see #decodeHex(char[]) 381 */ 382 @Override 383 public Object decode(final Object object) throws DecoderException { 384 if (object instanceof String) { 385 return decode(((String) object).toCharArray()); 386 } else if (object instanceof byte[]) { 387 return decode((byte[]) object); 388 } else if (object instanceof ByteBuffer) { 389 return decode((ByteBuffer) object); 390 } else { 391 try { 392 return decodeHex((char[]) object); 393 } catch (final ClassCastException e) { 394 throw new DecoderException(e.getMessage(), e); 395 } 396 } 397 } 398 399 /** 400 * Converts an array of bytes into an array of bytes for the characters representing the hexadecimal values of each 401 * byte in order. The returned array will be double the length of the passed array, as it takes two characters to 402 * represent any given byte. 403 * <p> 404 * The conversion from hexadecimal characters to the returned bytes is performed with the charset named by 405 * {@link #getCharset()}. 406 * </p> 407 * 408 * @param array a byte[] to convert to Hex characters 409 * @return A byte[] containing the bytes of the lower-case hexadecimal characters 410 * @since 1.7 No longer throws IllegalStateException if the charsetName is invalid. 411 * @see #encodeHex(byte[]) 412 */ 413 @Override 414 public byte[] encode(final byte[] array) { 415 return encodeHexString(array).getBytes(this.getCharset()); 416 } 417 418 /** 419 * Converts byte buffer into an array of bytes for the characters representing the hexadecimal values of each byte 420 * in order. The returned array will be double the length of the passed array, as it takes two characters to 421 * represent any given byte. 422 * 423 * <p>The conversion from hexadecimal characters to the returned bytes is performed with the charset named by 424 * {@link #getCharset()}.</p> 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 array a byte buffer to convert to Hex characters 430 * @return A byte[] containing the bytes of the lower-case hexadecimal characters 431 * @see #encodeHex(byte[]) 432 * @since 1.11 433 */ 434 public byte[] encode(final ByteBuffer array) { 435 return encodeHexString(array).getBytes(this.getCharset()); 436 } 437 438 /** 439 * Converts a String or an array of bytes into an array of characters representing the hexadecimal values of each 440 * byte in order. The returned array will be double the length of the passed String or array, as it takes two 441 * characters to represent any given byte. 442 * <p> 443 * The conversion from hexadecimal characters to bytes to be encoded to performed with the charset named by 444 * {@link #getCharset()}. 445 * </p> 446 * 447 * @param object a String, ByteBuffer, or byte[] to convert to Hex characters 448 * @return A char[] containing lower-case hexadecimal characters 449 * @throws EncoderException Thrown if the given object is not a String or byte[] 450 * @see #encodeHex(byte[]) 451 */ 452 @Override 453 public Object encode(final Object object) throws EncoderException { 454 byte[] byteArray; 455 if (object instanceof String) { 456 byteArray = ((String) object).getBytes(this.getCharset()); 457 } else if (object instanceof ByteBuffer) { 458 byteArray = toByteArray((ByteBuffer) object); 459 } else { 460 try { 461 byteArray = (byte[]) object; 462 } catch (final ClassCastException e) { 463 throw new EncoderException(e.getMessage(), e); 464 } 465 } 466 return encodeHex(byteArray); 467 } 468 469 /** 470 * Gets the charset. 471 * 472 * @return the charset. 473 * @since 1.7 474 */ 475 public Charset getCharset() { 476 return this.charset; 477 } 478 479 /** 480 * Gets the charset name. 481 * 482 * @return the charset name. 483 * @since 1.4 484 */ 485 public String getCharsetName() { 486 return this.charset.name(); 487 } 488 489 /** 490 * Returns a string representation of the object, which includes the charset name. 491 * 492 * @return a string representation of the object. 493 */ 494 @Override 495 public String toString() { 496 return super.toString() + "[charsetName=" + this.charset + "]"; 497 } 498}