[pypy-commit] pypy fastjson: merge heads

antocuni noreply at buildbot.pypy.org
Fri Jun 28 16:28:11 CEST 2013


Author: Antonio Cuni <anto.cuni at gmail.com>
Branch: fastjson
Changeset: r65068:54438eb6c42c
Date: 2013-06-28 15:49 +0200
http://bitbucket.org/pypy/pypy/changeset/54438eb6c42c/

Log:	merge heads

diff --git a/pypy/module/_fastjson/interp_decoder.py b/pypy/module/_fastjson/interp_decoder.py
--- a/pypy/module/_fastjson/interp_decoder.py
+++ b/pypy/module/_fastjson/interp_decoder.py
@@ -1,11 +1,15 @@
+import sys
 import math
 from rpython.rlib.rstring import StringBuilder
 from rpython.rlib.objectmodel import specialize
+from rpython.rlib import rfloat
 from pypy.interpreter.error import OperationError, operationerrfmt
 from pypy.interpreter.gateway import unwrap_spec
 from pypy.interpreter import unicodehelper
 from rpython.rtyper.annlowlevel import llstr, hlunicode
 
+OVF_DIGITS = len(str(sys.maxint))
+
 def is_whitespace(ch):
     return ch == ' ' or ch == '\t' or ch == '\r' or ch == '\n'
 
@@ -53,8 +57,8 @@
         self.last_type = TYPE_UNKNOWN
 
     def getslice(self, start, end):
-        assert start > 0
-        assert end > 0
+        assert start >= 0
+        assert end >= 0
         return self.s[start:end]
 
     def skip_whitespace(self, i):
@@ -117,7 +121,20 @@
         self._raise("Error when decoding false at char %d", i)
 
     def decode_numeric(self, i):
-        i, intval = self.parse_integer(i)
+        w_res = self.decode_numeric_fast(i)
+        if w_res is self.space.w_None:
+            # possible overflow, reparse it
+            return self.decode_numeric_slow(i)
+        return w_res
+
+    def decode_numeric_fast(self, i):
+        i, ovf_maybe, intval = self.parse_integer(i, allow_leading_0=False)
+        if ovf_maybe:
+            # apparently we get a ~30% slowdown on my microbenchmark if we
+            # return None instead of w_None, probably because the annotation
+            # of the results geta can_be_None=True. We need to check if this
+            # is still true also for the full pypy
+            return self.space.w_None
         #
         is_float = False
         exp = 0
@@ -134,28 +151,72 @@
         # check for the optional exponent part
         if ch == 'E' or ch == 'e':
             is_float = True
-            i, exp = self.parse_integer(i+1)
+            i, ovf_maybe, exp = self.parse_integer(i+1, allow_leading_0=True)
         #
         self.pos = i
         if is_float:
             # build the float
             floatval = intval + frcval
             if exp != 0:
-                floatval = floatval * math.pow(10, exp)
+                try:
+                    floatval = floatval * math.pow(10, exp)
+                except OverflowError:
+                    floatval = rfloat.INFINITY
             return self.space.wrap(floatval)
         else:
             return self.space.wrap(intval)
 
-    def parse_integer(self, i):
+    def decode_numeric_slow(self, i):
+        is_float = False
+        start = i
+        if self.ll_chars[i] == '-':
+            i += 1
+        # skip the integral part
+        while self.ll_chars[i].isdigit():
+            i += 1
+        #
+        # skip the fractional part, if any
+        if self.ll_chars[i] == '.':
+            is_float = True
+            i += 1
+            while self.ll_chars[i].isdigit():
+                i += 1
+        #
+        # skip the exponent part, if any
+        if self.ll_chars[i] == 'e' or self.ll_chars[i] == 'E':
+            is_float = True
+            i += 1
+            while self.ll_chars[i].isdigit():
+                i += 1
+        #
+        self.pos = i
+        s = self.getslice(start, i)
+        if is_float:
+            w_func = self.space.w_float
+        else:
+            w_func = self.space.w_int
+        w_res = self.space.call_function(w_func, self.space.wrap(s))
+        #assert w_res is not None # XXX check if this brings any speedup in pypy-c
+        return w_res
+
+    def parse_integer(self, i, allow_leading_0=False):
         "Parse a decimal number with an optional minus sign"
+        start = i
         sign = 1
         if self.ll_chars[i] == '-':
             sign = -1
             i += 1
         elif self.ll_chars[i] == '+':
             i += 1
+        elif not allow_leading_0 and self.ll_chars[i] == '0':
+            i += 1
+            return i, False, 0
         i, intval, _ = self.parse_digits(i)
-        return i, sign * intval
+        # if the number has more digits than OVF_DIGITS, it might have
+        # overflowed
+        ovf_maybe = (i-start >= OVF_DIGITS)
+        return i, ovf_maybe, sign * intval
+    parse_integer._always_inline_ = True
 
     def parse_digits(self, i):
         "Parse a sequence of digits as a decimal number. No sign allowed"
@@ -172,7 +233,7 @@
         if count == 0:
             self._raise("Expected digit at char %d", i)
         return i, intval, count
-        
+
     def decode_array(self, i):
         w_list = self.space.newlist([])
         start = i
diff --git a/pypy/module/_fastjson/test/test__fastjson.py b/pypy/module/_fastjson/test/test__fastjson.py
--- a/pypy/module/_fastjson/test/test__fastjson.py
+++ b/pypy/module/_fastjson/test/test__fastjson.py
@@ -99,6 +99,7 @@
 
 
     def test_decode_numeric(self):
+        import sys
         import _fastjson
         def check(s, val):
             res = _fastjson.loads(s)
@@ -113,6 +114,20 @@
         check('42E-1', 4.2)
         check('42E+1', 420.0)
         check('42.123E3', 42123.0)
+        check('0', 0)
+        check('-0', 0)
+        check('0.123', 0.123)
+        check('0E3', 0.0)
+        check('5E0001', 50.0)
+        check(str(1 << 32), 1 << 32)
+        check(str(1 << 64), 1 << 64)
+        #
+        x = str(sys.maxint+1) + '.123'
+        check(x, float(x))
+        x = str(sys.maxint+1) + 'E1'
+        check(x, float(x))
+        #
+        check('1E400', float('inf'))
 
     def test_decode_numeric_invalid(self):
         import _fastjson
@@ -121,10 +136,12 @@
         #
         error('  42   abc')
         error('.123')
+        error('+123')
         error('12.')
         error('12.-3')
         error('12E')
         error('12E-')
+        error('0123') # numbers can't start with 0
 
     def test_decode_object(self):
         import _fastjson


More information about the pypy-commit mailing list