diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-01-25-20-07-03.gh-issue-129149.njeFJi.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-25-20-07-03.gh-issue-129149.njeFJi.rst new file mode 100644 index 00000000000000..b74607986d9a2e --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-25-20-07-03.gh-issue-129149.njeFJi.rst @@ -0,0 +1,2 @@ +Add fast path for medium-size integers in :c:func:`PyLong_FromSsize_t`. +Patch by Chris Eibl. diff --git a/Objects/longobject.c b/Objects/longobject.c index 370328dcfe8c9a..966c4d85e5cf45 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -332,49 +332,51 @@ _PyLong_Negate(PyLongObject **x_p) Py_DECREF(x); } +#define PYLONG_FROM_INT(UINT_TYPE, INT_TYPE, ival) \ + do { \ + /* Handle small and medium cases. */ \ + if (IS_SMALL_INT(ival)) { \ + return get_small_int((sdigit)(ival)); \ + } \ + if (-(INT_TYPE)PyLong_MASK <= (ival) && (ival) <= (INT_TYPE)PyLong_MASK) { \ + return _PyLong_FromMedium((sdigit)(ival)); \ + } \ + UINT_TYPE abs_ival = (ival) < 0 ? 0U-(UINT_TYPE)(ival) : (UINT_TYPE)(ival); \ + /* Do shift in two steps to avoid possible undefined behavior. */ \ + UINT_TYPE t = abs_ival >> PyLong_SHIFT >> PyLong_SHIFT; \ + /* Count digits (at least two - smaller cases were handled above). */ \ + Py_ssize_t ndigits = 2; \ + while (t) { \ + ++ndigits; \ + t >>= PyLong_SHIFT; \ + } \ + /* Construct output value. */ \ + PyLongObject *v = long_alloc(ndigits); \ + if (v == NULL) { \ + return NULL; \ + } \ + digit *p = v->long_value.ob_digit; \ + _PyLong_SetSignAndDigitCount(v, (ival) < 0 ? -1 : 1, ndigits); \ + t = abs_ival; \ + while (t) { \ + *p++ = (digit)(t & PyLong_MASK); \ + t >>= PyLong_SHIFT; \ + } \ + return (PyObject *)v; \ + } while(0) + + /* 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); - } - if (-(long)PyLong_MASK <= ival && ival <= (long)PyLong_MASK) { - return _PyLong_FromMedium((sdigit)ival); - } - - /* 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; - } - - /* Construct output value. */ - v = long_alloc(ndigits); - if (v != NULL) { - digit *p = v->long_value.ob_digit; - _PyLong_SetSignAndDigitCount(v, ival < 0 ? -1 : 1, ndigits); - t = abs_ival; - while (t) { - *p++ = (digit)(t & PyLong_MASK); - t >>= PyLong_SHIFT; - } - } - return (PyObject *)v; + PYLONG_FROM_INT(unsigned long, long, ival); } #define PYLONG_FROM_UINT(INT_TYPE, ival) \ do { \ + /* Handle small and medium cases. */ \ if (IS_SMALL_UINT(ival)) { \ return get_small_int((sdigit)(ival)); \ } \ @@ -383,12 +385,13 @@ PyLong_FromLong(long ival) } \ /* Do shift in two steps to avoid possible undefined behavior. */ \ INT_TYPE t = (ival) >> PyLong_SHIFT >> PyLong_SHIFT; \ - /* Count the number of Python digits. */ \ + /* Count digits (at least two - smaller cases were handled above). */ \ Py_ssize_t ndigits = 2; \ while (t) { \ ++ndigits; \ t >>= PyLong_SHIFT; \ } \ + /* Construct output value. */ \ PyLongObject *v = long_alloc(ndigits); \ if (v == NULL) { \ return NULL; \ @@ -1482,40 +1485,7 @@ PyLong_AsVoidPtr(PyObject *vv) PyObject * PyLong_FromLongLong(long long ival) { - PyLongObject *v; - 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 (-(long long)PyLong_MASK <= ival && ival <= (long long)PyLong_MASK) { - return _PyLong_FromMedium((sdigit)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 = long_alloc(ndigits); - if (v != NULL) { - digit *p = v->long_value.ob_digit; - _PyLong_SetSignAndDigitCount(v, ival < 0 ? -1 : 1, ndigits); - t = abs_ival; - while (t) { - *p++ = (digit)(t & PyLong_MASK); - t >>= PyLong_SHIFT; - } - } - return (PyObject *)v; + PYLONG_FROM_INT(unsigned long long, long long, ival); } /* Create a new int object from a C Py_ssize_t. */ @@ -1523,42 +1493,7 @@ PyLong_FromLongLong(long long ival) PyObject * PyLong_FromSsize_t(Py_ssize_t ival) { - PyLongObject *v; - size_t abs_ival; - size_t t; /* unsigned so >> doesn't propagate sign bit */ - int ndigits = 0; - int negative = 0; - - if (IS_SMALL_INT(ival)) { - return get_small_int((sdigit)ival); - } - - if (ival < 0) { - /* avoid signed overflow when ival = SIZE_T_MIN */ - abs_ival = (size_t)(-1-ival)+1; - negative = 1; - } - else { - abs_ival = (size_t)ival; - } - - /* Count the number of Python digits. */ - t = abs_ival; - while (t) { - ++ndigits; - t >>= PyLong_SHIFT; - } - v = long_alloc(ndigits); - if (v != NULL) { - digit *p = v->long_value.ob_digit; - _PyLong_SetSignAndDigitCount(v, negative ? -1 : 1, ndigits); - t = abs_ival; - while (t) { - *p++ = (digit)(t & PyLong_MASK); - t >>= PyLong_SHIFT; - } - } - return (PyObject *)v; + PYLONG_FROM_INT(size_t, Py_ssize_t, ival); } /* Get a C long long int from an int object or any object that has an