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