8264514: HexFormat implementation tweaks

Reviewed-by: rriggs
This commit is contained in:
Raffaello Giulietti 2021-04-22 13:54:41 +00:00 committed by Roger Riggs
parent e16d568c1f
commit fa82d47591

View File

@ -150,20 +150,24 @@ public final class HexFormat {
}; };
// Analysis has shown that generating the whole array allows the JIT to generate // Analysis has shown that generating the whole array allows the JIT to generate
// better code compared to a slimmed down array, such as one cutting off after 'f' // better code compared to a slimmed down array, such as one cutting off after 'f'
private static final byte[] DIGITS = new byte[] { private static final byte[] DIGITS = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
};
/** /**
* Format each byte of an array as a pair of hexadecimal digits. * Format each byte of an array as a pair of hexadecimal digits.
* The hexadecimal characters are from lowercase alpha digits. * The hexadecimal characters are from lowercase alpha digits.
@ -171,7 +175,7 @@ public final class HexFormat {
private static final HexFormat HEX_FORMAT = private static final HexFormat HEX_FORMAT =
new HexFormat("", "", "", LOWERCASE_DIGITS); new HexFormat("", "", "", LOWERCASE_DIGITS);
private static final byte[] EMPTY_BYTES = new byte[0]; private static final byte[] EMPTY_BYTES = {};
private final String delimiter; private final String delimiter;
private final String prefix; private final String prefix;
@ -191,7 +195,7 @@ public final class HexFormat {
this.delimiter = Objects.requireNonNull(delimiter, "delimiter"); this.delimiter = Objects.requireNonNull(delimiter, "delimiter");
this.prefix = Objects.requireNonNull(prefix, "prefix"); this.prefix = Objects.requireNonNull(prefix, "prefix");
this.suffix = Objects.requireNonNull(suffix, "suffix"); this.suffix = Objects.requireNonNull(suffix, "suffix");
this.digits = Objects.requireNonNull(digits, "digits"); this.digits = digits;
} }
/** /**
@ -468,7 +472,8 @@ public final class HexFormat {
} }
/** /**
* Checked that the requested size for the result string is less than the max array size. * Checked that the requested size for the result string is
* less than or equal to the max array size.
* *
* @param length the requested size of a byte array. * @param length the requested size of a byte array.
* @return the length * @return the length
@ -526,7 +531,7 @@ public final class HexFormat {
string = string.subSequence(fromIndex, toIndex); string = string.subSequence(fromIndex, toIndex);
} }
if (string.length() == 0) if (string.isEmpty())
return EMPTY_BYTES; return EMPTY_BYTES;
if (delimiter.isEmpty() && prefix.isEmpty() && suffix.isEmpty()) if (delimiter.isEmpty() && prefix.isEmpty() && suffix.isEmpty())
return parseNoDelimiter(string); return parseNoDelimiter(string);
@ -534,7 +539,7 @@ public final class HexFormat {
// avoid overflow for max length prefix or suffix // avoid overflow for max length prefix or suffix
long valueChars = prefix.length() + 2L + suffix.length(); long valueChars = prefix.length() + 2L + suffix.length();
long stride = valueChars + delimiter.length(); long stride = valueChars + delimiter.length();
if (string.length() < valueChars || (string.length() - valueChars) % stride != 0) if ((string.length() - valueChars) % stride != 0)
throw new IllegalArgumentException("extra or missing delimiters " + throw new IllegalArgumentException("extra or missing delimiters " +
"or values consisting of prefix, two hexadecimal digits, and suffix"); "or values consisting of prefix, two hexadecimal digits, and suffix");
@ -545,16 +550,10 @@ public final class HexFormat {
byte[] bytes = new byte[len]; byte[] bytes = new byte[len];
int i, offset; int i, offset;
for (i = 0, offset = prefix.length(); i < len - 1; i++, offset += 2 + between.length()) { for (i = 0, offset = prefix.length(); i < len - 1; i++, offset += 2 + between.length()) {
int v = fromHexDigits(string, offset); bytes[i] = (byte) fromHexDigits(string, offset);
if (v < 0)
throw new IllegalArgumentException("input contains non-hexadecimal characters");
bytes[i] = (byte) v;
checkLiteral(string, offset + 2, between); checkLiteral(string, offset + 2, between);
} }
int v = fromHexDigits(string, offset); bytes[i] = (byte) fromHexDigits(string, offset);
if (v < 0)
throw new IllegalArgumentException("input contains non-hexadecimal characters");
bytes[i] = (byte) v;
return bytes; return bytes;
} }
@ -830,21 +829,15 @@ public final class HexFormat {
* @throws IllegalArgumentException if the string length is not valid or * @throws IllegalArgumentException if the string length is not valid or
* the string contains non-hexadecimal characters * the string contains non-hexadecimal characters
*/ */
private byte[] parseNoDelimiter(CharSequence string) { private static byte[] parseNoDelimiter(CharSequence string) {
if ((string.length() & 1) != 0) if ((string.length() & 1) != 0)
throw new IllegalArgumentException("string length not even: " + throw new IllegalArgumentException("string length not even: " +
string.length()); string.length());
byte[] bytes = new byte[string.length() / 2]; byte[] bytes = new byte[string.length() / 2];
int illegal = 0; // Accumulate logical-or of all bytes
for (int i = 0; i < bytes.length; i++) { for (int i = 0; i < bytes.length; i++) {
int v = fromHexDigits(string, i * 2); bytes[i] = (byte) fromHexDigits(string, i * 2);
bytes[i] = (byte) v;
illegal |= v;
} }
// check if any character was an illegal character
if (illegal < 0)
throw new IllegalArgumentException("input contains non-hexadecimal characters");
return bytes; return bytes;
} }
@ -916,7 +909,6 @@ public final class HexFormat {
* for the {@code CharSequence} * for the {@code CharSequence}
*/ */
private static int fromHexDigits(CharSequence string, int index) { private static int fromHexDigits(CharSequence string, int index) {
Objects.requireNonNull(string, "string");
int high = fromHexDigit(string.charAt(index)); int high = fromHexDigit(string.charAt(index));
int low = fromHexDigit(string.charAt(index + 1)); int low = fromHexDigit(string.charAt(index + 1));
return (high << 4) | low; return (high << 4) | low;
@ -933,7 +925,8 @@ public final class HexFormat {
* {@link Integer#parseUnsignedInt(String, int) Integer.parseUnsignedInt(s, 16)} * {@link Integer#parseUnsignedInt(String, int) Integer.parseUnsignedInt(s, 16)}
* are similar but allow all Unicode hexadecimal digits defined by * are similar but allow all Unicode hexadecimal digits defined by
* {@link Character#digit(char, int) Character.digit(ch, 16)}. * {@link Character#digit(char, int) Character.digit(ch, 16)}.
* {@code HexFormat} uses only hexadecimal characters "0-9, "A-F", and "a-f". * {@code HexFormat} uses only hexadecimal characters
* {@code "0-9"}, {@code "A-F"} and {@code "a-f"}.
* Signed hexadecimal strings can be parsed with {@link Integer#parseInt(String, int)}. * Signed hexadecimal strings can be parsed with {@link Integer#parseInt(String, int)}.
* *
* @param string a CharSequence containing up to eight hexadecimal characters * @param string a CharSequence containing up to eight hexadecimal characters
@ -942,13 +935,7 @@ public final class HexFormat {
* if any of the characters is not a hexadecimal character * if any of the characters is not a hexadecimal character
*/ */
public static int fromHexDigits(CharSequence string) { public static int fromHexDigits(CharSequence string) {
Objects.requireNonNull(string, "string"); return fromHexDigits(string, 0, string.length());
int length = checkDigitCount(0, string.length(), 8);
int value = 0;
for (int i = 0; i < length; i++) {
value = (value << 4) + fromHexDigit(string.charAt(i));
}
return value;
} }
/** /**
@ -964,7 +951,8 @@ public final class HexFormat {
* {@link Integer#parseUnsignedInt(String, int) Integer.parseUnsignedInt(s, 16)} * {@link Integer#parseUnsignedInt(String, int) Integer.parseUnsignedInt(s, 16)}
* are similar but allow all Unicode hexadecimal digits defined by * are similar but allow all Unicode hexadecimal digits defined by
* {@link Character#digit(char, int) Character.digit(ch, 16)}. * {@link Character#digit(char, int) Character.digit(ch, 16)}.
* {@code HexFormat} uses only hexadecimal characters "0-9, "A-F", and "a-f". * {@code HexFormat} uses only hexadecimal characters
* {@code "0-9"}, {@code "A-F"} and {@code "a-f"}.
* Signed hexadecimal strings can be parsed with {@link Integer#parseInt(String, int)}. * Signed hexadecimal strings can be parsed with {@link Integer#parseInt(String, int)}.
* *
* @param string a CharSequence containing the characters * @param string a CharSequence containing the characters
@ -998,7 +986,8 @@ public final class HexFormat {
* {@link Long#parseUnsignedLong(String, int) Long.parseUnsignedLong(s, 16)} * {@link Long#parseUnsignedLong(String, int) Long.parseUnsignedLong(s, 16)}
* are similar but allow all Unicode hexadecimal digits defined by * are similar but allow all Unicode hexadecimal digits defined by
* {@link Character#digit(char, int) Character.digit(ch, 16)}. * {@link Character#digit(char, int) Character.digit(ch, 16)}.
* {@code HexFormat} uses only hexadecimal characters "0-9, "A-F", and "a-f". * {@code HexFormat} uses only hexadecimal characters
* {@code "0-9"}, {@code "A-F"} and {@code "a-f"}.
* Signed hexadecimal strings can be parsed with {@link Long#parseLong(String, int)}. * Signed hexadecimal strings can be parsed with {@link Long#parseLong(String, int)}.
* *
* @param string a CharSequence containing up to sixteen hexadecimal characters * @param string a CharSequence containing up to sixteen hexadecimal characters
@ -1007,13 +996,7 @@ public final class HexFormat {
* if any of the characters is not a hexadecimal character * if any of the characters is not a hexadecimal character
*/ */
public static long fromHexDigitsToLong(CharSequence string) { public static long fromHexDigitsToLong(CharSequence string) {
Objects.requireNonNull(string, "string"); return fromHexDigitsToLong(string, 0, string.length());
int length = checkDigitCount(0, string.length(), 16);
long value = 0L;
for (int i = 0; i < length; i++) {
value = (value << 4) + fromHexDigit(string.charAt(i));
}
return value;
} }
/** /**
@ -1029,7 +1012,8 @@ public final class HexFormat {
* {@link Long#parseUnsignedLong(String, int) Long.parseUnsignedLong(s, 16)} * {@link Long#parseUnsignedLong(String, int) Long.parseUnsignedLong(s, 16)}
* are similar but allow all Unicode hexadecimal digits defined by * are similar but allow all Unicode hexadecimal digits defined by
* {@link Character#digit(char, int) Character.digit(ch, 16)}. * {@link Character#digit(char, int) Character.digit(ch, 16)}.
* {@code HexFormat} uses only hexadecimal characters "0-9, "A-F", and "a-f". * {@code HexFormat} uses only hexadecimal characters
* {@code "0-9"}, {@code "A-F"} and {@code "a-f"}.
* Signed hexadecimal strings can be parsed with {@link Long#parseLong(String, int)}. * Signed hexadecimal strings can be parsed with {@link Long#parseLong(String, int)}.
* *
* @param string a CharSequence containing the characters * @param string a CharSequence containing the characters
@ -1068,10 +1052,10 @@ public final class HexFormat {
if (o == null || getClass() != o.getClass()) if (o == null || getClass() != o.getClass())
return false; return false;
HexFormat otherHex = (HexFormat) o; HexFormat otherHex = (HexFormat) o;
return delimiter.equals(otherHex.delimiter) && return Arrays.equals(digits, otherHex.digits) &&
delimiter.equals(otherHex.delimiter) &&
prefix.equals(otherHex.prefix) && prefix.equals(otherHex.prefix) &&
suffix.equals(otherHex.suffix) && suffix.equals(otherHex.suffix);
Arrays.equals(digits, otherHex.digits);
} }
/** /**