diff options
author | 2023-09-27 15:46:49 +0300 | |
---|---|---|
committer | 2023-09-27 15:46:49 +0300 | |
commit | d74e54767b5aff6f158b14815a84fd03d39ceb2f (patch) | |
tree | 7d99a505520640b63a9967a0b072c7ce344c2229 | |
parent | merge py3.9 (diff) | |
parent | merge default (diff) | |
download | pypy-d74e54767b5aff6f158b14815a84fd03d39ceb2f.tar.gz pypy-d74e54767b5aff6f158b14815a84fd03d39ceb2f.tar.bz2 pypy-d74e54767b5aff6f158b14815a84fd03d39ceb2f.zip |
merge py3.9, ignoring stdlib updates
-rw-r--r-- | lib-python/3/ssl.py | 35 | ||||
-rw-r--r-- | lib-python/3/tarfile.py | 11 | ||||
-rw-r--r-- | lib_pypy/_cffi_ssl/_stdssl/__init__.py | 24 | ||||
-rw-r--r-- | lib_pypy/_cffi_ssl/_stdssl/certificate.py | 2 | ||||
-rw-r--r-- | lib_pypy/_testcapimodule.c | 39 | ||||
-rw-r--r-- | pypy/doc/conf.py | 2 | ||||
-rw-r--r-- | pypy/doc/release-v7.3.13.rst | 178 | ||||
-rw-r--r-- | pypy/module/_cffi_backend/cdlopen.py | 2 | ||||
-rw-r--r-- | pypy/module/cpyext/include/patchlevel.h | 4 | ||||
-rw-r--r-- | pypy/module/cpyext/longobject.py | 28 | ||||
-rw-r--r-- | pypy/module/cpyext/number.py | 2 | ||||
-rw-r--r-- | pypy/module/cpyext/test/test_longobject.py | 39 | ||||
-rw-r--r-- | pypy/module/pypyjit/test_pypy_c/test_misc.py | 3 | ||||
-rw-r--r-- | pypy/module/pypyjit/test_pypy_c/test_string.py | 6 | ||||
-rw-r--r-- | pypy/module/sys/version.py | 2 | ||||
-rw-r--r-- | pypy/objspace/std/longobject.py | 14 | ||||
-rw-r--r-- | pypy/objspace/std/marshal_impl.py | 2 | ||||
-rw-r--r-- | rpython/doc/conf.py | 2 | ||||
-rw-r--r-- | rpython/rlib/rbigint.py | 343 | ||||
-rw-r--r-- | rpython/rlib/test/test_rbigint.py | 5 |
20 files changed, 553 insertions, 190 deletions
diff --git a/lib-python/3/ssl.py b/lib-python/3/ssl.py index c115fbb2aa..5cea032a0d 100644 --- a/lib-python/3/ssl.py +++ b/lib-python/3/ssl.py @@ -1033,7 +1033,7 @@ class SSLSocket(socket): ) self = cls.__new__(cls, **kwargs) super(SSLSocket, self).__init__(**kwargs) - self.settimeout(sock.gettimeout()) + sock_timeout = sock.gettimeout() sock.detach() self._context = context @@ -1052,9 +1052,42 @@ class SSLSocket(socket): if e.errno != errno.ENOTCONN: raise connected = False + blocking = self.getblocking() + self.setblocking(False) + try: + # We are not connected so this is not supposed to block, but + # testing revealed otherwise on macOS and Windows so we do + # the non-blocking dance regardless. Our raise when any data + # is found means consuming the data is harmless. + notconn_pre_handshake_data = self.recv(1) + except OSError as e: + # EINVAL occurs for recv(1) on non-connected on unix sockets. + if e.errno not in (errno.ENOTCONN, errno.EINVAL): + raise + notconn_pre_handshake_data = b'' + self.setblocking(blocking) + if notconn_pre_handshake_data: + # This prevents pending data sent to the socket before it was + # closed from escaping to the caller who could otherwise + # presume it came through a successful TLS connection. + reason = "Closed before TLS handshake with data in recv buffer." + notconn_pre_handshake_data_error = SSLError(e.errno, reason) + # Add the SSLError attributes that _ssl.c always adds. + notconn_pre_handshake_data_error.reason = reason + notconn_pre_handshake_data_error.library = None + try: + self.close() + except OSError: + pass + try: + raise notconn_pre_handshake_data_error + finally: + # Explicitly break the reference cycle. + notconn_pre_handshake_data_error = None else: connected = True + self.settimeout(sock_timeout) # Must come after setblocking() calls. self._connected = connected if connected: # create the SSL object diff --git a/lib-python/3/tarfile.py b/lib-python/3/tarfile.py index 40599f27bc..495349f08f 100644 --- a/lib-python/3/tarfile.py +++ b/lib-python/3/tarfile.py @@ -741,7 +741,7 @@ class SpecialFileError(FilterError): class AbsoluteLinkError(FilterError): def __init__(self, tarinfo): self.tarinfo = tarinfo - super().__init__(f'{tarinfo.name!r} is a symlink to an absolute path') + super().__init__(f'{tarinfo.name!r} is a link to an absolute path') class LinkOutsideDestinationError(FilterError): def __init__(self, tarinfo, path): @@ -801,7 +801,14 @@ def _get_filtered_attrs(member, dest_path, for_data=True): if member.islnk() or member.issym(): if os.path.isabs(member.linkname): raise AbsoluteLinkError(member) - target_path = os.path.realpath(os.path.join(dest_path, member.linkname)) + if member.issym(): + target_path = os.path.join(dest_path, + os.path.dirname(name), + member.linkname) + else: + target_path = os.path.join(dest_path, + member.linkname) + target_path = os.path.realpath(target_path) if os.path.commonpath([target_path, dest_path]) != dest_path: raise LinkOutsideDestinationError(member, target_path) return new_attrs diff --git a/lib_pypy/_cffi_ssl/_stdssl/__init__.py b/lib_pypy/_cffi_ssl/_stdssl/__init__.py index 9e0c472318..4c579c9e2a 100644 --- a/lib_pypy/_cffi_ssl/_stdssl/__init__.py +++ b/lib_pypy/_cffi_ssl/_stdssl/__init__.py @@ -1,5 +1,6 @@ -import sys import os +import pathlib +import sys import time import _thread import warnings @@ -1007,11 +1008,15 @@ class _SSLSocket(object): def _fs_decode(name): return name.decode(sys.getfilesystemencoding()) -def _fs_converter(name): +def _fs_converter(name, argname): """ name must not be None """ + if isinstance(name, pathlib.Path): + name = str(name) if isinstance(name, str): return name.encode(sys.getfilesystemencoding()) - return bytes(name) + elif isinstance(name, bytes): + return bytes(name) + raise TypeError(f"{argname} should be a valid filesystem path") def cipher_to_tuple(cipher): @@ -1461,7 +1466,7 @@ class _SSLContext(object): prev_errno = ffi.errno try: ffi.errno = 0 - certfilebuf = _str_to_ffi_buffer(certfile) + certfilebuf = _str_to_ffi_buffer(_fs_converter(certfile, "certfile")) ret = lib.SSL_CTX_use_certificate_chain_file(self.ctx, certfilebuf) if ret != 1: if pw_info.operationerror: @@ -1475,7 +1480,7 @@ class _SSLContext(object): raise ssl_error(None) ffi.errno = 0 - buf = _str_to_ffi_buffer(keyfile) + buf = _str_to_ffi_buffer(_fs_converter(keyfile, "keyfile")) ret = lib.SSL_CTX_use_PrivateKey_file(self.ctx, buf, lib.SSL_FILETYPE_PEM) if ret != 1: @@ -1531,14 +1536,15 @@ class _SSLContext(object): # load cafile or capath if cafile is not None or capath is not None: + print(cafile, capath) if cafile is None: cafilebuf = ffi.NULL else: - cafilebuf = _str_to_ffi_buffer(cafile) + cafilebuf = _str_to_ffi_buffer(_fs_converter(cafile, "cafile")) if capath is None: capathbuf = ffi.NULL else: - capathbuf = _str_to_ffi_buffer(capath) + capathbuf = _str_to_ffi_buffer(_fs_converter(capath, "capath")) ret = lib.SSL_CTX_load_verify_locations(self.ctx, cafilebuf, capathbuf) if ret != 1: _errno = ffi.errno @@ -1705,7 +1711,7 @@ class _SSLContext(object): ffi.errno = 0 if filepath is None: raise TypeError("filepath must not be None") - buf = _fs_converter(filepath) + buf = _fs_converter(filepath, "filepath") mode = ffi.new("char[]",b"rb") ffi.errno = 0 bio = lib.BIO_new_file(buf, mode) @@ -1758,7 +1764,7 @@ class _SSLContext(object): # needs to be zero terminated if name is None: raise TypeError() - buf = _fs_converter(name) + buf = _fs_converter(name, "name") nid = lib.OBJ_sn2nid(buf) if nid == 0: raise ValueError("unknown elliptic curve name '%s'" % name) diff --git a/lib_pypy/_cffi_ssl/_stdssl/certificate.py b/lib_pypy/_cffi_ssl/_stdssl/certificate.py index cba387a63c..283d9cbe05 100644 --- a/lib_pypy/_cffi_ssl/_stdssl/certificate.py +++ b/lib_pypy/_cffi_ssl/_stdssl/certificate.py @@ -399,7 +399,7 @@ def _test_decode_cert(path): lib.BIO_free(cert) raise ssl_error("Can't malloc memory to read file") try: - epath = path.encode() + epath = str(path).encode() if lib.BIO_read_filename(cert, epath) <= 0: raise ssl_error("Can't open file") diff --git a/lib_pypy/_testcapimodule.c b/lib_pypy/_testcapimodule.c index b2274f8b1a..6bffdf6dac 100644 --- a/lib_pypy/_testcapimodule.c +++ b/lib_pypy/_testcapimodule.c @@ -2116,6 +2116,42 @@ unicode_asutf8andsize(PyObject *self, PyObject *args) return Py_BuildValue("(Nn)", result, utf8_len); } +/* Test PyUnicode_DecodeUTF8() */ +static PyObject * +unicode_decodeutf8(PyObject *self, PyObject *args) +{ + const char *data; + Py_ssize_t size; + const char *errors = NULL; + + if (!PyArg_ParseTuple(args, "y#|z", &data, &size, &errors)) + return NULL; + + return PyUnicode_DecodeUTF8(data, size, errors); +} + +#ifndef PYPY_VERSION +/* Test PyUnicode_DecodeUTF8Stateful() */ +static PyObject * +unicode_decodeutf8stateful(PyObject *self, PyObject *args) +{ + const char *data; + Py_ssize_t size; + const char *errors = NULL; + Py_ssize_t consumed = 123456789; + PyObject *result; + + if (!PyArg_ParseTuple(args, "y#|z", &data, &size, &errors)) + return NULL; + + result = PyUnicode_DecodeUTF8Stateful(data, size, errors, &consumed); + if (!result) { + return NULL; + } + return Py_BuildValue("(Nn)", result, consumed); +} +#endif + static PyObject * unicode_findchar(PyObject *self, PyObject *args) { @@ -5905,8 +5941,9 @@ static PyMethodDef TestMethods[] = { {"unicode_asucs4", unicode_asucs4, METH_VARARGS}, {"unicode_asutf8", unicode_asutf8, METH_VARARGS}, {"unicode_asutf8andsize", unicode_asutf8andsize, METH_VARARGS}, - {"unicode_findchar", unicode_findchar, METH_VARARGS}, + {"unicode_decodeutf8", unicode_decodeutf8, METH_VARARGS}, #ifndef PYPY_VERSION + {"unicode_decodeutf8stateful",unicode_decodeutf8stateful, METH_VARARGS}, {"unicode_findchar", unicode_findchar, METH_VARARGS}, {"unicode_copycharacters", unicode_copycharacters, METH_VARARGS}, #endif #if USE_UNICODE_WCHAR_CACHE diff --git a/pypy/doc/conf.py b/pypy/doc/conf.py index 55b562c8ff..bfe2e309ed 100644 --- a/pypy/doc/conf.py +++ b/pypy/doc/conf.py @@ -89,7 +89,7 @@ copyright = u'2023, The PyPy Project' # The short X.Y version. version = '7.3' # The full version, including alpha/beta/rc tags. -release = '7.3.13alpha' +release = '7.3.14' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/pypy/doc/release-v7.3.13.rst b/pypy/doc/release-v7.3.13.rst new file mode 100644 index 0000000000..e97213eda6 --- /dev/null +++ b/pypy/doc/release-v7.3.13.rst @@ -0,0 +1,178 @@ +================================================== +PyPy v7.3.13: release of python 2.7, 3.9, and 3.10 +================================================== + + +The PyPy team is proud to release version 7.3.13 of PyPy. +This is primarily a bug-fix release. CPython released security patches, and +this release also improves the ability to use multi-phase module initialization +from the C-API. + +The release includes three different interpreters: + + - PyPy2.7, which is an interpreter supporting the syntax and the features of + Python 2.7 including the stdlib for CPython 2.7.18+ (the ``+`` is for + backported security updates) + + - PyPy3.9, which is an interpreter supporting the syntax and the features of + Python 3.9, including the stdlib for CPython 3.9.18. + + - PyPy3.10, which is an interpreter supporting the syntax and the features of + Python 3.10, including the stdlib for CPython 3.10.13. Note it requires at + least cython 0.29.35 or cython 3.0.0b3 + +The interpreters are based on much the same codebase, thus the multiple +release. This is a micro release, all APIs are compatible with the other 7.3 +releases. It follows after 7.3.12 release on June 16, 2023. + +We recommend updating. You can find links to download the v7.3.13 releases here: + + https://pypy.org/download.html + +We would like to thank our donors for the continued support of the PyPy +project. If PyPy is not quite good enough for your needs, we are available for +`direct consulting`_ work. If PyPy is helping you out, we would love to hear about +it and encourage submissions to our blog_ via a pull request +to https://github.com/pypy/pypy.org + +We would also like to thank our contributors and encourage new people to join +the project. PyPy has many layers and we need help with all of them: bug fixes, +`PyPy`_ and `RPython`_ documentation improvements, or general `help`_ with making +RPython's JIT even better. + +If you are a python library maintainer and use C-extensions, please consider +making a HPy_ / CFFI_ / cppyy_ version of your library that would be performant +on PyPy. In any case, both `cibuildwheel`_ and the `multibuild system`_ support +building wheels for PyPy. + +.. _`PyPy`: index.html +.. _`RPython`: https://rpython.readthedocs.org +.. _`help`: project-ideas.html +.. _CFFI: https://cffi.readthedocs.io +.. _cppyy: https://cppyy.readthedocs.io +.. _`multibuild system`: https://github.com/matthew-brett/multibuild +.. _`cibuildwheel`: https://github.com/joerick/cibuildwheel +.. _blog: https://pypy.org/blog +.. _HPy: https://hpyproject.org/ +.. _was sponsored: https://www.pypy.org/posts/2022/07/m1-support-for-pypy.html +.. _direct consulting: https://www.pypy.org/pypy-sponsors.html +.. _has built: https://www.pypy.org/posts/2022/11/pypy-and-conda-forge.html + +What is PyPy? +============= + +PyPy is a Python interpreter, a drop-in replacement for CPython +It's fast (`PyPy and CPython 3.7.4`_ performance +comparison) due to its integrated tracing JIT compiler. + +We also welcome developers of other `dynamic languages`_ to see what RPython +can do for them. + +We provide binary builds for: + + * **x86** machines on most common operating systems + (Linux 32/64 bits, Mac OS 64 bits, Windows 64 bits) + + * 64-bit **ARM** machines running Linux (``aarch64``). + + * Apple **M1 arm64** machines (``macos_arm64``). + + * **s390x** running Linux + +PyPy support Windows 32-bit, Linux PPC64 big- and little-endian, and Linux ARM +32 bit, but does not release binaries. Please reach out to us if you wish to +sponsor binary releases for those platforms. Downstream packagers provide +binary builds for debian, Fedora, conda, OpenBSD, FreeBSD, Gentoo, and more. + +.. _`PyPy and CPython 3.7.4`: https://speed.pypy.org +.. _`dynamic languages`: https://rpython.readthedocs.io/en/latest/examples.html + +Changelog +========= + +For all versions +---------------- +- Update to ssl 1.1.1w, or 3.0.10 when embedding + libraries for a portable build + +Bugfixes +~~~~~~~~ +- Report exceptions that are created when forcing an oefmt as unraisable (issue + _3978) + +Speedups and enhancements +~~~~~~~~~~~~~~~~~~~~~~~~~ +- Do type-erasure for rpython dicts and lists more generally, unifying all gced + pointer types into one implementation (as opposed to just unifying all + *instances* together, but not eg strings). This makes pypy-c ~700KiB smaller. +- Stop allocating three lists of length 256 for the registers in the MIFrames + and the blackhole frames. Almost all jitcodes have much smaller frame sizes. + Seems to make tracing a little bit faster. +- Improve ``strip()`` to not force the object +- Make sure that ``sys.flags`` are not in a cell in the sys module to speed up + access (especially important on py3.x, where every bytes.decode call checks + sys.flags.utf8_mode) +- Speed up the flowspace a lot for huge functions +- Make ``UnicodeIO`` store its data in a list of ``r_int32``, as opposed to + using the rpython (Python2.7) unicode type. we want to get rid of the unicode + type and also it requires an extra copy all the time. +- Make every ``rbigint`` one word smaller by storing the sign in the size field + +Python 3.9+ +----------- +- Create c-extension modules used in tests as part of the build (in + package.py), not as part of testing + +Bugfixes +~~~~~~~~ +- More selectively clear ``StopIteration`` exceptions on ``tp_iternext`` (issue + _3956) +- Copy less when creating a venv by using a ``PYPY_PORTABLE_DEPS.txt`` file to + state which dlls to copy in a portable build (issue _3611) +- On macos ``sendifle`` can return an error while sending part of the file + (issue _3964) +- Fixes on both app-level and C level for ``Py_TPFLAGS_BASETYPE=0`` (issue + _2742). Also set ``PyType_Type.tp_itemsize`` to ``sizeof(PyMemberDef)`` like + on CPython +- Fix ``PyType_FromSpecWithBases`` to correctly use ``Py_tp_doc``, + ``Py_tp_members`` in spec, fix ``__module__`` assignment, better handle + ``__name__`` and ``tp_name`` +- Hide ``functools`` wrappers from the stack when reporting warnings (issue + _3988) +- Fix edge case of datetime isoformat parsing (issue _3989) +- Accept NULL ``tp_doc`` (_bpo-41832) +- Align ``nb_int`` with ``PyNumber_Long`` (to get consistent error messages) +- Handle ``pathlib.Path`` objects in ``_ssl`` (issue _4002) +- Implement ``_PyLong_AsInt`` which is not part of the stable API but used in + testing + +Speedups and enhancements +~~~~~~~~~~~~~~~~~~~~~~~~~ +- Avoid compiling a new regex where not needed (in email, csv, and + elsewhere) (issue _3961) + +Python 3.10 +----------- + +Bugfixes +~~~~~~~~ +- Fix, test locking in HMAC update (issue _3962) +- When re-assigning to ``type.__bases__``, rebuild the cpyext type struct + (issue _3976) +- Add missing slot macro ``Py_am_send`` (issue _3990) + +.. _bpo-41832: https://bugs.python.org/issue41832 +.. _GH-100242: https://github.com/python/cpython/issues/100242 +.. _2742: https://foss.heptapod.net/pypy/pypy/-/issues/2742 +.. _3611: https://foss.heptapod.net/pypy/pypy/-/issues/3611 +.. _3956: https://foss.heptapod.net/pypy/pypy/-/issues/3956 +.. _3961: https://foss.heptapod.net/pypy/pypy/-/issues/3961 +.. _3962: https://foss.heptapod.net/pypy/pypy/-/issues/3962 +.. _3964: https://foss.heptapod.net/pypy/pypy/-/issues/3964 +.. _3976: https://foss.heptapod.net/pypy/pypy/-/issues/3976 +.. _3978: https://foss.heptapod.net/pypy/pypy/-/issues/3978 +.. _3988: https://foss.heptapod.net/pypy/pypy/-/issues/3988 +.. _3989: https://foss.heptapod.net/pypy/pypy/-/issues/3989 +.. _3990: https://foss.heptapod.net/pypy/pypy/-/issues/3990 +.. _4002: https://foss.heptapod.net/pypy/pypy/-/issues/4002 + diff --git a/pypy/module/_cffi_backend/cdlopen.py b/pypy/module/_cffi_backend/cdlopen.py index e204c0fc21..61d69fe9ff 100644 --- a/pypy/module/_cffi_backend/cdlopen.py +++ b/pypy/module/_cffi_backend/cdlopen.py @@ -157,7 +157,7 @@ def ffiobj_init(ffi, module_name, version, types, w_globals, ll_set_cdl_realize_global_int(nglobs[i]) bigint = space.bigint_w(w_integer) ullvalue = bigint.ulonglongmask() - rffi.setintfield(nintconsts[i], 'neg', int(bigint.sign <= 0)) + rffi.setintfield(nintconsts[i], 'neg', int(bigint.get_sign() <= 0)) rffi.setintfield(nintconsts[i], 'value', ullvalue) ffi.ctxobj.ctx.c_globals = nglobs rffi.setintfield(ffi.ctxobj.ctx, 'c_num_globals', n) diff --git a/pypy/module/cpyext/include/patchlevel.h b/pypy/module/cpyext/include/patchlevel.h index 10788e65fb..00698e1c00 100644 --- a/pypy/module/cpyext/include/patchlevel.h +++ b/pypy/module/cpyext/include/patchlevel.h @@ -32,8 +32,8 @@ * module/sys/version.py * doc/conf.py */ -#define PYPY_VERSION "7.3.13-alpha0" -#define PYPY_VERSION_NUM 0x07030d00 +#define PYPY_VERSION "7.3.14-alpha0" +#define PYPY_VERSION_NUM 0x07030e00 /* Defined to mean a PyPy where cpyext holds more regular references to PyObjects, e.g. staying alive as long as the internal PyPy object stays alive. */ diff --git a/pypy/module/cpyext/longobject.py b/pypy/module/cpyext/longobject.py index fd61909288..6ac86657d5 100644 --- a/pypy/module/cpyext/longobject.py +++ b/pypy/module/cpyext/longobject.py @@ -52,6 +52,8 @@ ULONG_MASK = (2 ** (8 * rffi.sizeof(rffi.ULONG)) -1) ULONG_MAX = (2 ** (8 * rffi.sizeof(rffi.ULONG)) -1) LONG_MAX = (2 ** (8 * rffi.sizeof(rffi.ULONG) - 1) -1) LONG_MIN = (-2 ** (8 * rffi.sizeof(rffi.ULONG) - 1)) +INT_MAX = (2 ** (8 * rffi.sizeof(rffi.UINT) - 1) -1) +INT_MIN = (-2 ** (8 * rffi.sizeof(rffi.UINT) - 1)) need_to_check = maxint > ULONG_MAX @cpython_api([PyObject], rffi.ULONG, error=-1) @@ -118,6 +120,30 @@ def PyLong_AsLong(space, w_long): "Python int too large to convert to C long") return rffi.cast(rffi.LONG, val) +@cpython_api([PyObject], rffi.INT, error=-1) +def _PyLong_AsInt(space, w_long): + """ + Get a C int from an int object or any object that has an __int__ + method. Return -1 and set an error if overflow occurs. + """ + try: + if space.lookup(w_long, '__index__'): + val = space.int_w(space.index(w_long)) + else: + val = space.int_w(space.int(w_long)) + except OperationError as e: + if e.match(space, space.w_ValueError): + e.w_type = space.w_OverflowError + if (e.match(space, space.w_OverflowError) and + space.isinstance_w(w_long, space.w_int)): + raise oefmt(space.w_OverflowError, + "Python int too large to convert to C int") + raise e + if val > INT_MAX or val < INT_MIN: + raise oefmt(space.w_OverflowError, + "Python int too large to convert to C int") + return rffi.cast(rffi.INT, val) + @cpython_api([PyObject], Py_ssize_t, error=-1) def PyLong_AsSsize_t(space, w_long): """Return a C Py_ssize_t representation of the contents of pylong. If @@ -295,7 +321,7 @@ def _PyLong_NumBits(space, w_long): @cpython_api([PyObject], rffi.INT_real, error=CANNOT_FAIL) def _PyLong_Sign(space, w_long): bigint = space.bigint_w(w_long) - return bigint.sign + return bigint.get_sign() CONST_UCHARP = lltype.Ptr(lltype.Array(rffi.UCHAR, hints={'nolength': True, 'render_as_const': True})) diff --git a/pypy/module/cpyext/number.py b/pypy/module/cpyext/number.py index 0c2b2d7b1b..0f8de1964f 100644 --- a/pypy/module/cpyext/number.py +++ b/pypy/module/cpyext/number.py @@ -24,7 +24,7 @@ def PyNumber_AsSsize_t(space, w_obj, w_exc): if e.match(space, space.w_OverflowError): if not w_exc: if space.isinstance_w(w_obj, space.w_long): - if w_obj.bigint_w(space).sign < 0: + if w_obj.bigint_w(space).get_sign() < 0: return PY_SSIZE_T_MIN else: return PY_SSIZE_T_MAX diff --git a/pypy/module/cpyext/test/test_longobject.py b/pypy/module/cpyext/test/test_longobject.py index 34a4254885..65e0c0f27c 100644 --- a/pypy/module/cpyext/test/test_longobject.py +++ b/pypy/module/cpyext/test/test_longobject.py @@ -352,6 +352,14 @@ class AppTestLongObject(AppTestCpythonExtensionBase): } return PyLong_FromLong(n); """), + ("as_int", "METH_O", + """ + int n = _PyLong_AsInt(args); + if (n == -1 && PyErr_Occurred()) { + return NULL; + } + return PyLong_FromLong(n); + """), ("long_max", "METH_NOARGS", """ return PyLong_FromLong(LONG_MAX); @@ -362,6 +370,16 @@ class AppTestLongObject(AppTestCpythonExtensionBase): return PyLong_FromLong(LONG_MIN); """ ), + ("int_max", "METH_NOARGS", + """ + return PyLong_FromLong(INT_MAX); + """ + ), + ("int_min", "METH_NOARGS", + """ + return PyLong_FromLong(INT_MIN); + """ + ), ]) assert module.as_long(123) == 123 assert module.as_long(-1) == -1 @@ -384,6 +402,27 @@ class AppTestLongObject(AppTestCpythonExtensionBase): # new for python3.8: first try __index__ assert module.as_long(a) == 42 + assert module.as_int(123) == 123 + assert module.as_int(-1) == -1 + assert module.as_int(1.23) == 1 + INT_MAX = module.int_max() + INT_MIN = module.int_min() + assert module.as_int(INT_MAX) == INT_MAX + raises(OverflowError, module.as_int, INT_MAX+ 1) + assert module.as_int(INT_MIN) == INT_MIN + raises(OverflowError, module.as_int, INT_MIN - 1) + class A: + def __index__(self): + return 42 + + def __int__(self): + return 21 + + a = A() + assert int(a) == 21 + # new for python3.8: first try __index__ + assert module.as_int(a) == 42 + def test_strtol(self): module = self.import_extension('foo', [ ("from_str", "METH_NOARGS", diff --git a/pypy/module/pypyjit/test_pypy_c/test_misc.py b/pypy/module/pypyjit/test_pypy_c/test_misc.py index 8c05d56b7c..08e2d6e64b 100644 --- a/pypy/module/pypyjit/test_pypy_c/test_misc.py +++ b/pypy/module/pypyjit/test_pypy_c/test_misc.py @@ -432,8 +432,7 @@ class TestMisc(BaseTestPyPyC): log = self.run(main, [3000]) loop, = log.loops_by_filename(self.filepath) assert loop.match(""" - i79 = int_lt(i76, 0) - guard_false(i79, descr=...) + ... i80 = int_ge(i76, i33) guard_false(i80, descr=...) i82 = int_add(i76, 1) diff --git a/pypy/module/pypyjit/test_pypy_c/test_string.py b/pypy/module/pypyjit/test_pypy_c/test_string.py index ef454893d4..089970fe61 100644 --- a/pypy/module/pypyjit/test_pypy_c/test_string.py +++ b/pypy/module/pypyjit/test_pypy_c/test_string.py @@ -408,7 +408,7 @@ class TestString(BaseTestPyPyC): def test_strip_doesnt_escape_bytes(self): log = self.run(""" def main(n): - l = [unicode(x).decode("ascii") for x in range(10000)] + l = [str(x).decode("ascii") for x in range(10000)] res = 0 for data in l: res += len(data.strip(b'1')) # ID: striparg @@ -430,7 +430,7 @@ class TestString(BaseTestPyPyC): log = self.run(""" def main(n): uni = b'\xc3\xa4'.decode("utf-8") - l = [unicode(x) + uni + unicode(x) for x in range(10000)] + l = [str(x) + uni + str(x) for x in range(10000)] res = 0 for data in l: res += len(data.strip(u'1')) # ID: stripnone @@ -451,7 +451,7 @@ class TestString(BaseTestPyPyC): def test_unicode_strip_doesnt_escape_uniobject_ascii(self): log = self.run(""" def main(n): - l = [unicode(x) for x in range(10000)] + l = [str(x) for x in range(10000)] res = 0 for data in l: res += len(data.strip(u'1')) # ID: stripnone diff --git a/pypy/module/sys/version.py b/pypy/module/sys/version.py index ea0f014249..8a3877309e 100644 --- a/pypy/module/sys/version.py +++ b/pypy/module/sys/version.py @@ -15,7 +15,7 @@ CPYTHON_API_VERSION = 1013 #XXX # sync with include/modsupport.h # make sure to keep PYPY_VERSION in sync with: # module/cpyext/include/patchlevel.h # doc/conf.py -PYPY_VERSION = (7, 3, 13, "alpha", 0) +PYPY_VERSION = (7, 3, 14, "alpha", 0) import pypy diff --git a/pypy/objspace/std/longobject.py b/pypy/objspace/std/longobject.py index 43b2643188..fe3bc0d8fe 100644 --- a/pypy/objspace/std/longobject.py +++ b/pypy/objspace/std/longobject.py @@ -212,7 +212,7 @@ class W_LongObject(W_AbstractLongObject): exponent = w_exponent.asbigint() if space.is_none(w_modulus): - if exponent.sign < 0: + if exponent.get_sign() < 0: self = self.descr_float(space) w_exponent = w_exponent.descr_float(space) return space.pow(self, w_exponent, space.w_None) @@ -223,7 +223,7 @@ class W_LongObject(W_AbstractLongObject): return space.w_NotImplemented base = self.num - if exponent.sign < 0: + if exponent.get_sign() < 0: w_base = invmod(space, self, space.abs(w_modulus)) if isinstance(w_base, W_IntObject): w_base = w_base.as_w_long(space) @@ -345,12 +345,12 @@ class W_LongObject(W_AbstractLongObject): return descr_binop, descr_rbinop def _lshift(self, space, w_other): - if w_other.asbigint().sign < 0: + if w_other.asbigint().get_sign() < 0: raise oefmt(space.w_ValueError, "negative shift count") try: shift = w_other.asbigint().toint() except OverflowError: # b too big - if self.num.sign == 0: + if self.num.get_sign() == 0: return self raise oefmt(space.w_OverflowError, "shift count too large") return W_LongObject(self.num.lshift(shift)) @@ -363,12 +363,12 @@ class W_LongObject(W_AbstractLongObject): descr_lshift, descr_rlshift = _make_descr_binop(_lshift, _int_lshift) def _rshift(self, space, w_other): - if w_other.asbigint().sign < 0: + if w_other.asbigint().get_sign() < 0: raise oefmt(space.w_ValueError, "negative shift count") try: shift = w_other.asbigint().toint() except OverflowError: - if self.num.sign < 0: + if self.num.get_sign() < 0: return space.newint(-1) return space.newint(0) raise oefmt(space.w_OverflowError, "shift count too large") @@ -463,7 +463,7 @@ def _hash_long(v): if x >= HASH_MODULUS: x -= HASH_MODULUS i -= 1 - h = intmask(intmask(x) * v.sign) + h = intmask(intmask(x) * v.get_sign()) return h - (h == -1) diff --git a/pypy/objspace/std/marshal_impl.py b/pypy/objspace/std/marshal_impl.py index af4fc1429a..3660b83872 100644 --- a/pypy/objspace/std/marshal_impl.py +++ b/pypy/objspace/std/marshal_impl.py @@ -201,7 +201,7 @@ def marshal_long(space, w_long, m): SHIFT = 15 MASK = (1 << SHIFT) - 1 num = space.bigint_w(w_long) - sign = num.sign + sign = num.get_sign() num = num.abs() total_length = (num.bit_length() + (SHIFT - 1)) / SHIFT m.put_int(total_length * sign) diff --git a/rpython/doc/conf.py b/rpython/doc/conf.py index 7d72156c6e..b3ac8399b8 100644 --- a/rpython/doc/conf.py +++ b/rpython/doc/conf.py @@ -81,7 +81,7 @@ copyright = u'2023, The PyPy Project' # The short X.Y version. version = '7.3' # The full version, including alpha/beta/rc tags. -release = '7.3.13' +release = '7.3.14' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/rpython/rlib/rbigint.py b/rpython/rlib/rbigint.py index 7ad26bfea2..6a976d7f76 100644 --- a/rpython/rlib/rbigint.py +++ b/rpython/rlib/rbigint.py @@ -154,7 +154,7 @@ def intsign(i): class rbigint(object): """This is a reimplementation of longs using a list of digits.""" _immutable_ = True - _immutable_fields_ = ["_digits[*]", "size", "sign"] + _immutable_fields_ = ["_digits[*]", "_size"] def __init__(self, digits=NULLDIGITS, sign=0, size=0): if not we_are_translated(): @@ -163,9 +163,13 @@ class rbigint(object): self._digits = digits assert size >= 0 - self.size = size or len(digits) + self._size = (size or len(digits)) * sign - self.sign = sign + def get_sign(self): + return intsign(self._size) + + def _set_sign(self, sign): + self._size = abs(self._size) * sign # __eq__ and __ne__ method exist for testing only, they are not RPython! @not_rpython @@ -209,7 +213,9 @@ class rbigint(object): setdigit._always_inline_ = True def numdigits(self): - w = self.size + w = abs(self._size) + if not w: + w = 1 assert w > 0 return w numdigits._always_inline_ = True @@ -386,7 +392,7 @@ class rbigint(object): def tobytes(self, nbytes, byteorder, signed): if byteorder not in ('big', 'little'): raise InvalidEndiannessError() - if not signed and self.sign == -1: + if not signed and self.get_sign() == -1: raise InvalidSignednessError() bswap = byteorder == 'big' @@ -400,7 +406,7 @@ class rbigint(object): for i in range(0, imax): d = self.widedigit(i) - if self.sign == -1: + if self.get_sign() == -1: d = (d ^ MASK) + carry carry = d >> SHIFT d &= MASK @@ -408,7 +414,7 @@ class rbigint(object): accum |= d << accumbits if i == imax - 1: # Avoid bogus 0's - s = d ^ MASK if self.sign == -1 else d + s = d ^ MASK if self.get_sign() == -1 else d while s: s >>= 1 accumbits += 1 @@ -429,14 +435,14 @@ class rbigint(object): raise OverflowError() j += 1 - if self.sign == -1: + if self.get_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 + signbyte = 0xFF if self.get_sign() == -1 else 0 result.append_multiple_char(chr(signbyte), nbytes - j) digits = result.build() @@ -444,7 +450,7 @@ class rbigint(object): 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): + if (self.get_sign() == -1) != (ord(msb) >= 0x80): raise OverflowError() if bswap: @@ -455,6 +461,7 @@ class rbigint(object): digits = ''.join([digits[i] for i in range(length-1, -1, -1)]) return digits + @jit.elidable def toint(self): """ Get an integer from a bigint object. @@ -468,7 +475,7 @@ class rbigint(object): def _toint_helper(self): x = self._touint_helper() # Haven't lost any bits so far - if self.sign >= 0: + if self.get_sign() >= 0: res = intmask(x) if res < 0: raise OverflowError @@ -490,7 +497,7 @@ class rbigint(object): x = self._touint_helper() except OverflowError: return False - if self.sign >= 0: + if self.get_sign() >= 0: res = intmask(x) return res >= 0 else: @@ -502,11 +509,11 @@ class rbigint(object): return _AsLongLong(self) def tobool(self): - return self.sign != 0 + return self.get_sign() != 0 @jit.elidable def touint(self): - if self.sign == -1: + if self.get_sign() == -1: raise ValueError("cannot convert negative integer to unsigned int") return self._touint_helper() @@ -524,7 +531,7 @@ class rbigint(object): @jit.elidable def toulonglong(self): - if self.sign == -1: + if self.get_sign() == -1: raise ValueError("cannot convert negative integer to unsigned int") return _AsULonglong_ignore_sign(self) @@ -565,7 +572,7 @@ class rbigint(object): @jit.elidable def eq(self, other): - if (self.sign != other.sign or + if (self.get_sign() != other.get_sign() or self.numdigits() != other.numdigits()): return False @@ -587,7 +594,7 @@ class rbigint(object): if self.numdigits() > 1: return False - return (self.sign * self.digit(0)) == iother + return (self.get_sign() * self.digit(0)) == iother def ne(self, other): return not self.eq(other) @@ -597,19 +604,21 @@ class rbigint(object): @jit.elidable def lt(self, other): - if self.sign > other.sign: + selfsign = self.get_sign() + othersign = other.get_sign() + if selfsign > othersign: return False - if self.sign < other.sign: + if selfsign < othersign: return True ld1 = self.numdigits() ld2 = other.numdigits() if ld1 > ld2: - if other.sign > 0: + if othersign > 0: return False else: return True elif ld1 < ld2: - if other.sign > 0: + if othersign > 0: return True else: return False @@ -618,12 +627,12 @@ class rbigint(object): d1 = self.digit(i) d2 = other.digit(i) if d1 < d2: - if other.sign > 0: + if othersign > 0: return True else: return False elif d1 > d2: - if other.sign > 0: + if othersign > 0: return False else: return True @@ -670,86 +679,94 @@ class rbigint(object): @jit.elidable def add(self, other): - if self.sign == 0: + selfsign = self.get_sign() + othersign = other.get_sign() + if selfsign == 0: return other - if other.sign == 0: + if othersign == 0: return self - if self.sign == other.sign: + if selfsign == othersign: result = _x_add(self, other) else: result = _x_sub(other, self) - result.sign *= other.sign + result._set_sign(result.get_sign() * othersign) return result @jit.elidable def int_add(self, iother): + selfsign = self.get_sign() if not int_in_valid_range(iother): # Fallback to long. return self.add(rbigint.fromint(iother)) - elif self.sign == 0: + elif selfsign == 0: return rbigint.fromint(iother) elif iother == 0: return self - sign = intsign(iother) - if self.sign == sign: + othersign = intsign(iother) + if selfsign == othersign: result = _x_int_add(self, iother) else: result = _x_int_sub(self, iother) - result.sign *= -1 - result.sign *= sign + result._set_sign(-result.get_sign()) + result._set_sign(result.get_sign() * othersign) return result @jit.elidable def sub(self, other): - if other.sign == 0: + selfsign = self.get_sign() + othersign = other.get_sign() + if othersign == 0: return self - elif self.sign == 0: - return rbigint(other._digits[:other.numdigits()], -other.sign, other.numdigits()) - elif self.sign == other.sign: + elif selfsign == 0: + return rbigint(other._digits[:other.numdigits()], -othersign, other.numdigits()) + elif selfsign == othersign: result = _x_sub(self, other) else: result = _x_add(self, other) - result.sign *= self.sign + result._set_sign(result.get_sign() * selfsign) return result @jit.elidable def int_sub(self, iother): + selfsign = self.get_sign() if not int_in_valid_range(iother): # Fallback to long. return self.sub(rbigint.fromint(iother)) elif iother == 0: return self - elif self.sign == 0: + elif selfsign == 0: return rbigint.fromint(-iother) - elif self.sign == intsign(iother): + elif selfsign == intsign(iother): result = _x_int_sub(self, iother) else: result = _x_int_add(self, iother) - result.sign *= self.sign + result._set_sign(result.get_sign() * selfsign) return result @jit.elidable def mul(self, other): selfsize = self.numdigits() othersize = other.numdigits() + selfsign = self.get_sign() + othersign = other.get_sign() if selfsize > othersize: self, other, selfsize, othersize = other, self, othersize, selfsize - if self.sign == 0 or other.sign == 0: + if selfsign == 0 or othersign == 0: return NULLRBIGINT if selfsize == 1: if self._digits[0] == ONEDIGIT: - return rbigint(other._digits[:othersize], self.sign * other.sign, othersize) + return rbigint(other._digits[:othersize], selfsign * othersign, othersize) elif othersize == 1: res = other.uwidedigit(0) * self.udigit(0) carry = res >> SHIFT if carry: - return rbigint([_store_digit(res & MASK), _store_digit(carry)], self.sign * other.sign, 2) + return rbigint([_store_digit(res & MASK), _store_digit(carry)], selfsign * othersign, 2) else: - return rbigint([_store_digit(res & MASK)], self.sign * other.sign, 1) + return rbigint([_store_digit(res & MASK)], selfsign * othersign, 1) result = _x_mul(self, other, self.digit(0)) elif USE_KARATSUBA: @@ -765,7 +782,7 @@ class rbigint(object): else: result = _x_mul(self, other) - result.sign = self.sign * other.sign + result._set_sign(selfsign * othersign) return result @jit.elidable @@ -774,7 +791,8 @@ class rbigint(object): # Fallback to long. return self.mul(rbigint.fromint(iother)) - if self.sign == 0 or iother == 0: + selfsign = self.get_sign() + if selfsign == 0 or iother == 0: return NULLRBIGINT asize = self.numdigits() @@ -785,21 +803,21 @@ class rbigint(object): if digit == 1: if othersign == 1: return self - return rbigint(self._digits[:asize], self.sign * othersign, asize) + return rbigint(self._digits[:asize], selfsign * othersign, asize) elif asize == 1: udigit = r_uint(digit) res = self.uwidedigit(0) * udigit carry = res >> SHIFT if carry: - return rbigint([_store_digit(res & MASK), _store_digit(carry)], self.sign * othersign, 2) + return rbigint([_store_digit(res & MASK), _store_digit(carry)], selfsign * othersign, 2) else: - return rbigint([_store_digit(res & MASK)], self.sign * othersign, 1) + return rbigint([_store_digit(res & MASK)], selfsign * othersign, 1) elif digit & (digit - 1) == 0: result = self.lqshift(ptwotable[digit]) else: result = _muladd1(self, digit) - result.sign = self.sign * othersign + result._set_sign(selfsign * othersign) return result @jit.elidable @@ -826,7 +844,8 @@ class rbigint(object): digit = abs(iother) assert digit > 0 - if self.sign == 1 and iother > 0: + selfsign = self.get_sign() + if selfsign == 1 and iother > 0: if digit == 1: return self elif digit & (digit - 1) == 0: @@ -834,11 +853,12 @@ class rbigint(object): div, mod = _divrem1(self, digit) - if mod != 0 and self.sign * intsign(iother) == -1: - if div.sign == 0: + othersign = intsign(iother) + if mod != 0 and selfsign * othersign == -1: + if div.get_sign() == 0: return ONENEGATIVERBIGINT div = div.int_add(1) - div.sign = self.sign * intsign(iother) + div._set_sign(selfsign * othersign) div._normalize() return div @@ -853,7 +873,8 @@ class rbigint(object): def int_mod(self, iother): if iother == 0: raise ZeroDivisionError("long division or modulo by zero") - if self.sign == 0: + selfsign = self.get_sign() + if selfsign == 0: return NULLRBIGINT elif not int_in_valid_range(iother): @@ -875,17 +896,18 @@ class rbigint(object): rem = _int_rem_core(self, digit) if rem == 0: return NULLRBIGINT - mod = rbigint([rem], -1 if self.sign < 0 else 1, 1) + mod = rbigint([rem], -1 if selfsign < 0 else 1, 1) - if mod.sign * intsign(iother) == -1: + if mod.get_sign() * intsign(iother) == -1: mod = mod.int_add(iother) return mod @jit.elidable def int_mod_int_result(self, iother): + selfsign = self.get_sign() if iother == 0: raise ZeroDivisionError("long division or modulo by zero") - if self.sign == 0: + if selfsign == 0: return 0 elif not int_in_valid_range(iother): @@ -904,7 +926,7 @@ class rbigint(object): elif digit & (digit - 1) == 0: mod = self.int_and_(digit - 1).toint() # XXX improve else: - mod = _int_rem_core(self, digit) * self.sign + mod = _int_rem_core(self, digit) * selfsign if intsign(mod) * intsign(iother) == -1: mod = mod + iother return mod @@ -927,12 +949,14 @@ class rbigint(object): have different signs. We then subtract one from the 'div' part of the outcome to keep the invariant intact. """ - if other.sign == 0: + selfsign = self.get_sign() + othersign = other.get_sign() + if othersign == 0: raise ZeroDivisionError("long division or modulo by zero") - if self.sign == 0: + if selfsign == 0: return TWO_NULLRBIGINTS - if other.numdigits() == 1 and not (-1 == other.sign != self.sign): - otherint = other.digit(0) * other.sign + if other.numdigits() == 1 and not (-1 == othersign != selfsign): + otherint = other.digit(0) * othersign assert int_in_valid_range(otherint) return self.int_divmod(otherint) @@ -948,9 +972,9 @@ class rbigint(object): def _divmod_small(self, other): div, mod = _divrem(self, other) - if mod.sign * other.sign == -1: + if mod.get_sign() * other.get_sign() == -1: mod = mod.add(other) - if div.sign == 0: + if div.get_sign() == 0: return ONENEGATIVERBIGINT, mod div = div.int_sub(1) return div, mod @@ -962,8 +986,9 @@ class rbigint(object): if iother == 0: raise ZeroDivisionError("long division or modulo by zero") - wsign = intsign(iother) - if not int_in_valid_range(iother) or (wsign == -1 and self.sign != wsign): + selfsign = self.get_sign() + othersign = intsign(iother) + if not int_in_valid_range(iother) or (othersign == -1 and selfsign != othersign): # Just fallback. return self.divmod(rbigint.fromint(iother)) @@ -972,15 +997,15 @@ class rbigint(object): div, mod = _divrem1(self, digit) # _divrem1 doesn't fix the sign - if div.size == 1 and div._digits[0] == NULLDIGIT: - div.sign = 0 + if div._size == 0: + assert div.get_sign() == 0 else: - div.sign = self.sign * wsign - if self.sign < 0: + div._set_sign(selfsign * othersign) + if selfsign < 0: mod = -mod - if mod and self.sign * wsign == -1: + if mod and selfsign * othersign == -1: mod += iother - if div.sign == 0: + if div.get_sign() == 0: div = ONENEGATIVERBIGINT else: div = div.int_sub(1) @@ -989,13 +1014,15 @@ class rbigint(object): @jit.elidable def pow(self, other, modulus=None): + selfsign = self.get_sign() + othersign = other.get_sign() negativeOutput = False # if x<0 return negative output # 5-ary values. If the exponent is large enough, table is # precomputed so that table[i] == self**i % modulus for i in range(32). # python translation: the table is computed when needed. - if other.sign < 0: # if exponent is negative + if othersign < 0: # if exponent is negative if modulus is not None: raise TypeError( "pow() 2nd argument " @@ -1005,10 +1032,11 @@ class rbigint(object): size_b = UDIGIT_TYPE(other.numdigits()) if modulus is not None: - if modulus.sign == 0: + modulussign = modulus.get_sign() + if modulussign == 0: raise ValueError("pow() 3rd argument cannot be 0") - if modulus.sign < 0: + if modulussign < 0: negativeOutput = True modulus = modulus.neg() @@ -1026,11 +1054,11 @@ class rbigint(object): # base % modulus instead. # We could _always_ do this reduction, but mod() isn't cheap, # so we only do it when it buys something. - if self.sign < 0 or self.numdigits() > modulus.numdigits(): + if selfsign < 0 or self.numdigits() > modulus.numdigits(): self = self.mod(modulus) - elif other.sign == 0: + elif othersign == 0: return ONERBIGINT - elif self.sign == 0: + elif selfsign == 0: return NULLRBIGINT elif size_b == 1: if other._digits[0] == ONEDIGIT: @@ -1039,13 +1067,13 @@ class rbigint(object): adigit = self.digit(0) digit = other.digit(0) if adigit == 1: - if self.sign == -1 and digit % 2: + if selfsign == -1 and digit % 2: return ONENEGATIVERBIGINT return ONERBIGINT elif adigit & (adigit - 1) == 0: ret = self.lshift(((digit-1)*(ptwotable[adigit]-1)) + digit-1) - if self.sign == -1 and not digit % 2: - ret.sign = 1 + if selfsign == -1 and not digit % 2: + ret._set_sign(1) return ret # At this point self, other, and modulus are guaranteed non-negative UNLESS @@ -1117,7 +1145,7 @@ class rbigint(object): # assert j == -5 - if negativeOutput and z.sign != 0: + if negativeOutput and z.get_sign() != 0: z = z.sub(modulus) return z @@ -1136,12 +1164,14 @@ class rbigint(object): "cannot be negative when 3rd argument specified") raise ValueError("bigint pow() too negative") + selfsign = self.get_sign() assert iother >= 0 if modulus is not None: - if modulus.sign == 0: + modulussign = modulus.get_sign() + if modulussign == 0: raise ValueError("pow() 3rd argument cannot be 0") - if modulus.sign < 0: + if modulussign < 0: negativeOutput = True modulus = modulus.neg() @@ -1159,24 +1189,24 @@ class rbigint(object): # base % modulus instead. # We could _always_ do this reduction, but mod() isn't cheap, # so we only do it when it buys something. - if self.sign < 0 or self.numdigits() > modulus.numdigits(): + if selfsign < 0 or self.numdigits() > modulus.numdigits(): self = self.mod(modulus) elif iother == 0: return ONERBIGINT - elif self.sign == 0: + elif selfsign == 0: return NULLRBIGINT elif iother == 1: return self elif self.numdigits() == 1: adigit = self.digit(0) if adigit == 1: - if self.sign == -1 and iother % 2: + if selfsign == -1 and iother % 2: return ONENEGATIVERBIGINT return ONERBIGINT elif adigit & (adigit - 1) == 0: ret = self.lshift(((iother-1)*(ptwotable[adigit]-1)) + iother-1) - if self.sign == -1 and not iother % 2: - ret.sign = 1 + if selfsign == -1 and not iother % 2: + ret._set_sign(1) return ret # At this point self, iother, and modulus are guaranteed non-negative UNLESS @@ -1194,34 +1224,36 @@ class rbigint(object): z = _help_mult(z, self, modulus) j >>= 1 - if negativeOutput and z.sign != 0: + if negativeOutput and z.get_sign() != 0: z = z.sub(modulus) return z @jit.elidable def neg(self): - return rbigint(self._digits, -self.sign, self.numdigits()) + return rbigint(self._digits, -self.get_sign(), self.numdigits()) @jit.elidable def abs(self): - if self.sign != -1: + selfsign = self.get_sign() + if selfsign != -1: return self - return rbigint(self._digits, 1, self.numdigits()) + return rbigint(self._digits, abs(selfsign), self.numdigits()) @jit.elidable def invert(self): #Implement ~x as -(x + 1) - if self.sign == 0: + if self.get_sign() == 0: return ONENEGATIVERBIGINT ret = self.int_add(1) - ret.sign = -ret.sign + ret._set_sign(-ret.get_sign()) return ret @jit.elidable def lshift(self, int_other): + selfsign = self.get_sign() if int_other < 0: raise ValueError("negative shift count") - elif int_other == 0 or self.sign == 0: + elif int_other == 0 or selfsign == 0: return self # wordshift, remshift = divmod(int_other, SHIFT) @@ -1230,11 +1262,11 @@ class rbigint(object): if not remshift: # So we can avoid problems with eq, AND avoid the need for normalize. - return rbigint([NULLDIGIT] * wordshift + self._digits, self.sign, self.numdigits() + wordshift) + return rbigint([NULLDIGIT] * wordshift + self._digits, selfsign, self.numdigits() + wordshift) oldsize = self.numdigits() newsize = oldsize + wordshift + 1 - z = rbigint([NULLDIGIT] * newsize, self.sign, newsize) + z = rbigint([NULLDIGIT] * newsize, selfsign, newsize) accum = _unsigned_widen_digit(0) j = 0 while j < oldsize: @@ -1258,8 +1290,9 @@ class rbigint(object): assert int_other > 0 oldsize = self.numdigits() + selfsign = self.get_sign() - z = rbigint([NULLDIGIT] * (oldsize + 1), self.sign, (oldsize + 1)) + z = rbigint([NULLDIGIT] * (oldsize + 1), selfsign, (oldsize + 1)) accum = _unsigned_widen_digit(0) i = 0 while i < oldsize: @@ -1278,7 +1311,8 @@ class rbigint(object): raise ValueError("negative shift count") elif int_other == 0: return self - if self.sign == -1 and not dont_invert: + selfsign = self.get_sign() + if selfsign == -1 and not dont_invert: a = self.invert().rshift(int_other) return a.invert() @@ -1289,7 +1323,7 @@ class rbigint(object): loshift = int_other % SHIFT hishift = SHIFT - loshift - z = rbigint([NULLDIGIT] * newsize, self.sign, newsize) + z = rbigint([NULLDIGIT] * newsize, selfsign, newsize) i = 0 while i < newsize: newdigit = (self.digit(wordshift) >> loshift) @@ -1312,7 +1346,8 @@ class rbigint(object): return NULLRBIGINT hishift = SHIFT - loshift - z = rbigint([NULLDIGIT] * newsize, self.sign, newsize) + selfsign = self.get_sign() + z = rbigint([NULLDIGIT] * newsize, selfsign, newsize) i = 0 while i < newsize: @@ -1407,7 +1442,7 @@ class rbigint(object): @jit.elidable def oct(self): - if self.sign == 0: + if self.get_sign() == 0: return '0L' else: return _format(self, BASE8, '0', 'L') @@ -1437,9 +1472,9 @@ class rbigint(object): for d in digits: l = l << SHIFT l += intmask(d) - result = l * self.sign + result = l * self.get_sign() if result == 0: - assert self.sign == 0 + assert self.get_sign() == 0 return result def _normalize(self): @@ -1449,9 +1484,9 @@ class rbigint(object): i -= 1 assert i > 0 - self.size = i + self._size = i * self.get_sign() if i == 1 and self._digits[0] == NULLDIGIT: - self.sign = 0 + self._size = 0 self._digits = NULLDIGITS _normalize._always_inline_ = True @@ -1498,7 +1533,7 @@ class rbigint(object): def __repr__(self): return "<rbigint digits=%s, sign=%s, size=%d, len=%d, %s>" % (self._digits, - self.sign, self.numdigits(), len(self._digits), + self.get_sign(), self.numdigits(), len(self._digits), self.tolong()) ONERBIGINT = rbigint([ONEDIGIT], 1, 1) @@ -1911,14 +1946,14 @@ def _k_mul(a, b): # 2. t1 <- ah*bh, and copy into high digits of result. t1 = ah.mul(bh) - assert t1.sign >= 0 + assert t1.get_sign() >= 0 assert 2*shift + t1.numdigits() <= ret.numdigits() for i in range(t1.numdigits()): ret._digits[2*shift + i] = t1._digits[i] # 3. t2 <- al*bl, and copy into the low digits. t2 = al.mul(bl) - assert t2.sign >= 0 + assert t2.get_sign() >= 0 assert t2.numdigits() <= 2*shift # no overlap with high digits for i in range(t2.numdigits()): ret._digits[i] = t2._digits[i] @@ -1938,7 +1973,7 @@ def _k_mul(a, b): t2 = _x_add(bh, bl) t3 = t1.mul(t2) - assert t3.sign >= 0 + assert t3.get_sign() >= 0 # Add t3. It's not obvious why we can't run out of room here. # See the (*) comment after this function. @@ -2249,7 +2284,7 @@ def _divrem(a, b): size_a = a.numdigits() size_b = b.numdigits() - if b.sign == 0: + if b.get_sign() == 0: raise ZeroDivisionError("long division or modulo by zero") if (size_a < size_b or @@ -2266,10 +2301,10 @@ def _divrem(a, b): # The quotient z has the sign of a*b; # the remainder r has the sign of a, # so a = b*z + r. - if a.sign != b.sign: - z.sign = - z.sign - if a.sign < 0 and rem.sign != 0: - rem.sign = - rem.sign + if a.get_sign() != b.get_sign(): + z._set_sign(-z.get_sign()) + if a.get_sign() < 0 and rem.get_sign() != 0: + rem._set_sign(-rem.get_sign()) return z, rem @@ -2313,7 +2348,7 @@ def div2n1n(a_container, a_startindex, b, n_S): """ if n_S <= HOLDER.DIV_LIMIT: a = _extract_digits(a_container, a_startindex, 2 * n_S) - if a.sign == 0: + if a.get_sign() == 0: return NULLRBIGINT, NULLRBIGINT res = _divrem(a, b) return res @@ -2349,7 +2384,7 @@ def div3n2n(a12_container, a12_startindex, a3_container, a3_startindex, b, b1, b """Helper function for div2n1n; not intended to be called directly.""" q, r = div2n1n(a12_container, a12_startindex, b1, n_S) # equivalent to r = _full_digits_lshift_then_or(r, n_S, _extract_digits(a_container, a3_startindex, n_S)) - if r.sign == 0: + if r.get_sign() == 0: r = _extract_digits(a3_container, a3_startindex, n_S) else: digits = [NULLDIGIT] * (n_S + r.numdigits()) @@ -2363,12 +2398,12 @@ def div3n2n(a12_container, a12_startindex, a3_container, a3_startindex, b, b1, b index += 1 r = rbigint(digits, 1) r._normalize() - if q.sign == 0: + if q.get_sign() == 0: return q, r r = r.sub(q.mul(b2)) # loop runs at most twice - while r.sign < 0: + while r.get_sign() < 0: q = q.int_sub(1) r = r.add(b) return q, r @@ -2377,7 +2412,7 @@ def _full_digits_lshift_then_or(a, n, b): """ equivalent to a.lshift(n * SHIFT).or_(b) the size of b must be smaller than n """ - if a.sign == 0: + if a.get_sign() == 0: return b bdigits = b.numdigits() assert bdigits <= n @@ -2455,13 +2490,13 @@ def divmod_big(a, b): # follows cr.yp.to/bib/1998/burnikel.ps if b.eq(NULLRBIGINT): raise ZeroDivisionError - elif b.sign < 0: + elif b.get_sign() < 0: q, r = divmod_big(a.neg(), b.neg()) return q, r.neg() - elif a.sign < 0: + elif a.get_sign() < 0: q, r = divmod_big(a.invert(), b) return q.invert(), b.add(r.invert()) - elif a.sign == 0: + elif a.get_sign() == 0: return TWO_NULLRBIGINTS else: return _divmod_fast_pos(a, b) @@ -2474,9 +2509,9 @@ def _x_int_lt(a, b, eq=False): elif b < 0: osign = -1 - if a.sign > osign: + if a.get_sign() > osign: return False - elif a.sign < osign: + elif a.get_sign() < osign: return True digits = a.numdigits() @@ -2487,7 +2522,7 @@ def _x_int_lt(a, b, eq=False): else: return True - d1 = a.sign * a.digit(0) + d1 = a.get_sign() * a.digit(0) if eq: if d1 <= b: return True @@ -2511,10 +2546,10 @@ def _AsScaledDouble(v): least one round bit to stand in for the ignored least-significant bits. """ NBITS_WANTED = 57 - if v.sign == 0: + if v.get_sign() == 0: return 0.0, 0 i = v.numdigits() - 1 - sign = v.sign + sign = v.get_sign() x = float(v.digit(i)) nbitsneeded = NBITS_WANTED - 1 # Invariant: i Python digits remain unaccounted for. @@ -2555,7 +2590,7 @@ def _AsDouble(n): assert DBL_MANT_DIG < r_ulonglong.BITS # Reduce to case n positive. - sign = n.sign + sign = n.get_sign() if sign == 0: return 0.0 elif sign < 0: @@ -2669,7 +2704,7 @@ def _bigint_true_divide(a, b): MANT_DIG_BITS = DBL_MANT_DIG % SHIFT # Reduce to case where a and b are both positive. - negate = (a.sign < 0) ^ (b.sign < 0) + negate = (a.get_sign() < 0) ^ (b.get_sign() < 0) if not b.tobool(): raise ZeroDivisionError("long division or modulo by zero") if not a.tobool(): @@ -2825,7 +2860,7 @@ def _format_base2_notzero(a, digits, prefix='', suffix='', max_str_digits=0): j -= 1 result[next_char_index] = prefix[j] - if a.sign < 0: + if a.get_sign() < 0: next_char_index -= 1 result[next_char_index] = '-' @@ -2886,7 +2921,7 @@ def _format_recursive(x, i, output, pts, digits, size_prefix, mindigits, _format if i < 0: # this checks whether any digit has been appended yet if curlen == size_prefix: - if x.sign != 0: + if x.get_sign() != 0: s = _format_int(x.toint(), digits) output.append(s) else: @@ -2902,14 +2937,14 @@ def _format_recursive(x, i, output, pts, digits, size_prefix, mindigits, _format _format_recursive(bot, i-1, output, pts, digits, size_prefix, mindigits, _format_int, max_str_digits) def _format(x, digits, prefix='', suffix='', max_str_digits=0): - if x.sign == 0: + if x.get_sign() == 0: return prefix + "0" + suffix base = len(digits) assert base >= 2 and base <= 36 if (base & (base - 1)) == 0: # base is 2, 4, 8, 16, ... return _format_base2_notzero(x, digits, prefix, suffix, max_str_digits) - negative = x.sign < 0 + negative = x.get_sign() < 0 if negative: x = x.neg() rbase = rbigint.fromint(base) @@ -2955,12 +2990,12 @@ def _format(x, digits, prefix='', suffix='', max_str_digits=0): def _bitwise(a, op, b): # '&', '|', '^' """ Bitwise and/or/xor operations """ - if a.sign < 0: + if a.get_sign() < 0: a = a.invert() maska = MASK else: maska = 0 - if b.sign < 0: + if b.get_sign() < 0: b = b.invert() maskb = MASK else: @@ -3042,7 +3077,7 @@ def _int_bitwise(a, op, b): # '&', '|', '^' # Fallback to long. return _bitwise(a, op, rbigint.fromint(b)) - if a.sign < 0: + if a.get_sign() < 0: a = a.invert() maska = MASK else: @@ -3129,13 +3164,13 @@ def _AsLongLong(v): x = _AsULonglong_ignore_sign(v) # grr grr grr if x >= ULONGLONG_BOUND: - if x == ULONGLONG_BOUND and v.sign < 0: + if x == ULONGLONG_BOUND and v.get_sign() < 0: x = LONGLONG_MIN else: raise OverflowError else: x = r_longlong(x) - if v.sign < 0: + if v.get_sign() < 0: x = -x return x @@ -3158,7 +3193,7 @@ def make_unsigned_mask_conversion(T): while i >= 0: x = (x << SHIFT) + T(v.digit(i)) i -= 1 - if v.sign < 0: + if v.get_sign() < 0: x = -x return x return _As_unsigned_mask @@ -3174,7 +3209,7 @@ def _hash(v): # hash(x) == hash(x % ULONG_MAX). In particular, this # implies that hash(x) == hash(x % (2**64-1)). i = v.numdigits() - 1 - sign = v.sign + sign = v.get_sign() x = r_uint(0) LONG_BIT_SHIFT = LONG_BIT - SHIFT while i >= 0: @@ -3234,8 +3269,8 @@ def _decimalstr_to_bigint(s, start=0, lim=-1): a = rbigint([_store_digit(dig)], int(dig != 0)) tens = 1 dig = 0 - if sign and a.sign == 1: - a.sign = -1 + if sign and a.get_sign() == 1: + a._set_sign(-1) return a def parse_digit_string(parser): @@ -3247,7 +3282,7 @@ def parse_digit_string(parser): # check for errors and potentially remove underscores s, start, end = parser._all_digits10() a = _str_to_int_big_base10(s, start, end, HOLDER.STR2INT_LIMIT) - a.sign *= parser.sign + a._set_sign(a.get_sign() * parser.sign) return a a = NULLRBIGINT digitmax = BASE_MAX[base] @@ -3267,7 +3302,7 @@ def parse_digit_string(parser): else: dig = dig * base + digit baseexp *= base - a.sign *= parser.sign + a._set_sign(a.get_sign() * parser.sign) return a @@ -3422,8 +3457,8 @@ def gcd_lehmer(a, b): if a.lt(b): a, b = b, a - while b.size > 1: - a_ms = a.digit(abs(a.size-1)) + while b.numdigits() > 1: + a_ms = a.digit(abs(a.numdigits()-1)) x = 0 while a_ms & (0xFF << SHIFT-8) == 0: @@ -3434,12 +3469,12 @@ def gcd_lehmer(a, b): a_ms <<= 1 x += 1 - a_ms |= a.digit(abs(a.size-2)) >> SHIFT-x + a_ms |= a.digit(abs(a.numdigits()-2)) >> SHIFT-x - if a.size == b.size: - b_ms = (b.digit(abs(b.size-1)) << x) | (b.digit(abs(b.size-2)) >> SHIFT-x) - elif a.size == b.size+1: - b_ms = b.digit(abs(b.size-1)) >> SHIFT-x + if a.numdigits() == b.numdigits(): + b_ms = (b.digit(abs(b.numdigits()-1)) << x) | (b.digit(abs(b.numdigits()-2)) >> SHIFT-x) + elif a.numdigits() == b.numdigits()+1: + b_ms = b.digit(abs(b.numdigits()-1)) >> SHIFT-x else: b_ms = 0 diff --git a/rpython/rlib/test/test_rbigint.py b/rpython/rlib/test/test_rbigint.py index 2882ecb601..35bce60793 100644 --- a/rpython/rlib/test/test_rbigint.py +++ b/rpython/rlib/test/test_rbigint.py @@ -835,8 +835,11 @@ class Test_rbigint(object): def test_normalize(self): f1 = bigint([1, 0], 1) f1._normalize() - assert f1.size == 1 + assert f1.numdigits() == 1 f0 = bigint([0], 0) + f0._normalize() + assert f0.numdigits() == 1 + assert f0._size == 0 assert f1.sub(f1).eq(f0) def test_invert(self): |