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