# HG changeset patch # User redestad # Date 1469761270 -7200 # Fri Jul 29 05:01:10 2016 +0200 # Node ID 8a93c04106ae436631cca4e4672e828863b15c17 # Parent 9410dfad9f32d852199b8d826a13b69af9d63c2f 8162439: Runtime.Version.parse needs fast-path for major versions Reviewed-by: psandoz, sdrach, iris diff --git a/src/java.base/share/classes/java/lang/Runtime.java b/src/java.base/share/classes/java/lang/Runtime.java --- a/src/java.base/share/classes/java/lang/Runtime.java +++ b/src/java.base/share/classes/java/lang/Runtime.java @@ -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 version = new ArrayList<>(); + for (String i : m.group(VersionPattern.VNUM_GROUP).split("\\.")) + version.add(Integer.parseInt(i)); + + Optional pre = Optional.ofNullable( + m.group(VersionPattern.PRE_GROUP)); + + String b = m.group(VersionPattern.BUILD_GROUP); + // $BUILD is an integer + Optional build = (b == null) + ? Optional.empty() + : Optional.of(Integer.parseInt(b)); + + Optional 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 = "(?[1-9][0-9]*(?:(?:\\.0)*\\.[1-9][0-9]*)*)"; - private static final String VNUM_GROUP = "VNUM"; - private static final String PRE = "(?:-(?
[a-zA-Z0-9]+))?";
-        private static final String PRE_GROUP   = "PRE";
-
         private static final String 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      = "(?:-(?[-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 version string containing
-         * a version number 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();
+        static final Pattern VSTR_PATTERN = Pattern.compile(VSTR_FORMAT);
 
-            Matcher m = 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 version = new ArrayList<>();
-            for (String i : m.group(VNUM_GROUP).split("\\."))
-                version.add(Integer.parseInt(i));
-
-            Optional pre = Optional.ofNullable(m.group(PRE_GROUP));
-
-            String b = m.group(BUILD_GROUP);
-            // $BUILD is an integer
-            Optional build = (b == null)
-                ? Optional.empty()
-                : Optional.of(Integer.parseInt(b));
-
-            Optional 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";
     }
 }