Skip to content

Commit 05a9692

Browse files
committed
PyLong_Export API returns PyLongExport_Kind
1 parent 167d75e commit 05a9692

File tree

5 files changed

+73
-89
lines changed

5 files changed

+73
-89
lines changed

Doc/c-api/long.rst

Lines changed: 34 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -619,7 +619,8 @@ Export API
619619
620620
.. c:struct:: PyLongLayout
621621
622-
Layout of an array of digits, used by Python :class:`int` object.
622+
Layout of an array of digits, used by Python :class:`int` object to
623+
represent "big enough" integers.
623624
624625
Use :c:func:`PyLong_GetNativeLayout` to get the native layout of Python
625626
:class:`int` objects.
@@ -641,7 +642,7 @@ Export API
641642
- ``1`` for most significant digit first
642643
- ``-1`` for least significant digit first
643644
644-
.. c:member:: int8_t endian
645+
.. c:member:: int8_t endianness
645646
646647
Digit endianness:
647648
@@ -655,56 +656,60 @@ Export API
655656
656657
See the :c:struct:`PyLongLayout` structure.
657658
659+
The function must not be called before Python initialization nor after
660+
Python finalization. The returned layout is valid until Python is
661+
finalized. The layout is the same for all Python sub-interpreters and
662+
so it can be cached.
658663
659-
.. c:struct:: PyLongExport
660664
661-
Export of a Python :class:`int` object.
665+
.. c:type:: PyLongExport_Kind
666+
667+
The enum value used to represent different results of
668+
:c:func:`PyLong_Export`.
669+
662670
663-
There are two cases:
671+
.. c:struct:: PyLongExport
664672
665-
* If :c:member:`digits` is ``NULL``, only use the :c:member:`value` member.
666-
Calling :c:func:`PyLong_FreeExport` is optional in this case.
667-
* If :c:member:`digits` is not ``NULL``, use :c:member:`negative`,
668-
:c:member:`ndigits` and :c:member:`digits` members.
669-
Calling :c:func:`PyLong_FreeExport` is mandatory in this case.
673+
Export of a Python :class:`int` object.
670674
671-
.. c:member:: int64_t value
675+
.. c:struct:: digit_array
672676
673-
The native integer value of the exported :class:`int` object.
674-
Only valid if :c:member:`digits` is ``NULL``.
677+
Export an integer as an array of digits; corresponds to
678+
``PyLongExport_DigitArray`` value of the enum
679+
:c:type:`PyLongExport_Kind`.
675680
676-
.. c:member:: uint8_t negative
681+
.. c:member:: Py_ssize_t ndigits
677682
678-
1 if the number is negative, 0 otherwise.
679-
Only valid if :c:member:`digits` is not ``NULL``.
683+
Number of digits in :c:member:`digits` array.
680684
681-
.. c:member:: Py_ssize_t ndigits
685+
.. c:member:: const void *digits
682686
683-
Number of digits in :c:member:`digits` array.
684-
Only valid if :c:member:`digits` is not ``NULL``.
687+
Read-only array of unsigned digits.
685688
686-
.. c:member:: const void *digits
689+
.. c:member:: uint8_t negative
687690
688-
Read-only array of unsigned digits. Can be ``NULL``.
691+
1 if the number is negative, 0 otherwise.
689692
690693
691-
.. c:function:: int PyLong_Export(PyObject *obj, PyLongExport *export_long)
694+
.. c:function:: PyLongExport_Kind PyLong_Export(PyObject *obj, PyLongExport *export)
692695
693696
Export a Python :class:`int` object.
694697
695-
On success, set *\*export_long* and return 0.
696-
On error, set an exception and return -1.
698+
On success, set *\*export* and return an appropriate export type
699+
for the given value, see the :c:struct:`PyLongExport` struct.
700+
:c:func:`PyLong_FreeExport` must be called when the export is no
701+
longer needed. Currently the only available type is
702+
``PyLongExport_DigitArray``.
703+
704+
On error, set an exception and return ``PyLongExport_Error``.
697705
698706
This function always succeeds if *obj* is a Python :class:`int` object or a
699707
subclass.
700708
701-
If *export_long.digits* is not ``NULL``, :c:func:`PyLong_FreeExport` must be
702-
called when the export is no longer needed.
703-
704709
705-
.. c:function:: void PyLong_FreeExport(PyLongExport *export_long)
710+
.. c:function:: void PyLong_FreeExport(PyLongExport *export)
706711
707-
Release the export *export_long* created by :c:func:`PyLong_Export`.
712+
Release the *export* created by :c:func:`PyLong_Export`.
708713
709714
710715
PyLongWriter API

Include/cpython/longintrepr.h

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -148,29 +148,37 @@ typedef struct PyLongLayout {
148148
// Digit size in bytes
149149
uint8_t digit_size;
150150

151-
// Word endian:
152-
// * 1 for most significant word first (big endian)
151+
// Digits order:
152+
// * 1 for most significant digit first (big endian)
153153
// * -1 for least significant first (little endian)
154154
int8_t digits_order;
155155

156-
// Array endian:
156+
// Digit endianness:
157157
// * 1 for most significant byte first (big endian)
158158
// * -1 for least significant first (little endian)
159-
int8_t endian;
159+
int8_t endianness;
160160
} PyLongLayout;
161161

162162
PyAPI_FUNC(const PyLongLayout*) PyLong_GetNativeLayout(void);
163163

164+
typedef enum {
165+
PyLongExport_Error = -1,
166+
PyLongExport_DigitArray = 0,
167+
} PyLongExport_Kind;
168+
164169
typedef struct PyLongExport {
165-
int64_t value;
166-
uint8_t negative;
167-
Py_ssize_t ndigits;
168-
const void *digits;
169170
// Member used internally, must not be used for other purpose.
170171
Py_uintptr_t _reserved;
172+
union {
173+
struct {
174+
Py_ssize_t ndigits;
175+
const void *digits;
176+
uint8_t negative;
177+
} digit_array;
178+
};
171179
} PyLongExport;
172180

173-
PyAPI_FUNC(int) PyLong_Export(
181+
PyAPI_FUNC(PyLongExport_Kind) PyLong_Export(
174182
PyObject *obj,
175183
PyLongExport *export_long);
176184
PyAPI_FUNC(void) PyLong_FreeExport(

Lib/test/test_capi/test_long.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -678,7 +678,7 @@ def test_long_layout(self):
678678
'bits_per_digit': int_info.bits_per_digit,
679679
'digit_size': int_info.sizeof_digit,
680680
'digits_order': -1,
681-
'endian': -1 if sys.byteorder == 'little' else 1,
681+
'endianness': -1 if sys.byteorder == 'little' else 1,
682682
}
683683
self.assertEqual(layout, expected)
684684

@@ -689,12 +689,9 @@ def test_long_export(self):
689689

690690
pylong_export = _testcapi.pylong_export
691691

692-
# value fits into int64_t
693-
self.assertEqual(pylong_export(0), 0)
694-
self.assertEqual(pylong_export(123), 123)
695-
self.assertEqual(pylong_export(-123), -123)
696-
697-
# use an array, doesn't fit into int64_t
692+
self.assertEqual(pylong_export(0), (0, [0]))
693+
self.assertEqual(pylong_export(123), (0, [123]))
694+
self.assertEqual(pylong_export(-123), (1, [123]))
698695
self.assertEqual(pylong_export(base**10 * 2 + 1),
699696
(0, [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2]))
700697
self.assertEqual(pylong_export(-(base**10 * 2 + 1)),

Modules/_testcapi/long.c

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -155,11 +155,11 @@ layout_to_dict(const PyLongLayout *layout)
155155
goto error;
156156
}
157157

158-
value = PyLong_FromLong(layout->endian);
158+
value = PyLong_FromLong(layout->endianness);
159159
if (value == NULL) {
160160
goto error;
161161
}
162-
res = PyDict_SetItemString(dict, "endian", value);
162+
res = PyDict_SetItemString(dict, "endianness", value);
163163
Py_DECREF(value);
164164
if (res < 0) {
165165
goto error;
@@ -177,20 +177,15 @@ static PyObject *
177177
pylong_export(PyObject *module, PyObject *obj)
178178
{
179179
PyLongExport export_long;
180-
if (PyLong_Export(obj, &export_long) < 0) {
180+
if (PyLong_Export(obj, &export_long) == PyLongExport_Error) {
181181
return NULL;
182182
}
183183

184-
if (export_long.digits == NULL) {
185-
return PyLong_FromInt64(export_long.value);
186-
// PyLong_FreeExport() is not needed in this case
187-
}
188-
189184
assert(PyLong_GetNativeLayout()->digit_size == sizeof(digit));
190-
const digit *export_long_digits = export_long.digits;
185+
const digit *export_long_digits = export_long.digit_array.digits;
191186

192187
PyObject *digits = PyList_New(0);
193-
for (Py_ssize_t i=0; i < export_long.ndigits; i++) {
188+
for (Py_ssize_t i=0; i < export_long.digit_array.ndigits; i++) {
194189
PyObject *item = PyLong_FromUnsignedLong(export_long_digits[i]);
195190
if (item == NULL) {
196191
goto error;
@@ -203,7 +198,8 @@ pylong_export(PyObject *module, PyObject *obj)
203198
Py_DECREF(item);
204199
}
205200

206-
PyObject *res = Py_BuildValue("(iN)", export_long.negative, digits);
201+
PyObject *res = Py_BuildValue("(iN)", export_long.digit_array.negative,
202+
digits);
207203

208204
PyLong_FreeExport(&export_long);
209205
assert(export_long._reserved == 0);

Objects/longobject.c

Lines changed: 11 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6779,7 +6779,7 @@ int PyLong_AsUInt64(PyObject *obj, uint64_t *value)
67796779
static const PyLongLayout PyLong_LAYOUT = {
67806780
.bits_per_digit = PyLong_SHIFT,
67816781
.digits_order = -1, // least significant first
6782-
.endian = PY_LITTLE_ENDIAN ? -1 : 1,
6782+
.endianness = PY_LITTLE_ENDIAN ? -1 : 1,
67836783
.digit_size = sizeof(digit),
67846784
};
67856785

@@ -6790,45 +6790,23 @@ const PyLongLayout* PyLong_GetNativeLayout(void)
67906790
}
67916791

67926792

6793-
int
6793+
PyLongExport_Kind
67946794
PyLong_Export(PyObject *obj, PyLongExport *export_long)
67956795
{
67966796
if (!PyLong_Check(obj)) {
67976797
PyErr_Format(PyExc_TypeError, "expect int, got %T", obj);
6798-
return -1;
6798+
return PyLongExport_Error;
67996799
}
68006800

6801-
// Fast-path: try to convert to a int64_t
6802-
int overflow;
6803-
#if SIZEOF_LONG == 8
6804-
long value = PyLong_AsLongAndOverflow(obj, &overflow);
6805-
#else
6806-
// Windows has 32-bit long, so use 64-bit long long instead
6807-
long long value = PyLong_AsLongLongAndOverflow(obj, &overflow);
6808-
#endif
6809-
Py_BUILD_ASSERT(sizeof(value) == sizeof(int64_t));
6810-
// the function cannot fail since obj is a PyLongObject
6811-
assert(!(value == -1 && PyErr_Occurred()));
6812-
6813-
if (!overflow) {
6814-
export_long->value = value;
6815-
export_long->negative = 0;
6816-
export_long->ndigits = 0;
6817-
export_long->digits = 0;
6818-
export_long->_reserved = 0;
6801+
PyLongObject *self = (PyLongObject*)obj;
6802+
export_long->digit_array.negative = _PyLong_IsNegative(self);
6803+
export_long->digit_array.ndigits = _PyLong_DigitCount(self);
6804+
if (export_long->digit_array.ndigits == 0) {
6805+
export_long->digit_array.ndigits = 1;
68196806
}
6820-
else {
6821-
PyLongObject *self = (PyLongObject*)obj;
6822-
export_long->value = 0;
6823-
export_long->negative = _PyLong_IsNegative(self);
6824-
export_long->ndigits = _PyLong_DigitCount(self);
6825-
if (export_long->ndigits == 0) {
6826-
export_long->ndigits = 1;
6827-
}
6828-
export_long->digits = self->long_value.ob_digit;
6829-
export_long->_reserved = (Py_uintptr_t)Py_NewRef(obj);
6830-
}
6831-
return 0;
6807+
export_long->digit_array.digits = self->long_value.ob_digit;
6808+
export_long->_reserved = (Py_uintptr_t)Py_NewRef(obj);
6809+
return PyLongExport_DigitArray;
68326810
}
68336811

68346812

0 commit comments

Comments
 (0)