mirror of https://github.com/python/cpython
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:
parent
a2d4653740
commit
a36507c64c
|
@ -146,6 +146,19 @@ Long Integer Objects
|
|||
.. 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)
|
||||
|
||||
.. index::
|
||||
|
|
|
@ -51,6 +51,7 @@ PyAPI_FUNC(PyObject *) PyLong_FromUnsignedLongLong(unsigned PY_LONG_LONG);
|
|||
PyAPI_FUNC(PY_LONG_LONG) PyLong_AsLongLong(PyObject *);
|
||||
PyAPI_FUNC(unsigned PY_LONG_LONG) PyLong_AsUnsignedLongLong(PyObject *);
|
||||
PyAPI_FUNC(unsigned PY_LONG_LONG) PyLong_AsUnsignedLongLongMask(PyObject *);
|
||||
PyAPI_FUNC(PY_LONG_LONG) PyLong_AsLongLongAndOverflow(PyObject *, int *);
|
||||
#endif /* HAVE_LONG_LONG */
|
||||
|
||||
PyAPI_FUNC(PyObject *) PyLong_FromString(char *, char **, int);
|
||||
|
|
|
@ -254,6 +254,9 @@ Library
|
|||
C-API
|
||||
-----
|
||||
|
||||
- Issue #7767: New function PyLong_AsLongLongAndOverflow added,
|
||||
analogous to PyLong_AsLongAndOverflow.
|
||||
|
||||
- Issue #5080: The argument parsing functions PyArg_ParseTuple,
|
||||
PyArg_ParseTupleAndKeywords, PyArg_VaParse,
|
||||
PyArg_VaParseTupleAndKeywords and PyArg_Parse no longer accept float
|
||||
|
|
|
@ -523,6 +523,171 @@ test_long_and_overflow(PyObject *self)
|
|||
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
|
||||
for both long and int arguments. The test may leak a little memory if
|
||||
it fails.
|
||||
|
@ -1252,6 +1417,8 @@ static PyMethodDef TestMethods[] = {
|
|||
{"getargs_L", getargs_L, METH_VARARGS},
|
||||
{"getargs_K", getargs_K, METH_VARARGS},
|
||||
{"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},
|
||||
{"codec_incrementalencoder",
|
||||
(PyCFunction)codec_incrementalencoder, METH_VARARGS},
|
||||
|
|
|
@ -821,6 +821,7 @@ PyLong_AsVoidPtr(PyObject *vv)
|
|||
*/
|
||||
|
||||
#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. */
|
||||
|
||||
|
@ -1023,6 +1024,109 @@ PyLong_AsUnsignedLongLongMask(PyObject *vv)
|
|||
}
|
||||
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
|
||||
|
||||
#endif /* HAVE_LONG_LONG */
|
||||
|
|
Loading…
Reference in New Issue