summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Dickinson <mdickinson@enthought.com>2022-03-01 14:20:52 (GMT)
committerGitHub <noreply@github.com>2022-03-01 14:20:52 (GMT)
commitc60e6b6ad7aaf9c72035ff9fb1575e2710aeb4b4 (patch)
treea610b84cd0f694e0af62a35f040ad47b463e3a6f
parent7820a5897e7762df23bff1cbe749652130654a08 (diff)
downloadcpython-c60e6b6ad7aaf9c72035ff9fb1575e2710aeb4b4.zip
cpython-c60e6b6ad7aaf9c72035ff9fb1575e2710aeb4b4.tar.gz
cpython-c60e6b6ad7aaf9c72035ff9fb1575e2710aeb4b4.tar.bz2
bpo-46311: Clean up PyLong_FromLong and PyLong_FromLongLong (GH-30496)
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2022-01-09-11-59-04.bpo-30496.KvuuGT.rst3
-rw-r--r--Objects/longobject.c72
2 files changed, 34 insertions, 41 deletions
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 0000000..cc29684
--- /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 3438906..f85ef24 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);