From d1a7da6c0d377d2296b79c4203d267ffe1664bfb Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Wed, 13 Jun 2001 00:35:57 +0000 Subject: longobject.c: Replaced PyLong_{As,From}{Unsigned,}LongLong guts with calls to _PyLong_{As,From}ByteArray. _testcapimodule.c: Added strong tests of PyLong_{As,From}{Unsigned,}LongLong. Fixes SF bug #432552 PyLong_AsLongLong() problems. Possible bugfix candidate, but the fix relies on code added to longobject to support the new q/Q structmodule format codes. --- Modules/_testcapimodule.c | 173 ++++++++++++++++++++++++++++++++++++++++++---- Objects/longobject.c | 167 +++++++++++--------------------------------- 2 files changed, 199 insertions(+), 141 deletions(-) diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index a49c60a..06602e9 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -177,28 +177,171 @@ test_dict_iteration(PyObject* self, PyObject* args) /* Basic sanity checks for PyLong_{As, From}{Unsigned,}LongLong(). */ static PyObject * +raise_test_longlong_error(const char* msg) +{ + return raiseTestError("test_longlong_api", msg); +} + +#define UNBIND(X) Py_DECREF(X); (X) = NULL + +static PyObject * test_longlong_api(PyObject* self, PyObject* args) { - /* unsigned LONG_LONG uinput, uoutput; */ - LONG_LONG input, output; + const int NBITS = SIZEOF_LONG_LONG * 8; + unsigned LONG_LONG base; PyObject *pyresult; + int i; if (!PyArg_ParseTuple(args, ":test_longlong_api")) return NULL; - input = 0; - pyresult = PyLong_FromLongLong(input); - if (pyresult == NULL) - return raiseTestError("test_longlong_api", - "unexpected null result"); - output = PyLong_AsLongLong(pyresult); - if (output == (LONG_LONG)-1 && PyErr_Occurred()) - return raiseTestError("test_longlong_api", - "unexpected -1 result"); - if (output != input) - return raiseTestError("test_longlong_api", - "output != input"); - Py_DECREF(pyresult); + + /* Note: This test lets PyObjects leak if an error is raised. Since + an error should never be raised, leaks are impossible . */ + + /* Test native -> PyLong -> native roundtrip identity. + * Generate all powers of 2, and test them and their negations, + * plus the numbers +-1 off from them. + */ + base = 1; + for (i = 0; + i < NBITS + 1; /* on last, base overflows to 0 */ + ++i, base <<= 1) + { + int j; + for (j = 0; j < 6; ++j) { + LONG_LONG in, out; + unsigned LONG_LONG uin, uout; + + /* For 0, 1, 2 use base; for 3, 4, 5 use -base */ + uin = j < 3 ? base + : (unsigned LONG_LONG)(-(LONG_LONG)base); + + /* For 0 & 3, subtract 1. + * For 1 & 4, leave alone. + * For 2 & 5, add 1. + */ + uin += (unsigned LONG_LONG)(LONG_LONG)(j % 3 - 1); + + pyresult = PyLong_FromUnsignedLongLong(uin); + if (pyresult == NULL) + return raise_test_longlong_error( + "unsigned unexpected null result"); + + uout = PyLong_AsUnsignedLongLong(pyresult); + if (uout == (unsigned LONG_LONG)-1 && PyErr_Occurred()) + return raise_test_longlong_error( + "unsigned unexpected -1 result"); + if (uout != uin) + return raise_test_longlong_error( + "unsigned output != input"); + UNBIND(pyresult); + + in = (LONG_LONG)uin; + pyresult = PyLong_FromLongLong(in); + if (pyresult == NULL) + return raise_test_longlong_error( + "signed unexpected null result"); + + out = PyLong_AsLongLong(pyresult); + if (out == (LONG_LONG)-1 && PyErr_Occurred()) + return raise_test_longlong_error( + "signed unexpected -1 result"); + if (out != in) + return raise_test_longlong_error( + "signed output != input"); + UNBIND(pyresult); + } + } + + /* Overflow tests. The loop above ensured that all limit cases that + * should not overflow don't overflow, so all we need to do here is + * provoke one-over-the-limit cases (not exhaustive, but sharp). + */ + { + PyObject *one, *x, *y; + LONG_LONG out; + unsigned LONG_LONG uout; + + one = PyLong_FromLong(1); + if (one == NULL) + return raise_test_longlong_error( + "unexpected NULL from PyLong_FromLong"); + + /* Unsigned complains about -1? */ + x = PyNumber_Negative(one); + if (x == NULL) + return raise_test_longlong_error( + "unexpected NULL from PyNumber_Negative"); + + uout = PyLong_AsUnsignedLongLong(x); + if (uout != (unsigned LONG_LONG)-1 || !PyErr_Occurred()) + return raise_test_longlong_error( + "PyLong_AsUnsignedLongLong(-1) didn't " + "complain"); + PyErr_Clear(); + UNBIND(x); + + /* Unsigned complains about 2**NBITS? */ + y = PyLong_FromLong((long)NBITS); + if (y == NULL) + return raise_test_longlong_error( + "unexpected NULL from PyLong_FromLong"); + + x = PyNumber_Lshift(one, y); /* 1L << NBITS, == 2**NBITS */ + UNBIND(y); + if (x == NULL) + return raise_test_longlong_error( + "unexpected NULL from PyNumber_Lshift"); + + uout = PyLong_AsUnsignedLongLong(x); + if (uout != (unsigned LONG_LONG)-1 || !PyErr_Occurred()) + return raise_test_longlong_error( + "PyLong_AsUnsignedLongLong(2**NBITS) didn't " + "complain"); + PyErr_Clear(); + + /* Signed complains about 2**(NBITS-1)? + x still has 2**NBITS. */ + y = PyNumber_Rshift(x, one); /* 2**(NBITS-1) */ + UNBIND(x); + if (y == NULL) + return raise_test_longlong_error( + "unexpected NULL from PyNumber_Rshift"); + + out = PyLong_AsLongLong(y); + if (out != (LONG_LONG)-1 || !PyErr_Occurred()) + return raise_test_longlong_error( + "PyLong_AsLongLong(2**(NBITS-1)) didn't " + "complain"); + PyErr_Clear(); + + /* Signed complains about -2**(NBITS-1)-1?; + y still has 2**(NBITS-1). */ + x = PyNumber_Negative(y); /* -(2**(NBITS-1)) */ + UNBIND(y); + if (x == NULL) + return raise_test_longlong_error( + "unexpected NULL from PyNumber_Negative"); + + y = PyNumber_Subtract(x, one); /* -(2**(NBITS-1))-1 */ + UNBIND(x); + if (y == NULL) + return raise_test_longlong_error( + "unexpected NULL from PyNumber_Subtract"); + + out = PyLong_AsLongLong(y); + if (out != (LONG_LONG)-1 || !PyErr_Occurred()) + return raise_test_longlong_error( + "PyLong_AsLongLong(-2**(NBITS-1)-1) didn't " + "complain"); + PyErr_Clear(); + UNBIND(y); + + Py_XDECREF(x); + Py_XDECREF(y); + Py_DECREF(one); + } Py_INCREF(Py_None); return Py_None; diff --git a/Objects/longobject.c b/Objects/longobject.c index 17af671..615d497 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -522,168 +522,83 @@ PyLong_AsVoidPtr(PyObject *vv) } #ifdef HAVE_LONG_LONG -/* - * LONG_LONG support by Chris Herborth (chrish@qnx.com) - * - * For better or worse :-), I tried to follow the coding style already - * here. + +/* Initial LONG_LONG support by Chris Herborth (chrish@qnx.com), later + * rewritten to use the newer PyLong_{As,From}ByteArray API. */ -/* Create a new long int object from a C LONG_LONG int */ +#define IS_LITTLE_ENDIAN *(char*)&one != '\0' + +/* Create a new long int object from a C LONG_LONG int. */ PyObject * PyLong_FromLongLong(LONG_LONG ival) { -#if SIZEOF_LONG_LONG == SIZEOF_LONG - /* In case the compiler is faking it. */ - return PyLong_FromLong( (long)ival ); -#else - if ((LONG_LONG)LONG_MIN <= ival && ival <= (LONG_LONG)LONG_MAX) { - return PyLong_FromLong( (long)ival ); - } - else if (0 <= ival && ival <= (unsigned LONG_LONG)ULONG_MAX) { - return PyLong_FromUnsignedLong( (unsigned long)ival ); - } - else { - /* Assume a C LONG_LONG fits in at most 10 'digits'. - * Should be OK if we're assuming long fits in 5. - */ - PyLongObject *v = _PyLong_New(10); - - if (v != NULL) { - unsigned LONG_LONG t = ival; - int i; - if (ival < 0) { - t = -ival; - v->ob_size = -(v->ob_size); - } - - for (i = 0; i < 10; i++) { - v->ob_digit[i] = (digit) (t & MASK); - t >>= SHIFT; - } - - v = long_normalize(v); - } - - return (PyObject *)v; - } -#endif + LONG_LONG bytes = ival; + int one = 1; + return _PyLong_FromByteArray( + (unsigned char *)&bytes, + SIZEOF_LONG_LONG, IS_LITTLE_ENDIAN, 1); } -/* Create a new long int object from a C unsigned LONG_LONG int */ +/* Create a new long int object from a C unsigned LONG_LONG int. */ + PyObject * PyLong_FromUnsignedLongLong(unsigned LONG_LONG ival) { -#if SIZEOF_LONG_LONG == SIZEOF_LONG - /* In case the compiler is faking it. */ - return PyLong_FromUnsignedLong( (unsigned long)ival ); -#else - if( ival <= (unsigned LONG_LONG)ULONG_MAX ) { - return PyLong_FromUnsignedLong( (unsigned long)ival ); - } - else { - /* Assume a C long fits in at most 10 'digits'. */ - PyLongObject *v = _PyLong_New(10); - - if (v != NULL) { - unsigned LONG_LONG t = ival; - int i; - for (i = 0; i < 10; i++) { - v->ob_digit[i] = (digit) (t & MASK); - t >>= SHIFT; - } - - v = long_normalize(v); - } - - return (PyObject *)v; - } -#endif + unsigned LONG_LONG bytes = ival; + int one = 1; + return _PyLong_FromByteArray( + (unsigned char *)&bytes, + SIZEOF_LONG_LONG, IS_LITTLE_ENDIAN, 0); } /* Get a C LONG_LONG int from a long int object. - Returns -1 and sets an error condition if overflow occurs. */ + Return -1 and set an error if overflow occurs. */ LONG_LONG PyLong_AsLongLong(PyObject *vv) { -#if SIZEOF_LONG_LONG == SIZEOF_LONG - /* In case the compiler is faking it. */ - return (LONG_LONG)PyLong_AsLong( vv ); -#else - register PyLongObject *v; - LONG_LONG x, prev; - int i, sign; - + LONG_LONG bytes; + int one = 1; + int res; + if (vv == NULL || !PyLong_Check(vv)) { PyErr_BadInternalCall(); return -1; } - v = (PyLongObject *)vv; - i = v->ob_size; - sign = 1; - x = 0; + res = _PyLong_AsByteArray( + (PyLongObject *)vv, (unsigned char *)&bytes, + SIZEOF_LONG_LONG, IS_LITTLE_ENDIAN, 1); - if (i < 0) { - sign = -1; - i = -(i); - } - - while (--i >= 0) { - prev = x; - x = (x << SHIFT) + v->ob_digit[i]; - if ((x >> SHIFT) != prev) { - PyErr_SetString(PyExc_OverflowError, - "long int too long to convert"); - return -1; - } - } - - return x * sign; -#endif + return (LONG_LONG)(res < 0 ? res : bytes); } +/* Get a C unsigned LONG_LONG int from a long int object. + Return -1 and set an error if overflow occurs. */ + unsigned LONG_LONG PyLong_AsUnsignedLongLong(PyObject *vv) { -#if SIZEOF_LONG_LONG == 4 - /* In case the compiler is faking it. */ - return (unsigned LONG_LONG)PyLong_AsUnsignedLong( vv ); -#else - register PyLongObject *v; - unsigned LONG_LONG x, prev; - int i; - + unsigned LONG_LONG bytes; + int one = 1; + int res; + if (vv == NULL || !PyLong_Check(vv)) { PyErr_BadInternalCall(); - return (unsigned LONG_LONG) -1; + return -1; } - v = (PyLongObject *)vv; - i = v->ob_size; - x = 0; + res = _PyLong_AsByteArray( + (PyLongObject *)vv, (unsigned char *)&bytes, + SIZEOF_LONG_LONG, IS_LITTLE_ENDIAN, 0); - if (i < 0) { - PyErr_SetString(PyExc_OverflowError, - "can't convert negative value to unsigned long"); - return (unsigned LONG_LONG) -1; - } + return (unsigned LONG_LONG)(res < 0 ? res : bytes); +} - while (--i >= 0) { - prev = x; - x = (x << SHIFT) + v->ob_digit[i]; - if ((x >> SHIFT) != prev) { - PyErr_SetString(PyExc_OverflowError, - "long int too long to convert"); - return (unsigned LONG_LONG) -1; - } - } +#undef IS_LITTLE_ENDIAN - return x; -#endif -} #endif /* HAVE_LONG_LONG */ -- cgit v0.12