Skip to content

Commit 0bf9838

Browse files
committed
6805750: Improve handling of Attributes.Name
Reviewed-by: sherman
1 parent 3e6e4c1 commit 0bf9838

1 file changed

Lines changed: 95 additions & 37 deletions

File tree

src/java.base/share/classes/java/util/jar/Attributes.java

Lines changed: 95 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,10 @@
2828
import java.io.DataOutputStream;
2929
import java.io.IOException;
3030
import java.util.Collection;
31-
import java.util.Comparator;
31+
import java.util.HashMap;
3232
import java.util.LinkedHashMap;
33-
import java.util.Locale;
3433
import java.util.Map;
34+
import java.util.Objects;
3535
import java.util.Set;
3636

3737
import sun.util.logging.PlatformLogger;
@@ -116,7 +116,7 @@ public Object get(Object name) {
116116
* @throws IllegalArgumentException if the attribute name is invalid
117117
*/
118118
public String getValue(String name) {
119-
return (String)get(new Attributes.Name(name));
119+
return (String)get(Name.of(name));
120120
}
121121

122122
/**
@@ -168,7 +168,7 @@ public Object put(Object name, Object value) {
168168
* @exception IllegalArgumentException if the attribute name is invalid
169169
*/
170170
public String putValue(String name, String value) {
171-
return (String)put(new Name(name), value);
171+
return (String)put(Name.of(name), value);
172172
}
173173

174174
/**
@@ -371,7 +371,7 @@ void writeMain(DataOutputStream out) throws IOException
371371
*/
372372
@SuppressWarnings("deprecation")
373373
void read(Manifest.FastInputStream is, byte[] lbuf) throws IOException {
374-
String name = null, value = null;
374+
String name = null, value;
375375
byte[] lastline = null;
376376

377377
int len;
@@ -447,8 +447,21 @@ void read(Manifest.FastInputStream is, byte[] lbuf) throws IOException {
447447
* for more information about valid attribute names and values.
448448
*/
449449
public static class Name {
450-
private String name;
451-
private int hashCode = -1;
450+
private final String name;
451+
private final int hashCode;
452+
453+
/**
454+
* Avoid allocation for common Names
455+
*/
456+
private static final Map<String, Name> KNOWN_NAMES;
457+
458+
static final Name of(String name) {
459+
Name n = KNOWN_NAMES.get(name);
460+
if (n != null) {
461+
return n;
462+
}
463+
return new Name(name);
464+
}
452465

453466
/**
454467
* Constructs a new attribute name using the given string name.
@@ -459,38 +472,33 @@ public static class Name {
459472
* @exception NullPointerException if the attribute name was null
460473
*/
461474
public Name(String name) {
462-
if (name == null) {
463-
throw new NullPointerException("name");
464-
}
465-
if (!isValid(name)) {
466-
throw new IllegalArgumentException(name);
467-
}
475+
this.hashCode = hash(name);
468476
this.name = name.intern();
469477
}
470478

471-
private static boolean isValid(String name) {
479+
// Checks the string is valid
480+
private final int hash(String name) {
481+
Objects.requireNonNull(name, "name");
472482
int len = name.length();
473483
if (len > 70 || len == 0) {
474-
return false;
484+
throw new IllegalArgumentException(name);
475485
}
486+
// Calculate hash code case insensitively
487+
int h = 0;
476488
for (int i = 0; i < len; i++) {
477-
if (!isValid(name.charAt(i))) {
478-
return false;
489+
char c = name.charAt(i);
490+
if (c >= 'a' && c <= 'z') {
491+
// hashcode must be identical for upper and lower case
492+
h = h * 31 + (c - 0x20);
493+
} else if ((c >= 'A' && c <= 'Z' ||
494+
c >= '0' && c <= '9' ||
495+
c == '_' || c == '-')) {
496+
h = h * 31 + c;
497+
} else {
498+
throw new IllegalArgumentException(name);
479499
}
480500
}
481-
return true;
482-
}
483-
484-
private static boolean isValid(char c) {
485-
return isAlpha(c) || isDigit(c) || c == '_' || c == '-';
486-
}
487-
488-
private static boolean isAlpha(char c) {
489-
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
490-
}
491-
492-
private static boolean isDigit(char c) {
493-
return c >= '0' && c <= '9';
501+
return h;
494502
}
495503

496504
/**
@@ -500,9 +508,12 @@ private static boolean isDigit(char c) {
500508
* specified attribute object
501509
*/
502510
public boolean equals(Object o) {
511+
if (this == o) {
512+
return true;
513+
}
503514
if (o instanceof Name) {
504-
Comparator<String> c = String.CASE_INSENSITIVE_ORDER;
505-
return c.compare(name, ((Name)o).name) == 0;
515+
Name other = (Name)o;
516+
return other.name.equalsIgnoreCase(name);
506517
} else {
507518
return false;
508519
}
@@ -512,9 +523,6 @@ public boolean equals(Object o) {
512523
* Computes the hash value for this attribute name.
513524
*/
514525
public int hashCode() {
515-
if (hashCode == -1) {
516-
hashCode = name.toLowerCase(Locale.ROOT).hashCode();
517-
}
518526
return hashCode;
519527
}
520528

@@ -573,7 +581,7 @@ public String toString() {
573581
*/
574582
public static final Name SEALED = new Name("Sealed");
575583

576-
/**
584+
/**
577585
* {@code Name} object for {@code Extension-List} manifest attribute
578586
* used for the extension mechanism that is no longer supported.
579587
*/
@@ -620,7 +628,7 @@ public String toString() {
620628
@Deprecated
621629
public static final Name IMPLEMENTATION_VENDOR_ID = new Name("Implementation-Vendor-Id");
622630

623-
/**
631+
/**
624632
* {@code Name} object for {@code Implementation-URL}
625633
* manifest attribute.
626634
*
@@ -654,5 +662,55 @@ public String toString() {
654662
* @since 9
655663
*/
656664
public static final Name MULTI_RELEASE = new Name("Multi-Release");
665+
666+
private static void addName(Map<String, Name> names, Name name) {
667+
names.put(name.name, name);
668+
}
669+
670+
static {
671+
var names = new HashMap<String, Name>(64);
672+
addName(names, MANIFEST_VERSION);
673+
addName(names, SIGNATURE_VERSION);
674+
addName(names, CONTENT_TYPE);
675+
addName(names, CLASS_PATH);
676+
addName(names, MAIN_CLASS);
677+
addName(names, SEALED);
678+
addName(names, EXTENSION_LIST);
679+
addName(names, EXTENSION_NAME);
680+
addName(names, IMPLEMENTATION_TITLE);
681+
addName(names, IMPLEMENTATION_VERSION);
682+
addName(names, IMPLEMENTATION_VENDOR);
683+
addName(names, SPECIFICATION_TITLE);
684+
addName(names, SPECIFICATION_VERSION);
685+
addName(names, SPECIFICATION_VENDOR);
686+
addName(names, MULTI_RELEASE);
687+
688+
// Common attributes used in MANIFEST.MF et.al; adding these has a
689+
// small footprint cost, but is likely to be quickly paid for by
690+
// reducing allocation when reading and parsing typical manifests
691+
addName(names, new Name("Add-Exports"));
692+
addName(names, new Name("Add-Opens"));
693+
addName(names, new Name("Ant-Version"));
694+
addName(names, new Name("Archiver-Version"));
695+
addName(names, new Name("Build-Jdk"));
696+
addName(names, new Name("Built-By"));
697+
addName(names, new Name("Bnd-LastModified"));
698+
addName(names, new Name("Bundle-Description"));
699+
addName(names, new Name("Bundle-DocURL"));
700+
addName(names, new Name("Bundle-License"));
701+
addName(names, new Name("Bundle-ManifestVersion"));
702+
addName(names, new Name("Bundle-Name"));
703+
addName(names, new Name("Bundle-Vendor"));
704+
addName(names, new Name("Bundle-Version"));
705+
addName(names, new Name("Bundle-SymbolicName"));
706+
addName(names, new Name("Created-By"));
707+
addName(names, new Name("Export-Package"));
708+
addName(names, new Name("Import-Package"));
709+
addName(names, new Name("Name"));
710+
addName(names, new Name("SHA1-Digest"));
711+
addName(names, new Name("X-Compile-Source-JDK"));
712+
addName(names, new Name("X-Compile-Target-JDK"));
713+
KNOWN_NAMES = names;
714+
}
657715
}
658716
}

0 commit comments

Comments
 (0)