Path: blob/master/src/java.base/share/classes/java/util/HexFormat.java
41152 views
/*1* Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package java.util;2627import jdk.internal.access.JavaLangAccess;28import jdk.internal.access.SharedSecrets;2930import java.io.IOException;31import java.io.UncheckedIOException;32import java.nio.CharBuffer;33import java.nio.charset.CharacterCodingException;34import java.nio.charset.StandardCharsets;3536/**37* {@code HexFormat} converts between bytes and chars and hex-encoded strings which may include38* additional formatting markup such as prefixes, suffixes, and delimiters.39* <p>40* There are two factories of {@code HexFormat} with preset parameters {@link #of()} and41* {@link #ofDelimiter(String) ofDelimiter(delimiter)}. For other parameter combinations42* the {@code withXXX} methods return copies of {@code HexFormat} modified43* {@link #withPrefix(String)}, {@link #withSuffix(String)}, {@link #withDelimiter(String)}44* or choice of {@link #withUpperCase()} or {@link #withLowerCase()} parameters.45* <p>46* For primitive to hexadecimal string conversions the {@code toHexDigits}47* methods include {@link #toHexDigits(byte)}, {@link #toHexDigits(int)}, and48* {@link #toHexDigits(long)}, etc. The default is to use lowercase characters {@code "0-9","a-f"}.49* For conversions producing uppercase hexadecimal the characters are {@code "0-9","A-F"}.50* Only the {@link HexFormat#isUpperCase() HexFormat.isUpperCase()} parameter is51* considered; the delimiter, prefix and suffix are not used.52*53* <p>54* For hexadecimal string to primitive conversions the {@code fromHexDigits}55* methods include {@link #fromHexDigits(CharSequence) fromHexDigits(string)},56* {@link #fromHexDigitsToLong(CharSequence) fromHexDigitsToLong(string)}, and57* {@link #fromHexDigit(int) fromHexDigit(int)} converts a single character or codepoint.58* For conversions from hexadecimal characters the digits and uppercase and lowercase59* characters in {@code "0-9", "a-f", and "A-F"} are converted to corresponding values60* {@code 0-15}. The delimiter, prefix, suffix, and uppercase parameters are not used.61*62* <p>63* For byte array to formatted hexadecimal string conversions64* the {@code formatHex} methods include {@link #formatHex(byte[]) formatHex(byte[])}65* and {@link #formatHex(Appendable, byte[]) formatHex(Appendable, byte[])}.66* The formatted output is a string or is appended to an {@link Appendable} such as67* {@link StringBuilder} or {@link java.io.PrintStream}.68* Each byte value is formatted as the prefix, two hexadecimal characters from the69* uppercase or lowercase digits, and the suffix.70* A delimiter follows each formatted value, except the last.71* For conversions producing uppercase hexadecimal strings use {@link #withUpperCase()}.72*73* <p>74* For formatted hexadecimal string to byte array conversions the75* {@code parseHex} methods include {@link #parseHex(CharSequence) parseHex(CharSequence)} and76* {@link #parseHex(char[], int, int) parseHex(char[], offset, length)}.77* Each byte value is parsed from the prefix, two case insensitive hexadecimal characters,78* and the suffix. A delimiter follows each formatted value, except the last.79*80* @apiNote81* For example, an individual byte is converted to a string of hexadecimal digits using82* {@link HexFormat#toHexDigits(int) toHexDigits(int)} and converted from a string to a83* primitive value using {@link HexFormat#fromHexDigits(CharSequence) fromHexDigits(string)}.84* <pre>{@code85* HexFormat hex = HexFormat.of();86* byte b = 127;87* String byteStr = hex.toHexDigits(b);88*89* byte byteVal = (byte)hex.fromHexDigits(byteStr);90* assert(byteStr.equals("7f"));91* assert(b == byteVal);92*93* // The hexadecimal digits are: "7f"94* }</pre>95* <p>96* For a comma ({@code ", "}) separated format with a prefix ({@code "#"})97* using lowercase hex digits the {@code HexFormat} is:98* <pre>{@code99* HexFormat commaFormat = HexFormat.ofDelimiter(", ").withPrefix("#");100* byte[] bytes = {0, 1, 2, 3, 124, 125, 126, 127};101* String str = commaFormat.formatHex(bytes);102*103* byte[] parsed = commaFormat.parseHex(str);104* assert(Arrays.equals(bytes, parsed));105*106* // The formatted string is: "#00, #01, #02, #03, #7c, #7d, #7e, #7f"107* }</pre>108* <p>109* For a fingerprint of byte values that uses the delimiter colon ({@code ":"})110* and uppercase characters the {@code HexFormat} is:111* <pre>{@code112* HexFormat formatFingerprint = HexFormat.ofDelimiter(":").withUpperCase();113* byte[] bytes = {0, 1, 2, 3, 124, 125, 126, 127};114* String str = formatFingerprint.formatHex(bytes);115* byte[] parsed = formatFingerprint.parseHex(str);116* assert(Arrays.equals(bytes, parsed));117*118* // The formatted string is: "00:01:02:03:7C:7D:7E:7F"119* }</pre>120*121* <p>122* This is a <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>123* class; use of identity-sensitive operations (including reference equality124* ({@code ==}), identity hash code, or synchronization) on instances of125* {@code HexFormat} may have unpredictable results and should be avoided.126* The {@code equals} method should be used for comparisons.127* <p>128* This class is immutable and thread-safe.129* <p>130* Unless otherwise noted, passing a null argument to any method will cause a131* {@link java.lang.NullPointerException NullPointerException} to be thrown.132*133* @since 17134*/135136137public final class HexFormat {138139// Access to create strings from a byte array.140private static final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();141142private static final byte[] UPPERCASE_DIGITS = {143'0', '1', '2', '3', '4', '5', '6', '7',144'8', '9', 'A', 'B', 'C', 'D', 'E', 'F',145};146private static final byte[] LOWERCASE_DIGITS = {147'0', '1', '2', '3', '4', '5', '6', '7',148'8', '9', 'a', 'b', 'c', 'd', 'e', 'f',149};150// Analysis has shown that generating the whole array allows the JIT to generate151// better code compared to a slimmed down array, such as one cutting off after 'f'152private static final byte[] DIGITS = {153-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,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,1560, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,157-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,158-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,159-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,160-1, -1, -1, -1, -1, -1, -1, -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};170/**171* Format each byte of an array as a pair of hexadecimal digits.172* The hexadecimal characters are from lowercase alpha digits.173*/174private static final HexFormat HEX_FORMAT =175new HexFormat("", "", "", LOWERCASE_DIGITS);176177private static final byte[] EMPTY_BYTES = {};178179private final String delimiter;180private final String prefix;181private final String suffix;182private final byte[] digits;183184/**185* Returns a HexFormat with a delimiter, prefix, suffix, and array of digits.186*187* @param delimiter a delimiter, non-null188* @param prefix a prefix, non-null189* @param suffix a suffix, non-null190* @param digits byte array of digits indexed by low nibble, non-null191* @throws NullPointerException if any argument is null192*/193private HexFormat(String delimiter, String prefix, String suffix, byte[] digits) {194this.delimiter = Objects.requireNonNull(delimiter, "delimiter");195this.prefix = Objects.requireNonNull(prefix, "prefix");196this.suffix = Objects.requireNonNull(suffix, "suffix");197this.digits = digits;198}199200/**201* Returns a hexadecimal formatter with no delimiter and lowercase characters.202* The delimiter, prefix, and suffix are empty.203* The methods {@link #withDelimiter(String) withDelimiter},204* {@link #withUpperCase() withUpperCase}, {@link #withLowerCase() withLowerCase},205* {@link #withPrefix(String) withPrefix}, and {@link #withSuffix(String) withSuffix}206* return copies of formatters with new parameters.207*208* @return a hexadecimal formatter with no delimiter and lowercase characters209*/210public static HexFormat of() {211return HEX_FORMAT;212}213214/**215* Returns a hexadecimal formatter with the delimiter and lowercase characters.216* The prefix and suffix are empty.217* The methods {@link #withDelimiter(String) withDelimiter},218* {@link #withUpperCase() withUpperCase}, {@link #withLowerCase() withLowerCase},219* {@link #withPrefix(String) withPrefix}, and {@link #withSuffix(String) withSuffix}220* return copies of formatters with new parameters.221*222* @param delimiter a delimiter, non-null, may be empty223* @return a {@link HexFormat} with the delimiter and lowercase characters224*/225public static HexFormat ofDelimiter(String delimiter) {226return new HexFormat(delimiter, "", "", LOWERCASE_DIGITS);227}228229/**230* Returns a copy of this {@code HexFormat} with the delimiter.231* @param delimiter the delimiter, non-null, may be empty232* @return a copy of this {@code HexFormat} with the delimiter233*/234public HexFormat withDelimiter(String delimiter) {235return new HexFormat(delimiter, this.prefix, this.suffix, this.digits);236}237238/**239* Returns a copy of this {@code HexFormat} with the prefix.240*241* @param prefix a prefix, non-null, may be empty242* @return a copy of this {@code HexFormat} with the prefix243*/244public HexFormat withPrefix(String prefix) {245return new HexFormat(this.delimiter, prefix, this.suffix, this.digits);246}247248/**249* Returns a copy of this {@code HexFormat} with the suffix.250*251* @param suffix a suffix, non-null, may be empty252* @return a copy of this {@code HexFormat} with the suffix253*/254public HexFormat withSuffix(String suffix) {255return new HexFormat(this.delimiter, this.prefix, suffix, this.digits);256}257258/**259* Returns a copy of this {@code HexFormat} to use uppercase hexadecimal characters.260* The uppercase hexadecimal characters are {@code "0-9", "A-F"}.261*262* @return a copy of this {@code HexFormat} with uppercase hexadecimal characters263*/264public HexFormat withUpperCase() {265return new HexFormat(this.delimiter, this.prefix, this.suffix, UPPERCASE_DIGITS);266}267268/**269* Returns a copy of this {@code HexFormat} to use lowercase hexadecimal characters.270* The lowercase hexadecimal characters are {@code "0-9", "a-f"}.271*272* @return a copy of this {@code HexFormat} with lowercase hexadecimal characters273*/274public HexFormat withLowerCase() {275return new HexFormat(this.delimiter, this.prefix, this.suffix, LOWERCASE_DIGITS);276}277278/**279* Returns the delimiter between hexadecimal values in formatted hexadecimal strings.280*281* @return the delimiter, non-null, may be empty {@code ""}282*/283public String delimiter() {284return delimiter;285}286287/**288* Returns the prefix used for each hexadecimal value in formatted hexadecimal strings.289*290* @return the prefix, non-null, may be empty {@code ""}291*/292public String prefix() {293return prefix;294}295296/**297* Returns the suffix used for each hexadecimal value in formatted hexadecimal strings.298*299* @return the suffix, non-null, may be empty {@code ""}300*/301public String suffix() {302return suffix;303}304305/**306* Returns {@code true} if the hexadecimal digits are uppercase,307* otherwise {@code false}.308*309* @return {@code true} if the hexadecimal digits are uppercase,310* otherwise {@code false}311*/312public boolean isUpperCase() {313return Arrays.equals(digits, UPPERCASE_DIGITS);314}315316/**317* Returns a hexadecimal string formatted from a byte array.318* Each byte value is formatted as the prefix, two hexadecimal characters319* {@linkplain #isUpperCase selected from} uppercase or lowercase digits, and the suffix.320* A delimiter follows each formatted value, except the last.321*322* The behavior is equivalent to323* {@link #formatHex(byte[], int, int) formatHex(bytes, 0, bytes.length))}.324*325* @param bytes a non-null array of bytes326* @return a string hexadecimal formatting of the byte array327*/328public String formatHex(byte[] bytes) {329return formatHex(bytes, 0, bytes.length);330}331332/**333* Returns a hexadecimal string formatted from a byte array range.334* Each byte value is formatted as the prefix, two hexadecimal characters335* {@linkplain #isUpperCase selected from} uppercase or lowercase digits, and the suffix.336* A delimiter follows each formatted value, except the last.337*338* @param bytes a non-null array of bytes339* @param fromIndex the initial index of the range, inclusive340* @param toIndex the final index of the range, exclusive341* @return a string hexadecimal formatting each byte of the array range342* @throws IndexOutOfBoundsException if the array range is out of bounds343*/344public String formatHex(byte[] bytes, int fromIndex, int toIndex) {345Objects.requireNonNull(bytes,"bytes");346Objects.checkFromToIndex(fromIndex, toIndex, bytes.length);347if (toIndex - fromIndex == 0) {348return "";349}350// Format efficiently if possible351String s = formatOptDelimiter(bytes, fromIndex, toIndex);352if (s == null) {353long stride = prefix.length() + 2L + suffix.length() + delimiter.length();354int capacity = checkMaxArraySize((toIndex - fromIndex) * stride - delimiter.length());355StringBuilder sb = new StringBuilder(capacity);356formatHex(sb, bytes, fromIndex, toIndex);357s = sb.toString();358}359return s;360}361362/**363* Appends formatted hexadecimal strings from a byte array to the {@link Appendable}.364* Each byte value is formatted as the prefix, two hexadecimal characters365* {@linkplain #isUpperCase selected from} uppercase or lowercase digits, and the suffix.366* A delimiter follows each formatted value, except the last.367* The formatted hexadecimal strings are appended in zero or more calls to the {@link Appendable} methods.368*369* @param <A> The type of {@code Appendable}370* @param out an {@code Appendable}, non-null371* @param bytes a byte array372* @return the {@code Appendable}373* @throws UncheckedIOException if an I/O exception occurs appending to the output374*/375public <A extends Appendable> A formatHex(A out, byte[] bytes) {376return formatHex(out, bytes, 0, bytes.length);377}378379/**380* Appends formatted hexadecimal strings from a byte array range to the {@link Appendable}.381* Each byte value is formatted as the prefix, two hexadecimal characters382* {@linkplain #isUpperCase selected from} uppercase or lowercase digits, and the suffix.383* A delimiter follows each formatted value, except the last.384* The formatted hexadecimal strings are appended in zero or more calls to the {@link Appendable} methods.385*386* @param <A> The type of {@code Appendable}387* @param out an {@code Appendable}, non-null388* @param bytes a byte array, non-null389* @param fromIndex the initial index of the range, inclusive390* @param toIndex the final index of the range, exclusive.391* @return the {@code Appendable}392* @throws IndexOutOfBoundsException if the array range is out of bounds393* @throws UncheckedIOException if an I/O exception occurs appending to the output394*/395public <A extends Appendable> A formatHex(A out, byte[] bytes, int fromIndex, int toIndex) {396Objects.requireNonNull(out, "out");397Objects.requireNonNull(bytes, "bytes");398Objects.checkFromToIndex(fromIndex, toIndex, bytes.length);399400int length = toIndex - fromIndex;401if (length > 0) {402try {403String between = suffix + delimiter + prefix;404out.append(prefix);405toHexDigits(out, bytes[fromIndex]);406if (between.isEmpty()) {407for (int i = 1; i < length; i++) {408toHexDigits(out, bytes[fromIndex + i]);409}410} else {411for (int i = 1; i < length; i++) {412out.append(between);413toHexDigits(out, bytes[fromIndex + i]);414}415}416out.append(suffix);417} catch (IOException ioe) {418throw new UncheckedIOException(ioe.getMessage(), ioe);419}420}421return out;422}423424/**425* Returns a string formatting of the range of bytes optimized426* for a single allocation.427* Prefix and suffix must be empty and the delimiter428* must be empty or a single byte character, otherwise null is returned.429*430* @param bytes the bytes, non-null431* @param fromIndex the initial index of the range, inclusive432* @param toIndex the final index of the range, exclusive.433* @return a String formatted or null for non-single byte delimiter434* or non-empty prefix or suffix435*/436private String formatOptDelimiter(byte[] bytes, int fromIndex, int toIndex) {437byte[] rep;438if (!prefix.isEmpty() || !suffix.isEmpty()) {439return null;440}441int length = toIndex - fromIndex;442if (delimiter.isEmpty()) {443// Allocate the byte array and fill in the hex pairs for each byte444rep = new byte[checkMaxArraySize(length * 2L)];445for (int i = 0; i < length; i++) {446rep[i * 2] = (byte)toHighHexDigit(bytes[fromIndex + i]);447rep[i * 2 + 1] = (byte)toLowHexDigit(bytes[fromIndex + i]);448}449} else if (delimiter.length() == 1 && delimiter.charAt(0) < 256) {450// Allocate the byte array and fill in the characters for the first byte451// Then insert the delimiter and hexadecimal characters for each of the remaining bytes452char sep = delimiter.charAt(0);453rep = new byte[checkMaxArraySize(length * 3L - 1L)];454rep[0] = (byte) toHighHexDigit(bytes[fromIndex]);455rep[1] = (byte) toLowHexDigit(bytes[fromIndex]);456for (int i = 1; i < length; i++) {457rep[i * 3 - 1] = (byte) sep;458rep[i * 3 ] = (byte) toHighHexDigit(bytes[fromIndex + i]);459rep[i * 3 + 1] = (byte) toLowHexDigit(bytes[fromIndex + i]);460}461} else {462// Delimiter formatting not to a single byte463return null;464}465try {466// Return a new string using the bytes without making a copy467return jla.newStringNoRepl(rep, StandardCharsets.ISO_8859_1);468} catch (CharacterCodingException cce) {469throw new AssertionError(cce);470}471}472473/**474* Checked that the requested size for the result string is475* less than or equal to the max array size.476*477* @param length the requested size of a byte array.478* @return the length479* @throws OutOfMemoryError if the size is larger than Integer.MAX_VALUE480*/481private static int checkMaxArraySize(long length) {482if (length > Integer.MAX_VALUE)483throw new OutOfMemoryError("String size " + length +484" exceeds maximum " + Integer.MAX_VALUE);485return (int)length;486}487488/**489* Returns a byte array containing hexadecimal values parsed from the string.490*491* Each byte value is parsed from the prefix, two case insensitive hexadecimal characters,492* and the suffix. A delimiter follows each formatted value, except the last.493* The delimiters, prefixes, and suffixes strings must be present; they may be empty strings.494* A valid string consists only of the above format.495*496* @param string a string containing the byte values with prefix, hexadecimal digits, suffix,497* and delimiters498* @return a byte array with the values parsed from the string499* @throws IllegalArgumentException if the prefix or suffix is not present for each byte value,500* the byte values are not hexadecimal characters, or if the delimiter is not present501* after all but the last byte value502*/503public byte[] parseHex(CharSequence string) {504return parseHex(string, 0, string.length());505}506507/**508* Returns a byte array containing hexadecimal values parsed from a range of the string.509*510* Each byte value is parsed from the prefix, two case insensitive hexadecimal characters,511* and the suffix. A delimiter follows each formatted value, except the last.512* The delimiters, prefixes, and suffixes strings must be present; they may be empty strings.513* A valid string consists only of the above format.514*515* @param string a string range containing hexadecimal digits,516* delimiters, prefix, and suffix.517* @param fromIndex the initial index of the range, inclusive518* @param toIndex the final index of the range, exclusive.519* @return a byte array with the values parsed from the string range520* @throws IllegalArgumentException if the prefix or suffix is not present for each byte value,521* the byte values are not hexadecimal characters, or if the delimiter is not present522* after all but the last byte value523* @throws IndexOutOfBoundsException if the string range is out of bounds524*/525public byte[] parseHex(CharSequence string, int fromIndex, int toIndex) {526Objects.requireNonNull(string, "string");527Objects.checkFromToIndex(fromIndex, toIndex, string.length());528529if (fromIndex != 0 || toIndex != string.length()) {530string = string.subSequence(fromIndex, toIndex);531}532533if (string.isEmpty())534return EMPTY_BYTES;535if (delimiter.isEmpty() && prefix.isEmpty() && suffix.isEmpty())536return parseNoDelimiter(string);537538// avoid overflow for max length prefix or suffix539long valueChars = prefix.length() + 2L + suffix.length();540long stride = valueChars + delimiter.length();541if ((string.length() - valueChars) % stride != 0)542throw new IllegalArgumentException("extra or missing delimiters " +543"or values consisting of prefix, two hexadecimal digits, and suffix");544545checkLiteral(string, 0, prefix);546checkLiteral(string, string.length() - suffix.length(), suffix);547String between = suffix + delimiter + prefix;548final int len = (int)((string.length() - valueChars) / stride + 1L);549byte[] bytes = new byte[len];550int i, offset;551for (i = 0, offset = prefix.length(); i < len - 1; i++, offset += 2 + between.length()) {552bytes[i] = (byte) fromHexDigits(string, offset);553checkLiteral(string, offset + 2, between);554}555bytes[i] = (byte) fromHexDigits(string, offset);556557return bytes;558}559560/**561* Returns a byte array containing hexadecimal values parsed from562* a range of the character array.563*564* Each byte value is parsed from the prefix, two case insensitive hexadecimal characters,565* and the suffix. A delimiter follows each formatted value, except the last.566* The delimiters, prefixes, and suffixes strings must be present; they may be empty strings.567* A valid character array range consists only of the above format.568*569* @param chars a character array range containing an even number of hexadecimal digits,570* delimiters, prefix, and suffix.571* @param fromIndex the initial index of the range, inclusive572* @param toIndex the final index of the range, exclusive.573* @return a byte array with the values parsed from the character array range574* @throws IllegalArgumentException if the prefix or suffix is not present for each byte value,575* the byte values are not hexadecimal characters, or if the delimiter is not present576* after all but the last byte value577* @throws IndexOutOfBoundsException if the character array range is out of bounds578*/579public byte[] parseHex(char[] chars, int fromIndex, int toIndex) {580Objects.requireNonNull(chars, "chars");581Objects.checkFromToIndex(fromIndex, toIndex, chars.length);582CharBuffer cb = CharBuffer.wrap(chars, fromIndex, toIndex - fromIndex);583return parseHex(cb);584}585586/**587* Compare the literal and throw an exception if it does not match.588* Pre-condition: {@code index + literal.length() <= string.length()}.589*590* @param string a CharSequence591* @param index the index of the literal in the CharSequence592* @param literal the expected literal593* @throws IllegalArgumentException if the literal is not present594*/595private static void checkLiteral(CharSequence string, int index, String literal) {596assert index <= string.length() - literal.length() : "pre-checked invariant error";597if (literal.isEmpty() ||598(literal.length() == 1 && literal.charAt(0) == string.charAt(index))) {599return;600}601for (int i = 0; i < literal.length(); i++) {602if (string.charAt(index + i) != literal.charAt(i)) {603throw new IllegalArgumentException(escapeNL("found: \"" +604string.subSequence(index, index + literal.length()) +605"\", expected: \"" + literal + "\", index: " + index +606" ch: " + (int)string.charAt(index + i)));607}608}609}610611/**612* Expands new line characters to escaped newlines for display.613*614* @param string a string615* @return a string with newline characters escaped616*/617private static String escapeNL(String string) {618return string.replace("\n", "\\n")619.replace("\r", "\\r");620}621622/**623* Returns the hexadecimal character for the low 4 bits of the value considering it to be a byte.624* If the parameter {@link #isUpperCase()} is {@code true} the625* character returned for values {@code 10-15} is uppercase {@code "A-F"},626* otherwise the character returned is lowercase {@code "a-f"}.627* The values in the range {@code 0-9} are returned as {@code "0-9"}.628*629* @param value a value, only the low 4 bits {@code 0-3} of the value are used630* @return the hexadecimal character for the low 4 bits {@code 0-3} of the value631*/632public char toLowHexDigit(int value) {633return (char)digits[value & 0xf];634}635636/**637* Returns the hexadecimal character for the high 4 bits of the value considering it to be a byte.638* If the parameter {@link #isUpperCase()} is {@code true} the639* character returned for values {@code 10-15} is uppercase {@code "A-F"},640* otherwise the character returned is lowercase {@code "a-f"}.641* The values in the range {@code 0-9} are returned as {@code "0-9"}.642*643* @param value a value, only bits {@code 4-7} of the value are used644* @return the hexadecimal character for the bits {@code 4-7} of the value645*/646public char toHighHexDigit(int value) {647return (char)digits[(value >> 4) & 0xf];648}649650/**651* Appends two hexadecimal characters for the byte value to the {@link Appendable}.652* Each nibble (4 bits) from most significant to least significant of the value653* is formatted as if by {@link #toLowHexDigit(int) toLowHexDigit(nibble)}.654* The hexadecimal characters are appended in one or more calls to the655* {@link Appendable} methods. The delimiter, prefix and suffix are not used.656*657* @param <A> The type of {@code Appendable}658* @param out an {@code Appendable}, non-null659* @param value a byte value660* @return the {@code Appendable}661* @throws UncheckedIOException if an I/O exception occurs appending to the output662*/663public <A extends Appendable> A toHexDigits(A out, byte value) {664Objects.requireNonNull(out, "out");665try {666out.append(toHighHexDigit(value));667out.append(toLowHexDigit(value));668return out;669} catch (IOException ioe) {670throw new UncheckedIOException(ioe.getMessage(), ioe);671}672}673674/**675* Returns the two hexadecimal characters for the {@code byte} value.676* Each nibble (4 bits) from most significant to least significant of the value677* is formatted as if by {@link #toLowHexDigit(int) toLowHexDigit(nibble)}.678* The delimiter, prefix and suffix are not used.679*680* @param value a byte value681* @return the two hexadecimal characters for the byte value682*/683public String toHexDigits(byte value) {684byte[] rep = new byte[2];685rep[0] = (byte)toHighHexDigit(value);686rep[1] = (byte)toLowHexDigit(value);687try {688return jla.newStringNoRepl(rep, StandardCharsets.ISO_8859_1);689} catch (CharacterCodingException cce) {690throw new AssertionError(cce);691}692}693694/**695* Returns the four hexadecimal characters for the {@code char} value.696* Each nibble (4 bits) from most significant to least significant of the value697* is formatted as if by {@link #toLowHexDigit(int) toLowHexDigit(nibble)}.698* The delimiter, prefix and suffix are not used.699*700* @param value a {@code char} value701* @return the four hexadecimal characters for the {@code char} value702*/703public String toHexDigits(char value) {704return toHexDigits((short)value);705}706707/**708* Returns the four hexadecimal characters for the {@code short} value.709* Each nibble (4 bits) from most significant to least significant of the value710* is formatted as if by {@link #toLowHexDigit(int) toLowHexDigit(nibble)}.711* The delimiter, prefix and suffix are not used.712*713* @param value a {@code short} value714* @return the four hexadecimal characters for the {@code short} value715*/716public String toHexDigits(short value) {717byte[] rep = new byte[4];718rep[0] = (byte)toHighHexDigit((byte)(value >> 8));719rep[1] = (byte)toLowHexDigit((byte)(value >> 8));720rep[2] = (byte)toHighHexDigit((byte)value);721rep[3] = (byte)toLowHexDigit((byte)value);722723try {724return jla.newStringNoRepl(rep, StandardCharsets.ISO_8859_1);725} catch (CharacterCodingException cce) {726throw new AssertionError(cce);727}728}729730/**731* Returns the eight hexadecimal characters for the {@code int} value.732* Each nibble (4 bits) from most significant to least significant of the value733* is formatted as if by {@link #toLowHexDigit(int) toLowHexDigit(nibble)}.734* The delimiter, prefix and suffix are not used.735*736* @param value an {@code int} value737* @return the eight hexadecimal characters for the {@code int} value738* @see Integer#toHexString739*/740public String toHexDigits(int value) {741byte[] rep = new byte[8];742rep[0] = (byte)toHighHexDigit((byte)(value >> 24));743rep[1] = (byte)toLowHexDigit((byte)(value >> 24));744rep[2] = (byte)toHighHexDigit((byte)(value >> 16));745rep[3] = (byte)toLowHexDigit((byte)(value >> 16));746rep[4] = (byte)toHighHexDigit((byte)(value >> 8));747rep[5] = (byte)toLowHexDigit((byte)(value >> 8));748rep[6] = (byte)toHighHexDigit((byte)value);749rep[7] = (byte)toLowHexDigit((byte)value);750751try {752return jla.newStringNoRepl(rep, StandardCharsets.ISO_8859_1);753} catch (CharacterCodingException cce) {754throw new AssertionError(cce);755}756}757758/**759* Returns the sixteen hexadecimal characters for the {@code long} value.760* Each nibble (4 bits) from most significant to least significant of the value761* is formatted as if by {@link #toLowHexDigit(int) toLowHexDigit(nibble)}.762* The delimiter, prefix and suffix are not used.763*764* @param value a {@code long} value765* @return the sixteen hexadecimal characters for the {@code long} value766* @see Long#toHexString767*/768public String toHexDigits(long value) {769byte[] rep = new byte[16];770rep[0] = (byte)toHighHexDigit((byte)(value >>> 56));771rep[1] = (byte)toLowHexDigit((byte)(value >>> 56));772rep[2] = (byte)toHighHexDigit((byte)(value >>> 48));773rep[3] = (byte)toLowHexDigit((byte)(value >>> 48));774rep[4] = (byte)toHighHexDigit((byte)(value >>> 40));775rep[5] = (byte)toLowHexDigit((byte)(value >>> 40));776rep[6] = (byte)toHighHexDigit((byte)(value >>> 32));777rep[7] = (byte)toLowHexDigit((byte)(value >>> 32));778rep[8] = (byte)toHighHexDigit((byte)(value >>> 24));779rep[9] = (byte)toLowHexDigit((byte)(value >>> 24));780rep[10] = (byte)toHighHexDigit((byte)(value >>> 16));781rep[11] = (byte)toLowHexDigit((byte)(value >>> 16));782rep[12] = (byte)toHighHexDigit((byte)(value >>> 8));783rep[13] = (byte)toLowHexDigit((byte)(value >>> 8));784rep[14] = (byte)toHighHexDigit((byte)value);785rep[15] = (byte)toLowHexDigit((byte)value);786787try {788return jla.newStringNoRepl(rep, StandardCharsets.ISO_8859_1);789} catch (CharacterCodingException cce) {790throw new AssertionError(cce);791}792}793794/**795* Returns up to sixteen hexadecimal characters for the {@code long} value.796* Each nibble (4 bits) from most significant to least significant of the value797* is formatted as if by {@link #toLowHexDigit(int) toLowHexDigit(nibble)}.798* The delimiter, prefix and suffix are not used.799*800* @param value a {@code long} value801* @param digits the number of hexadecimal digits to return, 0 to 16802* @return the hexadecimal characters for the {@code long} value803* @throws IllegalArgumentException if {@code digits} is negative or greater than 16804*/805public String toHexDigits(long value, int digits) {806if (digits < 0 || digits > 16)807throw new IllegalArgumentException("number of digits: " + digits);808if (digits == 0)809return "";810byte[] rep = new byte[digits];811for (int i = rep.length - 1; i >= 0; i--) {812rep[i] = (byte)toLowHexDigit((byte)(value));813value = value >>> 4;814}815try {816return jla.newStringNoRepl(rep, StandardCharsets.ISO_8859_1);817} catch (CharacterCodingException cce) {818throw new AssertionError(cce);819}820}821822/**823* Returns a byte array containing the parsed hex digits.824* A valid string consists only of an even number of hex digits.825*826* @param string a string containing an even number of only hex digits827* @return a byte array828* @throws IllegalArgumentException if the string length is not valid or829* the string contains non-hexadecimal characters830*/831private static byte[] parseNoDelimiter(CharSequence string) {832if ((string.length() & 1) != 0)833throw new IllegalArgumentException("string length not even: " +834string.length());835836byte[] bytes = new byte[string.length() / 2];837for (int i = 0; i < bytes.length; i++) {838bytes[i] = (byte) fromHexDigits(string, i * 2);839}840841return bytes;842}843844/**845* Check the number of requested digits against a limit.846*847* @param fromIndex the initial index of the range, inclusive848* @param toIndex the final index of the range, exclusive.849* @param limit the maximum allowed850* @return the length of the range851*/852private static int checkDigitCount(int fromIndex, int toIndex, int limit) {853int length = toIndex - fromIndex;854if (length > limit)855throw new IllegalArgumentException("string length greater than " +856limit + ": " + length);857return length;858}859860/**861* Returns {@code true} if the character is a valid hexadecimal character or codepoint.862* The valid hexadecimal characters are:863* <ul>864* <li>{@code '0' ('\u005Cu0030')} through {@code '9' ('\u005Cu0039')} inclusive,865* <li>{@code 'A' ('\u005Cu0041')} through {@code 'F' ('\u005Cu0046')} inclusive, and866* <li>{@code 'a' ('\u005Cu0061')} through {@code 'f' ('\u005Cu0066')} inclusive.867* </ul>868* @param ch a codepoint869* @return {@code true} if the character is valid a hexadecimal character,870* otherwise {@code false}871*/872public static boolean isHexDigit(int ch) {873return ((ch >>> 8) == 0 && DIGITS[ch] >= 0);874}875876/**877* Returns the value for the hexadecimal character or codepoint.878* The value is:879* <ul>880* <li>{@code (ch - '0')} for {@code '0'} through {@code '9'} inclusive,881* <li>{@code (ch - 'A' + 10)} for {@code 'A'} through {@code 'F'} inclusive, and882* <li>{@code (ch - 'a' + 10)} for {@code 'a'} through {@code 'f'} inclusive.883* </ul>884*885* @param ch a character or codepoint886* @return the value {@code 0-15}887* @throws NumberFormatException if the codepoint is not a hexadecimal character888*/889public static int fromHexDigit(int ch) {890int value;891if ((ch >>> 8) == 0 && (value = DIGITS[ch]) >= 0) {892return value;893}894throw new NumberFormatException("not a hexadecimal digit: \"" + (char) ch + "\" = " + ch);895}896897/**898* Returns a value parsed from two hexadecimal characters in a string.899* The characters in the range from {@code index} to {@code index + 1},900* inclusive, must be valid hex digits according to {@link #fromHexDigit(int)}.901*902* @param string a CharSequence containing the characters903* @param index the index of the first character of the range904* @return the value parsed from the string range905* @throws NumberFormatException if any of the characters in the range906* is not a hexadecimal character907* @throws IndexOutOfBoundsException if the range is out of bounds908* for the {@code CharSequence}909*/910private static int fromHexDigits(CharSequence string, int index) {911int high = fromHexDigit(string.charAt(index));912int low = fromHexDigit(string.charAt(index + 1));913return (high << 4) | low;914}915916/**917* Returns the {@code int} value parsed from a string of up to eight hexadecimal characters.918* The hexadecimal characters are parsed from most significant to least significant919* using {@link #fromHexDigit(int)} to form an unsigned value.920* The value is zero extended to 32 bits and is returned as an {@code int}.921*922* @apiNote923* {@link Integer#parseInt(String, int) Integer.parseInt(s, 16)} and924* {@link Integer#parseUnsignedInt(String, int) Integer.parseUnsignedInt(s, 16)}925* are similar but allow all Unicode hexadecimal digits defined by926* {@link Character#digit(char, int) Character.digit(ch, 16)}.927* {@code HexFormat} uses only hexadecimal characters928* {@code "0-9"}, {@code "A-F"} and {@code "a-f"}.929* Signed hexadecimal strings can be parsed with {@link Integer#parseInt(String, int)}.930*931* @param string a CharSequence containing up to eight hexadecimal characters932* @return the value parsed from the string933* @throws IllegalArgumentException if the string length is greater than eight (8) or934* if any of the characters is not a hexadecimal character935*/936public static int fromHexDigits(CharSequence string) {937return fromHexDigits(string, 0, string.length());938}939940/**941* Returns the {@code int} value parsed from a string range of up to eight hexadecimal942* characters.943* The characters in the range {@code fromIndex} to {@code toIndex}, exclusive,944* are parsed from most significant to least significant945* using {@link #fromHexDigit(int)} to form an unsigned value.946* The value is zero extended to 32 bits and is returned as an {@code int}.947*948* @apiNote949* {@link Integer#parseInt(String, int) Integer.parseInt(s, 16)} and950* {@link Integer#parseUnsignedInt(String, int) Integer.parseUnsignedInt(s, 16)}951* are similar but allow all Unicode hexadecimal digits defined by952* {@link Character#digit(char, int) Character.digit(ch, 16)}.953* {@code HexFormat} uses only hexadecimal characters954* {@code "0-9"}, {@code "A-F"} and {@code "a-f"}.955* Signed hexadecimal strings can be parsed with {@link Integer#parseInt(String, int)}.956*957* @param string a CharSequence containing the characters958* @param fromIndex the initial index of the range, inclusive959* @param toIndex the final index of the range, exclusive.960* @return the value parsed from the string range961* @throws IndexOutOfBoundsException if the range is out of bounds962* for the {@code CharSequence}963* @throws IllegalArgumentException if length of the range is greater than eight (8) or964* if any of the characters is not a hexadecimal character965*/966public static int fromHexDigits(CharSequence string, int fromIndex, int toIndex) {967Objects.requireNonNull(string, "string");968Objects.checkFromToIndex(fromIndex, toIndex, string.length());969int length = checkDigitCount(fromIndex, toIndex, 8);970int value = 0;971for (int i = 0; i < length; i++) {972value = (value << 4) + fromHexDigit(string.charAt(fromIndex + i));973}974return value;975}976977/**978* Returns the long value parsed from a string of up to sixteen hexadecimal characters.979* The hexadecimal characters are parsed from most significant to least significant980* using {@link #fromHexDigit(int)} to form an unsigned value.981* The value is zero extended to 64 bits and is returned as a {@code long}.982*983* @apiNote984* {@link Long#parseLong(String, int) Long.parseLong(s, 16)} and985* {@link Long#parseUnsignedLong(String, int) Long.parseUnsignedLong(s, 16)}986* are similar but allow all Unicode hexadecimal digits defined by987* {@link Character#digit(char, int) Character.digit(ch, 16)}.988* {@code HexFormat} uses only hexadecimal characters989* {@code "0-9"}, {@code "A-F"} and {@code "a-f"}.990* Signed hexadecimal strings can be parsed with {@link Long#parseLong(String, int)}.991*992* @param string a CharSequence containing up to sixteen hexadecimal characters993* @return the value parsed from the string994* @throws IllegalArgumentException if the string length is greater than sixteen (16) or995* if any of the characters is not a hexadecimal character996*/997public static long fromHexDigitsToLong(CharSequence string) {998return fromHexDigitsToLong(string, 0, string.length());999}10001001/**1002* Returns the long value parsed from a string range of up to sixteen hexadecimal1003* characters.1004* The characters in the range {@code fromIndex} to {@code toIndex}, exclusive,1005* are parsed from most significant to least significant1006* using {@link #fromHexDigit(int)} to form an unsigned value.1007* The value is zero extended to 64 bits and is returned as a {@code long}.1008*1009* @apiNote1010* {@link Long#parseLong(String, int) Long.parseLong(s, 16)} and1011* {@link Long#parseUnsignedLong(String, int) Long.parseUnsignedLong(s, 16)}1012* are similar but allow all Unicode hexadecimal digits defined by1013* {@link Character#digit(char, int) Character.digit(ch, 16)}.1014* {@code HexFormat} uses only hexadecimal characters1015* {@code "0-9"}, {@code "A-F"} and {@code "a-f"}.1016* Signed hexadecimal strings can be parsed with {@link Long#parseLong(String, int)}.1017*1018* @param string a CharSequence containing the characters1019* @param fromIndex the initial index of the range, inclusive1020* @param toIndex the final index of the range, exclusive.1021* @return the value parsed from the string range1022* @throws IndexOutOfBoundsException if the range is out of bounds1023* for the {@code CharSequence}1024* @throws IllegalArgumentException if the length of the range is greater than sixteen (16) or1025* if any of the characters is not a hexadecimal character1026*/1027public static long fromHexDigitsToLong(CharSequence string, int fromIndex, int toIndex) {1028Objects.requireNonNull(string, "string");1029Objects.checkFromToIndex(fromIndex, toIndex, string.length());1030int length = checkDigitCount(fromIndex, toIndex, 16);1031long value = 0L;1032for (int i = 0; i < length; i++) {1033value = (value << 4) + fromHexDigit(string.charAt(fromIndex + i));1034}1035return value;1036}10371038/**1039* Returns {@code true} if the other object is a {@code HexFormat}1040* with the same parameters.1041*1042* @param o an object, may be null1043* @return {@code true} if the other object is a {@code HexFormat} and the parameters1044* uppercase, delimiter, prefix, and suffix are equal;1045* otherwise {@code false}1046*/1047@Override1048public boolean equals(Object o) {1049if (this == o)1050return true;1051if (o == null || getClass() != o.getClass())1052return false;1053HexFormat otherHex = (HexFormat) o;1054return Arrays.equals(digits, otherHex.digits) &&1055delimiter.equals(otherHex.delimiter) &&1056prefix.equals(otherHex.prefix) &&1057suffix.equals(otherHex.suffix);1058}10591060/**1061* Returns a hashcode for this {@code HexFormat}.1062*1063* @return a hashcode for this {@code HexFormat}1064*/1065@Override1066public int hashCode() {1067int result = Objects.hash(delimiter, prefix, suffix);1068result = 31 * result + Boolean.hashCode(Arrays.equals(digits, UPPERCASE_DIGITS));1069return result;1070}10711072/**1073* Returns a description of the formatter parameters for uppercase,1074* delimiter, prefix, and suffix.1075*1076* @return a description of this {@code HexFormat}1077*/1078@Override1079public String toString() {1080return escapeNL("uppercase: " + Arrays.equals(digits, UPPERCASE_DIGITS) +1081", delimiter: \"" + delimiter +1082"\", prefix: \"" + prefix +1083"\", suffix: \"" + suffix + "\"");1084}1085}108610871088