Issue #7528: Backport PyLong_AsLongAndOverflow from py3k to trunk.

Thanks Case Van Horsen for the patch.
This commit is contained in:
Mark Dickinson 2009-12-21 11:21:25 +00:00
parent 2b66da7d15
commit e31d300664
5 changed files with 190 additions and 34 deletions

View File

@ -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::

View File

@ -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 *);

View File

@ -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.

View File

@ -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},

View File

@ -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.