Skip to content
Closed
4 changes: 3 additions & 1 deletion Doc/c-api/long.rst
Original file line number Diff line number Diff line change
Expand Up @@ -529,10 +529,12 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate.
Exactly what values are considered compact is an implementation detail
and is subject to change.

.. c:function:: Py_ssize_t PyUnstable_Long_CompactValue(const PyLongObject* op)
.. c:function:: int PyUnstable_Long_CompactValue(const PyLongObject* op)

If *op* is compact, as determined by :c:func:`PyUnstable_Long_IsCompact`,
return its value.

Otherwise, the return value is undefined.

.. versionchanged:: 3.14
Change return type from :c:type:`Py_ssize_t` to ``int``.
4 changes: 4 additions & 0 deletions Doc/whatsnew/3.14.rst
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,10 @@ Porting to Python 3.14
an opaque function call to hide implementation details.
(Contributed by Victor Stinner in :gh:`120600`.)

* Change :c:func:`PyUnstable_Long_CompactValue` return type from
:c:type:`Py_ssize_t` to ``int``.
(Contributed by Serhiy Storchaka and Victor Stinner in :gh:`121153`.)


Deprecated
----------
Expand Down
6 changes: 3 additions & 3 deletions Include/cpython/longintrepr.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,14 +126,14 @@ _PyLong_IsCompact(PyLongObject* op) {

#define PyUnstable_Long_IsCompact _PyLong_IsCompact

static inline Py_ssize_t
static inline int
_PyLong_CompactValue(PyLongObject *op)
{
Py_ssize_t sign;
int sign;
assert(PyType_HasFeature(Py_TYPE(op), Py_TPFLAGS_LONG_SUBCLASS));
assert(PyUnstable_Long_IsCompact(op));
sign = 1 - (op->long_value.lv_tag & _PyLong_SIGN_MASK);
return sign * (Py_ssize_t)op->long_value.ob_digit[0];
return sign * (int)op->long_value.ob_digit[0];
}

#define PyUnstable_Long_CompactValue _PyLong_CompactValue
Expand Down
2 changes: 1 addition & 1 deletion Include/cpython/longobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ PyAPI_FUNC(PyObject*) PyLong_FromUnsignedNativeBytes(const void* buffer,
size_t n_bytes, int flags);

PyAPI_FUNC(int) PyUnstable_Long_IsCompact(const PyLongObject* op);
PyAPI_FUNC(Py_ssize_t) PyUnstable_Long_CompactValue(const PyLongObject* op);
PyAPI_FUNC(int) PyUnstable_Long_CompactValue(const PyLongObject* op);

/* PyLong_GetSign. Get the sign of an integer object:
0, -1 or +1 for zero, negative or positive integer, respectively.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Change :c:func:`PyUnstable_Long_CompactValue` return type from
:c:type:`Py_ssize_t` to ``int``. Patch by Serhiy Storchaka and Victor
Stinner.
4 changes: 2 additions & 2 deletions Modules/_testcapi/long.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ _testcapi_call_long_compact_api(PyObject *module, PyObject *arg)
{
assert(PyLong_Check(arg));
int is_compact = PyUnstable_Long_IsCompact((PyLongObject*)arg);
Py_ssize_t value = -1;
int value = -1;
if (is_compact) {
value = PyUnstable_Long_CompactValue((PyLongObject*)arg);
}
return Py_BuildValue("in", is_compact, value);
return Py_BuildValue("ii", is_compact, value);
}


Expand Down
5 changes: 2 additions & 3 deletions Objects/listobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -2751,7 +2751,6 @@ static int
unsafe_long_compare(PyObject *v, PyObject *w, MergeState *ms)
{
PyLongObject *vl, *wl;
intptr_t v0, w0;
int res;

/* Modified from Objects/longobject.c:long_compare, assuming: */
Expand All @@ -2763,8 +2762,8 @@ unsafe_long_compare(PyObject *v, PyObject *w, MergeState *ms)
vl = (PyLongObject*)v;
wl = (PyLongObject*)w;

v0 = _PyLong_CompactValue(vl);
w0 = _PyLong_CompactValue(wl);
int v0 = _PyLong_CompactValue(vl);
int w0 = _PyLong_CompactValue(wl);

res = v0 < w0;
assert(res == PyObject_RichCompareBool(v, w, Py_LT));
Expand Down
34 changes: 9 additions & 25 deletions Objects/longobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -483,15 +483,7 @@ PyLong_AsLongAndOverflow(PyObject *vv, int *overflow)
do_decref = 1;
}
if (_PyLong_IsCompact(v)) {
#if SIZEOF_LONG < SIZEOF_VOID_P
intptr_t tmp = _PyLong_CompactValue(v);
res = (long)tmp;
if (res != tmp) {
*overflow = tmp < 0 ? -1 : 1;
}
#else
res = _PyLong_CompactValue(v);
#endif
}
else {
res = -1;
Expand Down Expand Up @@ -632,15 +624,7 @@ PyLong_AsUnsignedLong(PyObject *vv)

v = (PyLongObject *)vv;
if (_PyLong_IsNonNegativeCompact(v)) {
#if SIZEOF_LONG < SIZEOF_VOID_P
intptr_t tmp = _PyLong_CompactValue(v);
unsigned long res = (unsigned long)tmp;
if (res != tmp) {
goto overflow;
}
#else
return _PyLong_CompactValue(v);
#endif
return (unsigned long)(long)_PyLong_CompactValue(v);
}
if (_PyLong_IsNegative(v)) {
PyErr_SetString(PyExc_OverflowError,
Expand Down Expand Up @@ -685,7 +669,7 @@ PyLong_AsSize_t(PyObject *vv)

v = (PyLongObject *)vv;
if (_PyLong_IsNonNegativeCompact(v)) {
return _PyLong_CompactValue(v);
return (size_t)(Py_ssize_t)_PyLong_CompactValue(v);
}
if (_PyLong_IsNegative(v)) {
PyErr_SetString(PyExc_OverflowError,
Expand Down Expand Up @@ -722,7 +706,7 @@ _PyLong_AsUnsignedLongMask(PyObject *vv)
}
v = (PyLongObject *)vv;
if (_PyLong_IsCompact(v)) {
return (unsigned long)_PyLong_CompactValue(v);
return (unsigned long)(long)_PyLong_CompactValue(v);
}
i = _PyLong_DigitCount(v);
int sign = _PyLong_NonCompactSign(v);
Expand Down Expand Up @@ -1109,8 +1093,8 @@ PyLong_AsNativeBytes(PyObject* vv, void* buffer, Py_ssize_t n, int flags)
{
PyLongObject *v;
union {
Py_ssize_t v;
unsigned char b[sizeof(Py_ssize_t)];
int v;
unsigned char b[sizeof(int)];
} cv;
int do_decref = 0;
Py_ssize_t res = 0;
Expand Down Expand Up @@ -1540,7 +1524,7 @@ PyLong_AsUnsignedLongLong(PyObject *vv)
v = (PyLongObject*)vv;
if (_PyLong_IsNonNegativeCompact(v)) {
res = 0;
bytes = _PyLong_CompactValue(v);
bytes = (unsigned long long)(long long)_PyLong_CompactValue(v);
}
else {
res = _PyLong_AsByteArray((PyLongObject *)vv, (unsigned char *)&bytes,
Expand Down Expand Up @@ -1571,7 +1555,7 @@ _PyLong_AsUnsignedLongLongMask(PyObject *vv)
}
v = (PyLongObject *)vv;
if (_PyLong_IsCompact(v)) {
return (unsigned long long)(signed long long)_PyLong_CompactValue(v);
return (unsigned long long)(long long)_PyLong_CompactValue(v);
}
i = _PyLong_DigitCount(v);
sign = _PyLong_NonCompactSign(v);
Expand Down Expand Up @@ -3579,7 +3563,7 @@ long_hash(PyLongObject *v)
int sign;

if (_PyLong_IsCompact(v)) {
x = _PyLong_CompactValue(v);
x = (Py_uhash_t)(Py_hash_t)_PyLong_CompactValue(v);
if (x == (Py_uhash_t)-1) {
x = (Py_uhash_t)-2;
}
Expand Down Expand Up @@ -6681,7 +6665,7 @@ PyUnstable_Long_IsCompact(const PyLongObject* op) {

#undef PyUnstable_Long_CompactValue

Py_ssize_t
int
PyUnstable_Long_CompactValue(const PyLongObject* op) {
return _PyLong_CompactValue((PyLongObject*)op);
}
7 changes: 4 additions & 3 deletions Python/bltinmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -2634,7 +2634,7 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start)
return PyLong_FromSsize_t(i_result);
}
if (PyLong_CheckExact(item) || PyBool_Check(item)) {
Py_ssize_t b;
long b;
overflow = 0;
/* Single digits are common, fast, and cannot overflow on unpacking. */
if (_PyLong_IsCompact((PyLongObject *)item)) {
Expand All @@ -2644,8 +2644,9 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start)
b = PyLong_AsLongAndOverflow(item, &overflow);
}
if (overflow == 0 &&
(i_result >= 0 ? (b <= LONG_MAX - i_result)
: (b >= LONG_MIN - i_result)))
/* PY_SSIZE_T_MIN <= i_result + b <= PY_SSIZE_T_MAX */
(i_result >= 0 ? (b <= PY_SSIZE_T_MAX - i_result)
: (b >= PY_SSIZE_T_MIN - i_result)))
{
i_result += b;
Py_DECREF(item);
Expand Down
4 changes: 2 additions & 2 deletions Python/bytecodes.c
Original file line number Diff line number Diff line change
Expand Up @@ -2482,8 +2482,8 @@ dummy_func(
STAT_INC(COMPARE_OP, hit);
assert(_PyLong_DigitCount((PyLongObject *)left_o) <= 1 &&
_PyLong_DigitCount((PyLongObject *)right_o) <= 1);
Py_ssize_t ileft = _PyLong_CompactValue((PyLongObject *)left_o);
Py_ssize_t iright = _PyLong_CompactValue((PyLongObject *)right_o);
int ileft = _PyLong_CompactValue((PyLongObject *)left_o);
int iright = _PyLong_CompactValue((PyLongObject *)right_o);
// 2 if <, 4 if >, 8 if ==; this matches the low 4 bits of the oparg
int sign_ish = COMPARISON_BIT(ileft, iright);
_Py_DECREF_SPECIALIZED(left_o, (destructor)PyObject_Free);
Expand Down
4 changes: 2 additions & 2 deletions Python/executor_cases.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Python/generated_cases.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.