bpo-29816: Shift operation now has less opportunity to raise OverflowError. (#680)

ValueError always is raised rather than OverflowError for negative counts.
Shifting zero with non-negative count always returns zero.
This commit is contained in:
Serhiy Storchaka 2017-03-30 09:47:07 +03:00 committed by GitHub
parent 762bf40438
commit 918403cfc3
3 changed files with 82 additions and 24 deletions

View file

@ -4277,15 +4277,54 @@ long_bool(PyLongObject *v)
return Py_SIZE(v) != 0;
}
/* wordshift, remshift = divmod(shiftby, PyLong_SHIFT) */
static int
divmod_shift(PyLongObject *shiftby, Py_ssize_t *wordshift, digit *remshift)
{
assert(PyLong_Check((PyObject *)shiftby));
assert(Py_SIZE(shiftby) >= 0);
Py_ssize_t lshiftby = PyLong_AsSsize_t((PyObject *)shiftby);
if (lshiftby >= 0) {
*wordshift = lshiftby / PyLong_SHIFT;
*remshift = lshiftby % PyLong_SHIFT;
return 0;
}
/* PyLong_Check(shiftby) is true and Py_SIZE(shiftby) >= 0, so it must
be that PyLong_AsSsize_t raised an OverflowError. */
assert(PyErr_ExceptionMatches(PyExc_OverflowError));
PyErr_Clear();
PyLongObject *wordshift_obj = divrem1(shiftby, PyLong_SHIFT, remshift);
if (wordshift_obj == NULL) {
return -1;
}
*wordshift = PyLong_AsSsize_t((PyObject *)wordshift_obj);
Py_DECREF(wordshift_obj);
if (*wordshift >= 0 && *wordshift < PY_SSIZE_T_MAX / (Py_ssize_t)sizeof(digit)) {
return 0;
}
PyErr_Clear();
/* Clip the value. With such large wordshift the right shift
returns 0 and the left shift raises an error in _PyLong_New(). */
*wordshift = PY_SSIZE_T_MAX / sizeof(digit);
*remshift = 0;
return 0;
}
static PyObject *
long_rshift(PyLongObject *a, PyLongObject *b)
{
PyLongObject *z = NULL;
Py_ssize_t shiftby, newsize, wordshift, loshift, hishift, i, j;
digit lomask, himask;
Py_ssize_t newsize, wordshift, hishift, i, j;
digit loshift, lomask, himask;
CHECK_BINOP(a, b);
if (Py_SIZE(b) < 0) {
PyErr_SetString(PyExc_ValueError,
"negative shift count");
return NULL;
}
if (Py_SIZE(a) < 0) {
/* Right shifting negative numbers is harder */
PyLongObject *a1, *a2;
@ -4300,19 +4339,11 @@ long_rshift(PyLongObject *a, PyLongObject *b)
Py_DECREF(a2);
}
else {
shiftby = PyLong_AsSsize_t((PyObject *)b);
if (shiftby == -1L && PyErr_Occurred())
if (divmod_shift(b, &wordshift, &loshift) < 0)
return NULL;
if (shiftby < 0) {
PyErr_SetString(PyExc_ValueError,
"negative shift count");
return NULL;
}
wordshift = shiftby / PyLong_SHIFT;
newsize = Py_ABS(Py_SIZE(a)) - wordshift;
newsize = Py_SIZE(a) - wordshift;
if (newsize <= 0)
return PyLong_FromLong(0);
loshift = shiftby % PyLong_SHIFT;
hishift = PyLong_SHIFT - loshift;
lomask = ((digit)1 << hishift) - 1;
himask = PyLong_MASK ^ lomask;
@ -4336,27 +4367,22 @@ long_lshift(PyObject *v, PyObject *w)
PyLongObject *a = (PyLongObject*)v;
PyLongObject *b = (PyLongObject*)w;
PyLongObject *z = NULL;
Py_ssize_t shiftby, oldsize, newsize, wordshift, remshift, i, j;
Py_ssize_t oldsize, newsize, wordshift, i, j;
digit remshift;
twodigits accum;
CHECK_BINOP(a, b);
shiftby = PyLong_AsSsize_t((PyObject *)b);
if (shiftby == -1L && PyErr_Occurred())
return NULL;
if (shiftby < 0) {
if (Py_SIZE(b) < 0) {
PyErr_SetString(PyExc_ValueError, "negative shift count");
return NULL;
}
if (Py_SIZE(a) == 0) {
return PyLong_FromLong(0);
}
/* wordshift, remshift = divmod(shiftby, PyLong_SHIFT) */
wordshift = shiftby / PyLong_SHIFT;
remshift = shiftby - wordshift * PyLong_SHIFT;
if (divmod_shift(b, &wordshift, &remshift) < 0)
return NULL;
oldsize = Py_ABS(Py_SIZE(a));
newsize = oldsize + wordshift;
if (remshift)