From 060be1d700d87c0c3599329886cacaf496204dae Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Tue, 8 Oct 2024 09:02:35 +0300 Subject: [PATCH 01/17] Clarify PyLong_FreeExport usage --- peps/pep-0757.rst | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/peps/pep-0757.rst b/peps/pep-0757.rst index 12b967c1bbf..3d6fb41d1a7 100644 --- a/peps/pep-0757.rst +++ b/peps/pep-0757.rst @@ -105,10 +105,8 @@ Export API 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. + * Else use :c:member:`negative`, :c:member:`ndigits` and :c:member:`digits` + members. Calling :c:func:`PyLong_FreeExport` is mandatory in this case. .. c:member:: int64_t value From 9d2ea48a8c4caaa92323fe5ddc1e659341a2f091 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Tue, 8 Oct 2024 15:31:52 +0300 Subject: [PATCH 02/17] Clarify that PyLongLayout has nothing to say about small integers --- peps/pep-0757.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/peps/pep-0757.rst b/peps/pep-0757.rst index 3d6fb41d1a7..d1785e356ea 100644 --- a/peps/pep-0757.rst +++ b/peps/pep-0757.rst @@ -53,7 +53,8 @@ Data needed by `GMP `_-like import-export 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. Use :c:func:`PyLong_GetNativeLayout` to get the native layout of Python :class:`int` objects. From d19e217e16843bd6f0094fcfd5fc2df6282e9515 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Tue, 8 Oct 2024 15:38:40 +0300 Subject: [PATCH 03/17] Links to GMP API --- peps/pep-0757.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/peps/pep-0757.rst b/peps/pep-0757.rst index d1785e356ea..f595e780eba 100644 --- a/peps/pep-0757.rst +++ b/peps/pep-0757.rst @@ -49,7 +49,10 @@ Specification Layout API ---------- -Data needed by `GMP `_-like import-export functions. +Data needed by `GMP `_-like `import +`_-`export +`_ +functions. .. c:struct:: PyLongLayout From 08b76d8a7ae605c9f7b19598222f19325bcde9d0 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Tue, 8 Oct 2024 15:40:14 +0300 Subject: [PATCH 04/17] Hide obvious details for PyLong_Export (we don't raise exception, that enough) --- peps/pep-0757.rst | 3 --- 1 file changed, 3 deletions(-) diff --git a/peps/pep-0757.rst b/peps/pep-0757.rst index f595e780eba..283da369507 100644 --- a/peps/pep-0757.rst +++ b/peps/pep-0757.rst @@ -150,9 +150,6 @@ Export API 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. - .. c:function:: void PyLong_FreeExport(PyLongExport *export_long) From a3e51e27d1fc4f4fc9bade795217510661719817 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Tue, 8 Oct 2024 20:19:03 +0300 Subject: [PATCH 05/17] +1 --- peps/pep-0757.rst | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/peps/pep-0757.rst b/peps/pep-0757.rst index 283da369507..b51038dcb2d 100644 --- a/peps/pep-0757.rst +++ b/peps/pep-0757.rst @@ -151,6 +151,10 @@ Export API called when the export is no longer needed. +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) Release the export *export_long* created by :c:func:`PyLong_Export`. @@ -186,8 +190,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) From c0a725b64d9e6253309fe41c0afafed0bafff870 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Sat, 12 Oct 2024 16:24:25 +0300 Subject: [PATCH 06/17] An attempt to address Petr's concerns --- peps/pep-0757.rst | 78 ++++++++++++++++++----------------------------- 1 file changed, 30 insertions(+), 48 deletions(-) diff --git a/peps/pep-0757.rst b/peps/pep-0757.rst index b51038dcb2d..075c09c8adc 100644 --- a/peps/pep-0757.rst +++ b/peps/pep-0757.rst @@ -102,62 +102,56 @@ 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. - * Else 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 some 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 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 *export* created by :c:func:`PyLong_Export`. Import API @@ -263,32 +257,20 @@ Code:: mpz_set_PyLong(mpz_t z, PyObject *obj) { static PyLongExport long_export; + const PyLongExport_Kind kind = PyLong_Export(obj, &long_export); - PyLong_Export(obj, &long_export); - if (long_export.digits) { + switch (kind) { + case PyLongExport_DigitArray: mpz_import(z, long_export.ndigits, int_digits_order, int_digit_size, int_endianness, int_nails, long_export.digits); if (long_export.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 */ } } From f5c2b9a33b0a1432caf10d5beb2ed3d63ae01a0b Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Sat, 12 Oct 2024 16:33:10 +0300 Subject: [PATCH 07/17] +1 --- peps/pep-0757.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/peps/pep-0757.rst b/peps/pep-0757.rst index 075c09c8adc..71fd997ffee 100644 --- a/peps/pep-0757.rst +++ b/peps/pep-0757.rst @@ -261,9 +261,10 @@ Code:: switch (kind) { case PyLongExport_DigitArray: - mpz_import(z, long_export.ndigits, int_digits_order, int_digit_size, - int_endianness, int_nails, long_export.digits); - if (long_export.negative) { + 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); From 49018ea0c0f9f766c4d83d735683ee247d2f03b6 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Sun, 13 Oct 2024 06:26:51 +0300 Subject: [PATCH 08/17] Apply suggestions from code review Co-authored-by: Carol Willing --- peps/pep-0757.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/peps/pep-0757.rst b/peps/pep-0757.rst index 71fd997ffee..1ba8f8e0971 100644 --- a/peps/pep-0757.rst +++ b/peps/pep-0757.rst @@ -135,7 +135,7 @@ Export API Export a Python :class:`int` object. - On success, set *\*export* and return some appropriate export type for the + 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``. @@ -151,7 +151,7 @@ a thin wrapper to expose Python :class:`int` internal digits array. .. c:function:: void PyLong_FreeExport(PyLongExport *export) - Release the export *export* created by :c:func:`PyLong_Export`. + Release the *export* created by :c:func:`PyLong_Export`. Import API From f131ee6030b5dc5f1bdfb10d13d934a0b528d54c Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Sun, 13 Oct 2024 07:00:17 +0300 Subject: [PATCH 09/17] + expand PyLong_Export docs --- peps/pep-0757.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/peps/pep-0757.rst b/peps/pep-0757.rst index 1ba8f8e0971..d49af2c7c74 100644 --- a/peps/pep-0757.rst +++ b/peps/pep-0757.rst @@ -136,9 +136,10 @@ Export API Export a Python :class:`int` object. 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``. - + given value, see :c:struct:`PyLongExport`. :c:func:`PyLong_FreeExport` must + be called when the export is no longer needed. Currently the only available + type is ``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 From eb3a4a9e99fe975b2e2ef8ea8b4c7fbae027dad1 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Sun, 13 Oct 2024 07:39:18 +0300 Subject: [PATCH 10/17] +1 --- peps/pep-0757.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/peps/pep-0757.rst b/peps/pep-0757.rst index d49af2c7c74..bee9357cf52 100644 --- a/peps/pep-0757.rst +++ b/peps/pep-0757.rst @@ -135,10 +135,11 @@ Export API Export a Python :class:`int` object. - On success, set *\*export* and return an appropriate export type for the - given value, see :c:struct:`PyLongExport`. :c:func:`PyLong_FreeExport` must - be called when the export is no longer needed. Currently the only available - type is ``PyLongExport_DigitArray``. + On success, set *\*export* and return an appropriate export type + for the given value, see the :c:struct:`PyLongExport` struct. + :c:func:`PyLong_FreeExport` must be called when the export is no + longer needed. Currently the only available type is + ``PyLongExport_DigitArray``. On error, set an exception and return ``PyLongExport_Error``. From c3662381b290e45c9c8780c960893f986b9df305 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Sun, 13 Oct 2024 08:16:48 +0300 Subject: [PATCH 11/17] Adapt export code example to include a fast path for small ints --- peps/pep-0757.rst | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/peps/pep-0757.rst b/peps/pep-0757.rst index bee9357cf52..4a549fb98f6 100644 --- a/peps/pep-0757.rst +++ b/peps/pep-0757.rst @@ -258,22 +258,30 @@ Code:: static void mpz_set_PyLong(mpz_t z, PyObject *obj) { - static PyLongExport long_export; - 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); + int overflow; + long value = PyLong_AsLongAndOverflow(obj, &overflow); + + if (!overflow) { + mpz_set_si(z, value); + } + else { + static PyLongExport long_export; + 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); + break; + default: + PyLong_FreeExport(&long_export); + abort(); /* new CPython release come with a new format */ } - PyLong_FreeExport(&long_export); - break; - default: - PyLong_FreeExport(&long_export); - abort(); /* new CPython release come with a new format */ } } From 549c0ab7906082cb7e86b4eb779393dafd1b0fb6 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Sun, 13 Oct 2024 08:35:51 +0300 Subject: [PATCH 12/17] Update peps/pep-0757.rst (typo) --- peps/pep-0757.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/peps/pep-0757.rst b/peps/pep-0757.rst index 4a549fb98f6..fbe673306f8 100644 --- a/peps/pep-0757.rst +++ b/peps/pep-0757.rst @@ -280,7 +280,7 @@ Code:: break; default: PyLong_FreeExport(&long_export); - abort(); /* new CPython release come with a new format */ + abort(); /* new CPython release came with a new format */ } } } From 3739f92e49a040ec56f142d35531814e275f3591 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Sun, 13 Oct 2024 18:55:42 +0300 Subject: [PATCH 13/17] clarify PyLongWriter_Finish/Discard use cases --- peps/pep-0757.rst | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/peps/pep-0757.rst b/peps/pep-0757.rst index fbe673306f8..ed45914c404 100644 --- a/peps/pep-0757.rst +++ b/peps/pep-0757.rst @@ -166,7 +166,8 @@ create a Python :class:`int` object from a digits array. A Python :class:`int` writer instance. - The instance must be destroyed by :c:func:`PyLongWriter_Finish`. + The instance must be destroyed by :c:func:`PyLongWriter_Finish` or + :c:func:`PyLongWriter_Discard`. .. c:function:: PyLongWriter* PyLongWriter_Create(int negative, Py_ssize_t ndigits, void **digits) @@ -181,10 +182,11 @@ create a Python :class:`int` object from a digits array. *ndigits* is the number of digits in the *digits* array. It must be greater than or equal to 0. - The caller must initialize the array of digits *digits* and then call - :c:func:`PyLongWriter_Finish` to get a Python :class:`int`. Digits must be - in the range [``0``; ``(1 << sys.int_info.bits_per_digit) - 1``]. Unused digits must - be set to ``0``. + The caller can either initialize the array of digits *digits* and then call + :c:func:`PyLongWriter_Finish` to get a Python :class:`int`, or call + :c:func:`PyLongWriter_Discard` to destroy the writer instance. Digits must + be in the range [``0``; ``(1 << sys.int_info.bits_per_digit) - 1``]. Unused + digits must be set to ``0``. On CPython 3.14, the :c:func:`PyLongWriter_Create` implementation is a thin @@ -204,7 +206,7 @@ wrapper to the private :c:func:`!_PyLong_New()` function. .. c:function:: void PyLongWriter_Discard(PyLongWriter *writer) - Discard the internal object and destroy the writer instance. + Discard a :c:type:`PyLongWriter` created by :c:func:`PyLongWriter_Create`. Optimize import for small integers From 0419703e1c95e6ba9fdaab3fa036b54fc025f936 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Mon, 14 Oct 2024 07:24:21 +0300 Subject: [PATCH 14/17] + improve PyLongLayout description --- peps/pep-0757.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/peps/pep-0757.rst b/peps/pep-0757.rst index ed45914c404..d7cebc71e6a 100644 --- a/peps/pep-0757.rst +++ b/peps/pep-0757.rst @@ -56,11 +56,12 @@ functions. .. c:struct:: PyLongLayout - Layout of an array of digits, used by Python :class:`int` object to - represent "big enough" integers. + Layout of an array of "digits" ("limbs" in the GMP terminology), used to + represent absolute value for arbitrary precision integers. Use :c:func:`PyLong_GetNativeLayout` to get the native layout of Python - :class:`int` objects. + :class:`int` objects, used internally for integers with "big enough" + absolute value. See also :data:`sys.int_info` which exposes similar information to Python. From 77537845f77c842304d9dbbed5dd3f5672cdf144 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Mon, 14 Oct 2024 10:38:47 +0300 Subject: [PATCH 15/17] don't use enum for export types --- peps/pep-0757.rst | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/peps/pep-0757.rst b/peps/pep-0757.rst index d7cebc71e6a..cc59281b256 100644 --- a/peps/pep-0757.rst +++ b/peps/pep-0757.rst @@ -103,12 +103,6 @@ functions. Export API ---------- -.. c:type:: PyLongExport_Kind - - The enum value used to represent different results of - :c:func:`PyLong_Export`. - - .. c:struct:: PyLongExport Export of a Python :class:`int` object. @@ -116,8 +110,7 @@ Export API .. c:struct:: digit_array Export an integer as an array of digits; corresponds to - ``PyLongExport_DigitArray`` value of the enum - :c:type:`PyLongExport_Kind`. + :c:macro:`!PyLongExport_DigitArray` export type. .. c:member:: Py_ssize_t ndigits @@ -132,7 +125,7 @@ Export API 1 if the number is negative, 0 otherwise. -.. c:function:: PyLongExport_Kind PyLong_Export(PyObject *obj, PyLongExport *export) +.. c:function:: int PyLong_Export(PyObject *obj, PyLongExport *export) Export a Python :class:`int` object. @@ -140,9 +133,9 @@ Export API for the given value, see the :c:struct:`PyLongExport` struct. :c:func:`PyLong_FreeExport` must be called when the export is no longer needed. Currently the only available type is - ``PyLongExport_DigitArray``. + :c:macro:`!PyLongExport_DigitArray`. - On error, set an exception and return ``PyLongExport_Error``. + On error, set an exception and return :c:macro:`!PyLongExport_Error`. This function always succeeds if *obj* is a Python :class:`int` object or a subclass. From d6ad15e62e432f64db85e80cf25abdd84a567237 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Tue, 15 Oct 2024 12:29:18 +0300 Subject: [PATCH 16/17] revert old PyLongExport & return type for PyLong_Export :( --- peps/pep-0757.rst | 88 ++++++++++++++++++++++++++++------------------- 1 file changed, 52 insertions(+), 36 deletions(-) diff --git a/peps/pep-0757.rst b/peps/pep-0757.rst index cc59281b256..6e04cdb11ee 100644 --- a/peps/pep-0757.rst +++ b/peps/pep-0757.rst @@ -107,39 +107,52 @@ Export API Export of a Python :class:`int` object. - .. c:struct:: digit_array + There are two cases: - Export an integer as an array of digits; corresponds to - :c:macro:`!PyLongExport_DigitArray` export type. + * 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:member:: Py_ssize_t ndigits + .. c:member:: int64_t value - Number of digits in :c:member:`digits` array. + The native integer value of the exported :class:`int` object. + Only valid if :c:member:`digits` is ``NULL``. - .. c:member:: const void *digits + .. c:member:: uint8_t negative - Read-only array of unsigned digits. + 1 if the number is negative, 0 otherwise. + Only valid if :c:member:`digits` is not ``NULL``. - .. c:member:: uint8_t negative + .. c:member:: Py_ssize_t ndigits - 1 if the number is negative, 0 otherwise. + Number of digits in :c:member:`digits` array. + Only valid if :c:member:`digits` is not ``NULL``. + + .. c:member:: const void *digits + + Read-only array of unsigned digits. Can be ``NULL``. + + 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. .. c:function:: int PyLong_Export(PyObject *obj, PyLongExport *export) Export a Python :class:`int` object. - On success, set *\*export* and return an appropriate export type - for the given value, see the :c:struct:`PyLongExport` struct. - :c:func:`PyLong_FreeExport` must be called when the export is no - longer needed. Currently the only available type is - :c:macro:`!PyLongExport_DigitArray`. - - On error, set an exception and return :c:macro:`!PyLongExport_Error`. + On success, set *\*export* and return 0. + On error, set an exception and return -1. This function always succeeds if *obj* is a Python :class:`int` object or a subclass. + If *export->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 in :c:func:`PyLong_Export`, it's just a thin wrapper to expose Python :class:`int` internal digits array. @@ -254,29 +267,32 @@ Code:: static void mpz_set_PyLong(mpz_t z, PyObject *obj) { - int overflow; - long value = PyLong_AsLongAndOverflow(obj, &overflow); - - if (!overflow) { - mpz_set_si(z, value); + 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) { + mpz_neg(z, z); + } + PyLong_FreeExport(&long_export); } else { - static PyLongExport long_export; - 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); + 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); } - PyLong_FreeExport(&long_export); - break; - default: - PyLong_FreeExport(&long_export); - abort(); /* new CPython release came with a new format */ } } } From 8fe56c24534829426fc2d74a564b32a509b1f375 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Tue, 15 Oct 2024 12:41:09 +0300 Subject: [PATCH 17/17] + revert export_long --- peps/pep-0757.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/peps/pep-0757.rst b/peps/pep-0757.rst index 6e04cdb11ee..e3208cf410f 100644 --- a/peps/pep-0757.rst +++ b/peps/pep-0757.rst @@ -140,17 +140,17 @@ Export API :c:func:`PyLong_FreeExport()` is called. -.. c:function:: int PyLong_Export(PyObject *obj, PyLongExport *export) +.. c:function:: int PyLong_Export(PyObject *obj, PyLongExport *export_long) Export a Python :class:`int` object. - On success, set *\*export* and return 0. + On success, set *\*export_long* and return 0. On error, set an exception and return -1. This function always succeeds if *obj* is a Python :class:`int` object or a subclass. - If *export->digits* is not ``NULL``, :c:func:`PyLong_FreeExport` must be + If *export_long->digits* is not ``NULL``, :c:func:`PyLong_FreeExport` must be called when the export is no longer needed. @@ -158,9 +158,9 @@ 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) +.. c:function:: void PyLong_FreeExport(PyLongExport *export_long) - Release the *export* created by :c:func:`PyLong_Export`. + Release the export *export_long* created by :c:func:`PyLong_Export`. Import API