gh-127937: Convert decimal to use PEP 757 import API (#127925)

Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
Co-authored-by: Victor Stinner <vstinner@python.org>
This commit is contained in:
Sergey B Kirpichev 2025-01-24 14:05:52 +03:00 committed by GitHub
parent 6e63c4736b
commit 3d8fc8b9ae
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 55 additions and 32 deletions

View File

@ -0,0 +1,3 @@
Convert the :mod:`decimal` module to use :pep:`757` C API (export-import
integers), offering some speed-up if the integer part of the
:class:`~decimal.Decimal` instance is small. Patch by Sergey B Kirpichev.

View File

@ -2336,15 +2336,16 @@ dec_from_long(decimal_state *state, PyTypeObject *type, PyObject *v,
}
if (export_long.digits) {
const PyLongLayout *layout = PyLong_GetNativeLayout();
uint32_t base = (uint32_t)1 << layout->bits_per_digit;
uint8_t sign = export_long.negative ? MPD_NEG : MPD_POS;
Py_ssize_t len = export_long.ndigits;
assert(layout->bits_per_digit <= 32);
assert(layout->bits_per_digit < 32);
assert(layout->digits_order == -1);
assert(layout->digit_endianness == (PY_LITTLE_ENDIAN ? -1 : 1));
assert(layout->digit_size == 2 || layout->digit_size == 4);
uint32_t base = (uint32_t)1 << layout->bits_per_digit;
uint8_t sign = export_long.negative ? MPD_NEG : MPD_POS;
Py_ssize_t len = export_long.ndigits;
if (layout->digit_size == 4) {
mpd_qimport_u32(MPD(dec), export_long.digits, len, sign,
base, ctx, status);
@ -3642,13 +3643,6 @@ dec_format(PyObject *dec, PyObject *args)
static PyObject *
dec_as_long(PyObject *dec, PyObject *context, int round)
{
PyLongObject *pylong;
digit *ob_digit;
size_t n;
mpd_t *x;
mpd_context_t workctx;
uint32_t status = 0;
if (mpd_isspecial(MPD(dec))) {
if (mpd_isnan(MPD(dec))) {
PyErr_SetString(PyExc_ValueError,
@ -3661,12 +3655,16 @@ dec_as_long(PyObject *dec, PyObject *context, int round)
return NULL;
}
x = mpd_qnew();
mpd_t *x = mpd_qnew();
if (x == NULL) {
PyErr_NoMemory();
return NULL;
}
workctx = *CTX(context);
mpd_context_t workctx = *CTX(context);
uint32_t status = 0;
workctx.round = round;
mpd_qround_to_int(x, MPD(dec), &workctx, &status);
if (dec_addstatus(context, status)) {
@ -3675,34 +3673,56 @@ dec_as_long(PyObject *dec, PyObject *context, int round)
}
status = 0;
ob_digit = NULL;
#if PYLONG_BITS_IN_DIGIT == 30
n = mpd_qexport_u32(&ob_digit, 0, PyLong_BASE, x, &status);
#elif PYLONG_BITS_IN_DIGIT == 15
n = mpd_qexport_u16(&ob_digit, 0, PyLong_BASE, x, &status);
#else
#error "PYLONG_BITS_IN_DIGIT should be 15 or 30"
#endif
int64_t val = mpd_qget_i64(x, &status);
if (!status) {
mpd_del(x);
return PyLong_FromInt64(val);
}
assert(!mpd_iszero(x));
const PyLongLayout *layout = PyLong_GetNativeLayout();
assert(layout->bits_per_digit < 32);
assert(layout->digits_order == -1);
assert(layout->digit_endianness == (PY_LITTLE_ENDIAN ? -1 : 1));
assert(layout->digit_size == 2 || layout->digit_size == 4);
uint32_t base = (uint32_t)1 << layout->bits_per_digit;
/* We use a temporary buffer for digits for now, as for nonzero rdata
mpd_qexport_u32/u16() require either space "allocated by one of
libmpdecs allocation functions" or "rlen MUST be correct" (to avoid
reallocation). This can be further optimized by using rlen from
mpd_sizeinbase(). See gh-127925. */
void *tmp_digits = NULL;
size_t n;
status = 0;
if (layout->digit_size == 4) {
n = mpd_qexport_u32((uint32_t **)&tmp_digits, 0, base, x, &status);
}
else {
n = mpd_qexport_u16((uint16_t **)&tmp_digits, 0, base, x, &status);
}
if (n == SIZE_MAX) {
PyErr_NoMemory();
mpd_del(x);
mpd_free(tmp_digits);
return NULL;
}
if (n == 1) {
sdigit val = mpd_arith_sign(x) * ob_digit[0];
mpd_free(ob_digit);
mpd_del(x);
return PyLong_FromLong(val);
}
void *digits;
PyLongWriter *writer = PyLongWriter_Create(mpd_isnegative(x), n, &digits);
assert(n > 0);
assert(!mpd_iszero(x));
pylong = _PyLong_FromDigits(mpd_isnegative(x), n, ob_digit);
mpd_free(ob_digit);
mpd_del(x);
return (PyObject *) pylong;
if (writer == NULL) {
mpd_free(tmp_digits);
return NULL;
}
memcpy(digits, tmp_digits, layout->digit_size*n);
mpd_free(tmp_digits);
return PyLongWriter_Finish(writer);
}
/* Convert a Decimal to its exact integer ratio representation. */