Skip to content

Commit c60e6b6

Browse files
authored
bpo-46311: Clean up PyLong_FromLong and PyLong_FromLongLong (GH-30496)
1 parent 7820a58 commit c60e6b6

File tree

2 files changed

+34
-41
lines changed

2 files changed

+34
-41
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fixed a minor portability issue in the implementation of
2+
:c:func:`PyLong_FromLong`, and added a fast path for single-digit integers
3+
to :c:func:`PyLong_FromLongLong`.

Objects/longobject.c

Lines changed: 31 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -272,44 +272,40 @@ _PyLong_Negate(PyLongObject **x_p)
272272
}
273273

274274
/* Create a new int object from a C long int */
275+
275276
PyObject *
276277
PyLong_FromLong(long ival)
277278
{
279+
PyLongObject *v;
280+
unsigned long abs_ival, t;
281+
int ndigits;
282+
283+
/* Handle small and medium cases. */
278284
if (IS_SMALL_INT(ival)) {
279285
return get_small_int((sdigit)ival);
280286
}
281-
unsigned long abs_ival;
282-
int sign;
283-
if (ival < 0) {
284-
/* negate: can't write this as abs_ival = -ival since that
285-
invokes undefined behaviour when ival is LONG_MIN */
286-
abs_ival = 0U-(twodigits)ival;
287-
sign = -1;
288-
}
289-
else {
290-
abs_ival = (unsigned long)ival;
291-
sign = 1;
292-
}
293-
/* Fast path for single-digit ints */
294-
if (!(abs_ival >> PyLong_SHIFT)) {
287+
if (-(long)PyLong_MASK <= ival && ival <= (long)PyLong_MASK) {
295288
return _PyLong_FromMedium((sdigit)ival);
296289
}
297-
/* Must be at least two digits.
298-
* Do shift in two steps to avoid undefined behavior. */
299-
unsigned long t = (abs_ival >> PyLong_SHIFT) >> PyLong_SHIFT;
300-
Py_ssize_t ndigits = 2;
290+
291+
/* Count digits (at least two - smaller cases were handled above). */
292+
abs_ival = ival < 0 ? 0U-(unsigned long)ival : (unsigned long)ival;
293+
/* Do shift in two steps to avoid possible undefined behavior. */
294+
t = abs_ival >> PyLong_SHIFT >> PyLong_SHIFT;
295+
ndigits = 2;
301296
while (t) {
302297
++ndigits;
303298
t >>= PyLong_SHIFT;
304299
}
305-
PyLongObject *v = _PyLong_New(ndigits);
300+
301+
/* Construct output value. */
302+
v = _PyLong_New(ndigits);
306303
if (v != NULL) {
307304
digit *p = v->ob_digit;
308-
Py_SET_SIZE(v, ndigits * sign);
305+
Py_SET_SIZE(v, ival < 0 ? -ndigits : ndigits);
309306
t = abs_ival;
310307
while (t) {
311-
*p++ = Py_SAFE_DOWNCAST(
312-
t & PyLong_MASK, unsigned long, digit);
308+
*p++ = (digit)(t & PyLong_MASK);
313309
t >>= PyLong_SHIFT;
314310
}
315311
}
@@ -1105,38 +1101,32 @@ PyObject *
11051101
PyLong_FromLongLong(long long ival)
11061102
{
11071103
PyLongObject *v;
1108-
unsigned long long abs_ival;
1109-
unsigned long long t; /* unsigned so >> doesn't propagate sign bit */
1110-
int ndigits = 0;
1111-
int negative = 0;
1104+
unsigned long long abs_ival, t;
1105+
int ndigits;
11121106

1107+
/* Handle small and medium cases. */
11131108
if (IS_SMALL_INT(ival)) {
11141109
return get_small_int((sdigit)ival);
11151110
}
1116-
1117-
if (ival < 0) {
1118-
/* avoid signed overflow on negation; see comments
1119-
in PyLong_FromLong above. */
1120-
abs_ival = (unsigned long long)(-1-ival) + 1;
1121-
negative = 1;
1122-
}
1123-
else {
1124-
abs_ival = (unsigned long long)ival;
1111+
if (-(long long)PyLong_MASK <= ival && ival <= (long long)PyLong_MASK) {
1112+
return _PyLong_FromMedium((sdigit)ival);
11251113
}
11261114

1127-
/* Count the number of Python digits.
1128-
We used to pick 5 ("big enough for anything"), but that's a
1129-
waste of time and space given that 5*15 = 75 bits are rarely
1130-
needed. */
1131-
t = abs_ival;
1115+
/* Count digits (at least two - smaller cases were handled above). */
1116+
abs_ival = ival < 0 ? 0U-(unsigned long long)ival : (unsigned long long)ival;
1117+
/* Do shift in two steps to avoid possible undefined behavior. */
1118+
t = abs_ival >> PyLong_SHIFT >> PyLong_SHIFT;
1119+
ndigits = 2;
11321120
while (t) {
11331121
++ndigits;
11341122
t >>= PyLong_SHIFT;
11351123
}
1124+
1125+
/* Construct output value. */
11361126
v = _PyLong_New(ndigits);
11371127
if (v != NULL) {
11381128
digit *p = v->ob_digit;
1139-
Py_SET_SIZE(v, negative ? -ndigits : ndigits);
1129+
Py_SET_SIZE(v, ival < 0 ? -ndigits : ndigits);
11401130
t = abs_ival;
11411131
while (t) {
11421132
*p++ = (digit)(t & PyLong_MASK);

0 commit comments

Comments
 (0)