bpo-46311: Clean up PyLong_FromLong and PyLong_FromLongLong (GH-30496)

This commit is contained in:
Mark Dickinson 2022-03-01 14:20:52 +00:00 committed by GitHub
parent 7820a5897e
commit c60e6b6ad7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 34 additions and 41 deletions

View File

@ -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`.

View File

@ -272,44 +272,40 @@ _PyLong_Negate(PyLongObject **x_p)
} }
/* Create a new int object from a C long int */ /* Create a new int object from a C long int */
PyObject * PyObject *
PyLong_FromLong(long ival) PyLong_FromLong(long ival)
{ {
PyLongObject *v;
unsigned long abs_ival, t;
int ndigits;
/* Handle small and medium cases. */
if (IS_SMALL_INT(ival)) { if (IS_SMALL_INT(ival)) {
return get_small_int((sdigit)ival); return get_small_int((sdigit)ival);
} }
unsigned long abs_ival; if (-(long)PyLong_MASK <= ival && ival <= (long)PyLong_MASK) {
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)) {
return _PyLong_FromMedium((sdigit)ival); return _PyLong_FromMedium((sdigit)ival);
} }
/* Must be at least two digits.
* Do shift in two steps to avoid undefined behavior. */ /* Count digits (at least two - smaller cases were handled above). */
unsigned long t = (abs_ival >> PyLong_SHIFT) >> PyLong_SHIFT; abs_ival = ival < 0 ? 0U-(unsigned long)ival : (unsigned long)ival;
Py_ssize_t ndigits = 2; /* Do shift in two steps to avoid possible undefined behavior. */
t = abs_ival >> PyLong_SHIFT >> PyLong_SHIFT;
ndigits = 2;
while (t) { while (t) {
++ndigits; ++ndigits;
t >>= PyLong_SHIFT; t >>= PyLong_SHIFT;
} }
PyLongObject *v = _PyLong_New(ndigits);
/* Construct output value. */
v = _PyLong_New(ndigits);
if (v != NULL) { if (v != NULL) {
digit *p = v->ob_digit; digit *p = v->ob_digit;
Py_SET_SIZE(v, ndigits * sign); Py_SET_SIZE(v, ival < 0 ? -ndigits : ndigits);
t = abs_ival; t = abs_ival;
while (t) { while (t) {
*p++ = Py_SAFE_DOWNCAST( *p++ = (digit)(t & PyLong_MASK);
t & PyLong_MASK, unsigned long, digit);
t >>= PyLong_SHIFT; t >>= PyLong_SHIFT;
} }
} }
@ -1105,38 +1101,32 @@ PyObject *
PyLong_FromLongLong(long long ival) PyLong_FromLongLong(long long ival)
{ {
PyLongObject *v; PyLongObject *v;
unsigned long long abs_ival; unsigned long long abs_ival, t;
unsigned long long t; /* unsigned so >> doesn't propagate sign bit */ int ndigits;
int ndigits = 0;
int negative = 0;
/* Handle small and medium cases. */
if (IS_SMALL_INT(ival)) { if (IS_SMALL_INT(ival)) {
return get_small_int((sdigit)ival); return get_small_int((sdigit)ival);
} }
if (-(long long)PyLong_MASK <= ival && ival <= (long long)PyLong_MASK) {
if (ival < 0) { return _PyLong_FromMedium((sdigit)ival);
/* 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;
} }
/* Count the number of Python digits. /* Count digits (at least two - smaller cases were handled above). */
We used to pick 5 ("big enough for anything"), but that's a abs_ival = ival < 0 ? 0U-(unsigned long long)ival : (unsigned long long)ival;
waste of time and space given that 5*15 = 75 bits are rarely /* Do shift in two steps to avoid possible undefined behavior. */
needed. */ t = abs_ival >> PyLong_SHIFT >> PyLong_SHIFT;
t = abs_ival; ndigits = 2;
while (t) { while (t) {
++ndigits; ++ndigits;
t >>= PyLong_SHIFT; t >>= PyLong_SHIFT;
} }
/* Construct output value. */
v = _PyLong_New(ndigits); v = _PyLong_New(ndigits);
if (v != NULL) { if (v != NULL) {
digit *p = v->ob_digit; digit *p = v->ob_digit;
Py_SET_SIZE(v, negative ? -ndigits : ndigits); Py_SET_SIZE(v, ival < 0 ? -ndigits : ndigits);
t = abs_ival; t = abs_ival;
while (t) { while (t) {
*p++ = (digit)(t & PyLong_MASK); *p++ = (digit)(t & PyLong_MASK);