summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTim Peters <tim.peters@gmail.com>2001-06-13 00:35:57 (GMT)
committerTim Peters <tim.peters@gmail.com>2001-06-13 00:35:57 (GMT)
commitd1a7da6c0d377d2296b79c4203d267ffe1664bfb (patch)
treed9a0712ad0b51b7e5c56c20aa905d64d29c0c42e
parent91621dbcbe2891c50e5ade04310eb164f9da17f4 (diff)
downloadcpython-d1a7da6c0d377d2296b79c4203d267ffe1664bfb.zip
cpython-d1a7da6c0d377d2296b79c4203d267ffe1664bfb.tar.gz
cpython-d1a7da6c0d377d2296b79c4203d267ffe1664bfb.tar.bz2
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.
-rw-r--r--Modules/_testcapimodule.c173
-rw-r--r--Objects/longobject.c167
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 <wink>. */
+
+ /* 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 */