diff --git a/Doc/c-api/long.rst b/Doc/c-api/long.rst index c9cb034dc96..7453ccd131c 100644 --- a/Doc/c-api/long.rst +++ b/Doc/c-api/long.rst @@ -133,6 +133,19 @@ Long Integer Objects and ``-1`` will be returned. +.. cfunction:: long PyLong_AsLongAndOverflow(PyObject *pylong, int* overflow) + + Return a C :ctype:`long` representation of the contents of + *pylong*. If *pylong* is greater than :const:`LONG_MAX` or less + than :const:`LONG_MIN`, set `*overflow` to ``1`` or ``-1``, + respectively, and return ``-1``; otherwise, set `*overflow` to + ``0``. If any other exception occurs (for example a TypeError or + MemoryError), then ``-1`` will be returned and ``*overflow`` will + be ``0``. + + .. versionadded:: 2.7 + + .. cfunction:: Py_ssize_t PyLong_AsSsize_t(PyObject *pylong) .. index:: diff --git a/Include/longobject.h b/Include/longobject.h index ea9e38767cb..0cb8e2ff3a4 100644 --- a/Include/longobject.h +++ b/Include/longobject.h @@ -21,6 +21,7 @@ PyAPI_FUNC(PyObject *) PyLong_FromDouble(double); PyAPI_FUNC(PyObject *) PyLong_FromSize_t(size_t); PyAPI_FUNC(PyObject *) PyLong_FromSsize_t(Py_ssize_t); PyAPI_FUNC(long) PyLong_AsLong(PyObject *); +PyAPI_FUNC(long) PyLong_AsLongAndOverflow(PyObject *, int *); PyAPI_FUNC(unsigned long) PyLong_AsUnsignedLong(PyObject *); PyAPI_FUNC(unsigned long) PyLong_AsUnsignedLongMask(PyObject *); PyAPI_FUNC(Py_ssize_t) PyLong_AsSsize_t(PyObject *); diff --git a/Misc/NEWS b/Misc/NEWS index 3c407e0c2b2..c82eb63c27f 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -1648,6 +1648,8 @@ Documentation C-API ----- +- Issue #7528: Add PyLong_AsLongAndOverflow (backported from py3k). + - Issue #7228: Add '%lld' and '%llu' support to PyString_FromFormat(V) and PyErr_Format, on machines with HAVE_LONG_LONG defined. diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index b68e76dc0aa..2bcf75446ee 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -358,6 +358,75 @@ test_longlong_api(PyObject* self, PyObject *args) #undef F_U_TO_PY #undef F_PY_TO_U +/* Test the PyLong_AsLongAndOverflow API. General conversion to PY_LONG + is tested by test_long_api_inner. This test will concentrate on proper + handling of overflow. +*/ + +static PyObject * +test_long_and_overflow(PyObject *self) +{ + PyObject *num; + long value; + int overflow; + + /* a number larger than LONG_MAX even on 64-bit platforms */ + num = PyLong_FromString("FFFFFFFFFFFFFFFFFFFFFFFF", NULL, 16); + if (num == NULL) + return NULL; + + /* Test that overflow is set properly for a large value. */ + overflow = 1234; + value = PyLong_AsLongAndOverflow(num, &overflow); + if (overflow != 1) + return raiseTestError("test_long_and_overflow", + "overflow was not set to 1"); + + overflow = 0; + value = PyLong_AsLongAndOverflow(num, &overflow); + if (overflow != 1) + return raiseTestError("test_long_and_overflow", + "overflow was not set to 0"); + + /* a number smaller than LONG_MIN even on 64-bit platforms */ + num = PyLong_FromString("-FFFFFFFFFFFFFFFFFFFFFFFF", NULL, 16); + if (num == NULL) + return NULL; + + /* Test that overflow is set properly for a large negative value. */ + overflow = 1234; + value = PyLong_AsLongAndOverflow(num, &overflow); + if (overflow != -1) + return raiseTestError("test_long_and_overflow", + "overflow was not set to -1"); + + overflow = 0; + value = PyLong_AsLongAndOverflow(num, &overflow); + if (overflow != -1) + return raiseTestError("test_long_and_overflow", + "overflow was not set to 0"); + + num = PyLong_FromString("FF", NULL, 16); + if (num == NULL) + return NULL; + + /* Test that overflow is cleared properly for a small value. */ + overflow = 1234; + value = PyLong_AsLongAndOverflow(num, &overflow); + if (overflow != 0) + return raiseTestError("test_long_and_overflow", + "overflow was not cleared"); + + overflow = 0; + value = PyLong_AsLongAndOverflow(num, &overflow); + if (overflow != 0) + return raiseTestError("test_long_and_overflow", + "overflow was set incorrectly"); + + Py_INCREF(Py_None); + return Py_None; +} + /* Test the L code for PyArg_ParseTuple. This should deliver a PY_LONG_LONG for both long and int arguments. The test may leak a little memory if it fails. @@ -1041,6 +1110,7 @@ static PyMethodDef TestMethods[] = { {"test_dict_iteration", (PyCFunction)test_dict_iteration,METH_NOARGS}, {"test_lazy_hash_inheritance", (PyCFunction)test_lazy_hash_inheritance,METH_NOARGS}, {"test_long_api", (PyCFunction)test_long_api, METH_NOARGS}, + {"test_long_and_overflow", (PyCFunction)test_long_and_overflow, METH_NOARGS}, {"test_long_numbits", (PyCFunction)test_long_numbits, METH_NOARGS}, {"test_k_code", (PyCFunction)test_k_code, METH_NOARGS}, {"test_empty_argparse", (PyCFunction)test_empty_argparse,METH_NOARGS}, diff --git a/Objects/longobject.c b/Objects/longobject.c index 8bdb283eab2..b0170d27063 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -223,53 +223,123 @@ PyLong_FromDouble(double dval) #define PY_ABS_LONG_MIN (0-(unsigned long)LONG_MIN) #define PY_ABS_SSIZE_T_MIN (0-(size_t)PY_SSIZE_T_MIN) -/* Get a C long int from a long int object. - Returns -1 and sets an error condition if overflow occurs. */ +/* Get a C long int from a Python long or Python int object. + On overflow, returns -1 and sets *overflow to 1 or -1 depending + on the sign of the result. Otherwise *overflow is 0. + + For other errors (e.g., type error), returns -1 and sets an error + condition. +*/ long -PyLong_AsLong(PyObject *vv) +PyLong_AsLongAndOverflow(PyObject *vv, int *overflow) { /* This version by Tim Peters */ register PyLongObject *v; unsigned long x, prev; + long res; Py_ssize_t i; int sign; + int do_decref = 0; /* if nb_int was called */ - if (vv == NULL || !PyLong_Check(vv)) { - if (vv != NULL && PyInt_Check(vv)) - return PyInt_AsLong(vv); + *overflow = 0; + if (vv == NULL) { PyErr_BadInternalCall(); return -1; } - v = (PyLongObject *)vv; - i = v->ob_size; - sign = 1; - x = 0; - if (i < 0) { - sign = -1; - i = -(i); - } - while (--i >= 0) { - prev = x; - x = (x << PyLong_SHIFT) | v->ob_digit[i]; - if ((x >> PyLong_SHIFT) != prev) - goto overflow; - } - /* Haven't lost any bits, but casting to long requires extra care - * (see comment above). - */ - if (x <= (unsigned long)LONG_MAX) { - return (long)x * sign; - } - else if (sign < 0 && x == PY_ABS_LONG_MIN) { - return LONG_MIN; - } - /* else overflow */ - overflow: - PyErr_SetString(PyExc_OverflowError, - "long int too large to convert to int"); - return -1; + if(PyInt_Check(vv)) + return PyInt_AsLong(vv); + + if (!PyLong_Check(vv)) { + PyNumberMethods *nb; + nb = vv->ob_type->tp_as_number; + if (nb == NULL || nb->nb_int == NULL) { + PyErr_SetString(PyExc_TypeError, + "an integer is required"); + return -1; + } + vv = (*nb->nb_int) (vv); + if (vv == NULL) + return -1; + do_decref = 1; + if(PyInt_Check(vv)) { + res = PyInt_AsLong(vv); + goto exit; + } + if (!PyLong_Check(vv)) { + Py_DECREF(vv); + PyErr_SetString(PyExc_TypeError, + "nb_int should return int object"); + return -1; + } + } + + res = -1; + v = (PyLongObject *)vv; + i = Py_SIZE(v); + + switch (i) { + case -1: + res = -(sdigit)v->ob_digit[0]; + break; + case 0: + res = 0; + break; + case 1: + res = v->ob_digit[0]; + break; + default: + sign = 1; + x = 0; + if (i < 0) { + sign = -1; + i = -(i); + } + while (--i >= 0) { + prev = x; + x = (x << PyLong_SHIFT) + v->ob_digit[i]; + if ((x >> PyLong_SHIFT) != prev) { + *overflow = sign; + goto exit; + } + } + /* Haven't lost any bits, but casting to long requires extra + * care (see comment above). + */ + if (x <= (unsigned long)LONG_MAX) { + res = (long)x * sign; + } + else if (sign < 0 && x == PY_ABS_LONG_MIN) { + res = LONG_MIN; + } + else { + *overflow = sign; + /* res is already set to -1 */ + } + } + exit: + if (do_decref) { + Py_DECREF(vv); + } + return res; +} + +/* Get a C long int from a long int object. + Returns -1 and sets an error condition if overflow occurs. */ + +long +PyLong_AsLong(PyObject *obj) +{ + int overflow; + long result = PyLong_AsLongAndOverflow(obj, &overflow); + if (overflow) { + /* XXX: could be cute and give a different + message for overflow == -1 */ + PyErr_SetString(PyExc_OverflowError, + "Python int too large to convert to C long"); + } + return result; } /* Get a Py_ssize_t from a long int object.