src/share/classes/java/lang/Long.java

Print this page
rev 9925 : 8041972: Add improved parse/format methods for Long/Integer
Contributed-by: redestad

@@ -544,64 +544,105 @@
      *             the specified radix.
      * @throws     NumberFormatException  if the string does not contain a
      *             parsable {@code long}.
      */
     public static long parseLong(String s, int radix)
-              throws NumberFormatException
-    {
+              throws NumberFormatException {
+        return parseLong(s, radix, 0);
+    }
+
+    /**
+     * Extend upon Long.parseLong(String, int) by providing a start offset to enable parsing
+     * substrings without creating intermediary objects
+     * @see java.lang.Long#parseLong(String, int)
+     *
+     * @param start the start position in s to parse from, inclusive
+     */
+    static long parseLong(CharSequence s, int radix, int start)
+            throws NumberFormatException {
+        if (s == null) {
+            throw new NumberFormatException("null");
+        }
+        return parseLong(s, radix, start, s.length());
+    }
+
+    /**
+     * Extend upon Long.parseLong(String, int) by providing a start and end offset to enable parsing
+     * substrings without creating intermediary objects
+     * @see java.lang.Long#parseLong(String, int)
+     * @param start the start position in s to parse from, inclusive
+     * @param end the end position in s to parse to, exclusive
+     */
+    static long parseLong(CharSequence s, int radix, int start, int end)
+            throws NumberFormatException {
         if (s == null) {
             throw new NumberFormatException("null");
         }
 
         if (radix < Character.MIN_RADIX) {
             throw new NumberFormatException("radix " + radix +
                                             " less than Character.MIN_RADIX");
         }
+
         if (radix > Character.MAX_RADIX) {
             throw new NumberFormatException("radix " + radix +
                                             " greater than Character.MAX_RADIX");
         }
 
+        if (start < 0 || start >= end) {
+            throw new NumberFormatException("start position out of bounds");
+        }
+
+        if (start == end) {
+            throw NumberFormatException.forInputString("");
+        }
+
+        if (end > s.length()) {
+            throw new NumberFormatException("end position out of bounds");
+        }
+
         long result = 0;
         boolean negative = false;
-        int i = 0, len = s.length();
+        int i = start;
         long limit = -Long.MAX_VALUE;
         long multmin;
         int digit;
 
-        if (len > 0) {
-            char firstChar = s.charAt(0);
+        char firstChar = s.charAt(i);
             if (firstChar < '0') { // Possible leading "+" or "-"
                 if (firstChar == '-') {
                     negative = true;
                     limit = Long.MIN_VALUE;
-                } else if (firstChar != '+')
-                    throw NumberFormatException.forInputString(s);
-
-                if (len == 1) // Cannot have lone "+" or "-"
-                    throw NumberFormatException.forInputString(s);
+            } else if (firstChar != '+') {
+                throw NumberFormatException.forInputString(
+                        s.subSequence(start, end).toString());
+            }
+            if (end == start + 1) { // Cannot have lone "+" or "-"
+                throw NumberFormatException.forInputString(
+                        s.subSequence(start, end).toString());
+            }
                 i++;
             }
             multmin = limit / radix;
-            while (i < len) {
+        while (i < end) {
                 // Accumulating negatively avoids surprises near MAX_VALUE
-                digit = Character.digit(s.charAt(i++),radix);
+            digit = Character.digit(s.charAt(i++), radix);
                 if (digit < 0) {
-                    throw NumberFormatException.forInputString(s);
+                throw NumberFormatException.forInputString(
+                        s.subSequence(start, end).toString());
                 }
                 if (result < multmin) {
-                    throw NumberFormatException.forInputString(s);
+                throw NumberFormatException.forInputString(
+                        s.subSequence(start, end).toString());
                 }
                 result *= radix;
                 if (result < limit + digit) {
-                    throw NumberFormatException.forInputString(s);
+                throw NumberFormatException.forInputString(
+                        s.subSequence(start, end).toString());
                 }
                 result -= digit;
             }
-        } else {
-            throw NumberFormatException.forInputString(s);
-        }
         return negative ? result : -result;
     }
 
     /**
      * Parses the string argument as a signed decimal {@code long}.

@@ -677,29 +718,72 @@
     public static long parseUnsignedLong(String s, int radix)
                 throws NumberFormatException {
         if (s == null)  {
             throw new NumberFormatException("null");
         }
+        return parseUnsignedLong(s, radix, 0);
+    }
+
+    /**
+     * Extend upon Long.parseUnsignedLong(String, int) by providing a start offset
+     * to enable parsing substrings without creating intermediary objects
+     * @see java.lang.Long#parseUnsignedLong(String, int)
+     * @param start the start position in s to parse from, inclusive
+     */
+    static long parseUnsignedLong(CharSequence s, int radix, int start) {
+        if (s == null)  {
+            throw new NumberFormatException("null");
+        }
+        return parseUnsignedLong(s, radix, start, s.length());
+    }
+
+    /**
+     * Extend upon Long.parseUnsignedLong(String, int) by providing a start offset to enable parsing
+     * substrings without creating intermediary objects
+     * @see java.lang.Long#parseUnsignedLong(String, int)
+     * @param start the start position in s to parse from, inclusive
+     * @param end the end position in s to parse to, exclusive
+     */
+    static long parseUnsignedLong(CharSequence s, int radix, int start, int end) {
+        if (s == null) {
+            throw new NumberFormatException("null");
+        }
+
+        if (radix < Character.MIN_RADIX) {
+            throw new NumberFormatException("radix " + radix +
+                    " less than Character.MIN_RADIX");
+        }
+
+        if (radix > Character.MAX_RADIX) {
+            throw new NumberFormatException("radix " + radix +
+                    " greater than Character.MAX_RADIX");
+        }
+
+        if (start < 0 || start > end) {
+            throw new NumberFormatException("start position out of bounds");
+        }
 
-        int len = s.length();
-        if (len > 0) {
-            char firstChar = s.charAt(0);
+        if (end <= start || end > s.length()) {
+            throw new NumberFormatException("end position out of bounds");
+        }
+
+        int count = end - start;
+        char firstChar = s.charAt(start);
             if (firstChar == '-') {
-                throw new
-                    NumberFormatException(String.format("Illegal leading minus sign " +
-                                                       "on unsigned string %s.", s));
+            throw new NumberFormatException(String.format("Illegal leading minus sign " +
+                    "on unsigned string %s.", s.subSequence(start, end)));
             } else {
-                if (len <= 12 || // Long.MAX_VALUE in Character.MAX_RADIX is 13 digits
-                    (radix == 10 && len <= 18) ) { // Long.MAX_VALUE in base 10 is 19 digits
-                    return parseLong(s, radix);
+            if (count <= 12 || // Long.MAX_VALUE in Character.MAX_RADIX is 13 digits
+                    (radix == 10 && count <= 18) ) { // Long.MAX_VALUE in base 10 is 19 digits
+                return parseLong(s, radix, start, end);
                 }
 
-                // No need for range checks on len due to testing above.
-                long first = parseLong(s.substring(0, len - 1), radix);
-                int second = Character.digit(s.charAt(len - 1), radix);
+            // No need for range checks on end due to testing above.
+            long first = parseLong(s, radix, start, end - 1);
+            int second = Character.digit(s.charAt(end - 1), radix);
                 if (second < 0) {
-                    throw new NumberFormatException("Bad digit at end of " + s);
+                throw new NumberFormatException("Bad digit at end of " + s.subSequence(start, end));
                 }
                 long result = first * radix + second;
 
                 /*
                  * Test leftmost bits of multiprecision extension of first*radix

@@ -752,17 +836,14 @@
                      * upper bound is too small to overflow again after the
                      * signed long overflows to positive above 2^64 - 1. Hence
                      * result >= 0 implies overflow given C and D.
                      */
                     throw new NumberFormatException(String.format("String value %s exceeds " +
-                                                                  "range of unsigned long.", s));
+                        "range of unsigned long.", s.subSequence(start, end)));
                 }
                 return result;
             }
-        } else {
-            throw NumberFormatException.forInputString(s);
-        }
     }
 
     /**
      * Parses the string argument as an unsigned decimal {@code long}. The
      * characters in the string must all be decimal digits, except