Author: Alex Gaynor <alex.gay...@gmail.com> Branch: Changeset: r59211:4a2bb783d9a6 Date: 2012-12-02 08:13 -0800 http://bitbucket.org/pypy/pypy/changeset/4a2bb783d9a6/
Log: merged upstream diff --git a/pypy/module/_random/interp_random.py b/pypy/module/_random/interp_random.py --- a/pypy/module/_random/interp_random.py +++ b/pypy/module/_random/interp_random.py @@ -3,7 +3,7 @@ from pypy.interpreter.gateway import interp2app, unwrap_spec from pypy.interpreter.baseobjspace import Wrappable from pypy.rlib.rarithmetic import r_uint, intmask -from pypy.rlib import rrandom +from pypy.rlib import rbigint, rrandom, rstring import time @@ -89,25 +89,21 @@ strerror = space.wrap("number of bits must be greater than zero") raise OperationError(space.w_ValueError, strerror) bytes = ((k - 1) // 32 + 1) * 4 - bytesarray = [0] * bytes + bytesarray = rstring.StringBuilder(bytes) for i in range(0, bytes, 4): r = self._rnd.genrand32() if k < 32: r >>= (32 - k) - bytesarray[i + 0] = r & r_uint(0xff) - bytesarray[i + 1] = (r >> 8) & r_uint(0xff) - bytesarray[i + 2] = (r >> 16) & r_uint(0xff) - bytesarray[i + 3] = (r >> 24) & r_uint(0xff) + bytesarray.append(chr(r & r_uint(0xff))) + bytesarray.append(chr((r >> 8) & r_uint(0xff))) + bytesarray.append(chr((r >> 16) & r_uint(0xff))) + bytesarray.append(chr((r >> 24) & r_uint(0xff))) k -= 32 - # XXX so far this is quadratic - w_result = space.newint(0) - w_eight = space.newint(8) - for i in range(len(bytesarray) - 1, -1, -1): - byte = bytesarray[i] - w_result = space.or_(space.lshift(w_result, w_eight), - space.newint(intmask(byte))) - return w_result + # little endian order to match bytearray assignment order + result = rbigint.rbigint.frombytes( + bytesarray.build(), 'little', signed=False) + return space.newlong_from_rbigint(result) W_Random.typedef = TypeDef("Random", diff --git a/pypy/objspace/fake/objspace.py b/pypy/objspace/fake/objspace.py --- a/pypy/objspace/fake/objspace.py +++ b/pypy/objspace/fake/objspace.py @@ -143,6 +143,9 @@ def newcomplex(self, x, y): return w_some_obj() + def newlong_from_rbigint(self, x): + return w_some_obj() + def marshal_w(self, w_obj): "NOT_RPYTHON" raise NotImplementedError 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): """ @@ -1122,85 +1241,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 @@ -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) + diff --git a/pypy/tool/option.py b/pypy/tool/option.py --- a/pypy/tool/option.py +++ b/pypy/tool/option.py @@ -10,7 +10,8 @@ def get_standard_options(): config = get_pypy_config() - parser = to_optparse(config, extra_useage=extra_useage) + parser = to_optparse(config, useoptions=["objspace.*"], + extra_useage=extra_useage) return config, parser def process_options(parser, argv=None): _______________________________________________ pypy-commit mailing list pypy-commit@python.org http://mail.python.org/mailman/listinfo/pypy-commit