8145862: Improve lazy initialization of fields in java.net.URI
authorredestad
Tue, 22 Dec 2015 19:14:47 +0100
changeset 34783 337afb24ec6c
parent 34782 fe102f179318
child 34822 72b281962e95
8145862: Improve lazy initialization of fields in java.net.URI Reviewed-by: shade, chegar
jdk/src/java.base/share/classes/java/net/URI.java
--- a/jdk/src/java.base/share/classes/java/net/URI.java	Tue Dec 22 16:42:16 2015 +0100
+++ b/jdk/src/java.base/share/classes/java/net/URI.java	Tue Dec 22 19:14:47 2015 +0100
@@ -489,17 +489,17 @@
     private transient String path;              // null ==> opaque
     private transient String query;
 
-    // The remaining fields may be computed on demand
-
-    private transient volatile String schemeSpecificPart;
-    private transient volatile int hash;        // Zero ==> undefined
-
-    private transient volatile String decodedUserInfo;
-    private transient volatile String decodedAuthority;
-    private transient volatile String decodedPath;
-    private transient volatile String decodedQuery;
-    private transient volatile String decodedFragment;
-    private transient volatile String decodedSchemeSpecificPart;
+    // The remaining fields may be computed on demand, which is safe even in
+    // the face of multiple threads racing to initialize them
+    private transient String schemeSpecificPart;
+    private transient int hash;        // Zero ==> undefined
+
+    private transient String decodedUserInfo;
+    private transient String decodedAuthority;
+    private transient String decodedPath;
+    private transient String decodedQuery;
+    private transient String decodedFragment;
+    private transient String decodedSchemeSpecificPart;
 
     /**
      * The string form of this URI.
@@ -910,8 +910,7 @@
         // either more fields or a more-obscure representation.
         if ((host != null) || (authority == null))
             return this;
-        defineString();
-        new Parser(string).parse(true);
+        new Parser(toString()).parse(true);
         return this;
     }
 
@@ -1143,8 +1142,17 @@
      *          (never {@code null})
      */
     public String getRawSchemeSpecificPart() {
-        defineSchemeSpecificPart();
-        return schemeSpecificPart;
+        String part = schemeSpecificPart;
+        if (part != null) {
+            return part;
+        }
+        StringBuilder sb = new StringBuilder();
+        appendSchemeSpecificPart(sb, null, getAuthority(), getUserInfo(),
+                                 host, port, getPath(), getQuery());
+        if (sb.length() == 0) {
+            return null;
+        }
+        return schemeSpecificPart = sb.toString();
     }
 
     /**
@@ -1159,9 +1167,11 @@
      *          (never {@code null})
      */
     public String getSchemeSpecificPart() {
-        if (decodedSchemeSpecificPart == null)
-            decodedSchemeSpecificPart = decode(getRawSchemeSpecificPart());
-        return decodedSchemeSpecificPart;
+        String part = decodedSchemeSpecificPart;
+        if (part == null) {
+            decodedSchemeSpecificPart = part = decode(getRawSchemeSpecificPart());
+        }
+        return part;
     }
 
     /**
@@ -1192,9 +1202,11 @@
      *          or {@code null} if the authority is undefined
      */
     public String getAuthority() {
-        if (decodedAuthority == null)
-            decodedAuthority = decode(authority);
-        return decodedAuthority;
+        String auth = decodedAuthority;
+        if ((auth == null) && (authority != null)) {
+            decodedAuthority = auth = decode(authority);
+        }
+        return auth;
     }
 
     /**
@@ -1222,9 +1234,11 @@
      *          or {@code null} if the user information is undefined
      */
     public String getUserInfo() {
-        if ((decodedUserInfo == null) && (userInfo != null))
-            decodedUserInfo = decode(userInfo);
-        return decodedUserInfo;
+        String user = decodedUserInfo;
+        if ((user == null) && (userInfo != null)) {
+            decodedUserInfo = user = decode(userInfo);
+        }
+        return user;
     }
 
     /**
@@ -1306,9 +1320,11 @@
      *          or {@code null} if the path is undefined
      */
     public String getPath() {
-        if ((decodedPath == null) && (path != null))
-            decodedPath = decode(path);
-        return decodedPath;
+        String decoded = decodedPath;
+        if ((decoded == null) && (path != null)) {
+            decodedPath = decoded = decode(path);
+        }
+        return decoded;
     }
 
     /**
@@ -1335,9 +1351,11 @@
      *          or {@code null} if the query is undefined
      */
     public String getQuery() {
-        if ((decodedQuery == null) && (query != null))
-            decodedQuery = decode(query, false);
-        return decodedQuery;
+        String decoded = decodedQuery;
+        if ((decoded == null) && (query != null)) {
+            decodedQuery = decoded = decode(query, false);
+        }
+        return decoded;
     }
 
     /**
@@ -1364,9 +1382,11 @@
      *          or {@code null} if the fragment is undefined
      */
     public String getFragment() {
-        if ((decodedFragment == null) && (fragment != null))
-            decodedFragment = decode(fragment, false);
-        return decodedFragment;
+        String decoded = decodedFragment;
+        if ((decoded == null) && (fragment != null)) {
+            decodedFragment = decoded = decode(fragment, false);
+        }
+        return decoded;
     }
 
 
@@ -1452,24 +1472,27 @@
      * @return  A hash-code value for this URI
      */
     public int hashCode() {
-        if (hash != 0)
-            return hash;
-        int h = hashIgnoringCase(0, scheme);
-        h = hash(h, fragment);
-        if (isOpaque()) {
-            h = hash(h, schemeSpecificPart);
-        } else {
-            h = hash(h, path);
-            h = hash(h, query);
-            if (host != null) {
-                h = hash(h, userInfo);
-                h = hashIgnoringCase(h, host);
-                h += 1949 * port;
+        int h = hash;
+        if (h == 0) {
+            h = hashIgnoringCase(0, scheme);
+            h = hash(h, fragment);
+            if (isOpaque()) {
+                h = hash(h, schemeSpecificPart);
             } else {
-                h = hash(h, authority);
+                h = hash(h, path);
+                h = hash(h, query);
+                if (host != null) {
+                    h = hash(h, userInfo);
+                    h = hashIgnoringCase(h, host);
+                    h += 1949 * port;
+                } else {
+                    h = hash(h, authority);
+                }
+            }
+            if (h != 0) {
+                hash = h;
             }
         }
-        hash = h;
         return h;
     }
 
@@ -1599,8 +1622,59 @@
      * @return  The string form of this URI
      */
     public String toString() {
-        defineString();
-        return string;
+        String s = string;
+        if (s == null) {
+            s = defineString();
+        }
+        return s;
+    }
+
+    private String defineString() {
+        String s = string;
+        if (s != null) {
+            return s;
+        }
+
+        StringBuilder sb = new StringBuilder();
+        if (scheme != null) {
+            sb.append(scheme);
+            sb.append(':');
+        }
+        if (isOpaque()) {
+            sb.append(schemeSpecificPart);
+        } else {
+            if (host != null) {
+                sb.append("//");
+                if (userInfo != null) {
+                    sb.append(userInfo);
+                    sb.append('@');
+                }
+                boolean needBrackets = ((host.indexOf(':') >= 0)
+                        && !host.startsWith("[")
+                        && !host.endsWith("]"));
+                if (needBrackets) sb.append('[');
+                sb.append(host);
+                if (needBrackets) sb.append(']');
+                if (port != -1) {
+                    sb.append(':');
+                    sb.append(port);
+                }
+            } else if (authority != null) {
+                sb.append("//");
+                sb.append(authority);
+            }
+            if (path != null)
+                sb.append(path);
+            if (query != null) {
+                sb.append('?');
+                sb.append(query);
+            }
+        }
+        if (fragment != null) {
+            sb.append('#');
+            sb.append(fragment);
+        }
+        return string = sb.toString();
     }
 
     /**
@@ -1617,8 +1691,7 @@
      *          charset
      */
     public String toASCIIString() {
-        defineString();
-        return encode(string);
+        return encode(toString());
     }
 
 
@@ -1824,7 +1897,7 @@
         }
     }
 
-    private void appendAuthority(StringBuffer sb,
+    private void appendAuthority(StringBuilder sb,
                                  String authority,
                                  String userInfo,
                                  String host,
@@ -1874,7 +1947,7 @@
         }
     }
 
-    private void appendSchemeSpecificPart(StringBuffer sb,
+    private void appendSchemeSpecificPart(StringBuilder sb,
                                           String opaquePart,
                                           String authority,
                                           String userInfo,
@@ -1915,7 +1988,7 @@
         }
     }
 
-    private void appendFragment(StringBuffer sb, String fragment) {
+    private void appendFragment(StringBuilder sb, String fragment) {
         if (fragment != null) {
             sb.append('#');
             sb.append(quote(fragment, L_URIC, H_URIC));
@@ -1932,7 +2005,7 @@
                             String query,
                             String fragment)
     {
-        StringBuffer sb = new StringBuffer();
+        StringBuilder sb = new StringBuilder();
         if (scheme != null) {
             sb.append(scheme);
             sb.append(':');
@@ -1944,61 +2017,6 @@
         return sb.toString();
     }
 
-    private void defineSchemeSpecificPart() {
-        if (schemeSpecificPart != null) return;
-        StringBuffer sb = new StringBuffer();
-        appendSchemeSpecificPart(sb, null, getAuthority(), getUserInfo(),
-                                 host, port, getPath(), getQuery());
-        if (sb.length() == 0) return;
-        schemeSpecificPart = sb.toString();
-    }
-
-    private void defineString() {
-        if (string != null) return;
-
-        StringBuilder sb = new StringBuilder();
-        if (scheme != null) {
-            sb.append(scheme);
-            sb.append(':');
-        }
-        if (isOpaque()) {
-            sb.append(schemeSpecificPart);
-        } else {
-            if (host != null) {
-                sb.append("//");
-                if (userInfo != null) {
-                    sb.append(userInfo);
-                    sb.append('@');
-                }
-                boolean needBrackets = ((host.indexOf(':') >= 0)
-                                    && !host.startsWith("[")
-                                    && !host.endsWith("]"));
-                if (needBrackets) sb.append('[');
-                sb.append(host);
-                if (needBrackets) sb.append(']');
-                if (port != -1) {
-                    sb.append(':');
-                    sb.append(port);
-                }
-            } else if (authority != null) {
-                sb.append("//");
-                sb.append(authority);
-            }
-            if (path != null)
-                sb.append(path);
-            if (query != null) {
-                sb.append('?');
-                sb.append(query);
-            }
-        }
-        if (fragment != null) {
-            sb.append('#');
-            sb.append(fragment);
-        }
-        string = sb.toString();
-    }
-
-
     // -- Normalization, resolution, and relativization --
 
     // RFC2396 5.2 (6)
@@ -2649,13 +2667,13 @@
         '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
     };
 
-    private static void appendEscape(StringBuffer sb, byte b) {
+    private static void appendEscape(StringBuilder sb, byte b) {
         sb.append('%');
         sb.append(hexDigits[(b >> 4) & 0x0f]);
         sb.append(hexDigits[(b >> 0) & 0x0f]);
     }
 
-    private static void appendEncoded(StringBuffer sb, char c) {
+    private static void appendEncoded(StringBuilder sb, char c) {
         ByteBuffer bb = null;
         try {
             bb = ThreadLocalCoders.encoderFor("UTF-8")
@@ -2676,15 +2694,14 @@
     // by the given mask pair
     //
     private static String quote(String s, long lowMask, long highMask) {
-        int n = s.length();
-        StringBuffer sb = null;
+        StringBuilder sb = null;
         boolean allowNonASCII = ((lowMask & L_ESCAPED) != 0);
         for (int i = 0; i < s.length(); i++) {
             char c = s.charAt(i);
             if (c < '\u0080') {
                 if (!match(c, lowMask, highMask)) {
                     if (sb == null) {
-                        sb = new StringBuffer();
+                        sb = new StringBuilder();
                         sb.append(s, 0, i);
                     }
                     appendEscape(sb, (byte)c);
@@ -2696,7 +2713,7 @@
                        && (Character.isSpaceChar(c)
                            || Character.isISOControl(c))) {
                 if (sb == null) {
-                    sb = new StringBuffer();
+                    sb = new StringBuilder();
                     sb.append(s, 0, i);
                 }
                 appendEncoded(sb, c);
@@ -2733,7 +2750,7 @@
             assert false;
         }
 
-        StringBuffer sb = new StringBuffer();
+        StringBuilder sb = new StringBuilder();
         while (bb.hasRemaining()) {
             int b = bb.get() & 0xff;
             if (b >= 0x80)
@@ -2864,12 +2881,6 @@
             fail("Expected " + expected, p);
         }
 
-        private void failExpecting(String expected, String prior, int p)
-            throws URISyntaxException
-        {
-            fail("Expected " + expected + " following " + prior, p);
-        }
-
 
         // -- Simple access to the input string --