Author: Philip Jenvey <[email protected]>
Branch: py3k
Changeset: r58692:74d461f1edfd
Date: 2012-11-02 17:08 -0700
http://bitbucket.org/pypy/pypy/changeset/74d461f1edfd/
Log: add int.from/to_bytes patch from Kenny Levinsen (thanks!)
diff --git a/pypy/objspace/std/longtype.py b/pypy/objspace/std/longtype.py
--- a/pypy/objspace/std/longtype.py
+++ b/pypy/objspace/std/longtype.py
@@ -5,7 +5,7 @@
from pypy.objspace.std.register_all import register_all
from pypy.objspace.std.stdtypedef import StdTypeDef, SMM
from pypy.objspace.std.strutil import string_to_bigint, ParseStringError
-from pypy.rlib.rbigint import rbigint
+from pypy.rlib.rbigint import rbigint, InvalidEndiannessError,
InvalidSignednessError
def descr_conjugate(space, w_int):
return space.int(w_int)
@@ -119,13 +119,33 @@
raise OperationError(space.w_OverflowError,
space.wrap("too many digits in integer"))
-@unwrap_spec(s='bufferstr', byteorder=str)
-def descr_from_bytes(space, w_cls, s, byteorder):
- bigint = rbigint.frombytes(s)
- from pypy.objspace.std.longobject import W_LongObject
- w_obj = space.allocate_instance(W_LongObject, w_cls)
- W_LongObject.__init__(w_obj, bigint)
- return w_obj
+@unwrap_spec(s='bufferstr', byteorder=str, signed=bool)
+def descr_from_bytes(space, w_cls, s, byteorder, signed):
+ try:
+ bigint = rbigint.frombytes(s, byteorder=byteorder, signed=signed)
+ except InvalidEndiannessError:
+ raise OperationError(
+ space.w_ValueError,
+ space.wrap("byteorder must be either 'little' or 'big'"))
+ return newbigint(space, w_cls, bigint)
+
+@unwrap_spec(nbytes=int, byteorder=str, signed=bool)
+def descr_to_bytes(space, w_obj, nbytes, byteorder, signed):
+ try:
+ byte_string = space.bigint_w(w_obj).tobytes(nbytes,
byteorder=byteorder, signed=signed)
+ except InvalidEndiannessError:
+ raise OperationError(
+ space.w_ValueError,
+ space.wrap("byteorder must be either 'little' or 'big'"))
+ except InvalidSignednessError:
+ raise OperationError(
+ space.w_OverflowError,
+ space.wrap("can't convert negative int to unsigned"))
+ except OverflowError:
+ raise OperationError(
+ space.w_OverflowError,
+ space.wrap('int too big to convert'))
+ return space.wrapbytes(byte_string)
divmod_near = applevel('''
def divmod_near(a, b):
@@ -198,5 +218,6 @@
imag = typedef.GetSetProperty(descr_get_imag),
bit_length = interp2app(bit_length),
from_bytes = interp2app(descr_from_bytes, as_classmethod=True),
+ to_bytes = interp2app(descr_to_bytes)
)
long_typedef.registermethods(globals())
diff --git a/pypy/rlib/rbigint.py b/pypy/rlib/rbigint.py
--- a/pypy/rlib/rbigint.py
+++ b/pypy/rlib/rbigint.py
@@ -11,6 +11,7 @@
import math, sys
SUPPORT_INT128 = hasattr(rffi, '__INT128_T')
+BYTEORDER = sys.byteorder
# note about digit sizes:
# In division, the native integer type must be able to hold
@@ -94,6 +95,12 @@
assert type(x) is type(NULLDIGIT)
assert UDIGIT_MASK(x) & MASK == UDIGIT_MASK(x)
+class InvalidEndiannessError(Exception):
+ pass
+
+class InvalidSignednessError(Exception):
+ pass
+
class Entry(extregistry.ExtRegistryEntry):
_about_ = _check_digits
def compute_result_annotation(self, s_list):
@@ -262,22 +269,121 @@
return _decimalstr_to_bigint(s)
@staticmethod
- def frombytes(s):
- accum = 0
+ def frombytes(s, byteorder, signed):
+ if byteorder != "big" and byteorder != "little":
+ raise InvalidEndiannessError()
+
+ if byteorder != BYTEORDER:
+ msb = ord(s[0])
+ itr = range(len(s)-1, -1, -1)
+ else:
+ msb = ord(s[-1])
+ itr = range(0, len(s))
+
+ sign = -1 if msb >= 0x80 and signed else 1
+ accum = _widen_digit(0)
accumbits = 0
digits = []
- for ch in s:
- c = ord(ch)
- accum <<= 8
- accum |= c
+ carry = 1
+
+ for i in itr:
+ c = _widen_digit(ord(s[i]))
+ if sign == -1:
+ c = (0xFF ^ c) + carry
+ carry = c >> 8
+ c &= 0xFF
+
+ accum |= c << accumbits
accumbits += 8
if accumbits >= SHIFT:
digits.append(_store_digit(intmask(accum & MASK)))
accum >>= SHIFT
accumbits -= SHIFT
+
if accumbits:
digits.append(_store_digit(intmask(accum)))
- return rbigint(digits[:], 1)
+ return rbigint(digits[:], sign)
+
+ @jit.elidable
+ def tobytes(self, nbytes, byteorder, signed):
+ if byteorder != "big" and byteorder != "little":
+ raise InvalidEndiannessError()
+ if not signed and self.sign == -1:
+ raise InvalidSignednessError()
+
+ bswap = byteorder != BYTEORDER
+ d = _widen_digit(0)
+ j = 0
+ imax = self.numdigits()
+ accum = _widen_digit(0)
+ accumbits = 0
+ digits = ''
+ carry = 1
+
+ for i in range(0, imax):
+ d = self.widedigit(i)
+ if self.sign == -1:
+ d = (d ^ MASK) + carry
+ carry = d >> SHIFT
+ d &= MASK
+
+ accum |= d << accumbits
+ if i == imax - 1:
+ # Avoid bogus 0's
+ s = d ^ MASK if self.sign == -1 else d
+ while s:
+ s >>=1
+ accumbits += 1
+ else:
+ accumbits += SHIFT
+
+ while accumbits >= 8:
+ if j >= nbytes:
+ raise OverflowError()
+ j += 1
+
+ if bswap:
+ digits = chr(accum & 0xFF) + digits
+ else:
+ digits += chr(accum & 0xFF)
+ accum >>= 8
+ accumbits -= 8
+
+ if accumbits:
+ if j >= nbytes:
+ raise OverflowError()
+ j += 1
+
+ if self.sign == -1:
+ # Add a sign bit
+ accum |= (~_widen_digit(0)) << accumbits;
+
+ if bswap:
+ digits = chr(accum & 0xFF) + digits
+ else:
+ digits += chr(accum & 0xFF)
+
+ elif j == nbytes and nbytes > 0 and signed:
+ # If not already set, we cannot contain the sign bit
+ assert len(digits) > 0
+ if bswap:
+ msb = digits[0]
+ else:
+ msb = digits[-1]
+
+ if (self.sign == -1) != (ord(msb) >= 0x80):
+ raise OverflowError()
+
+ signbyte = 0xFF if self.sign == -1 else 0
+ while j < nbytes:
+ # Set INFINITE signbits!
+ if bswap:
+ digits = chr(signbyte) + digits
+ else:
+ digits += chr(signbyte)
+ j += 1
+
+ return digits
@jit.elidable
def toint(self):
@@ -1134,85 +1240,6 @@
z._normalize()
return z
-def _x_mul(a, b, digit=0):
- """
- Grade school multiplication, ignoring the signs.
- Returns the absolute value of the product, or None if error.
- """
-
- size_a = a.numdigits()
- size_b = b.numdigits()
-
- if a is b:
- # Efficient squaring per HAC, Algorithm 14.16:
- # http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf
- # Gives slightly less than a 2x speedup when a == b,
- # via exploiting that each entry in the multiplication
- # pyramid appears twice (except for the size_a squares).
- z = rbigint([NULLDIGIT] * (size_a + size_b), 1)
- i = UDIGIT_TYPE(0)
- while i < size_a:
- f = a.widedigit(i)
- pz = i << 1
- pa = i + 1
-
- carry = z.widedigit(pz) + f * f
- z.setdigit(pz, carry)
- pz += 1
- carry >>= SHIFT
- assert carry <= MASK
-
- # Now f is added in twice in each column of the
- # pyramid it appears. Same as adding f<<1 once.
- f <<= 1
- while pa < size_a:
- carry += z.widedigit(pz) + a.widedigit(pa) * f
- pa += 1
- z.setdigit(pz, carry)
- pz += 1
- carry >>= SHIFT
- if carry:
- carry += z.widedigit(pz)
- z.setdigit(pz, carry)
- pz += 1
- carry >>= SHIFT
- if carry:
- z.setdigit(pz, z.widedigit(pz) + carry)
- assert (carry >> SHIFT) == 0
- i += 1
- z._normalize()
- return z
-
- elif digit:
- if digit & (digit - 1) == 0:
- return b.lqshift(ptwotable[digit])
-
- # Even if it's not power of two it can still be useful.
- return _muladd1(b, digit)
-
- z = rbigint([NULLDIGIT] * (size_a + size_b), 1)
- # gradeschool long mult
- i = UDIGIT_TYPE(0)
- while i < size_a:
- carry = 0
- f = a.widedigit(i)
- pz = i
- pb = 0
- while pb < size_b:
- carry += z.widedigit(pz) + b.widedigit(pb) * f
- pb += 1
- z.setdigit(pz, carry)
- pz += 1
- carry >>= SHIFT
- assert carry <= MASK
- if carry:
- assert pz >= 0
- z.setdigit(pz, z.widedigit(pz) + carry)
- assert (carry >> SHIFT) == 0
- i += 1
- z._normalize()
- return z
-
def _kmul_split(n, size):
"""
A helper for Karatsuba multiplication (k_mul).
diff --git a/pypy/rlib/test/test_rbigint.py b/pypy/rlib/test/test_rbigint.py
--- a/pypy/rlib/test/test_rbigint.py
+++ b/pypy/rlib/test/test_rbigint.py
@@ -765,5 +765,10 @@
def test_frombytes(self):
s = "\xFF\x12\x34\x56"
- bigint = rbigint.frombytes(s)
+ bigint = rbigint.frombytes(s, byteorder="big", signed=False)
assert bigint.tolong() == 0xFF123456
+ bigint = rbigint.frombytes(s, byteorder="little", signed=False)
+ assert bigint.tolong() == 0x563412FF
+ s = "\xFF\x02\x03\x04\x05\x06\x07\x08\x09\x10\x11\x12\x13\x14\x15\xFF"
+ bigint = rbigint.frombytes(s, byteorder="big", signed=False)
+ assert s == bigint.tobytes(byteorder="big", signed=False)
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit