Skip to content

Commit b4e7700

Browse files
authored
PEP 757: use PyLong_Export (#3970)
1 parent b6cf6d4 commit b4e7700

File tree

1 file changed

+49
-39
lines changed

1 file changed

+49
-39
lines changed

peps/pep-0757.rst

Lines changed: 49 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@ Abstract
1717
========
1818

1919
Add a new C API to import and export Python integers, :class:`int` objects:
20-
especially ``PyLongWriter_Create()`` and ``PyLong_AsDigitArray()``
21-
functions.
20+
especially ``PyLongWriter_Create()`` and ``PyLong_Export()`` functions.
2221

2322

2423
Rationale
@@ -89,9 +88,12 @@ Export API
8988

9089
Export a Python integer as a digits array::
9190

92-
typedef struct PyLong_DigitArray {
91+
typedef struct PyLongExport {
92+
// use value, if digits set to NULL.
93+
int64_t value;
94+
9395
// 1 if the number is negative, 0 otherwise.
94-
int negative;
96+
uint8_t negative;
9597

9698
// Number of digits in the 'digits' array.
9799
Py_ssize_t ndigits;
@@ -101,49 +103,47 @@ Export a Python integer as a digits array::
101103

102104
// Member used internally, must not be used for other purpose.
103105
Py_uintptr_t _reserved;
104-
} PyLong_DigitArray;
106+
} PyLongExport;
105107

106-
PyAPI_FUNC(int) PyLong_AsDigitArray(
107-
PyObject *obj,
108-
PyLong_DigitArray *array);
109-
PyAPI_FUNC(void) PyLong_FreeDigitArray(
110-
PyLong_DigitArray *array);
108+
int PyLong_Export(PyObject *obj, PyLongExport *array);
111109

112110
On CPython 3.14, no memory copy is needed, it's just a thin wrapper to
113111
expose Python int internal digits array.
114112

115-
``PyLong_DigitArray.obj`` stores a strong reference to the Python
116-
:class:`int` object to make sure that that structure remains valid until
117-
``PyLong_FreeDigitArray()`` is called.
113+
``PyLongExport._reserved``, if ``digits`` not ``NULL``, stores a strong
114+
reference to the Python :class:`int` object to make sure that that structure
115+
remains valid until ``PyLong_FreeExport()`` is called.
118116

119117

120-
PyLong_AsDigitArray()
121-
^^^^^^^^^^^^^^^^^^^^^
118+
PyLong_Export()
119+
^^^^^^^^^^^^^^^
122120

123121
API::
124122

125-
int PyLong_AsDigitArray(PyObject *obj, PyLong_DigitArray *array)
123+
int PyLong_Export(PyObject *obj, PyLongExport *array)
126124

127125
Export a Python :class:`int` object as a digits array.
128126

129127
On success, set *\*array* and return 0.
130128
On error, set an exception and return -1.
131129

132-
This function always succeeds if *obj* is a Python :class:`int` object or a
133-
subclass.
130+
If ``array->digits`` set to ``NULL``, caller must use instead ``array->value``
131+
to get value of an :class:`int` object.
134132

135-
``PyLong_FreeDigitArray()`` must be called once done with using
136-
*array*.
133+
CPython implementation detail: This function always succeeds if *obj* is a
134+
Python :class:`int` object or a subclass.
137135

136+
``PyLong_FreeExport()`` must be called once done with using *array*.
138137

139-
PyLong_FreeDigitArray()
140-
^^^^^^^^^^^^^^^^^^^^^^^
138+
139+
PyLong_FreeExport()
140+
^^^^^^^^^^^^^^^^^^^
141141

142142
API::
143143

144-
void PyLong_FreeDigitArray(PyLong_DigitArray *array)
144+
void PyLong_FreeExport(PyLongExport *array)
145145

146-
Release the export *array* created by ``PyLong_AsDigitArray()``.
146+
Free the export *array* created by ``PyLong_Export()``.
147147

148148

149149
Import API
@@ -223,7 +223,7 @@ directly Python internals, the proposed API can have a significant
223223
performance overhead on small integers.
224224

225225
For small integers of a few digits (for example, 1 or 2 digits), existing APIs
226-
can be used. Examples to import / export:
226+
can be used
227227

228228
* :external+py3.14:c:func:`PyLong_FromUInt64()` / :external+py3.14:c:func:`PyLong_AsUInt64()`;
229229
* :c:func:`PyLong_FromLong()` / :c:func:`PyLong_AsLong()` or :c:func:`PyLong_AsInt()`;
@@ -248,33 +248,43 @@ Implementation
248248
Benchmarks
249249
==========
250250

251-
Export: PyLong_AsDigitArray() with gmpy2
252-
----------------------------------------
251+
Export: PyLong_Export() with gmpy2
252+
----------------------------------
253253

254254
Code::
255255

256256
static void
257257
mpz_set_PyLong(mpz_t z, PyObject *obj)
258258
{
259-
int overflow;
260-
long val = PyLong_AsLongAndOverflow(obj, &overflow);
259+
const PyLongLayout* layout = PyLong_GetNativeLayout();
260+
static PyLongExport long_export;
261261

262-
if (overflow) {
263-
const PyLongLayout* layout = PyLong_GetNativeLayout();
264-
static PyLong_DigitArray long_export;
265-
266-
PyLong_AsDigitArray(obj, &long_export);
267-
mpz_import(z, long_export.ndigits, layout->endian,
268-
layout->digit_size, layout->digits_order,
262+
PyLong_Export(obj, &long_export);
263+
if (long_export.digits) {
264+
mpz_import(z, long_export.ndigits, layout->digits_order,
265+
layout->digit_size, layout->endian,
269266
layout->digit_size*8 - layout->bits_per_digit,
270267
long_export.digits);
271268
if (long_export.negative) {
272269
mpz_neg(z, z);
273270
}
274-
PyLong_FreeDigitArray(&long_export);
271+
PyLong_FreeExport(&long_export);
275272
}
276273
else {
277-
mpz_set_si(z, val);
274+
if (LONG_MIN <= long_export.value && long_export.value <= LONG_MAX) {
275+
mpz_set_si(z, long_export.value);
276+
}
277+
else {
278+
mpz_import(z, 1, -1, sizeof(int64_t), 0, 0,
279+
&long_export.value);
280+
if (long_export.value < 0) {
281+
mpz_t tmp;
282+
mpz_init(tmp);
283+
mpz_ui_pow_ui(tmp, 2, 8*sizeof(size_t));
284+
mpz_sub(z, z, tmp);
285+
mpz_clear(tmp);
286+
}
287+
}
278288
}
279289
}
280290

@@ -403,7 +413,7 @@ Python integers.
403413

404414
For example, it was proposed to add a *layout* parameter to
405415
``PyLongWriter_Create()`` and a *layout* member to the
406-
``PyLong_DigitArray`` structure.
416+
``PyLongExport`` structure.
407417

408418
The problem is that it's more complex to implement and not really
409419
needed. What's strictly needed is only an API to import-export using the

0 commit comments

Comments
 (0)