From 03d6c6a5032845048219f61c386a54b5fb78a196 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Thu, 9 Jul 2026 11:37:49 +0300 Subject: [PATCH 1/3] Use PyLong_As/FromNativeBytes API for mpz <-> int Closes #313 --- gmp.c | 121 +++++++++++++++++++++++++++++++++------------------------- 1 file changed, 68 insertions(+), 53 deletions(-) diff --git a/gmp.c b/gmp.c index 9485fd03..2972ddc4 100644 --- a/gmp.c +++ b/gmp.c @@ -180,49 +180,12 @@ MPZ_from_str(PyObject *obj, int base) return res; } +static zz_err zz_set_bytes(const unsigned char *buffer, size_t length, + bool is_signed, zz_t *u); + static MPZ_Object * MPZ_from_int(PyObject *obj) { -#if defined(ON_CPYTHON) && !defined(Py_LIMITED_API) - PyLongExport long_export = {0, 0, 0, 0, 0}; - const zz_layout *int_layout = (zz_layout *)PyLong_GetNativeLayout(); - MPZ_Object *res = NULL; - - if (PyLong_Export(obj, &long_export) < 0) { - return res; /* LCOV_EXCL_LINE */ - } - if (long_export.digits) { - res = MPZ_new(); - if (!res) { - return NULL; /* LCOV_EXCL_LINE */ - } - - zz_err ret = zz_import((size_t)long_export.ndigits, - long_export.digits, *int_layout, &res->z); - - if (ret) { - /* LCOV_EXCL_START */ - if (ret == ZZ_MEM) { - return (MPZ_Object *)PyErr_NoMemory(); - } - PyErr_SetString(PyExc_OverflowError, - "too many digits in integer"); - return NULL; - /* LCOV_EXCL_STOP */ - } - if (long_export.negative) { - (void)zz_neg(&res->z, &res->z); - } - PyLong_FreeExport(&long_export); - } - else { - res = MPZ_new(); - if (res && zz_set(long_export.value, &res->z)) { - PyErr_NoMemory(); /* LCOV_EXCL_LINE */ - } - } - return res; -#else int64_t value; if (!PyLong_AsInt64(obj, &value)) { @@ -235,6 +198,50 @@ MPZ_from_int(PyObject *obj) } PyErr_Clear(); +#if defined(ON_CPYTHON) && PY_VERSION_HEX > 0x030D00A0 \ + && !defined(Py_LIMITED_API) + Py_ssize_t expected = PyLong_AsNativeBytes(obj, NULL, 0, 0); + + if (expected < 0) { + return NULL; /* LCOV_EXCL_LINE */ + } + + unsigned char *buffer = malloc((size_t)expected); + + if (!buffer) { + return (MPZ_Object *)PyErr_NoMemory(); /* LCOV_EXCL_LINE */ + } + + expected = PyLong_AsNativeBytes(obj, buffer, expected, 0); + if (expected < 0) { +free: + /* LCOV_EXCL_START */ + free(buffer); + return NULL; + /* LCOV_EXCL_STOP */ + } + + MPZ_Object *res = MPZ_new(); + + if (!res) { + goto free; /* LCOV_EXCL_LINE */ + } + + zz_err ret = zz_set_bytes(buffer, (size_t)expected, true, &res->z); + + free(buffer); + if (ret) { + /* LCOV_EXCL_START */ + if (ret == ZZ_MEM) { + return (MPZ_Object *)PyErr_NoMemory(); + } + PyErr_SetString(PyExc_OverflowError, + "too many digits in integer"); + return NULL; + /* LCOV_EXCL_STOP */ + } + return res; +#else PyObject *str = PyNumber_ToBase(obj, 16); if (!str) { @@ -245,9 +252,14 @@ MPZ_from_int(PyObject *obj) Py_DECREF(str); return res; -#endif /* defined(ON_CPYTHON) && !defined(Py_LIMITED_API) */ +#endif /* defined(ON_CPYTHON) && PY_VERSION_HEX > 0x030D00A0 + && !defined(Py_LIMITED_API) */ } +static zz_err zz_get_bytes(const zz_t *u, size_t length, + bool is_signed, unsigned char **buffer); +static void revstr(unsigned char *s, Py_ssize_t l, Py_ssize_t r); + static PyObject * MPZ_to_int(MPZ_Object *u) { @@ -257,19 +269,21 @@ MPZ_to_int(MPZ_Object *u) return PyLong_FromInt64(value); } -#if defined(ON_CPYTHON) && !defined(Py_LIMITED_API) - const zz_layout *int_layout = (zz_layout *)PyLong_GetNativeLayout(); - size_t size = (zz_bitlen(&u->z) + int_layout->bits_per_digit - - 1)/int_layout->bits_per_digit; - void *digits; - PyLongWriter *writer = PyLongWriter_Create(zz_isneg(&u->z), - (Py_ssize_t)size, &digits); +#if defined(ON_CPYTHON) && PY_VERSION_HEX > 0x030D00A0 \ + && !defined(Py_LIMITED_API) + size_t size = (zz_bitlen(&u->z) + 7)/8 + 1; + unsigned char *buffer = malloc(size); - if (!writer) { - return NULL; /* LCOV_EXCL_LINE */ + if (!buffer) { + return PyErr_NoMemory(); /* LCOV_EXCL_LINE */ } - (void)zz_export(&u->z, *int_layout, size, digits); - return PyLongWriter_Finish(writer); + (void)zz_get_bytes(&u->z, size, true, &buffer); + revstr(buffer, 0, (Py_ssize_t)size - 1); + + PyObject *res = PyLong_FromNativeBytes(buffer, size, -1); + + free(buffer); + return res; #else size_t len; @@ -289,7 +303,8 @@ MPZ_to_int(MPZ_Object *u) free(buf); return res; -#endif /* defined(ON_CPYTHON) && !defined(Py_LIMITED_API) */ +#endif /* defined(ON_CPYTHON) && PY_VERSION_HEX > 0x030D00A0 + && !defined(Py_LIMITED_API) */ } static void From d315d7c349bf75ae3a0e36f052a4f1b96a41d264 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Fri, 10 Jul 2026 02:26:07 +0300 Subject: [PATCH 2/3] +1 --- gmp.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/gmp.c b/gmp.c index 2972ddc4..81204ecf 100644 --- a/gmp.c +++ b/gmp.c @@ -182,6 +182,7 @@ MPZ_from_str(PyObject *obj, int base) static zz_err zz_set_bytes(const unsigned char *buffer, size_t length, bool is_signed, zz_t *u); +static void revstr(unsigned char *s, Py_ssize_t l, Py_ssize_t r); static MPZ_Object * MPZ_from_int(PyObject *obj) @@ -200,7 +201,7 @@ MPZ_from_int(PyObject *obj) #if defined(ON_CPYTHON) && PY_VERSION_HEX > 0x030D00A0 \ && !defined(Py_LIMITED_API) - Py_ssize_t expected = PyLong_AsNativeBytes(obj, NULL, 0, 0); + Py_ssize_t expected = PyLong_AsNativeBytes(obj, NULL, 0, -1); if (expected < 0) { return NULL; /* LCOV_EXCL_LINE */ @@ -212,7 +213,7 @@ MPZ_from_int(PyObject *obj) return (MPZ_Object *)PyErr_NoMemory(); /* LCOV_EXCL_LINE */ } - expected = PyLong_AsNativeBytes(obj, buffer, expected, 0); + expected = PyLong_AsNativeBytes(obj, buffer, expected, -1); if (expected < 0) { free: /* LCOV_EXCL_START */ @@ -227,8 +228,16 @@ MPZ_from_int(PyObject *obj) goto free; /* LCOV_EXCL_LINE */ } - zz_err ret = zz_set_bytes(buffer, (size_t)expected, true, &res->z); + zz_err ret; + if (PyLong_IsNegative(obj)) { + revstr(buffer, 0, expected - 1); + ret = zz_set_bytes(buffer, (size_t)expected, true, &res->z); + } + else { + ret = zz_import((size_t)expected, buffer, + (zz_layout){8, 1, -1, 0}, &res->z); + } free(buffer); if (ret) { /* LCOV_EXCL_START */ @@ -258,7 +267,6 @@ MPZ_from_int(PyObject *obj) static zz_err zz_get_bytes(const zz_t *u, size_t length, bool is_signed, unsigned char **buffer); -static void revstr(unsigned char *s, Py_ssize_t l, Py_ssize_t r); static PyObject * MPZ_to_int(MPZ_Object *u) From 0b2d0b563abfea9e9bad8aa7017c83059ce93507 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Fri, 10 Jul 2026 03:45:31 +0300 Subject: [PATCH 3/3] Revert "+1" This reverts commit d315d7c349bf75ae3a0e36f052a4f1b96a41d264. --- gmp.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/gmp.c b/gmp.c index 81204ecf..2972ddc4 100644 --- a/gmp.c +++ b/gmp.c @@ -182,7 +182,6 @@ MPZ_from_str(PyObject *obj, int base) static zz_err zz_set_bytes(const unsigned char *buffer, size_t length, bool is_signed, zz_t *u); -static void revstr(unsigned char *s, Py_ssize_t l, Py_ssize_t r); static MPZ_Object * MPZ_from_int(PyObject *obj) @@ -201,7 +200,7 @@ MPZ_from_int(PyObject *obj) #if defined(ON_CPYTHON) && PY_VERSION_HEX > 0x030D00A0 \ && !defined(Py_LIMITED_API) - Py_ssize_t expected = PyLong_AsNativeBytes(obj, NULL, 0, -1); + Py_ssize_t expected = PyLong_AsNativeBytes(obj, NULL, 0, 0); if (expected < 0) { return NULL; /* LCOV_EXCL_LINE */ @@ -213,7 +212,7 @@ MPZ_from_int(PyObject *obj) return (MPZ_Object *)PyErr_NoMemory(); /* LCOV_EXCL_LINE */ } - expected = PyLong_AsNativeBytes(obj, buffer, expected, -1); + expected = PyLong_AsNativeBytes(obj, buffer, expected, 0); if (expected < 0) { free: /* LCOV_EXCL_START */ @@ -228,16 +227,8 @@ MPZ_from_int(PyObject *obj) goto free; /* LCOV_EXCL_LINE */ } - zz_err ret; + zz_err ret = zz_set_bytes(buffer, (size_t)expected, true, &res->z); - if (PyLong_IsNegative(obj)) { - revstr(buffer, 0, expected - 1); - ret = zz_set_bytes(buffer, (size_t)expected, true, &res->z); - } - else { - ret = zz_import((size_t)expected, buffer, - (zz_layout){8, 1, -1, 0}, &res->z); - } free(buffer); if (ret) { /* LCOV_EXCL_START */ @@ -267,6 +258,7 @@ MPZ_from_int(PyObject *obj) static zz_err zz_get_bytes(const zz_t *u, size_t length, bool is_signed, unsigned char **buffer); +static void revstr(unsigned char *s, Py_ssize_t l, Py_ssize_t r); static PyObject * MPZ_to_int(MPZ_Object *u)