2828import java .io .DataOutputStream ;
2929import java .io .IOException ;
3030import java .util .Collection ;
31- import java .util .Comparator ;
31+ import java .util .HashMap ;
3232import java .util .LinkedHashMap ;
33- import java .util .Locale ;
3433import java .util .Map ;
34+ import java .util .Objects ;
3535import java .util .Set ;
3636
3737import 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