@@ -17,8 +17,7 @@ Abstract
17
17
========
18
18
19
19
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.
22
21
23
22
24
23
Rationale
@@ -89,9 +88,12 @@ Export API
89
88
90
89
Export a Python integer as a digits array::
91
90
92
- typedef struct PyLong_DigitArray {
91
+ typedef struct PyLongExport {
92
+ // use value, if digits set to NULL.
93
+ int64_t value;
94
+
93
95
// 1 if the number is negative, 0 otherwise.
94
- int negative;
96
+ uint8_t negative;
95
97
96
98
// Number of digits in the 'digits' array.
97
99
Py_ssize_t ndigits;
@@ -101,49 +103,47 @@ Export a Python integer as a digits array::
101
103
102
104
// Member used internally, must not be used for other purpose.
103
105
Py_uintptr_t _reserved;
104
- } PyLong_DigitArray ;
106
+ } PyLongExport ;
105
107
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);
111
109
112
110
On CPython 3.14, no memory copy is needed, it's just a thin wrapper to
113
111
expose Python int internal digits array.
114
112
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.
118
116
119
117
120
- PyLong_AsDigitArray ()
121
- ^^^^^^^^^^^^^^^^^^^^^
118
+ PyLong_Export ()
119
+ ^^^^^^^^^^^^^^^
122
120
123
121
API::
124
122
125
- int PyLong_AsDigitArray (PyObject *obj, PyLong_DigitArray *array)
123
+ int PyLong_Export (PyObject *obj, PyLongExport *array)
126
124
127
125
Export a Python :class: `int ` object as a digits array.
128
126
129
127
On success, set *\* array * and return 0.
130
128
On error, set an exception and return -1.
131
129
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 .
134
132
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 .
137
135
136
+ ``PyLong_FreeExport() `` must be called once done with using *array *.
138
137
139
- PyLong_FreeDigitArray()
140
- ^^^^^^^^^^^^^^^^^^^^^^^
138
+
139
+ PyLong_FreeExport()
140
+ ^^^^^^^^^^^^^^^^^^^
141
141
142
142
API::
143
143
144
- void PyLong_FreeDigitArray(PyLong_DigitArray *array)
144
+ void PyLong_FreeExport(PyLongExport *array)
145
145
146
- Release the export *array * created by ``PyLong_AsDigitArray () ``.
146
+ Free the export *array * created by ``PyLong_Export () ``.
147
147
148
148
149
149
Import API
@@ -223,7 +223,7 @@ directly Python internals, the proposed API can have a significant
223
223
performance overhead on small integers.
224
224
225
225
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
227
227
228
228
* :external+py3.14:c:func: `PyLong_FromUInt64() ` / :external+py3.14:c:func: `PyLong_AsUInt64() `;
229
229
* :c:func: `PyLong_FromLong() ` / :c:func: `PyLong_AsLong() ` or :c:func: `PyLong_AsInt() `;
@@ -248,33 +248,43 @@ Implementation
248
248
Benchmarks
249
249
==========
250
250
251
- Export: PyLong_AsDigitArray () with gmpy2
252
- ----------------------------------------
251
+ Export: PyLong_Export () with gmpy2
252
+ ----------------------------------
253
253
254
254
Code::
255
255
256
256
static void
257
257
mpz_set_PyLong(mpz_t z, PyObject *obj)
258
258
{
259
- int overflow ;
260
- long val = PyLong_AsLongAndOverflow(obj, &overflow) ;
259
+ const PyLongLayout* layout = PyLong_GetNativeLayout() ;
260
+ static PyLongExport long_export ;
261
261
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,
269
266
layout->digit_size*8 - layout->bits_per_digit,
270
267
long_export.digits);
271
268
if (long_export.negative) {
272
269
mpz_neg(z, z);
273
270
}
274
- PyLong_FreeDigitArray (&long_export);
271
+ PyLong_FreeExport (&long_export);
275
272
}
276
273
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
+ }
278
288
}
279
289
}
280
290
@@ -403,7 +413,7 @@ Python integers.
403
413
404
414
For example, it was proposed to add a *layout * parameter to
405
415
``PyLongWriter_Create() `` and a *layout * member to the
406
- ``PyLong_DigitArray `` structure.
416
+ ``PyLongExport `` structure.
407
417
408
418
The problem is that it's more complex to implement and not really
409
419
needed. What's strictly needed is only an API to import-export using the
0 commit comments