]> granicus.if.org Git - python/commitdiff
Merged revisions 77842 via svnmerge from
authorMark Dickinson <dickinsm@gmail.com>
Sat, 30 Jan 2010 10:30:15 +0000 (10:30 +0000)
committerMark Dickinson <dickinsm@gmail.com>
Sat, 30 Jan 2010 10:30:15 +0000 (10:30 +0000)
svn+ssh://pythondev@svn.python.org/python/trunk

........
  r77842 | mark.dickinson | 2010-01-30 10:08:33 +0000 (Sat, 30 Jan 2010) | 4 lines

  Issue #7767: Add new C-API function PyLong_AsLongLongAndOverflow, a
  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 083fe8bfd838be7c5766457f6bac592d3f90c9e7..ba9949f73d4a5f33ecd1b99e2eaa4b3874c3c146 100644 (file)
@@ -130,6 +130,19 @@ All integers are implemented as "long" integer objects of arbitrary size.
    be ``0``.
 
 
+.. 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:: 3.2
+
+
 .. cfunction:: Py_ssize_t PyLong_AsSsize_t(PyObject *pylong)
 
    .. index::
index 2bb1e716af82b30dfa85e2087885684209a97f50..83d780d63c9f938543f40146c5a3cc47a86c8372 100644 (file)
@@ -62,6 +62,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 0d94c45357bec0aea9e8af4a01eaafd6a57a53da..d21121dd41df46a8de3e8e2bdf48d305cff3d842 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -192,6 +192,9 @@ Core and Builtins
 C-API
 -----
 
+- Issue #7767: New function PyLong_AsLongLongAndOverflow added,
+  analogous to PyLong_AsLongAndOverflow.
+
 - Make PyUnicode_CompareWithASCIIString return not equal if the Python string
   has '\0' at the end.
 
index 56717837a300b9a511ae15ea33901b144df6cc60..b6354837b40c54f4ac1d495faf5168ed6cf867a1 100644 (file)
@@ -524,6 +524,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.
@@ -1791,6 +1956,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 afac856cd0aaa338cac24ed86c606d3eb71f93e8..db7a91c556768dd59d9c7e4819e6d02ba2dbaa96 100644 (file)
@@ -971,6 +971,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. */
 
@@ -1269,6 +1270,101 @@ PyLong_AsUnsignedLongLongMask(register PyObject *op)
 }
 #undef IS_LITTLE_ENDIAN
 
+/* 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 (!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 (!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;
+}
+
 #endif /* HAVE_LONG_LONG */
 
 #define CHECK_BINOP(v,w) \