]> granicus.if.org Git - python/commitdiff
Issue #7528: Backport PyLong_AsLongAndOverflow from py3k to trunk.
authorMark Dickinson <dickinsm@gmail.com>
Mon, 21 Dec 2009 11:21:25 +0000 (11:21 +0000)
committerMark Dickinson <dickinsm@gmail.com>
Mon, 21 Dec 2009 11:21:25 +0000 (11:21 +0000)
Thanks Case Van Horsen for the patch.

Doc/c-api/long.rst
Include/longobject.h
Misc/NEWS
Modules/_testcapimodule.c
Objects/longobject.c

index c9cb034dc9677a04f37d86ad3103c1652d5f464c..7453ccd131cd4cfbea3f09d9a0e837599b91c7ac 100644 (file)
@@ -133,6 +133,19 @@ Long Integer Objects
    and ``-1`` will be returned.
 
 
+.. cfunction:: long PyLong_AsLongAndOverflow(PyObject *pylong, int* overflow)
+
+   Return a C :ctype:`long` representation of the contents of
+   *pylong*.  If *pylong* is greater than :const:`LONG_MAX` or less
+   than :const:`LONG_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::
index ea9e38767cbce1dce2f95cd6ec09383a1d443f1c..0cb8e2ff3a40bffee30f381cf001a9bfaa9cfdc9 100644 (file)
@@ -21,6 +21,7 @@ PyAPI_FUNC(PyObject *) PyLong_FromDouble(double);
 PyAPI_FUNC(PyObject *) PyLong_FromSize_t(size_t);
 PyAPI_FUNC(PyObject *) PyLong_FromSsize_t(Py_ssize_t);
 PyAPI_FUNC(long) PyLong_AsLong(PyObject *);
+PyAPI_FUNC(long) PyLong_AsLongAndOverflow(PyObject *, int *);
 PyAPI_FUNC(unsigned long) PyLong_AsUnsignedLong(PyObject *);
 PyAPI_FUNC(unsigned long) PyLong_AsUnsignedLongMask(PyObject *);
 PyAPI_FUNC(Py_ssize_t) PyLong_AsSsize_t(PyObject *);
index 3c407e0c2b274983ded48e80dccf8930df8b0fbc..c82eb63c27fd3c5ef51b0032fb5baa0d1299c1de 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -1648,6 +1648,8 @@ Documentation
 C-API
 -----
 
+- Issue #7528: Add PyLong_AsLongAndOverflow (backported from py3k).
+
 - Issue #7228: Add '%lld' and '%llu' support to PyString_FromFormat(V)
   and PyErr_Format, on machines with HAVE_LONG_LONG defined.
 
index b68e76dc0aac5ca028f06d004c264b3f00a18045..2bcf75446ee251ab25890a32370ea0e7f719e311 100644 (file)
@@ -358,6 +358,75 @@ test_longlong_api(PyObject* self, PyObject *args)
 #undef F_U_TO_PY
 #undef F_PY_TO_U
 
+/* Test the PyLong_AsLongAndOverflow API. General conversion to PY_LONG
+   is tested by test_long_api_inner. This test will concentrate on proper
+   handling of overflow.
+*/
+
+static PyObject *
+test_long_and_overflow(PyObject *self)
+{
+       PyObject *num;
+       long value;
+       int overflow;
+
+       /* a number larger than LONG_MAX even on 64-bit platforms */
+       num = PyLong_FromString("FFFFFFFFFFFFFFFFFFFFFFFF", NULL, 16);
+       if (num == NULL)
+               return NULL;
+
+       /* Test that overflow is set properly for a large value. */
+       overflow = 1234;
+       value = PyLong_AsLongAndOverflow(num, &overflow);
+       if (overflow != 1)
+               return raiseTestError("test_long_and_overflow",
+                       "overflow was not set to 1");
+
+       overflow = 0;
+       value = PyLong_AsLongAndOverflow(num, &overflow);
+       if (overflow != 1)
+               return raiseTestError("test_long_and_overflow",
+                       "overflow was not set to 0");
+
+       /* a number smaller than LONG_MIN even on 64-bit platforms */
+       num = PyLong_FromString("-FFFFFFFFFFFFFFFFFFFFFFFF", NULL, 16);
+       if (num == NULL)
+               return NULL;
+
+       /* Test that overflow is set properly for a large negative value. */
+       overflow = 1234;
+       value = PyLong_AsLongAndOverflow(num, &overflow);
+       if (overflow != -1)
+               return raiseTestError("test_long_and_overflow",
+                       "overflow was not set to -1");
+
+       overflow = 0;
+       value = PyLong_AsLongAndOverflow(num, &overflow);
+       if (overflow != -1)
+               return raiseTestError("test_long_and_overflow",
+                       "overflow was not set to 0");
+
+       num = PyLong_FromString("FF", NULL, 16);
+       if (num == NULL)
+               return NULL;
+
+       /* Test that overflow is cleared properly for a small value. */
+       overflow = 1234;
+       value = PyLong_AsLongAndOverflow(num, &overflow);
+       if (overflow != 0)
+               return raiseTestError("test_long_and_overflow",
+                       "overflow was not cleared");
+
+       overflow = 0;
+       value = PyLong_AsLongAndOverflow(num, &overflow);
+       if (overflow != 0)
+               return raiseTestError("test_long_and_overflow",
+                       "overflow was set incorrectly");
+
+       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.
@@ -1041,6 +1110,7 @@ static PyMethodDef TestMethods[] = {
        {"test_dict_iteration", (PyCFunction)test_dict_iteration,METH_NOARGS},
        {"test_lazy_hash_inheritance",  (PyCFunction)test_lazy_hash_inheritance,METH_NOARGS},
        {"test_long_api",       (PyCFunction)test_long_api,      METH_NOARGS},
+       {"test_long_and_overflow",      (PyCFunction)test_long_and_overflow,    METH_NOARGS},
        {"test_long_numbits",   (PyCFunction)test_long_numbits,  METH_NOARGS},
        {"test_k_code",         (PyCFunction)test_k_code,        METH_NOARGS},
        {"test_empty_argparse", (PyCFunction)test_empty_argparse,METH_NOARGS},
index 8bdb283eab2ae67ee6871821ce735a6f1959679c..b0170d27063d74c720e5169b020e11ece1da6fed 100644 (file)
@@ -223,53 +223,123 @@ PyLong_FromDouble(double dval)
 #define PY_ABS_LONG_MIN                (0-(unsigned long)LONG_MIN)
 #define PY_ABS_SSIZE_T_MIN     (0-(size_t)PY_SSIZE_T_MIN)
 
-/* Get a C long int from a long int object.
-   Returns -1 and sets an error condition if overflow occurs. */
+/* Get a C 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.
+*/
 
 long
-PyLong_AsLong(PyObject *vv)
+PyLong_AsLongAndOverflow(PyObject *vv, int *overflow)
 {
        /* This version by Tim Peters */
        register PyLongObject *v;
        unsigned long x, prev;
+       long res;
        Py_ssize_t i;
        int sign;
+       int do_decref = 0; /* if nb_int was called */
 
-       if (vv == NULL || !PyLong_Check(vv)) {
-               if (vv != NULL && PyInt_Check(vv))
-                       return PyInt_AsLong(vv);
+       *overflow = 0;
+       if (vv == NULL) {
                PyErr_BadInternalCall();
                return -1;
        }
-       v = (PyLongObject *)vv;
-       i = v->ob_size;
-       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)
-                       goto overflow;
+
+       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;
+               }
        }
-       /* Haven't lost any bits, but casting to long requires extra care
-        * (see comment above).
-         */
-       if (x <= (unsigned long)LONG_MAX) {
-               return (long)x * sign;
+
+       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 long)LONG_MAX) {
+                       res = (long)x * sign;
+               }
+               else if (sign < 0 && x == PY_ABS_LONG_MIN) {
+                       res = LONG_MIN;
+               }
+               else {
+                       *overflow = sign;
+                       /* res is already set to -1 */
+               }
        }
-       else if (sign < 0 && x == PY_ABS_LONG_MIN) {
-               return LONG_MIN;
+ exit:
+       if (do_decref) {
+               Py_DECREF(vv);
        }
-       /* else overflow */
+       return res;
+}
 
- overflow:
-       PyErr_SetString(PyExc_OverflowError,
-                       "long int too large to convert to int");
-       return -1;
+/* Get a C long int from a long int object.
+   Returns -1 and sets an error condition if overflow occurs. */
+
+long
+PyLong_AsLong(PyObject *obj)
+{
+       int overflow;
+       long result = PyLong_AsLongAndOverflow(obj, &overflow);
+       if (overflow) {
+               /* XXX: could be cute and give a different
+                  message for overflow == -1 */
+               PyErr_SetString(PyExc_OverflowError,
+                               "Python int too large to convert to C long");
+       }
+       return result;
 }
 
 /* Get a Py_ssize_t from a long int object.