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