Skip to content

Commit 3d8fc8b

Browse files
skirpichevpicnixzvstinner
authored
gh-127937: Convert decimal to use PEP 757 import API (#127925)
Co-authored-by: Bénédikt Tran <[email protected]> Co-authored-by: Victor Stinner <[email protected]>
1 parent 6e63c47 commit 3d8fc8b

File tree

2 files changed

+55
-32
lines changed

2 files changed

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

Modules/_decimal/_decimal.c

+52-32
Original file line numberDiff line numberDiff line change
@@ -2336,15 +2336,16 @@ dec_from_long(decimal_state *state, PyTypeObject *type, PyObject *v,
23362336
}
23372337
if (export_long.digits) {
23382338
const PyLongLayout *layout = PyLong_GetNativeLayout();
2339-
uint32_t base = (uint32_t)1 << layout->bits_per_digit;
2340-
uint8_t sign = export_long.negative ? MPD_NEG : MPD_POS;
2341-
Py_ssize_t len = export_long.ndigits;
23422339

2343-
assert(layout->bits_per_digit <= 32);
2340+
assert(layout->bits_per_digit < 32);
23442341
assert(layout->digits_order == -1);
23452342
assert(layout->digit_endianness == (PY_LITTLE_ENDIAN ? -1 : 1));
23462343
assert(layout->digit_size == 2 || layout->digit_size == 4);
23472344

2345+
uint32_t base = (uint32_t)1 << layout->bits_per_digit;
2346+
uint8_t sign = export_long.negative ? MPD_NEG : MPD_POS;
2347+
Py_ssize_t len = export_long.ndigits;
2348+
23482349
if (layout->digit_size == 4) {
23492350
mpd_qimport_u32(MPD(dec), export_long.digits, len, sign,
23502351
base, ctx, status);
@@ -3642,13 +3643,6 @@ dec_format(PyObject *dec, PyObject *args)
36423643
static PyObject *
36433644
dec_as_long(PyObject *dec, PyObject *context, int round)
36443645
{
3645-
PyLongObject *pylong;
3646-
digit *ob_digit;
3647-
size_t n;
3648-
mpd_t *x;
3649-
mpd_context_t workctx;
3650-
uint32_t status = 0;
3651-
36523646
if (mpd_isspecial(MPD(dec))) {
36533647
if (mpd_isnan(MPD(dec))) {
36543648
PyErr_SetString(PyExc_ValueError,
@@ -3661,12 +3655,16 @@ dec_as_long(PyObject *dec, PyObject *context, int round)
36613655
return NULL;
36623656
}
36633657

3664-
x = mpd_qnew();
3658+
mpd_t *x = mpd_qnew();
3659+
36653660
if (x == NULL) {
36663661
PyErr_NoMemory();
36673662
return NULL;
36683663
}
3669-
workctx = *CTX(context);
3664+
3665+
mpd_context_t workctx = *CTX(context);
3666+
uint32_t status = 0;
3667+
36703668
workctx.round = round;
36713669
mpd_qround_to_int(x, MPD(dec), &workctx, &status);
36723670
if (dec_addstatus(context, status)) {
@@ -3675,34 +3673,56 @@ dec_as_long(PyObject *dec, PyObject *context, int round)
36753673
}
36763674

36773675
status = 0;
3678-
ob_digit = NULL;
3679-
#if PYLONG_BITS_IN_DIGIT == 30
3680-
n = mpd_qexport_u32(&ob_digit, 0, PyLong_BASE, x, &status);
3681-
#elif PYLONG_BITS_IN_DIGIT == 15
3682-
n = mpd_qexport_u16(&ob_digit, 0, PyLong_BASE, x, &status);
3683-
#else
3684-
#error "PYLONG_BITS_IN_DIGIT should be 15 or 30"
3685-
#endif
3676+
int64_t val = mpd_qget_i64(x, &status);
3677+
3678+
if (!status) {
3679+
mpd_del(x);
3680+
return PyLong_FromInt64(val);
3681+
}
3682+
assert(!mpd_iszero(x));
3683+
3684+
const PyLongLayout *layout = PyLong_GetNativeLayout();
3685+
3686+
assert(layout->bits_per_digit < 32);
3687+
assert(layout->digits_order == -1);
3688+
assert(layout->digit_endianness == (PY_LITTLE_ENDIAN ? -1 : 1));
3689+
assert(layout->digit_size == 2 || layout->digit_size == 4);
3690+
3691+
uint32_t base = (uint32_t)1 << layout->bits_per_digit;
3692+
/* We use a temporary buffer for digits for now, as for nonzero rdata
3693+
mpd_qexport_u32/u16() require either space "allocated by one of
3694+
libmpdec’s allocation functions" or "rlen MUST be correct" (to avoid
3695+
reallocation). This can be further optimized by using rlen from
3696+
mpd_sizeinbase(). See gh-127925. */
3697+
void *tmp_digits = NULL;
3698+
size_t n;
3699+
3700+
status = 0;
3701+
if (layout->digit_size == 4) {
3702+
n = mpd_qexport_u32((uint32_t **)&tmp_digits, 0, base, x, &status);
3703+
}
3704+
else {
3705+
n = mpd_qexport_u16((uint16_t **)&tmp_digits, 0, base, x, &status);
3706+
}
36863707

36873708
if (n == SIZE_MAX) {
36883709
PyErr_NoMemory();
36893710
mpd_del(x);
3711+
mpd_free(tmp_digits);
36903712
return NULL;
36913713
}
36923714

3693-
if (n == 1) {
3694-
sdigit val = mpd_arith_sign(x) * ob_digit[0];
3695-
mpd_free(ob_digit);
3696-
mpd_del(x);
3697-
return PyLong_FromLong(val);
3698-
}
3715+
void *digits;
3716+
PyLongWriter *writer = PyLongWriter_Create(mpd_isnegative(x), n, &digits);
36993717

3700-
assert(n > 0);
3701-
assert(!mpd_iszero(x));
3702-
pylong = _PyLong_FromDigits(mpd_isnegative(x), n, ob_digit);
3703-
mpd_free(ob_digit);
37043718
mpd_del(x);
3705-
return (PyObject *) pylong;
3719+
if (writer == NULL) {
3720+
mpd_free(tmp_digits);
3721+
return NULL;
3722+
}
3723+
memcpy(digits, tmp_digits, layout->digit_size*n);
3724+
mpd_free(tmp_digits);
3725+
return PyLongWriter_Finish(writer);
37063726
}
37073727

37083728
/* Convert a Decimal to its exact integer ratio representation. */

0 commit comments

Comments
 (0)