1 /* 2 * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.util; 27 28 // BEGIN Android-removed: JavaLangAccess and SharedSecrets for String allocation. 29 /* 30 import jdk.internal.access.JavaLangAccess; 31 import jdk.internal.access.SharedSecrets; 32 */ 33 // END Android-removed: JavaLangAccess and SharedSecrets for String allocation. 34 35 import java.io.IOException; 36 import java.io.UncheckedIOException; 37 import java.nio.CharBuffer; 38 import java.nio.charset.CharacterCodingException; 39 import java.nio.charset.StandardCharsets; 40 41 /** 42 * {@code HexFormat} converts between bytes and chars and hex-encoded strings which may include 43 * additional formatting markup such as prefixes, suffixes, and delimiters. 44 * <p> 45 * There are two factories of {@code HexFormat} with preset parameters {@link #of()} and 46 * {@link #ofDelimiter(String) ofDelimiter(delimiter)}. For other parameter combinations 47 * the {@code withXXX} methods return copies of {@code HexFormat} modified 48 * {@link #withPrefix(String)}, {@link #withSuffix(String)}, {@link #withDelimiter(String)} 49 * or choice of {@link #withUpperCase()} or {@link #withLowerCase()} parameters. 50 * <p> 51 * For primitive to hexadecimal string conversions the {@code toHexDigits} 52 * methods include {@link #toHexDigits(byte)}, {@link #toHexDigits(int)}, and 53 * {@link #toHexDigits(long)}, etc. The default is to use lowercase characters {@code "0-9","a-f"}. 54 * For conversions producing uppercase hexadecimal the characters are {@code "0-9","A-F"}. 55 * Only the {@link HexFormat#isUpperCase() HexFormat.isUpperCase()} parameter is 56 * considered; the delimiter, prefix and suffix are not used. 57 * 58 * <p> 59 * For hexadecimal string to primitive conversions the {@code fromHexDigits} 60 * methods include {@link #fromHexDigits(CharSequence) fromHexDigits(string)}, 61 * {@link #fromHexDigitsToLong(CharSequence) fromHexDigitsToLong(string)}, and 62 * {@link #fromHexDigit(int) fromHexDigit(int)} converts a single character or codepoint. 63 * For conversions from hexadecimal characters the digits and uppercase and lowercase 64 * characters in {@code "0-9", "a-f", and "A-F"} are converted to corresponding values 65 * {@code 0-15}. The delimiter, prefix, suffix, and uppercase parameters are not used. 66 * 67 * <p> 68 * For byte array to formatted hexadecimal string conversions 69 * the {@code formatHex} methods include {@link #formatHex(byte[]) formatHex(byte[])} 70 * and {@link #formatHex(Appendable, byte[]) formatHex(Appendable, byte[])}. 71 * The formatted output is a string or is appended to an {@link Appendable} such as 72 * {@link StringBuilder} or {@link java.io.PrintStream}. 73 * Each byte value is formatted as the prefix, two hexadecimal characters from the 74 * uppercase or lowercase digits, and the suffix. 75 * A delimiter follows each formatted value, except the last. 76 * For conversions producing uppercase hexadecimal strings use {@link #withUpperCase()}. 77 * 78 * <p> 79 * For formatted hexadecimal string to byte array conversions the 80 * {@code parseHex} methods include {@link #parseHex(CharSequence) parseHex(CharSequence)} and 81 * {@link #parseHex(char[], int, int) parseHex(char[], offset, length)}. 82 * Each byte value is parsed from the prefix, two case insensitive hexadecimal characters, 83 * and the suffix. A delimiter follows each formatted value, except the last. 84 * 85 * @apiNote 86 * For example, an individual byte is converted to a string of hexadecimal digits using 87 * {@link HexFormat#toHexDigits(int) toHexDigits(int)} and converted from a string to a 88 * primitive value using {@link HexFormat#fromHexDigits(CharSequence) fromHexDigits(string)}. 89 * <pre>{@code 90 * HexFormat hex = HexFormat.of(); 91 * byte b = 127; 92 * String byteStr = hex.toHexDigits(b); 93 * 94 * byte byteVal = (byte)hex.fromHexDigits(byteStr); 95 * assert(byteStr.equals("7f")); 96 * assert(b == byteVal); 97 * 98 * // The hexadecimal digits are: "7f" 99 * }</pre> 100 * <p> 101 * For a comma ({@code ", "}) separated format with a prefix ({@code "#"}) 102 * using lowercase hex digits the {@code HexFormat} is: 103 * <pre>{@code 104 * HexFormat commaFormat = HexFormat.ofDelimiter(", ").withPrefix("#"); 105 * byte[] bytes = {0, 1, 2, 3, 124, 125, 126, 127}; 106 * String str = commaFormat.formatHex(bytes); 107 * 108 * byte[] parsed = commaFormat.parseHex(str); 109 * assert(Arrays.equals(bytes, parsed)); 110 * 111 * // The formatted string is: "#00, #01, #02, #03, #7c, #7d, #7e, #7f" 112 * }</pre> 113 * <p> 114 * For a fingerprint of byte values that uses the delimiter colon ({@code ":"}) 115 * and uppercase characters the {@code HexFormat} is: 116 * <pre>{@code 117 * HexFormat formatFingerprint = HexFormat.ofDelimiter(":").withUpperCase(); 118 * byte[] bytes = {0, 1, 2, 3, 124, 125, 126, 127}; 119 * String str = formatFingerprint.formatHex(bytes); 120 * byte[] parsed = formatFingerprint.parseHex(str); 121 * assert(Arrays.equals(bytes, parsed)); 122 * 123 * // The formatted string is: "00:01:02:03:7C:7D:7E:7F" 124 * }</pre> 125 * 126 * <!-- Android-removed: paragraph on ValueBased 127 * <p> 128 * This is a <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a> 129 * class; use of identity-sensitive operations (including reference equality 130 * ({@code ==}), identity hash code, or synchronization) on instances of 131 * {@code HexFormat} may have unpredictable results and should be avoided. 132 * The {@code equals} method should be used for comparisons. 133 * --> 134 * 135 * <p> 136 * This class is immutable and thread-safe. 137 * <p> 138 * Unless otherwise noted, passing a null argument to any method will cause a 139 * {@link java.lang.NullPointerException NullPointerException} to be thrown. 140 * 141 * @since 17 142 */ 143 144 145 public final class HexFormat { 146 147 // BEGIN Android-removed: JavaLangAccess and SharedSecrets for String allocation. 148 /* 149 // Access to create strings from a byte array. 150 private static final JavaLangAccess jla = SharedSecrets.getJavaLangAccess(); 151 */ 152 // END Android-removed: JavaLangAccess and SharedSecrets for String allocation. 153 154 private static final byte[] UPPERCASE_DIGITS = { 155 '0', '1', '2', '3', '4', '5', '6', '7', 156 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 157 }; 158 private static final byte[] LOWERCASE_DIGITS = { 159 '0', '1', '2', '3', '4', '5', '6', '7', 160 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 161 }; 162 // Analysis has shown that generating the whole array allows the JIT to generate 163 // better code compared to a slimmed down array, such as one cutting off after 'f' 164 private static final byte[] DIGITS = { 165 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 166 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 167 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 168 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, 169 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, 170 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 171 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, 172 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 173 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 174 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 175 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 176 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 177 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 178 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 179 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 180 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 181 }; 182 /** 183 * Format each byte of an array as a pair of hexadecimal digits. 184 * The hexadecimal characters are from lowercase alpha digits. 185 */ 186 private static final HexFormat HEX_FORMAT = 187 new HexFormat("", "", "", LOWERCASE_DIGITS); 188 189 private static final byte[] EMPTY_BYTES = {}; 190 191 private final String delimiter; 192 private final String prefix; 193 private final String suffix; 194 private final byte[] digits; 195 196 /** 197 * Returns a HexFormat with a delimiter, prefix, suffix, and array of digits. 198 * 199 * @param delimiter a delimiter, non-null 200 * @param prefix a prefix, non-null 201 * @param suffix a suffix, non-null 202 * @param digits byte array of digits indexed by low nibble, non-null 203 * @throws NullPointerException if any argument is null 204 */ HexFormat(String delimiter, String prefix, String suffix, byte[] digits)205 private HexFormat(String delimiter, String prefix, String suffix, byte[] digits) { 206 this.delimiter = Objects.requireNonNull(delimiter, "delimiter"); 207 this.prefix = Objects.requireNonNull(prefix, "prefix"); 208 this.suffix = Objects.requireNonNull(suffix, "suffix"); 209 this.digits = digits; 210 } 211 212 /** 213 * Returns a hexadecimal formatter with no delimiter and lowercase characters. 214 * The delimiter, prefix, and suffix are empty. 215 * The methods {@link #withDelimiter(String) withDelimiter}, 216 * {@link #withUpperCase() withUpperCase}, {@link #withLowerCase() withLowerCase}, 217 * {@link #withPrefix(String) withPrefix}, and {@link #withSuffix(String) withSuffix} 218 * return copies of formatters with new parameters. 219 * 220 * @return a hexadecimal formatter with no delimiter and lowercase characters 221 */ of()222 public static HexFormat of() { 223 return HEX_FORMAT; 224 } 225 226 /** 227 * Returns a hexadecimal formatter with the delimiter and lowercase characters. 228 * The prefix and suffix are empty. 229 * The methods {@link #withDelimiter(String) withDelimiter}, 230 * {@link #withUpperCase() withUpperCase}, {@link #withLowerCase() withLowerCase}, 231 * {@link #withPrefix(String) withPrefix}, and {@link #withSuffix(String) withSuffix} 232 * return copies of formatters with new parameters. 233 * 234 * @param delimiter a delimiter, non-null, may be empty 235 * @return a {@link HexFormat} with the delimiter and lowercase characters 236 */ ofDelimiter(String delimiter)237 public static HexFormat ofDelimiter(String delimiter) { 238 return new HexFormat(delimiter, "", "", LOWERCASE_DIGITS); 239 } 240 241 /** 242 * Returns a copy of this {@code HexFormat} with the delimiter. 243 * @param delimiter the delimiter, non-null, may be empty 244 * @return a copy of this {@code HexFormat} with the delimiter 245 */ withDelimiter(String delimiter)246 public HexFormat withDelimiter(String delimiter) { 247 return new HexFormat(delimiter, this.prefix, this.suffix, this.digits); 248 } 249 250 /** 251 * Returns a copy of this {@code HexFormat} with the prefix. 252 * 253 * @param prefix a prefix, non-null, may be empty 254 * @return a copy of this {@code HexFormat} with the prefix 255 */ withPrefix(String prefix)256 public HexFormat withPrefix(String prefix) { 257 return new HexFormat(this.delimiter, prefix, this.suffix, this.digits); 258 } 259 260 /** 261 * Returns a copy of this {@code HexFormat} with the suffix. 262 * 263 * @param suffix a suffix, non-null, may be empty 264 * @return a copy of this {@code HexFormat} with the suffix 265 */ withSuffix(String suffix)266 public HexFormat withSuffix(String suffix) { 267 return new HexFormat(this.delimiter, this.prefix, suffix, this.digits); 268 } 269 270 /** 271 * Returns a copy of this {@code HexFormat} to use uppercase hexadecimal characters. 272 * The uppercase hexadecimal characters are {@code "0-9", "A-F"}. 273 * 274 * @return a copy of this {@code HexFormat} with uppercase hexadecimal characters 275 */ withUpperCase()276 public HexFormat withUpperCase() { 277 return new HexFormat(this.delimiter, this.prefix, this.suffix, UPPERCASE_DIGITS); 278 } 279 280 /** 281 * Returns a copy of this {@code HexFormat} to use lowercase hexadecimal characters. 282 * The lowercase hexadecimal characters are {@code "0-9", "a-f"}. 283 * 284 * @return a copy of this {@code HexFormat} with lowercase hexadecimal characters 285 */ withLowerCase()286 public HexFormat withLowerCase() { 287 return new HexFormat(this.delimiter, this.prefix, this.suffix, LOWERCASE_DIGITS); 288 } 289 290 /** 291 * Returns the delimiter between hexadecimal values in formatted hexadecimal strings. 292 * 293 * @return the delimiter, non-null, may be empty {@code ""} 294 */ delimiter()295 public String delimiter() { 296 return delimiter; 297 } 298 299 /** 300 * Returns the prefix used for each hexadecimal value in formatted hexadecimal strings. 301 * 302 * @return the prefix, non-null, may be empty {@code ""} 303 */ prefix()304 public String prefix() { 305 return prefix; 306 } 307 308 /** 309 * Returns the suffix used for each hexadecimal value in formatted hexadecimal strings. 310 * 311 * @return the suffix, non-null, may be empty {@code ""} 312 */ suffix()313 public String suffix() { 314 return suffix; 315 } 316 317 /** 318 * Returns {@code true} if the hexadecimal digits are uppercase, 319 * otherwise {@code false}. 320 * 321 * @return {@code true} if the hexadecimal digits are uppercase, 322 * otherwise {@code false} 323 */ isUpperCase()324 public boolean isUpperCase() { 325 return Arrays.equals(digits, UPPERCASE_DIGITS); 326 } 327 328 /** 329 * Returns a hexadecimal string formatted from a byte array. 330 * Each byte value is formatted as the prefix, two hexadecimal characters 331 * {@linkplain #isUpperCase selected from} uppercase or lowercase digits, and the suffix. 332 * A delimiter follows each formatted value, except the last. 333 * 334 * The behavior is equivalent to 335 * {@link #formatHex(byte[], int, int) formatHex(bytes, 0, bytes.length))}. 336 * 337 * @param bytes a non-null array of bytes 338 * @return a string hexadecimal formatting of the byte array 339 */ formatHex(byte[] bytes)340 public String formatHex(byte[] bytes) { 341 return formatHex(bytes, 0, bytes.length); 342 } 343 344 /** 345 * Returns a hexadecimal string formatted from a byte array range. 346 * Each byte value is formatted as the prefix, two hexadecimal characters 347 * {@linkplain #isUpperCase selected from} uppercase or lowercase digits, and the suffix. 348 * A delimiter follows each formatted value, except the last. 349 * 350 * @param bytes a non-null array of bytes 351 * @param fromIndex the initial index of the range, inclusive 352 * @param toIndex the final index of the range, exclusive 353 * @return a string hexadecimal formatting each byte of the array range 354 * @throws IndexOutOfBoundsException if the array range is out of bounds 355 */ formatHex(byte[] bytes, int fromIndex, int toIndex)356 public String formatHex(byte[] bytes, int fromIndex, int toIndex) { 357 Objects.requireNonNull(bytes,"bytes"); 358 Objects.checkFromToIndex(fromIndex, toIndex, bytes.length); 359 if (toIndex - fromIndex == 0) { 360 return ""; 361 } 362 // Format efficiently if possible 363 String s = formatOptDelimiter(bytes, fromIndex, toIndex); 364 if (s == null) { 365 long stride = prefix.length() + 2L + suffix.length() + delimiter.length(); 366 int capacity = checkMaxArraySize((toIndex - fromIndex) * stride - delimiter.length()); 367 StringBuilder sb = new StringBuilder(capacity); 368 formatHex(sb, bytes, fromIndex, toIndex); 369 s = sb.toString(); 370 } 371 return s; 372 } 373 374 /** 375 * Appends formatted hexadecimal strings from a byte array to the {@link Appendable}. 376 * Each byte value is formatted as the prefix, two hexadecimal characters 377 * {@linkplain #isUpperCase selected from} uppercase or lowercase digits, and the suffix. 378 * A delimiter follows each formatted value, except the last. 379 * The formatted hexadecimal strings are appended in zero or more calls to the {@link Appendable} methods. 380 * 381 * @param <A> The type of {@code Appendable} 382 * @param out an {@code Appendable}, non-null 383 * @param bytes a byte array 384 * @return the {@code Appendable} 385 * @throws UncheckedIOException if an I/O exception occurs appending to the output 386 */ formatHex(A out, byte[] bytes)387 public <A extends Appendable> A formatHex(A out, byte[] bytes) { 388 return formatHex(out, bytes, 0, bytes.length); 389 } 390 391 /** 392 * Appends formatted hexadecimal strings from a byte array range to the {@link Appendable}. 393 * Each byte value is formatted as the prefix, two hexadecimal characters 394 * {@linkplain #isUpperCase selected from} uppercase or lowercase digits, and the suffix. 395 * A delimiter follows each formatted value, except the last. 396 * The formatted hexadecimal strings are appended in zero or more calls to the {@link Appendable} methods. 397 * 398 * @param <A> The type of {@code Appendable} 399 * @param out an {@code Appendable}, non-null 400 * @param bytes a byte array, non-null 401 * @param fromIndex the initial index of the range, inclusive 402 * @param toIndex the final index of the range, exclusive. 403 * @return the {@code Appendable} 404 * @throws IndexOutOfBoundsException if the array range is out of bounds 405 * @throws UncheckedIOException if an I/O exception occurs appending to the output 406 */ formatHex(A out, byte[] bytes, int fromIndex, int toIndex)407 public <A extends Appendable> A formatHex(A out, byte[] bytes, int fromIndex, int toIndex) { 408 Objects.requireNonNull(out, "out"); 409 Objects.requireNonNull(bytes, "bytes"); 410 Objects.checkFromToIndex(fromIndex, toIndex, bytes.length); 411 412 int length = toIndex - fromIndex; 413 if (length > 0) { 414 try { 415 String between = suffix + delimiter + prefix; 416 out.append(prefix); 417 toHexDigits(out, bytes[fromIndex]); 418 if (between.isEmpty()) { 419 for (int i = 1; i < length; i++) { 420 toHexDigits(out, bytes[fromIndex + i]); 421 } 422 } else { 423 for (int i = 1; i < length; i++) { 424 out.append(between); 425 toHexDigits(out, bytes[fromIndex + i]); 426 } 427 } 428 out.append(suffix); 429 } catch (IOException ioe) { 430 throw new UncheckedIOException(ioe.getMessage(), ioe); 431 } 432 } 433 return out; 434 } 435 436 /** 437 * Returns a string formatting of the range of bytes optimized 438 * for a single allocation. 439 * Prefix and suffix must be empty and the delimiter 440 * must be empty or a single byte character, otherwise null is returned. 441 * 442 * @param bytes the bytes, non-null 443 * @param fromIndex the initial index of the range, inclusive 444 * @param toIndex the final index of the range, exclusive. 445 * @return a String formatted or null for non-single byte delimiter 446 * or non-empty prefix or suffix 447 */ formatOptDelimiter(byte[] bytes, int fromIndex, int toIndex)448 private String formatOptDelimiter(byte[] bytes, int fromIndex, int toIndex) { 449 byte[] rep; 450 if (!prefix.isEmpty() || !suffix.isEmpty()) { 451 return null; 452 } 453 int length = toIndex - fromIndex; 454 if (delimiter.isEmpty()) { 455 // Allocate the byte array and fill in the hex pairs for each byte 456 rep = new byte[checkMaxArraySize(length * 2L)]; 457 for (int i = 0; i < length; i++) { 458 rep[i * 2] = (byte)toHighHexDigit(bytes[fromIndex + i]); 459 rep[i * 2 + 1] = (byte)toLowHexDigit(bytes[fromIndex + i]); 460 } 461 } else if (delimiter.length() == 1 && delimiter.charAt(0) < 256) { 462 // Allocate the byte array and fill in the characters for the first byte 463 // Then insert the delimiter and hexadecimal characters for each of the remaining bytes 464 char sep = delimiter.charAt(0); 465 rep = new byte[checkMaxArraySize(length * 3L - 1L)]; 466 rep[0] = (byte) toHighHexDigit(bytes[fromIndex]); 467 rep[1] = (byte) toLowHexDigit(bytes[fromIndex]); 468 for (int i = 1; i < length; i++) { 469 rep[i * 3 - 1] = (byte) sep; 470 rep[i * 3 ] = (byte) toHighHexDigit(bytes[fromIndex + i]); 471 rep[i * 3 + 1] = (byte) toLowHexDigit(bytes[fromIndex + i]); 472 } 473 } else { 474 // Delimiter formatting not to a single byte 475 return null; 476 } 477 // BEGIN Android-removed: JavaLangAccess and SharedSecrets for String allocation. 478 /* 479 try { 480 // Return a new string using the bytes without making a copy 481 return jla.newStringNoRepl(rep, StandardCharsets.ISO_8859_1); 482 } catch (CharacterCodingException cce) { 483 throw new AssertionError(cce); 484 } 485 */ 486 return StringFactory.newStringFromBytes(rep, StandardCharsets.ISO_8859_1); 487 // END Android-removed: JavaLangAccess and SharedSecrets for String allocation. 488 } 489 490 /** 491 * Checked that the requested size for the result string is 492 * less than or equal to the max array size. 493 * 494 * @param length the requested size of a byte array. 495 * @return the length 496 * @throws OutOfMemoryError if the size is larger than Integer.MAX_VALUE 497 */ checkMaxArraySize(long length)498 private static int checkMaxArraySize(long length) { 499 if (length > Integer.MAX_VALUE) 500 throw new OutOfMemoryError("String size " + length + 501 " exceeds maximum " + Integer.MAX_VALUE); 502 return (int)length; 503 } 504 505 /** 506 * Returns a byte array containing hexadecimal values parsed from the string. 507 * 508 * Each byte value is parsed from the prefix, two case insensitive hexadecimal characters, 509 * and the suffix. A delimiter follows each formatted value, except the last. 510 * The delimiters, prefixes, and suffixes strings must be present; they may be empty strings. 511 * A valid string consists only of the above format. 512 * 513 * @param string a string containing the byte values with prefix, hexadecimal digits, suffix, 514 * and delimiters 515 * @return a byte array with the values parsed from the string 516 * @throws IllegalArgumentException if the prefix or suffix is not present for each byte value, 517 * the byte values are not hexadecimal characters, or if the delimiter is not present 518 * after all but the last byte value 519 */ parseHex(CharSequence string)520 public byte[] parseHex(CharSequence string) { 521 return parseHex(string, 0, string.length()); 522 } 523 524 /** 525 * Returns a byte array containing hexadecimal values parsed from a range of the string. 526 * 527 * Each byte value is parsed from the prefix, two case insensitive hexadecimal characters, 528 * and the suffix. A delimiter follows each formatted value, except the last. 529 * The delimiters, prefixes, and suffixes strings must be present; they may be empty strings. 530 * A valid string consists only of the above format. 531 * 532 * @param string a string range containing hexadecimal digits, 533 * delimiters, prefix, and suffix. 534 * @param fromIndex the initial index of the range, inclusive 535 * @param toIndex the final index of the range, exclusive. 536 * @return a byte array with the values parsed from the string range 537 * @throws IllegalArgumentException if the prefix or suffix is not present for each byte value, 538 * the byte values are not hexadecimal characters, or if the delimiter is not present 539 * after all but the last byte value 540 * @throws IndexOutOfBoundsException if the string range is out of bounds 541 */ parseHex(CharSequence string, int fromIndex, int toIndex)542 public byte[] parseHex(CharSequence string, int fromIndex, int toIndex) { 543 Objects.requireNonNull(string, "string"); 544 Objects.checkFromToIndex(fromIndex, toIndex, string.length()); 545 546 if (fromIndex != 0 || toIndex != string.length()) { 547 string = string.subSequence(fromIndex, toIndex); 548 } 549 550 // BEGIN Android-changed: CharSequence#isEmpty() is not imported yet. 551 // See http://b/241413330. 552 /* 553 if (string.isEmpty()) 554 */ 555 if (string.length() == 0) 556 // END Android-changed: CharSequence#isEmpty() is not imported yet. 557 return EMPTY_BYTES; 558 if (delimiter.isEmpty() && prefix.isEmpty() && suffix.isEmpty()) 559 return parseNoDelimiter(string); 560 561 // avoid overflow for max length prefix or suffix 562 long valueChars = prefix.length() + 2L + suffix.length(); 563 long stride = valueChars + delimiter.length(); 564 if ((string.length() - valueChars) % stride != 0) 565 throw new IllegalArgumentException("extra or missing delimiters " + 566 "or values consisting of prefix, two hexadecimal digits, and suffix"); 567 568 checkLiteral(string, 0, prefix); 569 checkLiteral(string, string.length() - suffix.length(), suffix); 570 String between = suffix + delimiter + prefix; 571 final int len = (int)((string.length() - valueChars) / stride + 1L); 572 byte[] bytes = new byte[len]; 573 int i, offset; 574 for (i = 0, offset = prefix.length(); i < len - 1; i++, offset += 2 + between.length()) { 575 bytes[i] = (byte) fromHexDigits(string, offset); 576 checkLiteral(string, offset + 2, between); 577 } 578 bytes[i] = (byte) fromHexDigits(string, offset); 579 580 return bytes; 581 } 582 583 /** 584 * Returns a byte array containing hexadecimal values parsed from 585 * a range of the character array. 586 * 587 * Each byte value is parsed from the prefix, two case insensitive hexadecimal characters, 588 * and the suffix. A delimiter follows each formatted value, except the last. 589 * The delimiters, prefixes, and suffixes strings must be present; they may be empty strings. 590 * A valid character array range consists only of the above format. 591 * 592 * @param chars a character array range containing an even number of hexadecimal digits, 593 * delimiters, prefix, and suffix. 594 * @param fromIndex the initial index of the range, inclusive 595 * @param toIndex the final index of the range, exclusive. 596 * @return a byte array with the values parsed from the character array range 597 * @throws IllegalArgumentException if the prefix or suffix is not present for each byte value, 598 * the byte values are not hexadecimal characters, or if the delimiter is not present 599 * after all but the last byte value 600 * @throws IndexOutOfBoundsException if the character array range is out of bounds 601 */ parseHex(char[] chars, int fromIndex, int toIndex)602 public byte[] parseHex(char[] chars, int fromIndex, int toIndex) { 603 Objects.requireNonNull(chars, "chars"); 604 Objects.checkFromToIndex(fromIndex, toIndex, chars.length); 605 CharBuffer cb = CharBuffer.wrap(chars, fromIndex, toIndex - fromIndex); 606 return parseHex(cb); 607 } 608 609 /** 610 * Compare the literal and throw an exception if it does not match. 611 * Pre-condition: {@code index + literal.length() <= string.length()}. 612 * 613 * @param string a CharSequence 614 * @param index the index of the literal in the CharSequence 615 * @param literal the expected literal 616 * @throws IllegalArgumentException if the literal is not present 617 */ checkLiteral(CharSequence string, int index, String literal)618 private static void checkLiteral(CharSequence string, int index, String literal) { 619 assert index <= string.length() - literal.length() : "pre-checked invariant error"; 620 if (literal.isEmpty() || 621 (literal.length() == 1 && literal.charAt(0) == string.charAt(index))) { 622 return; 623 } 624 for (int i = 0; i < literal.length(); i++) { 625 if (string.charAt(index + i) != literal.charAt(i)) { 626 throw new IllegalArgumentException(escapeNL("found: \"" + 627 string.subSequence(index, index + literal.length()) + 628 "\", expected: \"" + literal + "\", index: " + index + 629 " ch: " + (int)string.charAt(index + i))); 630 } 631 } 632 } 633 634 /** 635 * Expands new line characters to escaped newlines for display. 636 * 637 * @param string a string 638 * @return a string with newline characters escaped 639 */ escapeNL(String string)640 private static String escapeNL(String string) { 641 return string.replace("\n", "\\n") 642 .replace("\r", "\\r"); 643 } 644 645 /** 646 * Returns the hexadecimal character for the low 4 bits of the value considering it to be a byte. 647 * If the parameter {@link #isUpperCase()} is {@code true} the 648 * character returned for values {@code 10-15} is uppercase {@code "A-F"}, 649 * otherwise the character returned is lowercase {@code "a-f"}. 650 * The values in the range {@code 0-9} are returned as {@code "0-9"}. 651 * 652 * @param value a value, only the low 4 bits {@code 0-3} of the value are used 653 * @return the hexadecimal character for the low 4 bits {@code 0-3} of the value 654 */ toLowHexDigit(int value)655 public char toLowHexDigit(int value) { 656 return (char)digits[value & 0xf]; 657 } 658 659 /** 660 * Returns the hexadecimal character for the high 4 bits of the value considering it to be a byte. 661 * If the parameter {@link #isUpperCase()} is {@code true} the 662 * character returned for values {@code 10-15} is uppercase {@code "A-F"}, 663 * otherwise the character returned is lowercase {@code "a-f"}. 664 * The values in the range {@code 0-9} are returned as {@code "0-9"}. 665 * 666 * @param value a value, only bits {@code 4-7} of the value are used 667 * @return the hexadecimal character for the bits {@code 4-7} of the value 668 */ toHighHexDigit(int value)669 public char toHighHexDigit(int value) { 670 return (char)digits[(value >> 4) & 0xf]; 671 } 672 673 /** 674 * Appends two hexadecimal characters for the byte value to the {@link Appendable}. 675 * Each nibble (4 bits) from most significant to least significant of the value 676 * is formatted as if by {@link #toLowHexDigit(int) toLowHexDigit(nibble)}. 677 * The hexadecimal characters are appended in one or more calls to the 678 * {@link Appendable} methods. The delimiter, prefix and suffix are not used. 679 * 680 * @param <A> The type of {@code Appendable} 681 * @param out an {@code Appendable}, non-null 682 * @param value a byte value 683 * @return the {@code Appendable} 684 * @throws UncheckedIOException if an I/O exception occurs appending to the output 685 */ toHexDigits(A out, byte value)686 public <A extends Appendable> A toHexDigits(A out, byte value) { 687 Objects.requireNonNull(out, "out"); 688 try { 689 out.append(toHighHexDigit(value)); 690 out.append(toLowHexDigit(value)); 691 return out; 692 } catch (IOException ioe) { 693 throw new UncheckedIOException(ioe.getMessage(), ioe); 694 } 695 } 696 697 /** 698 * Returns the two hexadecimal characters for the {@code byte} value. 699 * Each nibble (4 bits) from most significant to least significant of the value 700 * is formatted as if by {@link #toLowHexDigit(int) toLowHexDigit(nibble)}. 701 * The delimiter, prefix and suffix are not used. 702 * 703 * @param value a byte value 704 * @return the two hexadecimal characters for the byte value 705 */ toHexDigits(byte value)706 public String toHexDigits(byte value) { 707 byte[] rep = new byte[2]; 708 rep[0] = (byte)toHighHexDigit(value); 709 rep[1] = (byte)toLowHexDigit(value); 710 // BEGIN Android-removed: JavaLangAccess and SharedSecrets for String allocation. 711 /* 712 try { 713 // Return a new string using the bytes without making a copy 714 return jla.newStringNoRepl(rep, StandardCharsets.ISO_8859_1); 715 } catch (CharacterCodingException cce) { 716 throw new AssertionError(cce); 717 } 718 */ 719 return StringFactory.newStringFromBytes(rep, StandardCharsets.ISO_8859_1); 720 // END Android-removed: JavaLangAccess and SharedSecrets for String allocation. 721 } 722 723 /** 724 * Returns the four hexadecimal characters for the {@code char} value. 725 * Each nibble (4 bits) from most significant to least significant of the value 726 * is formatted as if by {@link #toLowHexDigit(int) toLowHexDigit(nibble)}. 727 * The delimiter, prefix and suffix are not used. 728 * 729 * @param value a {@code char} value 730 * @return the four hexadecimal characters for the {@code char} value 731 */ toHexDigits(char value)732 public String toHexDigits(char value) { 733 return toHexDigits((short)value); 734 } 735 736 /** 737 * Returns the four hexadecimal characters for the {@code short} value. 738 * Each nibble (4 bits) from most significant to least significant of the value 739 * is formatted as if by {@link #toLowHexDigit(int) toLowHexDigit(nibble)}. 740 * The delimiter, prefix and suffix are not used. 741 * 742 * @param value a {@code short} value 743 * @return the four hexadecimal characters for the {@code short} value 744 */ toHexDigits(short value)745 public String toHexDigits(short value) { 746 byte[] rep = new byte[4]; 747 rep[0] = (byte)toHighHexDigit((byte)(value >> 8)); 748 rep[1] = (byte)toLowHexDigit((byte)(value >> 8)); 749 rep[2] = (byte)toHighHexDigit((byte)value); 750 rep[3] = (byte)toLowHexDigit((byte)value); 751 752 // BEGIN Android-removed: JavaLangAccess and SharedSecrets for String allocation. 753 /* 754 try { 755 // Return a new string using the bytes without making a copy 756 return jla.newStringNoRepl(rep, StandardCharsets.ISO_8859_1); 757 } catch (CharacterCodingException cce) { 758 throw new AssertionError(cce); 759 } 760 */ 761 return StringFactory.newStringFromBytes(rep, StandardCharsets.ISO_8859_1); 762 // END Android-removed: JavaLangAccess and SharedSecrets for String allocation. 763 } 764 765 /** 766 * Returns the eight hexadecimal characters for the {@code int} value. 767 * Each nibble (4 bits) from most significant to least significant of the value 768 * is formatted as if by {@link #toLowHexDigit(int) toLowHexDigit(nibble)}. 769 * The delimiter, prefix and suffix are not used. 770 * 771 * @param value an {@code int} value 772 * @return the eight hexadecimal characters for the {@code int} value 773 * @see Integer#toHexString 774 */ toHexDigits(int value)775 public String toHexDigits(int value) { 776 byte[] rep = new byte[8]; 777 rep[0] = (byte)toHighHexDigit((byte)(value >> 24)); 778 rep[1] = (byte)toLowHexDigit((byte)(value >> 24)); 779 rep[2] = (byte)toHighHexDigit((byte)(value >> 16)); 780 rep[3] = (byte)toLowHexDigit((byte)(value >> 16)); 781 rep[4] = (byte)toHighHexDigit((byte)(value >> 8)); 782 rep[5] = (byte)toLowHexDigit((byte)(value >> 8)); 783 rep[6] = (byte)toHighHexDigit((byte)value); 784 rep[7] = (byte)toLowHexDigit((byte)value); 785 786 // BEGIN Android-removed: JavaLangAccess and SharedSecrets for String allocation. 787 /* 788 try { 789 // Return a new string using the bytes without making a copy 790 return jla.newStringNoRepl(rep, StandardCharsets.ISO_8859_1); 791 } catch (CharacterCodingException cce) { 792 throw new AssertionError(cce); 793 } 794 */ 795 return StringFactory.newStringFromBytes(rep, StandardCharsets.ISO_8859_1); 796 // END Android-removed: JavaLangAccess and SharedSecrets for String allocation. 797 } 798 799 /** 800 * Returns the sixteen hexadecimal characters for the {@code long} value. 801 * Each nibble (4 bits) from most significant to least significant of the value 802 * is formatted as if by {@link #toLowHexDigit(int) toLowHexDigit(nibble)}. 803 * The delimiter, prefix and suffix are not used. 804 * 805 * @param value a {@code long} value 806 * @return the sixteen hexadecimal characters for the {@code long} value 807 * @see Long#toHexString 808 */ toHexDigits(long value)809 public String toHexDigits(long value) { 810 byte[] rep = new byte[16]; 811 rep[0] = (byte)toHighHexDigit((byte)(value >>> 56)); 812 rep[1] = (byte)toLowHexDigit((byte)(value >>> 56)); 813 rep[2] = (byte)toHighHexDigit((byte)(value >>> 48)); 814 rep[3] = (byte)toLowHexDigit((byte)(value >>> 48)); 815 rep[4] = (byte)toHighHexDigit((byte)(value >>> 40)); 816 rep[5] = (byte)toLowHexDigit((byte)(value >>> 40)); 817 rep[6] = (byte)toHighHexDigit((byte)(value >>> 32)); 818 rep[7] = (byte)toLowHexDigit((byte)(value >>> 32)); 819 rep[8] = (byte)toHighHexDigit((byte)(value >>> 24)); 820 rep[9] = (byte)toLowHexDigit((byte)(value >>> 24)); 821 rep[10] = (byte)toHighHexDigit((byte)(value >>> 16)); 822 rep[11] = (byte)toLowHexDigit((byte)(value >>> 16)); 823 rep[12] = (byte)toHighHexDigit((byte)(value >>> 8)); 824 rep[13] = (byte)toLowHexDigit((byte)(value >>> 8)); 825 rep[14] = (byte)toHighHexDigit((byte)value); 826 rep[15] = (byte)toLowHexDigit((byte)value); 827 828 // BEGIN Android-removed: JavaLangAccess and SharedSecrets for String allocation. 829 /* 830 try { 831 // Return a new string using the bytes without making a copy 832 return jla.newStringNoRepl(rep, StandardCharsets.ISO_8859_1); 833 } catch (CharacterCodingException cce) { 834 throw new AssertionError(cce); 835 } 836 */ 837 return StringFactory.newStringFromBytes(rep, StandardCharsets.ISO_8859_1); 838 // END Android-removed: JavaLangAccess and SharedSecrets for String allocation. 839 } 840 841 /** 842 * Returns up to sixteen hexadecimal characters for the {@code long} value. 843 * Each nibble (4 bits) from most significant to least significant of the value 844 * is formatted as if by {@link #toLowHexDigit(int) toLowHexDigit(nibble)}. 845 * The delimiter, prefix and suffix are not used. 846 * 847 * @param value a {@code long} value 848 * @param digits the number of hexadecimal digits to return, 0 to 16 849 * @return the hexadecimal characters for the {@code long} value 850 * @throws IllegalArgumentException if {@code digits} is negative or greater than 16 851 */ toHexDigits(long value, int digits)852 public String toHexDigits(long value, int digits) { 853 if (digits < 0 || digits > 16) 854 throw new IllegalArgumentException("number of digits: " + digits); 855 if (digits == 0) 856 return ""; 857 byte[] rep = new byte[digits]; 858 for (int i = rep.length - 1; i >= 0; i--) { 859 rep[i] = (byte)toLowHexDigit((byte)(value)); 860 value = value >>> 4; 861 } 862 // BEGIN Android-removed: JavaLangAccess and SharedSecrets for String allocation. 863 /* 864 try { 865 // Return a new string using the bytes without making a copy 866 return jla.newStringNoRepl(rep, StandardCharsets.ISO_8859_1); 867 } catch (CharacterCodingException cce) { 868 throw new AssertionError(cce); 869 } 870 */ 871 return StringFactory.newStringFromBytes(rep, StandardCharsets.ISO_8859_1); 872 // END Android-removed: JavaLangAccess and SharedSecrets for String allocation. 873 } 874 875 /** 876 * Returns a byte array containing the parsed hex digits. 877 * A valid string consists only of an even number of hex digits. 878 * 879 * @param string a string containing an even number of only hex digits 880 * @return a byte array 881 * @throws IllegalArgumentException if the string length is not valid or 882 * the string contains non-hexadecimal characters 883 */ parseNoDelimiter(CharSequence string)884 private static byte[] parseNoDelimiter(CharSequence string) { 885 if ((string.length() & 1) != 0) 886 throw new IllegalArgumentException("string length not even: " + 887 string.length()); 888 889 byte[] bytes = new byte[string.length() / 2]; 890 for (int i = 0; i < bytes.length; i++) { 891 bytes[i] = (byte) fromHexDigits(string, i * 2); 892 } 893 894 return bytes; 895 } 896 897 /** 898 * Check the number of requested digits against a limit. 899 * 900 * @param fromIndex the initial index of the range, inclusive 901 * @param toIndex the final index of the range, exclusive. 902 * @param limit the maximum allowed 903 * @return the length of the range 904 */ checkDigitCount(int fromIndex, int toIndex, int limit)905 private static int checkDigitCount(int fromIndex, int toIndex, int limit) { 906 int length = toIndex - fromIndex; 907 if (length > limit) 908 throw new IllegalArgumentException("string length greater than " + 909 limit + ": " + length); 910 return length; 911 } 912 913 /** 914 * Returns {@code true} if the character is a valid hexadecimal character or codepoint. 915 * The valid hexadecimal characters are: 916 * <ul> 917 * <li>{@code '0' ('\u005Cu0030')} through {@code '9' ('\u005Cu0039')} inclusive, 918 * <li>{@code 'A' ('\u005Cu0041')} through {@code 'F' ('\u005Cu0046')} inclusive, and 919 * <li>{@code 'a' ('\u005Cu0061')} through {@code 'f' ('\u005Cu0066')} inclusive. 920 * </ul> 921 * @param ch a codepoint 922 * @return {@code true} if the character is valid a hexadecimal character, 923 * otherwise {@code false} 924 */ isHexDigit(int ch)925 public static boolean isHexDigit(int ch) { 926 return ((ch >>> 8) == 0 && DIGITS[ch] >= 0); 927 } 928 929 /** 930 * Returns the value for the hexadecimal character or codepoint. 931 * The value is: 932 * <ul> 933 * <li>{@code (ch - '0')} for {@code '0'} through {@code '9'} inclusive, 934 * <li>{@code (ch - 'A' + 10)} for {@code 'A'} through {@code 'F'} inclusive, and 935 * <li>{@code (ch - 'a' + 10)} for {@code 'a'} through {@code 'f'} inclusive. 936 * </ul> 937 * 938 * @param ch a character or codepoint 939 * @return the value {@code 0-15} 940 * @throws NumberFormatException if the codepoint is not a hexadecimal character 941 */ fromHexDigit(int ch)942 public static int fromHexDigit(int ch) { 943 int value; 944 if ((ch >>> 8) == 0 && (value = DIGITS[ch]) >= 0) { 945 return value; 946 } 947 throw new NumberFormatException("not a hexadecimal digit: \"" + (char) ch + "\" = " + ch); 948 } 949 950 /** 951 * Returns a value parsed from two hexadecimal characters in a string. 952 * The characters in the range from {@code index} to {@code index + 1}, 953 * inclusive, must be valid hex digits according to {@link #fromHexDigit(int)}. 954 * 955 * @param string a CharSequence containing the characters 956 * @param index the index of the first character of the range 957 * @return the value parsed from the string range 958 * @throws NumberFormatException if any of the characters in the range 959 * is not a hexadecimal character 960 * @throws IndexOutOfBoundsException if the range is out of bounds 961 * for the {@code CharSequence} 962 */ fromHexDigits(CharSequence string, int index)963 private static int fromHexDigits(CharSequence string, int index) { 964 int high = fromHexDigit(string.charAt(index)); 965 int low = fromHexDigit(string.charAt(index + 1)); 966 return (high << 4) | low; 967 } 968 969 /** 970 * Returns the {@code int} value parsed from a string of up to eight hexadecimal characters. 971 * The hexadecimal characters are parsed from most significant to least significant 972 * using {@link #fromHexDigit(int)} to form an unsigned value. 973 * The value is zero extended to 32 bits and is returned as an {@code int}. 974 * 975 * @apiNote 976 * {@link Integer#parseInt(String, int) Integer.parseInt(s, 16)} and 977 * {@link Integer#parseUnsignedInt(String, int) Integer.parseUnsignedInt(s, 16)} 978 * are similar but allow all Unicode hexadecimal digits defined by 979 * {@link Character#digit(char, int) Character.digit(ch, 16)}. 980 * {@code HexFormat} uses only hexadecimal characters 981 * {@code "0-9"}, {@code "A-F"} and {@code "a-f"}. 982 * Signed hexadecimal strings can be parsed with {@link Integer#parseInt(String, int)}. 983 * 984 * @param string a CharSequence containing up to eight hexadecimal characters 985 * @return the value parsed from the string 986 * @throws IllegalArgumentException if the string length is greater than eight (8) or 987 * if any of the characters is not a hexadecimal character 988 */ fromHexDigits(CharSequence string)989 public static int fromHexDigits(CharSequence string) { 990 return fromHexDigits(string, 0, string.length()); 991 } 992 993 /** 994 * Returns the {@code int} value parsed from a string range of up to eight hexadecimal 995 * characters. 996 * The characters in the range {@code fromIndex} to {@code toIndex}, exclusive, 997 * are parsed from most significant to least significant 998 * using {@link #fromHexDigit(int)} to form an unsigned value. 999 * The value is zero extended to 32 bits and is returned as an {@code int}. 1000 * 1001 * @apiNote 1002 * {@link Integer#parseInt(String, int) Integer.parseInt(s, 16)} and 1003 * {@link Integer#parseUnsignedInt(String, int) Integer.parseUnsignedInt(s, 16)} 1004 * are similar but allow all Unicode hexadecimal digits defined by 1005 * {@link Character#digit(char, int) Character.digit(ch, 16)}. 1006 * {@code HexFormat} uses only hexadecimal characters 1007 * {@code "0-9"}, {@code "A-F"} and {@code "a-f"}. 1008 * Signed hexadecimal strings can be parsed with {@link Integer#parseInt(String, int)}. 1009 * 1010 * @param string a CharSequence containing the characters 1011 * @param fromIndex the initial index of the range, inclusive 1012 * @param toIndex the final index of the range, exclusive. 1013 * @return the value parsed from the string range 1014 * @throws IndexOutOfBoundsException if the range is out of bounds 1015 * for the {@code CharSequence} 1016 * @throws IllegalArgumentException if length of the range is greater than eight (8) or 1017 * if any of the characters is not a hexadecimal character 1018 */ fromHexDigits(CharSequence string, int fromIndex, int toIndex)1019 public static int fromHexDigits(CharSequence string, int fromIndex, int toIndex) { 1020 Objects.requireNonNull(string, "string"); 1021 Objects.checkFromToIndex(fromIndex, toIndex, string.length()); 1022 int length = checkDigitCount(fromIndex, toIndex, 8); 1023 int value = 0; 1024 for (int i = 0; i < length; i++) { 1025 value = (value << 4) + fromHexDigit(string.charAt(fromIndex + i)); 1026 } 1027 return value; 1028 } 1029 1030 /** 1031 * Returns the long value parsed from a string of up to sixteen hexadecimal characters. 1032 * The hexadecimal characters are parsed from most significant to least significant 1033 * using {@link #fromHexDigit(int)} to form an unsigned value. 1034 * The value is zero extended to 64 bits and is returned as a {@code long}. 1035 * 1036 * @apiNote 1037 * {@link Long#parseLong(String, int) Long.parseLong(s, 16)} and 1038 * {@link Long#parseUnsignedLong(String, int) Long.parseUnsignedLong(s, 16)} 1039 * are similar but allow all Unicode hexadecimal digits defined by 1040 * {@link Character#digit(char, int) Character.digit(ch, 16)}. 1041 * {@code HexFormat} uses only hexadecimal characters 1042 * {@code "0-9"}, {@code "A-F"} and {@code "a-f"}. 1043 * Signed hexadecimal strings can be parsed with {@link Long#parseLong(String, int)}. 1044 * 1045 * @param string a CharSequence containing up to sixteen hexadecimal characters 1046 * @return the value parsed from the string 1047 * @throws IllegalArgumentException if the string length is greater than sixteen (16) or 1048 * if any of the characters is not a hexadecimal character 1049 */ fromHexDigitsToLong(CharSequence string)1050 public static long fromHexDigitsToLong(CharSequence string) { 1051 return fromHexDigitsToLong(string, 0, string.length()); 1052 } 1053 1054 /** 1055 * Returns the long value parsed from a string range of up to sixteen hexadecimal 1056 * characters. 1057 * The characters in the range {@code fromIndex} to {@code toIndex}, exclusive, 1058 * are parsed from most significant to least significant 1059 * using {@link #fromHexDigit(int)} to form an unsigned value. 1060 * The value is zero extended to 64 bits and is returned as a {@code long}. 1061 * 1062 * @apiNote 1063 * {@link Long#parseLong(String, int) Long.parseLong(s, 16)} and 1064 * {@link Long#parseUnsignedLong(String, int) Long.parseUnsignedLong(s, 16)} 1065 * are similar but allow all Unicode hexadecimal digits defined by 1066 * {@link Character#digit(char, int) Character.digit(ch, 16)}. 1067 * {@code HexFormat} uses only hexadecimal characters 1068 * {@code "0-9"}, {@code "A-F"} and {@code "a-f"}. 1069 * Signed hexadecimal strings can be parsed with {@link Long#parseLong(String, int)}. 1070 * 1071 * @param string a CharSequence containing the characters 1072 * @param fromIndex the initial index of the range, inclusive 1073 * @param toIndex the final index of the range, exclusive. 1074 * @return the value parsed from the string range 1075 * @throws IndexOutOfBoundsException if the range is out of bounds 1076 * for the {@code CharSequence} 1077 * @throws IllegalArgumentException if the length of the range is greater than sixteen (16) or 1078 * if any of the characters is not a hexadecimal character 1079 */ fromHexDigitsToLong(CharSequence string, int fromIndex, int toIndex)1080 public static long fromHexDigitsToLong(CharSequence string, int fromIndex, int toIndex) { 1081 Objects.requireNonNull(string, "string"); 1082 Objects.checkFromToIndex(fromIndex, toIndex, string.length()); 1083 int length = checkDigitCount(fromIndex, toIndex, 16); 1084 long value = 0L; 1085 for (int i = 0; i < length; i++) { 1086 value = (value << 4) + fromHexDigit(string.charAt(fromIndex + i)); 1087 } 1088 return value; 1089 } 1090 1091 /** 1092 * Returns {@code true} if the other object is a {@code HexFormat} 1093 * with the same parameters. 1094 * 1095 * @param o an object, may be null 1096 * @return {@code true} if the other object is a {@code HexFormat} and the parameters 1097 * uppercase, delimiter, prefix, and suffix are equal; 1098 * otherwise {@code false} 1099 */ 1100 @Override equals(Object o)1101 public boolean equals(Object o) { 1102 if (this == o) 1103 return true; 1104 if (o == null || getClass() != o.getClass()) 1105 return false; 1106 HexFormat otherHex = (HexFormat) o; 1107 return Arrays.equals(digits, otherHex.digits) && 1108 delimiter.equals(otherHex.delimiter) && 1109 prefix.equals(otherHex.prefix) && 1110 suffix.equals(otherHex.suffix); 1111 } 1112 1113 /** 1114 * Returns a hashcode for this {@code HexFormat}. 1115 * 1116 * @return a hashcode for this {@code HexFormat} 1117 */ 1118 @Override hashCode()1119 public int hashCode() { 1120 int result = Objects.hash(delimiter, prefix, suffix); 1121 result = 31 * result + Boolean.hashCode(Arrays.equals(digits, UPPERCASE_DIGITS)); 1122 return result; 1123 } 1124 1125 /** 1126 * Returns a description of the formatter parameters for uppercase, 1127 * delimiter, prefix, and suffix. 1128 * 1129 * @return a description of this {@code HexFormat} 1130 */ 1131 @Override toString()1132 public String toString() { 1133 return escapeNL("uppercase: " + Arrays.equals(digits, UPPERCASE_DIGITS) + 1134 ", delimiter: \"" + delimiter + 1135 "\", prefix: \"" + prefix + 1136 "\", suffix: \"" + suffix + "\""); 1137 } 1138 } 1139