Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PEP 757: edits, based on C-API WG feedback #4026

Merged
merged 18 commits into from
Oct 15, 2024
Merged
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 46 additions & 59 deletions peps/pep-0757.rst
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,15 @@ Specification
Layout API
----------

Data needed by `GMP <https://gmplib.org/>`_-like import-export functions.
Data needed by `GMP <https://gmplib.org/>`_-like `import
<https://gmplib.org/manual/Integer-Import-and-Export#index-mpz_005fimport>`_-`export
<https://gmplib.org/manual/Integer-Import-and-Export#index-mpz_005fexport>`_
functions.

.. c:struct:: PyLongLayout

Layout of an array of digits, used by Python :class:`int` object.
Layout of an array of digits, used by Python :class:`int` object to
represent "big enough" integers.
skirpichev marked this conversation as resolved.
Show resolved Hide resolved

Use :c:func:`PyLong_GetNativeLayout` to get the native layout of Python
:class:`int` objects.
Expand Down Expand Up @@ -98,63 +102,56 @@ Data needed by `GMP <https://gmplib.org/>`_-like import-export functions.
Export API
----------

.. c:struct:: PyLongExport
.. c:type:: PyLongExport_Kind

Export of a Python :class:`int` object.
The enum value used to represent different results of
:c:func:`PyLong_Export`.

There are two cases:

* If :c:member:`digits` is ``NULL``, only use the :c:member:`value` member.
Calling :c:func:`PyLong_FreeExport` is optional in this case.
* If :c:member:`digits` is not ``NULL``, use :c:member:`negative`,
:c:member:`ndigits` and :c:member:`digits` members.
Calling :c:func:`PyLong_FreeExport` is mandatory in this case.
.. c:struct:: PyLongExport

.. c:member:: int64_t value
Export of a Python :class:`int` object.

The native integer value of the exported :class:`int` object.
Only valid if :c:member:`digits` is ``NULL``.
.. c:struct:: digit_array

.. c:member:: uint8_t negative
Export an integer as an array of digits; corresponds to
``PyLongExport_DigitArray`` value of the enum
:c:type:`PyLongExport_Kind`.

1 if the number is negative, 0 otherwise.
Only valid if :c:member:`digits` is not ``NULL``.
.. c:member:: Py_ssize_t ndigits

.. c:member:: Py_ssize_t ndigits
Number of digits in :c:member:`digits` array.

Number of digits in :c:member:`digits` array.
Only valid if :c:member:`digits` is not ``NULL``.
.. c:member:: const void *digits

.. c:member:: const void *digits
Read-only array of unsigned digits.

Read-only array of unsigned digits. Can be ``NULL``.
.. c:member:: uint8_t negative

If :c:member:`digits` not ``NULL``, a private field of the
:c:struct:`PyLongExport` structure stores a strong reference to the Python
:class:`int` object to make sure that that structure remains valid until
:c:func:`PyLong_FreeExport()` is called.
1 if the number is negative, 0 otherwise.


.. c:function:: int PyLong_Export(PyObject *obj, PyLongExport *export_long)
.. c:function:: PyLongExport_Kind PyLong_Export(PyObject *obj, PyLongExport *export)

Export a Python :class:`int` object.

On success, set *\*export_long* and return 0.
On error, set an exception and return -1.
On success, set *\*export* and return an appropriate export type for the
given value. :c:func:`PyLong_FreeExport` must be called when the export is
no longer needed. Currently available type: ``PyLongExport_DigitArray``.

On error, set an exception and return ``PyLongExport_Error``.

This function always succeeds if *obj* is a Python :class:`int` object or a
subclass.

If *export_long->digits* is not ``NULL``, :c:func:`PyLong_FreeExport` must be
called when the export is no longer needed.

On CPython 3.14, no memory copy is needed, it's just a thin wrapper to
expose Python int internal digits array.
skirpichev marked this conversation as resolved.
Show resolved Hide resolved
On CPython 3.14, no memory copy is needed in :c:func:`PyLong_Export`, it's just
a thin wrapper to expose Python :class:`int` internal digits array.


.. c:function:: void PyLong_FreeExport(PyLongExport *export_long)
.. c:function:: void PyLong_FreeExport(PyLongExport *export)

Release the export *export_long* created by :c:func:`PyLong_Export`.
Release the *export* created by :c:func:`PyLong_Export`.


Import API
Expand Down Expand Up @@ -187,8 +184,9 @@ create a Python :class:`int` object from a digits array.
in the range [``0``; ``(1 << sys.int_info.bits_per_digit) - 1``]. Unused digits must
be set to ``0``.

On CPython 3.14, the implementation is a thin wrapper to the private
:c:func:`!_PyLong_New()` function.

On CPython 3.14, the :c:func:`PyLongWriter_Create` implementation is a thin
wrapper to the private :c:func:`!_PyLong_New()` function.


.. c:function:: PyObject* PyLongWriter_Finish(PyLongWriter *writer)
Expand Down Expand Up @@ -259,32 +257,21 @@ Code::
mpz_set_PyLong(mpz_t z, PyObject *obj)
{
static PyLongExport long_export;

PyLong_Export(obj, &long_export);
if (long_export.digits) {
mpz_import(z, long_export.ndigits, int_digits_order, int_digit_size,
int_endianness, int_nails, long_export.digits);
if (long_export.negative) {
const PyLongExport_Kind kind = PyLong_Export(obj, &long_export);

switch (kind) {
case PyLongExport_DigitArray:
mpz_import(z, long_export.digit_array.ndigits, int_digits_order,
int_digit_size, int_endianness, int_nails,
long_export.digit_array.digits);
if (long_export.digit_array.negative) {
mpz_neg(z, z);
}
PyLong_FreeExport(&long_export);
}
else {
const int64_t value = long_export.value;

if (LONG_MIN <= value && value <= LONG_MAX) {
mpz_set_si(z, value);
}
else {
mpz_import(z, 1, -1, sizeof(int64_t), 0, 0, &value);
if (value < 0) {
mpz_t tmp;
mpz_init(tmp);
mpz_ui_pow_ui(tmp, 2, 64);
mpz_sub(z, z, tmp);
mpz_clear(tmp);
}
}
break;
default:
PyLong_FreeExport(&long_export);
abort(); /* new CPython release come with a new format */
skirpichev marked this conversation as resolved.
Show resolved Hide resolved
skirpichev marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand Down