Commits

Carl Friedrich Bolz committed a4f5018

(janzert): Add a bigint_w method to object spaces to unwrap python longs to
rbigints.

thanks a lot, janzert!

Comments (0)

Files changed (13)

pypy/doc/objspace.txt

 **int_w(w_x):**
   If w_x is an application-level integer or long which can be converted without overflow to an integer, return an interpreter-level integer.  Otherwise raise TypeError or OverflowError.
 
+**bigint_w(w_x):**
+  If w_x is an application-level integer or long, return an interpreter-level rbigint. Otherwise raise TypeError.
+
 **str_w(w_x):**
   If w_x is an application-level string, return an interpreter-level string.  Otherwise raise TypeError.
 

pypy/interpreter/baseobjspace.py

 #              int_w(w_ival or w_long_ival) -> ival
 #                       float_w(w_floatval) -> floatval
 #             uint_w(w_ival or w_long_ival) -> r_uint_val (unsigned int value)
+#             bigint_w(w_ival or w_long_ival) -> rbigint
 #interpclass_w(w_interpclass_inst or w_obj) -> interpclass_inst|w_obj
 #                               unwrap(w_x) -> x
 #                              is_true(w_x) -> True or False
     'int_w',
     'float_w',
     'uint_w',
+    'bigint_w',
     'interpclass_w',
     'unwrap',
     'is_true',

pypy/objspace/cpy/capi.py

 PyLong_FromUnsignedLongLong.argtypes = [c_ulonglong]
 PyLong_FromUnsignedLongLong.restype = W_Object
 
+_PyLong_Sign = cpyapi._PyLong_Sign
+_PyLong_Sign.argtypes = [W_Object]
+_PyLong_Sign.restype = c_long
+
+_PyLong_NumBits = cpyapi._PyLong_NumBits
+_PyLong_NumBits.argtypes = [W_Object]
+_PyLong_NumBits.restype = c_size_t
+
+_PyLong_AsByteArray = cpyapi._PyLong_AsByteArray
+_PyLong_AsByteArray.argtypes = [W_Object, POINTER(c_ubyte), c_size_t,
+                                c_long, c_long]
+_PyLong_AsByteArray.restype = c_long
+
 # a version of PyLong_FromVoidPtr() that pretends to take a PyObject* arg
 PyLong_FromVoidPtr_PYOBJ = cpyapi.PyLong_FromVoidPtr
 PyLong_FromVoidPtr_PYOBJ.argtypes = [W_Object]

pypy/objspace/cpy/objspace.py

 import types
 from pypy.objspace.cpy.capi import *
 from pypy.objspace.cpy.capi import _PyType_Lookup
+from pypy.objspace.cpy.capi import _PyLong_Sign, _PyLong_NumBits, _PyLong_AsByteArray
 from pypy.objspace.cpy.refcount import Py_Incref
 from pypy.objspace.cpy.appsupport import W_AppLevel
 from pypy.interpreter import baseobjspace
 from pypy.interpreter.function import Function
 from pypy.interpreter.typedef import GetSetProperty
 from pypy.rlib.rarithmetic import r_uint, r_longlong, r_ulonglong
+from pypy.rlib.rbigint import rbigint
 from pypy.rlib.objectmodel import we_are_translated, instantiate
 
 
     repr    = staticmethod(PyObject_Repr)
     id      = staticmethod(PyLong_FromVoidPtr_PYOBJ)
 
+    def bigint_w(self, w_obj):
+        if self.is_true(self.isinstance(w_obj, self.w_long)):
+            sign = _PyLong_Sign(w_obj)
+            #XXX Can throw exception if long larger than size_t bits
+            numbits = _PyLong_NumBits(w_obj)
+            numbytes = (numbits+1) / 8 # +1 sign bit cpython always adds
+            if (numbits+1) % 8 != 0:
+                numbytes += 1
+            ByteArray = c_ubyte * numbytes
+            cbytes = ByteArray()
+            _PyLong_AsByteArray(w_obj, cbytes, numbytes, 1, 1) # little endian, signed
+            rdigits = _cpylongarray_to_bigintarray(cbytes, numbytes, numbits, sign)
+            return rbigint(rdigits, sign)
+        elif self.is_true(self.isinstance(w_obj, self.w_int)):
+            value = self.int_w(w_obj)
+            return rbigint.fromint(value)
+        else:
+            raise OperationError(self.w_TypeError, self.wrap("Expected type int or long"))
+
     def len(self, w_obj):
         return self.wrap(PyObject_Size(w_obj))
 
             w_res = w_res.force()
         return w_res
 
+def _cpylongarray_to_bigintarray(cbytes, numbytes, numbits, sign):
+    """
+     helper function to convert an array of bytes from a cpython long to
+     the 15 bit digits needed by the rbigint implementation.
+    """
+    # convert from 2's complement to unsigned 
+    if sign == -1:
+        add = 1
+        for i in xrange(numbytes):
+            cbytes[i] = (cbytes[i] ^ 0xFF) + add
+            if add and cbytes[i] != 0:
+                add = 0
+            elif add and cbytes[i] == 0:
+                cbytes[i] == 0xFF
+    # convert 8 bit digits from cpython into 15 bit digits for rbigint
+    rdigits = []
+    digitbits = 0
+    usedbits = r_uint(0)    # XXX: Will break on platforms where size_t > uint
+    digit = 0
+    for i in xrange(numbytes):
+        cdigit = cbytes[i]
+        digit |= (cdigit << digitbits)
+        digitbits += 8
+        usedbits += 8
+        if digitbits >= 15: # XXX: 15 should be the same as rbigint.SHIFT
+            rdigits.append(digit & 0x7FFF) # need to mask off rbigint.SHIFT bits
+            digit = 0
+            digitbits = digitbits-15
+            usedbits -= digitbits
+            if digitbits > 0 and usedbits < numbits:
+                digit = cdigit >> (8-digitbits)
+                usedbits += digitbits
+            else:
+                # digitbits is either zero or we have used all the bits
+                # set it to zero so we don't append an extra rpython digit
+                digitbits = 0
+    if digitbits != 0:
+        rdigits.append(digit)
+    return rdigits
 
 # Register add, sub, neg, etc...
 for _name, _cname in UnaryOps.items() + BinaryOps.items():

pypy/objspace/cpy/test/test_objspace.py

 from pypy.objspace.cpy.objspace import CPyObjSpace
 from pypy.tool.pytest.appsupport import raises_w
 from pypy.rlib.rarithmetic import r_longlong, r_ulonglong
+from pypy.rlib.rbigint import rbigint
 
 def test_simple():
     space = CPyObjSpace()
     i2 = space.newint(42)
     assert space.is_true(space.eq(i1, i2))
     assert space.is_true(space.ne(space.type(i1), space.type(i2)))
+
+def test_bigint_w():
+    space = CPyObjSpace()
+    r1 = space.bigint_w(space.newlong(42))
+    assert isinstance(r1, rbigint)
+    assert r1.eq(rbigint.fromint(42))
+    # cpython digit size
+    assert space.bigint_w(space.newlong(2**8)).eq(rbigint.fromint(2**8))
+    # rpython digit size
+    assert space.bigint_w(space.newlong(2**15)).eq(rbigint.fromint(2**15))
+    # and negative numbers
+    assert space.bigint_w(space.newlong(-1)).eq(rbigint.fromint(-1))
+    assert space.bigint_w(space.newlong(-2**8)).eq(rbigint.fromint(-2**8))
+    assert space.bigint_w(space.newlong(-2**15)).eq(rbigint.fromlong(-2**15))
+

pypy/objspace/dump.py

         'int_w': 1,
         'float_w': 1,
         'uint_w': 1,
+        'bigint_w': 1,
         'interpclass_w': 1,
         'unwrap': 1,
         'is_true': 1,

pypy/objspace/logic.py

         'int_w': 1,
         'float_w': 1,
         'uint_w': 1,
+        'bigint_w': 1,
         'interpclass_w': 1,
         'unwrap': 1,
         'is_true': 1,

pypy/objspace/std/intobject.py

 from pypy.objspace.std.objspace import *
 from pypy.objspace.std.noneobject import W_NoneObject
 from pypy.rlib.rarithmetic import ovfcheck, ovfcheck_lshift, LONG_BIT, r_uint
+from pypy.rlib.rbigint import rbigint
 from pypy.objspace.std.inttype import wrapint
 
 """
     else:
         return r_uint(intval)
 
+def bigint_w__Int(space, w_int1):
+    return rbigint.fromint(w_int1.intval)
+
 def repr__Int(space, w_int1):
     a = w_int1.intval
     res = str(a)

pypy/objspace/std/longobject.py

         raise OperationError(space.w_OverflowError, space.wrap(
             "long int too large to convert to unsigned int"))
 
+def bigint_w__Long(space, w_value):
+    return w_value.num
+
 def repr__Long(space, w_long):
     return space.wrap(w_long.num.repr())
 

pypy/objspace/std/objspace.py

         str_w   = StdObjSpaceMultiMethod('str_w', 1, [])     # returns an unwrapped string
         float_w = StdObjSpaceMultiMethod('float_w', 1, [])   # returns an unwrapped float
         uint_w  = StdObjSpaceMultiMethod('uint_w', 1, [])    # returns an unwrapped unsigned int (r_uint)
+        bigint_w = StdObjSpaceMultiMethod('bigint_w', 1, []) # returns an unwrapped rbigint
         marshal_w = StdObjSpaceMultiMethod('marshal_w', 1, [], extra_args=['marshaller'])
         log     = StdObjSpaceMultiMethod('log', 1, [], extra_args=['base'])
 

pypy/objspace/std/test/test_intobject.py

 from pypy.objspace.std import intobject as iobj
 from pypy.objspace.std.objspace import FailedToImplement
 from pypy.rlib.rarithmetic import r_uint
+from pypy.rlib.rbigint import rbigint
 
 
 class TestW_IntObject:
         assert space.uint_w(space.wrap(42)) == 42
         assert isinstance(space.uint_w(space.wrap(42)), r_uint)
         space.raises_w(space.w_ValueError, space.uint_w, space.wrap(-1))
+
+    def test_bigint_w(self):
+        space = self.space
+        assert isinstance(space.bigint_w(space.wrap(42)), rbigint)
+        assert space.bigint_w(space.wrap(42)).eq(rbigint.fromint(42))
         
     def test_repr(self):
         x = 1

pypy/objspace/std/test/test_longobject.py

 from pypy.objspace.std.objspace import FailedToImplement
 from pypy.interpreter.error import OperationError
 from pypy.rlib.rarithmetic import r_uint
+from pypy.rlib.rbigint import rbigint
+
+class TestW_LongObject:
+
+    def test_bigint_w(self):
+        space = self.space
+        fromlong = lobj.W_LongObject.fromlong
+        assert isinstance(space.bigint_w(fromlong(42)), rbigint)
+        assert space.bigint_w(fromlong(42)).eq(rbigint.fromint(42))
+        assert space.bigint_w(fromlong(-1)).eq(rbigint.fromint(-1))
 
 class AppTestLong:
     def test_add(self):

pypy/objspace/thunk.py

         'int_w': 1,
         'float_w': 1,
         'uint_w': 1,
+        'bigint_w': 1,
         'interpclass_w': 1,
         'unwrap': 1,
         'is_true': 1,