Issue #7767: Add new C-API function PyLong_AsLongLongAndOverflow, a

long long variant of PyLong_AsLongAndOverflow.  Patch by Case Van
Horsen.
This commit is contained in:
Mark Dickinson 2010-01-30 10:08:33 +00:00
parent a2d4653740
commit a36507c64c
5 changed files with 288 additions and 0 deletions

View File

@ -146,6 +146,19 @@ Long Integer Objects
.. versionadded:: 2.7 .. versionadded:: 2.7
.. cfunction:: PY_LONG_LONG PyLong_AsLongLongAndOverflow(PyObject *pylong, int* overflow)
Return a C :ctype:`long long` representation of the contents of
*pylong*. If *pylong* is greater than :const:`PY_LLONG_MAX` or less
than :const:`PY_LLONG_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) .. cfunction:: Py_ssize_t PyLong_AsSsize_t(PyObject *pylong)
.. index:: .. index::

View File

@ -51,6 +51,7 @@ PyAPI_FUNC(PyObject *) PyLong_FromUnsignedLongLong(unsigned PY_LONG_LONG);
PyAPI_FUNC(PY_LONG_LONG) PyLong_AsLongLong(PyObject *); PyAPI_FUNC(PY_LONG_LONG) PyLong_AsLongLong(PyObject *);
PyAPI_FUNC(unsigned PY_LONG_LONG) PyLong_AsUnsignedLongLong(PyObject *); PyAPI_FUNC(unsigned PY_LONG_LONG) PyLong_AsUnsignedLongLong(PyObject *);
PyAPI_FUNC(unsigned PY_LONG_LONG) PyLong_AsUnsignedLongLongMask(PyObject *); PyAPI_FUNC(unsigned PY_LONG_LONG) PyLong_AsUnsignedLongLongMask(PyObject *);
PyAPI_FUNC(PY_LONG_LONG) PyLong_AsLongLongAndOverflow(PyObject *, int *);
#endif /* HAVE_LONG_LONG */ #endif /* HAVE_LONG_LONG */
PyAPI_FUNC(PyObject *) PyLong_FromString(char *, char **, int); PyAPI_FUNC(PyObject *) PyLong_FromString(char *, char **, int);

View File

@ -254,6 +254,9 @@ Library
C-API C-API
----- -----
- Issue #7767: New function PyLong_AsLongLongAndOverflow added,
analogous to PyLong_AsLongAndOverflow.
- Issue #5080: The argument parsing functions PyArg_ParseTuple, - Issue #5080: The argument parsing functions PyArg_ParseTuple,
PyArg_ParseTupleAndKeywords, PyArg_VaParse, PyArg_ParseTupleAndKeywords, PyArg_VaParse,
PyArg_VaParseTupleAndKeywords and PyArg_Parse no longer accept float PyArg_VaParseTupleAndKeywords and PyArg_Parse no longer accept float

View File

@ -523,6 +523,171 @@ test_long_and_overflow(PyObject *self)
return Py_None; return Py_None;
} }
/* Test the PyLong_AsLongLongAndOverflow API. General conversion to
PY_LONG_LONG is tested by test_long_api_inner. This test will
concentrate on proper handling of overflow.
*/
static PyObject *
test_long_long_and_overflow(PyObject *self)
{
PyObject *num, *one, *temp;
PY_LONG_LONG value;
int overflow;
/* Test that overflow is set properly for a large value. */
/* num is a number larger than PY_LLONG_MAX on a typical machine. */
num = PyLong_FromString("FFFFFFFFFFFFFFFFFFFFFFFF", NULL, 16);
if (num == NULL)
return NULL;
overflow = 1234;
value = PyLong_AsLongLongAndOverflow(num, &overflow);
Py_DECREF(num);
if (value == -1 && PyErr_Occurred())
return NULL;
if (value != -1)
return raiseTestError("test_long_long_and_overflow",
"return value was not set to -1");
if (overflow != 1)
return raiseTestError("test_long_long_and_overflow",
"overflow was not set to 1");
/* Same again, with num = PY_LLONG_MAX + 1 */
num = PyLong_FromLongLong(PY_LLONG_MAX);
if (num == NULL)
return NULL;
one = PyLong_FromLong(1L);
if (one == NULL) {
Py_DECREF(num);
return NULL;
}
temp = PyNumber_Add(num, one);
Py_DECREF(one);
Py_DECREF(num);
num = temp;
if (num == NULL)
return NULL;
overflow = 0;
value = PyLong_AsLongLongAndOverflow(num, &overflow);
Py_DECREF(num);
if (value == -1 && PyErr_Occurred())
return NULL;
if (value != -1)
return raiseTestError("test_long_long_and_overflow",
"return value was not set to -1");
if (overflow != 1)
return raiseTestError("test_long_long_and_overflow",
"overflow was not set to 1");
/* Test that overflow is set properly for a large negative value. */
/* num is a number smaller than PY_LLONG_MIN on a typical platform */
num = PyLong_FromString("-FFFFFFFFFFFFFFFFFFFFFFFF", NULL, 16);
if (num == NULL)
return NULL;
overflow = 1234;
value = PyLong_AsLongLongAndOverflow(num, &overflow);
Py_DECREF(num);
if (value == -1 && PyErr_Occurred())
return NULL;
if (value != -1)
return raiseTestError("test_long_long_and_overflow",
"return value was not set to -1");
if (overflow != -1)
return raiseTestError("test_long_long_and_overflow",
"overflow was not set to -1");
/* Same again, with num = PY_LLONG_MIN - 1 */
num = PyLong_FromLongLong(PY_LLONG_MIN);
if (num == NULL)
return NULL;
one = PyLong_FromLong(1L);
if (one == NULL) {
Py_DECREF(num);
return NULL;
}
temp = PyNumber_Subtract(num, one);
Py_DECREF(one);
Py_DECREF(num);
num = temp;
if (num == NULL)
return NULL;
overflow = 0;
value = PyLong_AsLongLongAndOverflow(num, &overflow);
Py_DECREF(num);
if (value == -1 && PyErr_Occurred())
return NULL;
if (value != -1)
return raiseTestError("test_long_long_and_overflow",
"return value was not set to -1");
if (overflow != -1)
return raiseTestError("test_long_long_and_overflow",
"overflow was not set to -1");
/* Test that overflow is cleared properly for small values. */
num = PyLong_FromString("FF", NULL, 16);
if (num == NULL)
return NULL;
overflow = 1234;
value = PyLong_AsLongLongAndOverflow(num, &overflow);
Py_DECREF(num);
if (value == -1 && PyErr_Occurred())
return NULL;
if (value != 0xFF)
return raiseTestError("test_long_long_and_overflow",
"expected return value 0xFF");
if (overflow != 0)
return raiseTestError("test_long_long_and_overflow",
"overflow was not cleared");
num = PyLong_FromString("-FF", NULL, 16);
if (num == NULL)
return NULL;
overflow = 0;
value = PyLong_AsLongLongAndOverflow(num, &overflow);
Py_DECREF(num);
if (value == -1 && PyErr_Occurred())
return NULL;
if (value != -0xFF)
return raiseTestError("test_long_long_and_overflow",
"expected return value 0xFF");
if (overflow != 0)
return raiseTestError("test_long_long_and_overflow",
"overflow was set incorrectly");
num = PyLong_FromLongLong(PY_LLONG_MAX);
if (num == NULL)
return NULL;
overflow = 1234;
value = PyLong_AsLongLongAndOverflow(num, &overflow);
Py_DECREF(num);
if (value == -1 && PyErr_Occurred())
return NULL;
if (value != PY_LLONG_MAX)
return raiseTestError("test_long_long_and_overflow",
"expected return value PY_LLONG_MAX");
if (overflow != 0)
return raiseTestError("test_long_long_and_overflow",
"overflow was not cleared");
num = PyLong_FromLongLong(PY_LLONG_MIN);
if (num == NULL)
return NULL;
overflow = 0;
value = PyLong_AsLongLongAndOverflow(num, &overflow);
Py_DECREF(num);
if (value == -1 && PyErr_Occurred())
return NULL;
if (value != PY_LLONG_MIN)
return raiseTestError("test_long_long_and_overflow",
"expected return value PY_LLONG_MIN");
if (overflow != 0)
return raiseTestError("test_long_long_and_overflow",
"overflow was not cleared");
Py_INCREF(Py_None);
return Py_None;
}
/* Test the L code for PyArg_ParseTuple. This should deliver a PY_LONG_LONG /* 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 for both long and int arguments. The test may leak a little memory if
it fails. it fails.
@ -1252,6 +1417,8 @@ static PyMethodDef TestMethods[] = {
{"getargs_L", getargs_L, METH_VARARGS}, {"getargs_L", getargs_L, METH_VARARGS},
{"getargs_K", getargs_K, METH_VARARGS}, {"getargs_K", getargs_K, METH_VARARGS},
{"test_longlong_api", test_longlong_api, METH_NOARGS}, {"test_longlong_api", test_longlong_api, METH_NOARGS},
{"test_long_long_and_overflow",
(PyCFunction)test_long_long_and_overflow, METH_NOARGS},
{"test_L_code", (PyCFunction)test_L_code, METH_NOARGS}, {"test_L_code", (PyCFunction)test_L_code, METH_NOARGS},
{"codec_incrementalencoder", {"codec_incrementalencoder",
(PyCFunction)codec_incrementalencoder, METH_VARARGS}, (PyCFunction)codec_incrementalencoder, METH_VARARGS},

View File

@ -821,6 +821,7 @@ PyLong_AsVoidPtr(PyObject *vv)
*/ */
#define IS_LITTLE_ENDIAN (int)*(unsigned char*)&one #define IS_LITTLE_ENDIAN (int)*(unsigned char*)&one
#define PY_ABS_LLONG_MIN (0-(unsigned PY_LONG_LONG)PY_LLONG_MIN)
/* Create a new long int object from a C PY_LONG_LONG int. */ /* Create a new long int object from a C PY_LONG_LONG int. */
@ -1023,6 +1024,109 @@ PyLong_AsUnsignedLongLongMask(PyObject *vv)
} }
return x * sign; return x * sign;
} }
/* Get a C long 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.
*/
PY_LONG_LONG
PyLong_AsLongLongAndOverflow(PyObject *vv, int *overflow)
{
/* This version by Tim Peters */
register PyLongObject *v;
unsigned PY_LONG_LONG x, prev;
PY_LONG_LONG res;
Py_ssize_t i;
int sign;
int do_decref = 0; /* if nb_int was called */
*overflow = 0;
if (vv == NULL) {
PyErr_BadInternalCall();
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 PY_LONG_LONG)PY_LLONG_MAX) {
res = (PY_LONG_LONG)x * sign;
}
else if (sign < 0 && x == PY_ABS_LLONG_MIN) {
res = PY_LLONG_MIN;
}
else {
*overflow = sign;
/* res is already set to -1 */
}
}
exit:
if (do_decref) {
Py_DECREF(vv);
}
return res;
}
#undef IS_LITTLE_ENDIAN #undef IS_LITTLE_ENDIAN
#endif /* HAVE_LONG_LONG */ #endif /* HAVE_LONG_LONG */