diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-01-09-11-59-04.bpo-30496.KvuuGT.rst b/Misc/NEWS.d/next/Core and Builtins/2022-01-09-11-59-04.bpo-30496.KvuuGT.rst new file mode 100644 index 00000000000..cc296841c4a --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-01-09-11-59-04.bpo-30496.KvuuGT.rst @@ -0,0 +1,3 @@ +Fixed a minor portability issue in the implementation of +:c:func:`PyLong_FromLong`, and added a fast path for single-digit integers +to :c:func:`PyLong_FromLongLong`. diff --git a/Objects/longobject.c b/Objects/longobject.c index 3438906d842..f85ef241a44 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -272,44 +272,40 @@ _PyLong_Negate(PyLongObject **x_p) } /* Create a new int object from a C long int */ + PyObject * PyLong_FromLong(long ival) { + PyLongObject *v; + unsigned long abs_ival, t; + int ndigits; + + /* Handle small and medium cases. */ if (IS_SMALL_INT(ival)) { return get_small_int((sdigit)ival); } - unsigned long abs_ival; - int sign; - if (ival < 0) { - /* negate: can't write this as abs_ival = -ival since that - invokes undefined behaviour when ival is LONG_MIN */ - abs_ival = 0U-(twodigits)ival; - sign = -1; - } - else { - abs_ival = (unsigned long)ival; - sign = 1; - } - /* Fast path for single-digit ints */ - if (!(abs_ival >> PyLong_SHIFT)) { + if (-(long)PyLong_MASK <= ival && ival <= (long)PyLong_MASK) { return _PyLong_FromMedium((sdigit)ival); } - /* Must be at least two digits. - * Do shift in two steps to avoid undefined behavior. */ - unsigned long t = (abs_ival >> PyLong_SHIFT) >> PyLong_SHIFT; - Py_ssize_t ndigits = 2; + + /* Count digits (at least two - smaller cases were handled above). */ + abs_ival = ival < 0 ? 0U-(unsigned long)ival : (unsigned long)ival; + /* Do shift in two steps to avoid possible undefined behavior. */ + t = abs_ival >> PyLong_SHIFT >> PyLong_SHIFT; + ndigits = 2; while (t) { ++ndigits; t >>= PyLong_SHIFT; } - PyLongObject *v = _PyLong_New(ndigits); + + /* Construct output value. */ + v = _PyLong_New(ndigits); if (v != NULL) { digit *p = v->ob_digit; - Py_SET_SIZE(v, ndigits * sign); + Py_SET_SIZE(v, ival < 0 ? -ndigits : ndigits); t = abs_ival; while (t) { - *p++ = Py_SAFE_DOWNCAST( - t & PyLong_MASK, unsigned long, digit); + *p++ = (digit)(t & PyLong_MASK); t >>= PyLong_SHIFT; } } @@ -1105,38 +1101,32 @@ PyObject * PyLong_FromLongLong(long long ival) { PyLongObject *v; - unsigned long long abs_ival; - unsigned long long t; /* unsigned so >> doesn't propagate sign bit */ - int ndigits = 0; - int negative = 0; + unsigned long long abs_ival, t; + int ndigits; + /* Handle small and medium cases. */ if (IS_SMALL_INT(ival)) { return get_small_int((sdigit)ival); } - - if (ival < 0) { - /* avoid signed overflow on negation; see comments - in PyLong_FromLong above. */ - abs_ival = (unsigned long long)(-1-ival) + 1; - negative = 1; - } - else { - abs_ival = (unsigned long long)ival; + if (-(long long)PyLong_MASK <= ival && ival <= (long long)PyLong_MASK) { + return _PyLong_FromMedium((sdigit)ival); } - /* Count the number of Python digits. - We used to pick 5 ("big enough for anything"), but that's a - waste of time and space given that 5*15 = 75 bits are rarely - needed. */ - t = abs_ival; + /* Count digits (at least two - smaller cases were handled above). */ + abs_ival = ival < 0 ? 0U-(unsigned long long)ival : (unsigned long long)ival; + /* Do shift in two steps to avoid possible undefined behavior. */ + t = abs_ival >> PyLong_SHIFT >> PyLong_SHIFT; + ndigits = 2; while (t) { ++ndigits; t >>= PyLong_SHIFT; } + + /* Construct output value. */ v = _PyLong_New(ndigits); if (v != NULL) { digit *p = v->ob_digit; - Py_SET_SIZE(v, negative ? -ndigits : ndigits); + Py_SET_SIZE(v, ival < 0 ? -ndigits : ndigits); t = abs_ival; while (t) { *p++ = (digit)(t & PyLong_MASK);