Author: Amaury Forgeot d'Arc <[email protected]>
Branch:
Changeset: r59206:5e1817833c19
Date: 2012-12-02 00:31 +0100
http://bitbucket.org/pypy/pypy/changeset/5e1817833c19/
Log: Backport frombytes() and tobytes() from the py3k branch.
diff --git a/pypy/rlib/rbigint.py b/pypy/rlib/rbigint.py
--- a/pypy/rlib/rbigint.py
+++ b/pypy/rlib/rbigint.py
@@ -2,6 +2,7 @@
from pypy.rlib.rarithmetic import ovfcheck, r_longlong, widen, is_valid_int
from pypy.rlib.rarithmetic import most_neg_value_of_same_type
from pypy.rlib.rfloat import isinf, isnan
+from pypy.rlib.rstring import StringBuilder
from pypy.rlib.debug import make_sure_not_resized, check_regular_int
from pypy.rlib.objectmodel import we_are_translated, specialize
from pypy.rlib import jit
@@ -11,6 +12,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 +96,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):
@@ -261,6 +269,117 @@
# then modify the result.
return _decimalstr_to_bigint(s)
+ @staticmethod
+ def frombytes(s, byteorder, signed):
+ if byteorder not in ('big', '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 = []
+ 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)))
+ result = rbigint(digits[:], sign)
+ result._normalize()
+ return result
+
+ @jit.elidable
+ def tobytes(self, nbytes, byteorder, signed):
+ if byteorder not in ('big', '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
+ result = StringBuilder(nbytes)
+ 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
+
+ result.append(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;
+
+ result.append(chr(accum & 0xFF))
+
+ if j < nbytes:
+ signbyte = 0xFF if self.sign == -1 else 0
+ result.append_multiple_char(chr(signbyte), nbytes - j)
+
+ digits = result.build()
+
+ if j == nbytes and nbytes > 0 and signed:
+ # If not already set, we cannot contain the sign bit
+ msb = digits[-1]
+ if (self.sign == -1) != (ord(msb) >= 0x80):
+ raise OverflowError()
+
+ if bswap:
+ # Bah, this is very inefficient. At least it's not
+ # quadratic.
+ length = len(digits)
+ if length >= 0:
+ digits = ''.join([digits[i] for i in range(length-1, -1, -1)])
+ return digits
+
@jit.elidable
def toint(self):
"""
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
@@ -9,7 +9,7 @@
from pypy.rlib import rbigint as lobj
from pypy.rlib.rarithmetic import r_uint, r_longlong, r_ulonglong, intmask
from pypy.rlib.rbigint import (rbigint, SHIFT, MASK, KARATSUBA_CUTOFF,
- _store_digit, _mask_digit)
+ _store_digit, _mask_digit, InvalidEndiannessError, InvalidSignednessError)
from pypy.rlib.rfloat import NAN
from pypy.rpython.test.test_llinterp import interpret
@@ -769,3 +769,31 @@
res = interpret(fn, [])
assert res == -42.0
+
+ def test_frombytes(self):
+ s = "\xFF\x12\x34\x56"
+ 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(16, byteorder="big", signed=False)
+ raises(InvalidEndiannessError, bigint.frombytes, '\xFF', 'foo',
+ signed=True)
+
+ def test_tobytes(self):
+ assert rbigint.fromint(0).tobytes(1, 'big', signed=True) == '\x00'
+ assert rbigint.fromint(1).tobytes(2, 'big', signed=True) == '\x00\x01'
+ raises(OverflowError, rbigint.fromint(255).tobytes, 1, 'big',
signed=True)
+ assert rbigint.fromint(-129).tobytes(2, 'big', signed=True) ==
'\xff\x7f'
+ assert rbigint.fromint(-129).tobytes(2, 'little', signed=True) ==
'\x7f\xff'
+ assert rbigint.fromint(65535).tobytes(3, 'big', signed=True) ==
'\x00\xff\xff'
+ assert rbigint.fromint(-65536).tobytes(3, 'little', signed=True) ==
'\x00\x00\xff'
+ assert rbigint.fromint(65535).tobytes(2, 'big', signed=False) ==
'\xff\xff'
+ assert rbigint.fromint(-8388608).tobytes(3, 'little', signed=True) ==
'\x00\x00\x80'
+ i = rbigint.fromint(-8388608)
+ raises(InvalidEndiannessError, i.tobytes, 3, 'foo', signed=True)
+ raises(InvalidSignednessError, i.tobytes, 3, 'little', signed=False)
+ raises(OverflowError, i.tobytes, 2, 'little', signed=True)
+
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit