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; 022 023import org.apache.commons.codec.BinaryDecoder; 024import org.apache.commons.codec.BinaryEncoder; 025import org.apache.commons.codec.CharEncoding; 026import org.apache.commons.codec.Charsets; 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 * @version $Id: Hex.java 1811344 2017-10-06 15:19:57Z ggregory $ 038 */ 039public class Hex implements BinaryEncoder, BinaryDecoder { 040 041 /** 042 * Default charset is {@link Charsets#UTF_8} 043 * 044 * @since 1.7 045 */ 046 public static final Charset DEFAULT_CHARSET = Charsets.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 = 059 {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; 060 061 /** 062 * Used to build output as Hex 063 */ 064 private static final char[] DIGITS_UPPER = 065 {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; 066 067 /** 068 * Converts a String representing hexadecimal values into an array of bytes of those same values. The 069 * returned array will be half the length of the passed String, as it takes two characters to represent any given 070 * byte. An exception is thrown if the passed String has an odd number of elements. 071 * 072 * @param data 073 * A String containing hexadecimal digits 074 * @return A byte array containing binary data decoded from the supplied char array. 075 * @throws DecoderException 076 * Thrown if an odd number or illegal of characters is supplied 077 * @since 1.11 078 */ 079 public static byte[] decodeHex(final String data) throws DecoderException { 080 return decodeHex(data.toCharArray()); 081 } 082 083 /** 084 * Converts an array of characters representing hexadecimal values into an array of bytes of those same values. The 085 * returned array will be half the length of the passed array, as it takes two characters to represent any given 086 * byte. An exception is thrown if the passed char array has an odd number of elements. 087 * 088 * @param data 089 * An array of characters containing hexadecimal digits 090 * @return A byte array containing binary data decoded from the supplied char array. 091 * @throws DecoderException 092 * Thrown if an odd number or illegal of characters is supplied 093 */ 094 public static byte[] decodeHex(final char[] data) throws DecoderException { 095 096 final int len = data.length; 097 098 if ((len & 0x01) != 0) { 099 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}