Author: Antonio Cuni <[email protected]>
Branch: fastjson
Changeset: r65089:55ed11b7b3be
Date: 2013-06-29 11:37 +0200
http://bitbucket.org/pypy/pypy/changeset/55ed11b7b3be/

Log:    add an option to enable fast but less precise parsing of floats

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
@@ -48,7 +48,7 @@
 TYPE_UNKNOWN = 0
 TYPE_STRING = 1
 class JSONDecoder(object):
-    def __init__(self, space, s):
+    def __init__(self, space, s, precise_float=True):
         self.space = space
         self.s = s
         # we put our string in a raw buffer so:
@@ -59,6 +59,7 @@
         self.end_ptr = lltype.malloc(rffi.CCHARPP.TO, 1, flavor='raw')
         self.pos = 0
         self.last_type = TYPE_UNKNOWN
+        self.precise_float = precise_float
 
     def close(self):
         rffi.free_charp(self.ll_chars)
@@ -144,9 +145,9 @@
         if ch == '.':
             if not self.ll_chars[i+1].isdigit():
                 self._raise("Expected digit at char %d", i+1)
-            return self.parse_float(start)
+            return self.parse_float(start, i, intval, ovf_maybe)
         elif ch == 'e' or ch == 'E':
-            return self.parse_float(start)
+            return self.parse_float(start, i, intval, ovf_maybe)
         elif ovf_maybe:
             # apparently we get a ~30% slowdown on my microbenchmark if we
             # return None instead of w_None, probably because the annotation
@@ -157,30 +158,12 @@
         self.pos = i
         return self.space.wrap(intval)
 
-        ## if ch == '.':
-        ##     is_float = True
-        ##     i, frcval, frccount = self.parse_digits(i+1)
-        ##     frcval = neg_pow_10(frcval, frccount)
-        ##     ch = self.ll_chars[i]
-        ## # check for the optional exponent part
-        ## if ch == 'E' or ch == 'e':
-        ##     is_float = True
-        ##     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:
-        ##         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_float(self, start, i, intval, ovf_maybe):
+        if self.precise_float or ovf_maybe:
+            return self.parse_float_precise(start)
+        return self.parse_float_fast(i, intval)
 
-    def parse_float(self, i):
+    def parse_float_precise(self, i):
         from rpython.rlib import rdtoa
         start = rffi.ptradd(self.ll_chars, i)
         floatval = rdtoa.dg_strtod(start, self.end_ptr)
@@ -188,6 +171,27 @@
         self.pos = i + diff
         return self.space.wrap(floatval)
 
+    def parse_float_fast(self, i, intval):
+        exp = 0
+        frcval = 0
+        ch = self.ll_chars[i]
+        if ch == '.':
+            i, frcval, frccount = self.parse_digits(i+1)
+            frcval = neg_pow_10(frcval, frccount)
+            ch = self.ll_chars[i]
+        # check for the optional exponent part
+        if ch == 'E' or ch == 'e':
+            i, ovf_maybe, exp = self.parse_integer(i+1, allow_leading_0=True)
+        #
+        floatval = float(intval) + frcval
+        if exp != 0:
+            try:
+                floatval = floatval * math.pow(10, exp)
+            except OverflowError:
+                floatval = rfloat.INFINITY
+        self.pos = i
+        return self.space.wrap(floatval)
+
     def decode_numeric_slow(self, i):
         is_float = False
         start = i
@@ -422,13 +426,13 @@
         lowsurr = int(hexdigits, 16) # the possible ValueError is caugth by 
the caller
         return 0x10000 + (((highsurr - 0xd800) << 10) | (lowsurr - 0xdc00))
 
-
-def loads(space, w_s):
+@unwrap_spec(precise_float=bool)
+def loads(space, w_s, precise_float=True):
     if space.isinstance_w(w_s, space.w_unicode):
         raise OperationError(space.w_TypeError,
                              space.wrap("Expected utf8-encoded str, got 
unicode"))
     s = space.str_w(w_s)
-    decoder = JSONDecoder(space, s)
+    decoder = JSONDecoder(space, s, precise_float)
     try:
         w_res = decoder.decode_any(0)
         i = decoder.skip_whitespace(decoder.pos)
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
@@ -105,6 +105,10 @@
             res = _fastjson.loads(s)
             assert type(res) is type(val)
             assert res == val
+            #
+            res = _fastjson.loads(s, precise_float=False)
+            assert type(res) is type(val)
+            assert res == val
         #
         check('42', 42)
         check('-42', -42)
@@ -135,6 +139,7 @@
         import _fastjson
         def error(s):
             raises(ValueError, _fastjson.loads, s)
+            raises(ValueError, _fastjson.loads, s, False)
         #
         error('  42   abc')
         error('.123')
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to