8315789: Minor HexFormat performance improvements

Reviewed-by: rriggs
This commit is contained in:
Claes Redestad 2023-09-13 20:58:23 +00:00
parent ce93d27fe5
commit 92ad4a2399
2 changed files with 201 additions and 28 deletions

View file

@ -140,14 +140,6 @@ public final class HexFormat {
// Access to create strings from a byte array.
private static final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
private static final byte[] UPPERCASE_DIGITS = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
};
private static final byte[] LOWERCASE_DIGITS = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
};
// 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'
private static final byte[] DIGITS = {
@ -173,14 +165,22 @@ public final class HexFormat {
* The hexadecimal characters are from lowercase alpha digits.
*/
private static final HexFormat HEX_FORMAT =
new HexFormat("", "", "", LOWERCASE_DIGITS);
new HexFormat("", "", "", Case.LOWERCASE);
private static final HexFormat HEX_UPPER_FORMAT =
new HexFormat("", "", "", Case.UPPERCASE);
private static final byte[] EMPTY_BYTES = {};
private final String delimiter;
private final String prefix;
private final String suffix;
private final byte[] digits;
private final Case digitCase;
private enum Case {
LOWERCASE,
UPPERCASE
}
/**
* Returns a HexFormat with a delimiter, prefix, suffix, and array of digits.
@ -188,14 +188,14 @@ public final class HexFormat {
* @param delimiter a delimiter, non-null
* @param prefix a prefix, non-null
* @param suffix a suffix, non-null
* @param digits byte array of digits indexed by low nibble, non-null
* @param digitCase enum indicating how to case digits
* @throws NullPointerException if any argument is null
*/
private HexFormat(String delimiter, String prefix, String suffix, byte[] digits) {
private HexFormat(String delimiter, String prefix, String suffix, Case digitCase) {
this.delimiter = Objects.requireNonNull(delimiter, "delimiter");
this.prefix = Objects.requireNonNull(prefix, "prefix");
this.suffix = Objects.requireNonNull(suffix, "suffix");
this.digits = digits;
this.digitCase = digitCase;
}
/**
@ -224,7 +224,7 @@ public final class HexFormat {
* @return a {@link HexFormat} with the delimiter and lowercase characters
*/
public static HexFormat ofDelimiter(String delimiter) {
return new HexFormat(delimiter, "", "", LOWERCASE_DIGITS);
return new HexFormat(delimiter, "", "", Case.LOWERCASE);
}
/**
@ -233,7 +233,7 @@ public final class HexFormat {
* @return a copy of this {@code HexFormat} with the delimiter
*/
public HexFormat withDelimiter(String delimiter) {
return new HexFormat(delimiter, this.prefix, this.suffix, this.digits);
return new HexFormat(delimiter, this.prefix, this.suffix, this.digitCase);
}
/**
@ -243,7 +243,7 @@ public final class HexFormat {
* @return a copy of this {@code HexFormat} with the prefix
*/
public HexFormat withPrefix(String prefix) {
return new HexFormat(this.delimiter, prefix, this.suffix, this.digits);
return new HexFormat(this.delimiter, prefix, this.suffix, this.digitCase);
}
/**
@ -253,7 +253,7 @@ public final class HexFormat {
* @return a copy of this {@code HexFormat} with the suffix
*/
public HexFormat withSuffix(String suffix) {
return new HexFormat(this.delimiter, this.prefix, suffix, this.digits);
return new HexFormat(this.delimiter, this.prefix, suffix, this.digitCase);
}
/**
@ -263,7 +263,9 @@ public final class HexFormat {
* @return a copy of this {@code HexFormat} with uppercase hexadecimal characters
*/
public HexFormat withUpperCase() {
return new HexFormat(this.delimiter, this.prefix, this.suffix, UPPERCASE_DIGITS);
if (this == HEX_FORMAT)
return HEX_UPPER_FORMAT;
return new HexFormat(this.delimiter, this.prefix, this.suffix, Case.UPPERCASE);
}
/**
@ -273,7 +275,7 @@ public final class HexFormat {
* @return a copy of this {@code HexFormat} with lowercase hexadecimal characters
*/
public HexFormat withLowerCase() {
return new HexFormat(this.delimiter, this.prefix, this.suffix, LOWERCASE_DIGITS);
return new HexFormat(this.delimiter, this.prefix, this.suffix, Case.LOWERCASE);
}
/**
@ -311,7 +313,7 @@ public final class HexFormat {
* otherwise {@code false}
*/
public boolean isUpperCase() {
return Arrays.equals(digits, UPPERCASE_DIGITS);
return digitCase == Case.UPPERCASE;
}
/**
@ -401,16 +403,17 @@ public final class HexFormat {
int length = toIndex - fromIndex;
if (length > 0) {
try {
String between = suffix + delimiter + prefix;
out.append(prefix);
toHexDigits(out, bytes[fromIndex]);
if (between.isEmpty()) {
if (suffix.isEmpty() && delimiter.isEmpty() && prefix.isEmpty()) {
for (int i = 1; i < length; i++) {
toHexDigits(out, bytes[fromIndex + i]);
}
} else {
for (int i = 1; i < length; i++) {
out.append(between);
out.append(suffix);
out.append(delimiter);
out.append(prefix);
toHexDigits(out, bytes[fromIndex + i]);
}
}
@ -631,7 +634,14 @@ public final class HexFormat {
* @return the hexadecimal character for the low 4 bits {@code 0-3} of the value
*/
public char toLowHexDigit(int value) {
return (char)digits[value & 0xf];
value = value & 0xf;
if (value < 10) {
return (char)('0' + value);
}
if (digitCase == Case.LOWERCASE) {
return (char)('a' - 10 + value);
}
return (char)('A' - 10 + value);
}
/**
@ -645,7 +655,14 @@ public final class HexFormat {
* @return the hexadecimal character for the bits {@code 4-7} of the value
*/
public char toHighHexDigit(int value) {
return (char)digits[(value >> 4) & 0xf];
value = (value >> 4) & 0xf;
if (value < 10) {
return (char)('0' + value);
}
if (digitCase == Case.LOWERCASE) {
return (char)('a' - 10 + value);
}
return (char)('A' - 10 + value);
}
/**
@ -1052,7 +1069,7 @@ public final class HexFormat {
if (o == null || getClass() != o.getClass())
return false;
HexFormat otherHex = (HexFormat) o;
return Arrays.equals(digits, otherHex.digits) &&
return digitCase == otherHex.digitCase &&
delimiter.equals(otherHex.delimiter) &&
prefix.equals(otherHex.prefix) &&
suffix.equals(otherHex.suffix);
@ -1066,7 +1083,7 @@ public final class HexFormat {
@Override
public int hashCode() {
int result = Objects.hash(delimiter, prefix, suffix);
result = 31 * result + Boolean.hashCode(Arrays.equals(digits, UPPERCASE_DIGITS));
result = 31 * result + Boolean.hashCode(digitCase == Case.UPPERCASE);
return result;
}
@ -1078,7 +1095,7 @@ public final class HexFormat {
*/
@Override
public String toString() {
return escapeNL("uppercase: " + Arrays.equals(digits, UPPERCASE_DIGITS) +
return escapeNL("uppercase: " + (digitCase == Case.UPPERCASE) +
", delimiter: \"" + delimiter +
"\", prefix: \"" + prefix +
"\", suffix: \"" + suffix + "\"");