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

@ -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 */