]> granicus.if.org Git - python/commitdiff
Issue #7767: Add new C-API function PyLong_AsLongLongAndOverflow, a
authorMark Dickinson <dickinsm@gmail.com>
Sat, 30 Jan 2010 10:08:33 +0000 (10:08 +0000)
committerMark Dickinson <dickinsm@gmail.com>
Sat, 30 Jan 2010 10:08:33 +0000 (10:08 +0000)
long long variant of PyLong_AsLongAndOverflow.  Patch by Case Van
Horsen.

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

index 7453ccd131cd4cfbea3f09d9a0e837599b91c7ac..b8fced59d9483ac552be166407f6f6f032d64537 100644 (file)
@@ -146,6 +146,19 @@ Long Integer Objects
    .. versionadded:: 2.7
 
 
+.. cfunction:: PY_LONG_LONG PyLong_AsLongLongAndOverflow(PyObject *pylong, int* overflow)
+
+   Return a C :ctype:`long long` representation of the contents of
+   *pylong*.  If *pylong* is greater than :const:`PY_LLONG_MAX` or less
+   than :const:`PY_LLONG_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 71815e5504057dd508a443397cfa4ed6499f8e70..2b4046128abfb8b29aae64909b4cf065ec45f425 100644 (file)
@@ -51,6 +51,7 @@ PyAPI_FUNC(PyObject *) PyLong_FromUnsignedLongLong(unsigned PY_LONG_LONG);
 PyAPI_FUNC(PY_LONG_LONG) PyLong_AsLongLong(PyObject *);
 PyAPI_FUNC(unsigned PY_LONG_LONG) PyLong_AsUnsignedLongLong(PyObject *);
 PyAPI_FUNC(unsigned PY_LONG_LONG) PyLong_AsUnsignedLongLongMask(PyObject *);
+PyAPI_FUNC(PY_LONG_LONG) PyLong_AsLongLongAndOverflow(PyObject *, int *);
 #endif /* HAVE_LONG_LONG */
 
 PyAPI_FUNC(PyObject *) PyLong_FromString(char *, char **, int);
index e19d902c2a8f118b84a165349d4d99da6a712ebe..056614da081381431c5d98f93949a66db5c68bc6 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -254,6 +254,9 @@ Library
 C-API
 -----
 
+- Issue #7767: New function PyLong_AsLongLongAndOverflow added,
+  analogous to PyLong_AsLongAndOverflow.
+
 - Issue #5080: The argument parsing functions PyArg_ParseTuple,
   PyArg_ParseTupleAndKeywords, PyArg_VaParse,
   PyArg_VaParseTupleAndKeywords and PyArg_Parse no longer accept float
index f0dd75e0a5a8f0dcb0f6e2707b69b722a27a8a1a..7aa57581d8a9646668637e3c6a69639746ac0cb5 100644 (file)
@@ -523,6 +523,171 @@ test_long_and_overflow(PyObject *self)
        return Py_None;
 }
 
+/* Test the PyLong_AsLongLongAndOverflow API. General conversion to
+   PY_LONG_LONG is tested by test_long_api_inner. This test will
+   concentrate on proper handling of overflow.
+*/
+
+static PyObject *
+test_long_long_and_overflow(PyObject *self)
+{
+       PyObject *num, *one, *temp;
+       PY_LONG_LONG value;
+       int overflow;
+
+       /* Test that overflow is set properly for a large value. */
+       /* num is a number larger than PY_LLONG_MAX on a typical machine. */
+       num = PyLong_FromString("FFFFFFFFFFFFFFFFFFFFFFFF", NULL, 16);
+       if (num == NULL)
+               return NULL;
+       overflow = 1234;
+       value = PyLong_AsLongLongAndOverflow(num, &overflow);
+       Py_DECREF(num);
+       if (value == -1 && PyErr_Occurred())
+               return NULL;
+       if (value != -1)
+               return raiseTestError("test_long_long_and_overflow",
+                       "return value was not set to -1");
+       if (overflow != 1)
+               return raiseTestError("test_long_long_and_overflow",
+                       "overflow was not set to 1");
+
+       /* Same again, with num = PY_LLONG_MAX + 1 */
+       num = PyLong_FromLongLong(PY_LLONG_MAX);
+       if (num == NULL)
+               return NULL;
+       one = PyLong_FromLong(1L);
+       if (one == NULL) {
+               Py_DECREF(num);
+               return NULL;
+       }
+       temp = PyNumber_Add(num, one);
+       Py_DECREF(one);
+       Py_DECREF(num);
+       num = temp;
+       if (num == NULL)
+               return NULL;
+       overflow = 0;
+       value = PyLong_AsLongLongAndOverflow(num, &overflow);
+       Py_DECREF(num);
+       if (value == -1 && PyErr_Occurred())
+               return NULL;
+       if (value != -1)
+               return raiseTestError("test_long_long_and_overflow",
+                       "return value was not set to -1");
+       if (overflow != 1)
+               return raiseTestError("test_long_long_and_overflow",
+                       "overflow was not set to 1");
+
+       /* Test that overflow is set properly for a large negative value. */
+       /* num is a number smaller than PY_LLONG_MIN on a typical platform */
+       num = PyLong_FromString("-FFFFFFFFFFFFFFFFFFFFFFFF", NULL, 16);
+       if (num == NULL)
+               return NULL;
+       overflow = 1234;
+       value = PyLong_AsLongLongAndOverflow(num, &overflow);
+       Py_DECREF(num);
+       if (value == -1 && PyErr_Occurred())
+               return NULL;
+       if (value != -1)
+               return raiseTestError("test_long_long_and_overflow",
+                       "return value was not set to -1");
+       if (overflow != -1)
+               return raiseTestError("test_long_long_and_overflow",
+                       "overflow was not set to -1");
+
+       /* Same again, with num = PY_LLONG_MIN - 1 */
+       num = PyLong_FromLongLong(PY_LLONG_MIN);
+       if (num == NULL)
+               return NULL;
+       one = PyLong_FromLong(1L);
+       if (one == NULL) {
+               Py_DECREF(num);
+               return NULL;
+       }
+       temp = PyNumber_Subtract(num, one);
+       Py_DECREF(one);
+       Py_DECREF(num);
+       num = temp;
+       if (num == NULL)
+               return NULL;
+       overflow = 0;
+       value = PyLong_AsLongLongAndOverflow(num, &overflow);
+       Py_DECREF(num);
+       if (value == -1 && PyErr_Occurred())
+               return NULL;
+       if (value != -1)
+               return raiseTestError("test_long_long_and_overflow",
+                       "return value was not set to -1");
+       if (overflow != -1)
+               return raiseTestError("test_long_long_and_overflow",
+                       "overflow was not set to -1");
+
+       /* Test that overflow is cleared properly for small values. */
+       num = PyLong_FromString("FF", NULL, 16);
+       if (num == NULL)
+               return NULL;
+       overflow = 1234;
+       value = PyLong_AsLongLongAndOverflow(num, &overflow);
+       Py_DECREF(num);
+       if (value == -1 && PyErr_Occurred())
+               return NULL;
+       if (value != 0xFF)
+               return raiseTestError("test_long_long_and_overflow",
+                       "expected return value 0xFF");
+       if (overflow != 0)
+               return raiseTestError("test_long_long_and_overflow",
+                       "overflow was not cleared");
+
+       num = PyLong_FromString("-FF", NULL, 16);
+       if (num == NULL)
+               return NULL;
+       overflow = 0;
+       value = PyLong_AsLongLongAndOverflow(num, &overflow);
+       Py_DECREF(num);
+       if (value == -1 && PyErr_Occurred())
+               return NULL;
+       if (value != -0xFF)
+               return raiseTestError("test_long_long_and_overflow",
+                       "expected return value 0xFF");
+       if (overflow != 0)
+               return raiseTestError("test_long_long_and_overflow",
+                       "overflow was set incorrectly");
+
+       num = PyLong_FromLongLong(PY_LLONG_MAX);
+       if (num == NULL)
+               return NULL;
+       overflow = 1234;
+       value = PyLong_AsLongLongAndOverflow(num, &overflow);
+       Py_DECREF(num);
+       if (value == -1 && PyErr_Occurred())
+               return NULL;
+       if (value != PY_LLONG_MAX)
+               return raiseTestError("test_long_long_and_overflow",
+                       "expected return value PY_LLONG_MAX");
+       if (overflow != 0)
+               return raiseTestError("test_long_long_and_overflow",
+                       "overflow was not cleared");
+
+       num = PyLong_FromLongLong(PY_LLONG_MIN);
+       if (num == NULL)
+               return NULL;
+       overflow = 0;
+       value = PyLong_AsLongLongAndOverflow(num, &overflow);
+       Py_DECREF(num);
+       if (value == -1 && PyErr_Occurred())
+               return NULL;
+       if (value != PY_LLONG_MIN)
+               return raiseTestError("test_long_long_and_overflow",
+                       "expected return value PY_LLONG_MIN");
+       if (overflow != 0)
+               return raiseTestError("test_long_long_and_overflow",
+                       "overflow was not cleared");
+
+       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.
@@ -1252,6 +1417,8 @@ static PyMethodDef TestMethods[] = {
        {"getargs_L",           getargs_L,                       METH_VARARGS},
        {"getargs_K",           getargs_K,                       METH_VARARGS},
        {"test_longlong_api",   test_longlong_api,               METH_NOARGS},
+       {"test_long_long_and_overflow",
+               (PyCFunction)test_long_long_and_overflow, METH_NOARGS},
        {"test_L_code",         (PyCFunction)test_L_code,        METH_NOARGS},
        {"codec_incrementalencoder",
         (PyCFunction)codec_incrementalencoder,  METH_VARARGS},
index 51442d33f57bc312fed308ae906b4856860197b1..4290f4eb8fd1fe9bee44344c05fccda65033e3f1 100644 (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 */