8162439: Runtime.Version.parse needs fast-path for major versions
authorredestad
Mon, 01 Aug 2016 20:32:10 +0200
changeset 39889 2e82e1c36e90
parent 39888 bec759b9b909
child 39891 429395d775e7
8162439: Runtime.Version.parse needs fast-path for major versions Reviewed-by: psandoz, sdrach, iris
jdk/src/java.base/share/classes/java/lang/Runtime.java
--- a/jdk/src/java.base/share/classes/java/lang/Runtime.java	Mon Aug 01 09:58:36 2016 -0400
+++ b/jdk/src/java.base/share/classes/java/lang/Runtime.java	Mon Aug 01 20:32:10 2016 +0200
@@ -1112,7 +1112,62 @@
          * @return  The Version of the given string
          */
         public static Version parse(String s) {
-            return VersionBuilder.parse(s);
+            if (s == null)
+                throw new NullPointerException();
+
+            // Shortcut to avoid initializing VersionPattern when creating
+            // major version constants during startup
+            if (isSimpleNumber(s)) {
+                return new Version(List.of(Integer.parseInt(s)),
+                        Optional.empty(), Optional.empty(), Optional.empty());
+            }
+            Matcher m = VersionPattern.VSTR_PATTERN.matcher(s);
+            if (!m.matches())
+                throw new IllegalArgumentException("Invalid version string: '"
+                                                   + s + "'");
+
+            // $VNUM is a dot-separated list of integers of arbitrary length
+            List<Integer> version = new ArrayList<>();
+            for (String i : m.group(VersionPattern.VNUM_GROUP).split("\\."))
+                version.add(Integer.parseInt(i));
+
+            Optional<String> pre = Optional.ofNullable(
+                    m.group(VersionPattern.PRE_GROUP));
+
+            String b = m.group(VersionPattern.BUILD_GROUP);
+            // $BUILD is an integer
+            Optional<Integer> build = (b == null)
+                ? Optional.empty()
+                : Optional.of(Integer.parseInt(b));
+
+            Optional<String> optional = Optional.ofNullable(
+                    m.group(VersionPattern.OPT_GROUP));
+
+            // empty '+'
+            if ((m.group(VersionPattern.PLUS_GROUP) != null)
+                    && !build.isPresent()) {
+                if (optional.isPresent()) {
+                    if (pre.isPresent())
+                        throw new IllegalArgumentException("'+' found with"
+                            + " pre-release and optional components:'" + s
+                            + "'");
+                } else {
+                    throw new IllegalArgumentException("'+' found with neither"
+                        + " build or optional components: '" + s + "'");
+                }
+            }
+            return new Version(version, pre, build, optional);
+        }
+
+        private static boolean isSimpleNumber(String s) {
+            for (int i = 0; i < s.length(); i++) {
+                char c = s.charAt(i);
+                char lowerBound = (i > 0) ? '0' : '1';
+                if (c < lowerBound || c > '9') {
+                    return false;
+                }
+            }
+            return true;
         }
 
         /**
@@ -1441,86 +1496,26 @@
         }
     }
 
-    private static class VersionBuilder {
+    private static class VersionPattern {
         // $VNUM(-$PRE)?(\+($BUILD)?(\-$OPT)?)?
         // RE limits the format of version strings
         // ([1-9][0-9]*(?:(?:\.0)*\.[1-9][0-9]*)*)(?:-([a-zA-Z0-9]+))?(?:(\+)(0|[1-9][0-9]*)?)?(?:-([-a-zA-Z0-9.]+))?
 
         private static final String VNUM
             = "(?<VNUM>[1-9][0-9]*(?:(?:\\.0)*\\.[1-9][0-9]*)*)";
-        private static final String VNUM_GROUP  = "VNUM";
-
         private static final String PRE      = "(?:-(?<PRE>[a-zA-Z0-9]+))?";
-        private static final String PRE_GROUP   = "PRE";
-
         private static final String BUILD
             = "(?:(?<PLUS>\\+)(?<BUILD>0|[1-9][0-9]*)?)?";
-        private static final String PLUS_GROUP  = "PLUS";
-        private static final String BUILD_GROUP = "BUILD";
-
         private static final String OPT      = "(?:-(?<OPT>[-a-zA-Z0-9.]+))?";
-        private static final String OPT_GROUP   = "OPT";
-
         private static final String VSTR_FORMAT
             = "^" + VNUM + PRE + BUILD + OPT + "$";
-        private static final Pattern VSTR_PATTERN = Pattern.compile(VSTR_FORMAT);
 
-        /**
-         * Constructs a valid <a href="verStr">version string</a> containing
-         * a <a href="#verNum">version number</a> followed by pre-release and
-         * build information.
-         *
-         * @param  s
-         *         A string to be interpreted as a version
-         *
-         * @throws  IllegalArgumentException
-         *          If the given string cannot be interpreted as a valid
-         *          version
-         *
-         * @throws  NullPointerException
-         *          If {@code s} is {@code null}
-         *
-         * @throws  NumberFormatException
-         *          If an element of the version number or the build number
-         *          cannot be represented as an {@link Integer}
-         */
-        static Version parse(String s) {
-            if (s == null)
-                throw new NullPointerException();
-
-            Matcher m = VSTR_PATTERN.matcher(s);
-            if (!m.matches())
-                throw new IllegalArgumentException("Invalid version string: '"
-                                                   + s + "'");
+        static final Pattern VSTR_PATTERN = Pattern.compile(VSTR_FORMAT);
 
-            // $VNUM is a dot-separated list of integers of arbitrary length
-            List<Integer> version = new ArrayList<>();
-            for (String i : m.group(VNUM_GROUP).split("\\."))
-                version.add(Integer.parseInt(i));
-
-            Optional<String> pre = Optional.ofNullable(m.group(PRE_GROUP));
-
-            String b = m.group(BUILD_GROUP);
-            // $BUILD is an integer
-            Optional<Integer> build = (b == null)
-                ? Optional.empty()
-                : Optional.of(Integer.parseInt(b));
-
-            Optional<String> optional = Optional.ofNullable(m.group(OPT_GROUP));
-
-            // empty '+'
-            if ((m.group(PLUS_GROUP) != null) && !build.isPresent()) {
-                if (optional.isPresent()) {
-                    if (pre.isPresent())
-                        throw new IllegalArgumentException("'+' found with"
-                            + " pre-release and optional components:'" + s
-                            + "'");
-                } else {
-                    throw new IllegalArgumentException("'+' found with neither"
-                        + " build or optional components: '" + s + "'");
-                }
-            }
-            return new Version(version, pre, build, optional);
-        }
+        static final String VNUM_GROUP  = "VNUM";
+        static final String PRE_GROUP   = "PRE";
+        static final String PLUS_GROUP  = "PLUS";
+        static final String BUILD_GROUP = "BUILD";
+        static final String OPT_GROUP   = "OPT";
     }
 }