]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
longobject.c:
authorTim Peters <tim.peters@gmail.com>
Wed, 13 Jun 2001 00:35:57 +0000 (00:35 +0000)
committerTim Peters <tim.peters@gmail.com>
Wed, 13 Jun 2001 00:35:57 +0000 (00:35 +0000)
    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
Objects/longobject.c

index a49c60a6b27cc2786e47782b54730887cb272873..06602e93e7b8c9897cb62d7925b673c3a046c429 100644 (file)
@@ -176,29 +176,172 @@ 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;
index 17af671d6ba3810a9b5d4a59539b87f407bc64ba..615d497632cd85a3085de1c880a18282b08a2f43 100644 (file)
@@ -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 */