diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 35eaffcb..cb3c3012 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: fail-fast: true matrix: os: [ubuntu-24.04] - python-version: [3.9, '3.10', pypy3.11, graalpy-24.2, + python-version: [3.9, '3.10', pypy3.11-nightly, graalpy-24.2, 3.11, 3.12, 3.13, 3.13t, 3.14, 3.14t] runs-on: ${{ matrix.os }} env: diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index c1d0c895..86f15716 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -11,7 +11,7 @@ jobs: fail-fast: true matrix: os: [ubuntu-24.04] - python-version: [pypy3.11, "3.x", 3.14] + python-version: [pypy3.11-nightly, "3.x", 3.14] steps: - uses: actions/checkout@v4 with: diff --git a/fmt.c b/fmt.c index 80a17d78..9fe60851 100644 --- a/fmt.c +++ b/fmt.c @@ -939,6 +939,7 @@ format_long_internal(MPZ_Object *value, const InternalFormatSpec *format) Py_ssize_t prefix = 0; NumberFieldWidths spec; int32_t x = -1; + gmp_state *state = PyType_GetModuleState(Py_TYPE(value)); /* Locale settings, either from the actual locale or from a hard-code pseudo-locale */ @@ -1025,7 +1026,7 @@ format_long_internal(MPZ_Object *value, const InternalFormatSpec *format) if (format->sign != '+' && format->sign != ' ' && format->width == -1 && format->type != 'n' && !format->thousands_separators - && MPZ_CheckExact(value)) + && MPZ_CheckExact(state, value)) { /* Fast path */ return MPZ_to_str(value, base, format->alternate ? OPT_PREFIX : 0); diff --git a/gmp.c b/gmp.c index 4e634f8e..0008ac8d 100644 --- a/gmp.c +++ b/gmp.c @@ -12,7 +12,7 @@ # define _Thread_local __declspec(thread) #endif -#if !defined(PYPY_VERSION) +#if !defined(PYPY_VERSION) && !defined(Py_GIL_DISABLED) # define CACHE_SIZE (99) #else # define CACHE_SIZE (0) @@ -29,11 +29,13 @@ _Thread_local gmp_global global = { }; static MPZ_Object * -MPZ_new(int64_t size) +MPZ_new(gmp_state * state, PyTypeObject *type, int64_t size) { MPZ_Object *res; - if (global.gmp_cache_size && size <= MAX_CACHE_MPZ_LIMBS) { + if (global.gmp_cache_size && size <= MAX_CACHE_MPZ_LIMBS + && type == state->MPZ_Type) + { res = global.gmp_cache[--(global.gmp_cache_size)]; if (zz_resize(size, &res->z) == ZZ_MEM) { /* LCOV_EXCL_START */ @@ -45,13 +47,21 @@ MPZ_new(int64_t size) Py_INCREF((PyObject *)res); } else { - res = PyObject_New(MPZ_Object, &MPZ_Type); + if (type == state->MPZ_Type) { + res = PyObject_GC_New(MPZ_Object, state->MPZ_Type); + } + else { + res = (MPZ_Object *)type->tp_alloc(type, 0); + } if (!res) { return NULL; /* LCOV_EXCL_LINE */ } if (zz_init(&res->z) || zz_resize(size, &res->z)) { return (MPZ_Object *)PyErr_NoMemory(); /* LCOV_EXCL_LINE */ } + if (type == state->MPZ_Type) { + PyObject_GC_Track((PyObject *)res); + } } res->hash_cache = -1; return res; @@ -143,7 +153,7 @@ MPZ_to_str(MPZ_Object *u, int base, int options) } static MPZ_Object * -MPZ_from_str(PyObject *obj, int base) +MPZ_from_str(gmp_state *state, PyTypeObject *type, PyObject *obj, int base) { Py_ssize_t len; int8_t *str = (int8_t *)PyUnicode_AsUTF8AndSize(obj, &len); @@ -155,7 +165,7 @@ MPZ_from_str(PyObject *obj, int base) goto bad_base; } - MPZ_Object *res = MPZ_new(0); + MPZ_Object *res = MPZ_new(state, type, 0); if (!res) { return (MPZ_Object *)PyErr_NoMemory(); /* LCOV_EXCL_LINE */ @@ -250,7 +260,7 @@ MPZ_from_str(PyObject *obj, int base) } static MPZ_Object * -MPZ_from_int(PyObject *obj) +MPZ_from_int(gmp_state *state, PyTypeObject *type, PyObject *obj) { #if !defined(PYPY_VERSION) && !defined(GRAALVM_PYTHON) \ && !defined(Py_LIMITED_API) @@ -262,7 +272,7 @@ MPZ_from_int(PyObject *obj) return res; /* LCOV_EXCL_LINE */ } if (long_export.digits) { - res = MPZ_new(0); + res = MPZ_new(state, type, 0); if (!res || zz_import((size_t)long_export.ndigits, long_export.digits, *int_layout, &res->z)) { @@ -274,7 +284,7 @@ MPZ_from_int(PyObject *obj) PyLong_FreeExport(&long_export); } else { - res = MPZ_new(0); + res = MPZ_new(state, type, 0); if (res && zz_from_i64(long_export.value, &res->z)) { PyErr_NoMemory(); /* LCOV_EXCL_LINE */ } @@ -284,7 +294,7 @@ MPZ_from_int(PyObject *obj) int64_t value; if (!PyLong_AsInt64(obj, &value)) { - MPZ_Object *res = MPZ_new(0); + MPZ_Object *res = MPZ_new(state, type, 0); if (res && zz_from_i64(value, &res->z)) { PyErr_NoMemory(); /* LCOV_EXCL_LINE */ @@ -299,7 +309,7 @@ MPZ_from_int(PyObject *obj) return NULL; /* LCOV_EXCL_LINE */ } - MPZ_Object *res = MPZ_from_str(str, 16); + MPZ_Object *res = MPZ_from_str(state, type, str, 16); Py_DECREF(str); return res; @@ -389,7 +399,7 @@ MPZ_to_bytes(MPZ_Object *u, Py_ssize_t length, int is_little, int is_signed) } static MPZ_Object * -MPZ_from_bytes(PyObject *obj, int is_little, int is_signed) +MPZ_from_bytes(gmp_state *state, PyTypeObject *type, PyObject *obj, int is_little, int is_signed) { PyObject *bytes = PyObject_Bytes(obj); uint8_t *buffer; @@ -418,7 +428,7 @@ MPZ_from_bytes(PyObject *obj, int is_little, int is_signed) buffer = tmp; } - MPZ_Object *res = MPZ_new(0); + MPZ_Object *res = MPZ_new(state, type, 0); if (!res || zz_from_bytes(buffer, (size_t)length, is_signed, &res->z)) { /* LCOV_EXCL_START */ @@ -437,15 +447,15 @@ MPZ_from_bytes(PyObject *obj, int is_little, int is_signed) } static PyObject * -new_impl(PyTypeObject *Py_UNUSED(type), PyObject *arg, PyObject *base_arg) +new_impl(gmp_state *state, PyTypeObject *type, PyObject *arg, PyObject *base_arg) { int base = 10; if (Py_IsNone(base_arg)) { if (PyLong_Check(arg)) { - return (PyObject *)MPZ_from_int(arg); + return (PyObject *)MPZ_from_int(state, type, arg); } - if (MPZ_CheckExact(arg)) { + if (MPZ_CheckExact(state, arg)) { return Py_NewRef(arg); } if (PyNumber_Check(arg)) { @@ -483,7 +493,8 @@ new_impl(PyTypeObject *Py_UNUSED(type), PyObject *arg, PyObject *base_arg) } } if (integer) { - Py_SETREF(integer, (PyObject *)MPZ_from_int(integer)); + Py_SETREF(integer, (PyObject *)MPZ_from_int(state, type, + integer)); return integer; } } @@ -503,7 +514,7 @@ new_impl(PyTypeObject *Py_UNUSED(type), PyObject *arg, PyObject *base_arg) return NULL; /* LCOV_EXCL_LINE */ } - PyObject *res = (PyObject *)MPZ_from_str(asciistr, base); + PyObject *res = (PyObject *)MPZ_from_str(state, type, asciistr, base); Py_DECREF(asciistr); return res; @@ -524,7 +535,7 @@ new_impl(PyTypeObject *Py_UNUSED(type), PyObject *arg, PyObject *base_arg) return NULL; /* LCOV_EXCL_LINE */ } - PyObject *res = (PyObject *)MPZ_from_str(str, base); + PyObject *res = (PyObject *)MPZ_from_str(state, type, str, base); Py_DECREF(str); return res; @@ -540,50 +551,67 @@ new_impl(PyTypeObject *Py_UNUSED(type), PyObject *arg, PyObject *base_arg) return NULL; } +static struct PyModuleDef gmp_module; + +#if PY_VERSION_HEX < 0x30A00A3 static PyObject * -new(PyTypeObject *type, PyObject *args, PyObject *keywds) +_PyType_GetModuleByDef(PyTypeObject *type, struct PyModuleDef *def) { - static char *kwlist[] = {"", "base", NULL}; - Py_ssize_t argc = PyTuple_GET_SIZE(args); - PyObject *arg, *base = Py_None; - - if (type != &MPZ_Type) { - MPZ_Object *tmp = (MPZ_Object *)new(&MPZ_Type, args, keywds); + assert(PyType_Check(type)); + assert(type->tp_mro); + for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(type->tp_mro); i++) { + PyObject *super = PyTuple_GET_ITEM(type->tp_mro, i); - if (!tmp) { - return NULL; /* LCOV_EXCL_LINE */ + if (!PyType_HasFeature((PyTypeObject *)super, Py_TPFLAGS_HEAPTYPE)) { + continue; } - MPZ_Object *newobj = (MPZ_Object *)type->tp_alloc(type, 0); + PyHeapTypeObject *ht = (PyHeapTypeObject*)super; - if (!newobj) { - /* LCOV_EXCL_START */ - Py_DECREF(tmp); - return NULL; - /* LCOV_EXCL_STOP */ + if (ht->ht_module && PyModule_GetDef(ht->ht_module) == def) { + return ht->ht_module; } - if (zz_init(&newobj->z) || zz_copy(&tmp->z, &newobj->z)) { - /* LCOV_EXCL_START */ - Py_DECREF(tmp); - return PyErr_NoMemory(); - /* LCOV_EXCL_STOP */ - } - Py_DECREF(tmp); - return (PyObject *)newobj; } + PyErr_Format(PyExc_TypeError, ("_PyType_GetModuleByDef: No superclass " + "of '%s' has the given module"), + type->tp_name); + return NULL; +} +#endif + +#if PY_VERSION_HEX < 0x030B00A6 +# define PyType_GetModuleByDef _PyType_GetModuleByDef +#endif + +static gmp_state * +get_state(PyTypeObject *type) +{ + PyObject *mod = PyType_GetModuleByDef(type, &gmp_module); + + return PyModule_GetState(mod); +} + +static PyObject * +new(PyTypeObject *type, PyObject *args, PyObject *keywds) +{ + static char *kwlist[] = {"", "base", NULL}; + Py_ssize_t argc = PyTuple_GET_SIZE(args); + PyObject *arg, *base = Py_None; + gmp_state *state = get_state(type); + if (argc == 0) { - return (PyObject *)MPZ_new(0); + return (PyObject *)MPZ_new(state, type, 0); } if (argc == 1 && !keywds) { arg = PyTuple_GET_ITEM(args, 0); - return new_impl(type, arg, Py_None); + return new_impl(state, type, arg, Py_None); } if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|O", kwlist, &arg, &base)) { return NULL; } - return new_impl(type, arg, base); + return new_impl(state, type, arg, base); } static void @@ -591,19 +619,30 @@ dealloc(PyObject *self) { MPZ_Object *u = (MPZ_Object *)self; PyTypeObject *type = Py_TYPE(self); + gmp_state *state = get_state(type); if (global.gmp_cache_size < CACHE_SIZE && (u->z).alloc <= MAX_CACHE_MPZ_LIMBS - && MPZ_CheckExact(self)) + && MPZ_CheckExact(state, self)) { global.gmp_cache[(global.gmp_cache_size)++] = u; } else { + PyObject_GC_UnTrack(self); zz_clear(&u->z); type->tp_free(self); + Py_DECREF(type); } } +static int +traverse(PyObject *self, visitproc visit, void *arg) +{ + Py_VISIT(Py_TYPE(self)); + return 0; +} + +#if PY_VERSION_HEX > 0x030E00A0 static PyObject * vectorcall(PyObject *type, PyObject *const *args, size_t nargsf, PyObject *kwnames) @@ -622,17 +661,21 @@ vectorcall(PyObject *type, PyObject *const *args, size_t nargsf, if (gmp_parse_pyargs(&fnargs, argidx, args, nargs, kwnames) == -1) { return NULL; } + + gmp_state *state = get_state((PyTypeObject *)type); + if (argidx[1] >= 0) { - return new_impl((PyTypeObject *)type, args[argidx[0]], + return new_impl(state, (PyTypeObject *)type, args[argidx[0]], args[argidx[1]]); } else if (argidx[0] >= 0) { - return new_impl((PyTypeObject *)type, args[argidx[0]], Py_None); + return new_impl(state, (PyTypeObject *)type, args[argidx[0]], Py_None); } else { - return (PyObject *)MPZ_new(0); + return (PyObject *)MPZ_new(state, (PyTypeObject *)type, 0); } } +#endif static PyObject * repr(PyObject *self) @@ -648,22 +691,22 @@ str(PyObject *self) #define Number_Check(op) (PyFloat_Check((op)) || PyComplex_Check((op))) -#define CHECK_OP(u, a) \ - if (MPZ_Check(a)) { \ - u = (MPZ_Object *)a; \ - Py_INCREF(u); \ - } \ - else if (PyLong_Check(a)) { \ - u = MPZ_from_int(a); \ - if (!u) { \ - goto end; \ - } \ - } \ - else if (Number_Check(a)) { \ - goto numbers; \ - } \ - else { \ - goto fallback; \ +#define CHECK_OP(u, a) \ + if (MPZ_Check(state, a)) { \ + u = (MPZ_Object *)a; \ + Py_INCREF(u); \ + } \ + else if (PyLong_Check(a)) { \ + u = MPZ_from_int(state, state->MPZ_Type, a); \ + if (!u) { \ + goto end; \ + } \ + } \ + else if (Number_Check(a)) { \ + goto numbers; \ + } \ + else { \ + goto fallback; \ } PyObject * @@ -681,12 +724,27 @@ to_float(PyObject *self) return PyFloat_FromDouble(d); } +static struct PyModuleDef gmp_module; + +static inline gmp_state * +find_state_left_or_right(PyObject *left, PyObject *right) +{ + PyObject *mod = PyType_GetModuleByDef(Py_TYPE(left), &gmp_module); + + if (mod) { + return PyModule_GetState(mod); + } + PyErr_Clear(); + return PyType_GetModuleState(Py_TYPE(right)); +} + static PyObject * richcompare(PyObject *self, PyObject *other, int op) { MPZ_Object *u = (MPZ_Object *)self, *v = NULL; + gmp_state *state = get_state(Py_TYPE(self)); - assert(MPZ_Check(self)); + assert(MPZ_Check(state, self)); CHECK_OP(v, other); zz_ord r = zz_cmp(&u->z, &v->z); @@ -753,17 +811,18 @@ hash(PyObject *self) return u->hash_cache = r; } -#define UNOP(suff, func) \ - static PyObject * \ - func(PyObject *self) \ - { \ - MPZ_Object *u = (MPZ_Object *)self; \ - MPZ_Object *res = MPZ_new(0); \ - \ - if (res && zz_##suff(&u->z, &res->z)) { \ - PyErr_NoMemory(); /* LCOV_EXCL_LINE */ \ - } \ - return (PyObject *)res; \ +#define UNOP(suff, func) \ + static PyObject * \ + func(PyObject *self) \ + { \ + MPZ_Object *u = (MPZ_Object *)self; \ + gmp_state *state = get_state(Py_TYPE(self)); \ + MPZ_Object *res = MPZ_new(state, Py_TYPE(self), 0); \ + \ + if (res && zz_##suff(&u->z, &res->z)) { \ + PyErr_NoMemory(); /* LCOV_EXCL_LINE */ \ + } \ + return (PyObject *)res; \ } UNOP(copy, plus) @@ -783,114 +842,117 @@ to_bool(PyObject *self) return !zz_iszero(&((MPZ_Object *)self)->z); } -#define BINOP_INT(suff) \ - static PyObject * \ - nb_##suff(PyObject *self, PyObject *other) \ - { \ - MPZ_Object *u = NULL, *v = NULL, *res = NULL; \ - \ - CHECK_OP(u, self); \ - CHECK_OP(v, other); \ - \ - res = MPZ_new(0); \ - zz_err ret = ZZ_OK; \ - \ - if (!res || (ret = zz_##suff(&u->z, &v->z, &res->z))) { \ - /* LCOV_EXCL_START */ \ - Py_CLEAR(res); \ - if (ret == ZZ_VAL) { \ - PyErr_SetString(PyExc_ValueError, \ - "negative shift count"); \ - } \ - else if (ret == ZZ_BUF) { \ - PyErr_SetString(PyExc_OverflowError, \ - "too many digits in integer"); \ - } \ - else { \ - PyErr_NoMemory(); \ - } \ - /* LCOV_EXCL_STOP */ \ - } \ - end: \ - Py_XDECREF(u); \ - Py_XDECREF(v); \ - return (PyObject *)res; \ - fallback: \ - numbers: \ - Py_XDECREF(u); \ - Py_XDECREF(v); \ - Py_RETURN_NOTIMPLEMENTED; \ - } - -#define BINOP(suff, slot) \ - static PyObject * \ - nb_##suff(PyObject *self, PyObject *other) \ - { \ - PyObject *res = NULL; \ - MPZ_Object *u = NULL, *v = NULL; \ - \ - CHECK_OP(u, self); \ - CHECK_OP(v, other); \ - \ - res = (PyObject *)MPZ_new(0); \ - if (!res) { \ - goto end; \ - } \ - \ - zz_err ret = zz_##suff(&u->z, &v->z, \ - &((MPZ_Object *)res)->z); \ - \ - if (ret == ZZ_OK) { \ - goto end; \ - } \ - if (ret == ZZ_VAL) { \ - Py_CLEAR(res); \ - PyErr_SetString(PyExc_ZeroDivisionError, \ - "division by zero"); \ - } \ - else { \ - Py_CLEAR(res); \ - PyErr_NoMemory(); \ - } \ - end: \ - Py_XDECREF(u); \ - Py_XDECREF(v); \ - return res; \ - fallback: \ - Py_XDECREF(u); \ - Py_XDECREF(v); \ - Py_RETURN_NOTIMPLEMENTED; \ - numbers: \ - Py_XDECREF(u); \ - Py_XDECREF(v); \ - \ - PyObject *uf, *vf; \ - \ - if (Number_Check(self)) { \ - uf = self; \ - Py_INCREF(uf); \ - } \ - else { \ - uf = to_float(self); \ - if (!uf) { \ - return NULL; \ - } \ - } \ - if (Number_Check(other)) { \ - vf = other; \ - Py_INCREF(vf); \ - } \ - else { \ - vf = to_float(other); \ - if (!vf) { \ - Py_DECREF(uf); \ - return NULL; \ - } \ - } \ - res = slot(uf, vf); \ - Py_DECREF(uf); \ - Py_DECREF(vf); \ - return res; \ +#define BINOP_INT(suff) \ + static PyObject * \ + nb_##suff(PyObject *self, PyObject *other) \ + { \ + MPZ_Object *u = NULL, *v = NULL, *res = NULL; \ + gmp_state *state = find_state_left_or_right(self, other); \ + \ + CHECK_OP(u, self); \ + CHECK_OP(v, other); \ + \ + res = MPZ_new(state, state->MPZ_Type, 0); \ + \ + zz_err ret = ZZ_OK; \ + \ + if (!res || (ret = zz_##suff(&u->z, &v->z, &res->z))) { \ + /* LCOV_EXCL_START */ \ + Py_CLEAR(res); \ + if (ret == ZZ_VAL) { \ + PyErr_SetString(PyExc_ValueError, \ + "negative shift count"); \ + } \ + else if (ret == ZZ_BUF) { \ + PyErr_SetString(PyExc_OverflowError, \ + "too many digits in integer"); \ + } \ + else { \ + PyErr_NoMemory(); \ + } \ + /* LCOV_EXCL_STOP */ \ + } \ + end: \ + Py_XDECREF(u); \ + Py_XDECREF(v); \ + return (PyObject *)res; \ + fallback: \ + numbers: \ + Py_XDECREF(u); \ + Py_XDECREF(v); \ + Py_RETURN_NOTIMPLEMENTED; \ + } + +#define BINOP(suff, slot) \ + static PyObject * \ + nb_##suff(PyObject *self, PyObject *other) \ + { \ + PyObject *res = NULL; \ + MPZ_Object *u = NULL, *v = NULL; \ + gmp_state *state = find_state_left_or_right(self, other); \ + \ + CHECK_OP(u, self); \ + CHECK_OP(v, other); \ + \ + res = (PyObject *)MPZ_new(state, state->MPZ_Type, 0); \ + if (!res) { \ + goto end; \ + } \ + \ + zz_err ret = zz_##suff(&u->z, &v->z, \ + &((MPZ_Object *)res)->z); \ + \ + if (ret == ZZ_OK) { \ + goto end; \ + } \ + if (ret == ZZ_VAL) { \ + Py_CLEAR(res); \ + PyErr_SetString(PyExc_ZeroDivisionError, \ + "division by zero"); \ + } \ + else { \ + Py_CLEAR(res); \ + PyErr_NoMemory(); \ + } \ + end: \ + Py_XDECREF(u); \ + Py_XDECREF(v); \ + return res; \ + fallback: \ + Py_XDECREF(u); \ + Py_XDECREF(v); \ + Py_RETURN_NOTIMPLEMENTED; \ + numbers: \ + Py_XDECREF(u); \ + Py_XDECREF(v); \ + \ + PyObject *uf, *vf; \ + \ + if (Number_Check(self)) { \ + uf = self; \ + Py_INCREF(uf); \ + } \ + else { \ + uf = to_float(self); \ + if (!uf) { \ + return NULL; \ + } \ + } \ + if (Number_Check(other)) { \ + vf = other; \ + Py_INCREF(vf); \ + } \ + else { \ + vf = to_float(other); \ + if (!vf) { \ + Py_DECREF(uf); \ + return NULL; \ + } \ + } \ + res = slot(uf, vf); \ + Py_DECREF(uf); \ + Py_DECREF(vf); \ + return res; \ } BINOP(add, PyNumber_Add) @@ -917,6 +979,7 @@ nb_divmod(PyObject *self, PyObject *other) { PyObject *res = PyTuple_New(2); MPZ_Object *u = NULL, *v = NULL; + gmp_state *state = find_state_left_or_right(self, other); if (!res) { return NULL; /* LCOV_EXCL_LINE */ @@ -924,8 +987,8 @@ nb_divmod(PyObject *self, PyObject *other) CHECK_OP(u, self); CHECK_OP(v, other); - MPZ_Object *q = MPZ_new(0); - MPZ_Object *r = MPZ_new(0); + MPZ_Object *q = MPZ_new(state, state->MPZ_Type, 0); + MPZ_Object *r = MPZ_new(state, state->MPZ_Type, 0); if (!q || !r) { /* LCOV_EXCL_START */ @@ -973,6 +1036,7 @@ nb_truediv(PyObject *self, PyObject *other) { PyObject *res = NULL; MPZ_Object *u = NULL, *v = NULL; + gmp_state *state = find_state_left_or_right(self, other); CHECK_OP(u, self); CHECK_OP(v, other); @@ -1068,24 +1132,40 @@ BINOP_INT(and) BINOP_INT(or) BINOP_INT(xor) -#define CHECK_OP_INT(u, a) \ - if (MPZ_Check(a)) { \ - u = (MPZ_Object *)a; \ - Py_INCREF(u); \ - } \ - else { \ - u = MPZ_from_int(a); \ - if (!u) { \ - goto end; \ - } \ - } \ +#define CHECK_OP_INT(u, a) \ + if (MPZ_Check(state, a)) { \ + u = (MPZ_Object *)a; \ + Py_INCREF(u); \ + } \ + else { \ + u = MPZ_from_int(state, state->MPZ_Type, a); \ + if (!u) { \ + goto end; \ + } \ + } \ static PyObject * power(PyObject *self, PyObject *other, PyObject *module) { MPZ_Object *res = NULL; MPZ_Object *u = NULL, *v = NULL; + PyObject *mod = PyType_GetModuleByDef(Py_TYPE(self), &gmp_module); + gmp_state *state; + if (mod) { + state = PyModule_GetState(mod); + } + else { + PyErr_Clear(); + mod = PyType_GetModuleByDef(Py_TYPE(other), &gmp_module); + if (mod) { + state = PyModule_GetState(mod); + } + else { + PyErr_Clear(); + state = PyType_GetModuleState(Py_TYPE(module)); + } + } CHECK_OP(u, self); CHECK_OP(v, other); if (Py_IsNone(module)) { @@ -1109,7 +1189,7 @@ power(PyObject *self, PyObject *other, PyObject *module) Py_DECREF(vf); return resf; } - res = MPZ_new(0); + res = MPZ_new(state, state->MPZ_Type, 0); int64_t exp; @@ -1132,7 +1212,7 @@ power(PyObject *self, PyObject *other, PyObject *module) zz_err ret = ZZ_OK; - res = MPZ_new(0); + res = MPZ_new(state, state->MPZ_Type, 0); if (!res || (ret = zz_powm(&u->z, &v->z, &w->z, &res->z))) { /* LCOV_EXCL_START */ if (ret == ZZ_VAL) { @@ -1190,30 +1270,6 @@ power(PyObject *self, PyObject *other, PyObject *module) return res2; } -static PyNumberMethods as_number = { - .nb_add = nb_add, - .nb_subtract = nb_sub, - .nb_multiply = nb_mul, - .nb_divmod = nb_divmod, - .nb_floor_divide = nb_quo, - .nb_true_divide = nb_truediv, - .nb_remainder = nb_rem, - .nb_power = power, - .nb_positive = plus, - .nb_negative = nb_negative, - .nb_absolute = nb_absolute, - .nb_invert = nb_invert, - .nb_lshift = nb_lshift, - .nb_rshift = nb_rshift, - .nb_and = nb_and, - .nb_or = nb_or, - .nb_xor = nb_xor, - .nb_int = to_int, - .nb_float = to_float, - .nb_index = to_int, - .nb_bool = to_bool, -}; - static PyObject * get_copy(PyObject *self, void *Py_UNUSED(closure)) { @@ -1221,9 +1277,10 @@ get_copy(PyObject *self, void *Py_UNUSED(closure)) } static PyObject * -get_one(PyObject *Py_UNUSED(self), void *Py_UNUSED(closure)) +get_one(PyObject *self, void *Py_UNUSED(closure)) { - MPZ_Object *res = MPZ_new(0); + gmp_state *state = get_state(Py_TYPE(self)); + MPZ_Object *res = MPZ_new(state, Py_TYPE(self), 0); if (res && zz_from_i32(1, &res->z)) { PyErr_NoMemory(); /* LCOV_EXCL_LINE */ @@ -1232,9 +1289,11 @@ get_one(PyObject *Py_UNUSED(self), void *Py_UNUSED(closure)) } static PyObject * -get_zero(PyObject *Py_UNUSED(self), void *Py_UNUSED(closure)) +get_zero(PyObject *self, void *Py_UNUSED(closure)) { - return (PyObject *)MPZ_new(0); + gmp_state *state = get_state(Py_TYPE(self)); + + return (PyObject *)MPZ_new(state, Py_TYPE(self), 0); } static PyGetSetDef getsetters[] = { @@ -1339,13 +1398,16 @@ to_bytes(PyObject *self, PyObject *const *args, Py_ssize_t nargs, } static PyObject * -_from_bytes(PyObject *Py_UNUSED(type), PyObject *arg) +_from_bytes(PyObject *type, PyObject *arg) { - return (PyObject *)MPZ_from_bytes(arg, 0, 1); + PyTypeObject *tp = (PyTypeObject *)type; + gmp_state *state = get_state(tp); + + return (PyObject *)MPZ_from_bytes(state, tp, arg, 0, 1); } static PyObject * -from_bytes(PyTypeObject *Py_UNUSED(type), PyObject *const *args, +from_bytes(PyTypeObject *type, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { static const char *const keywords[] = {"bytes", "byteorder", "signed"}; @@ -1357,6 +1419,7 @@ from_bytes(PyTypeObject *Py_UNUSED(type), PyObject *const *args, .fname = "from_bytes", }; Py_ssize_t argidx[3] = {-1, -1, -1}; + gmp_state *state = get_state(type); if (gmp_parse_pyargs(&fnargs, argidx, args, nargs, kwnames) == -1) { return NULL; @@ -1396,13 +1459,13 @@ from_bytes(PyTypeObject *Py_UNUSED(type), PyObject *const *args, if (argidx[2] >= 0) { is_signed = PyObject_IsTrue(args[argidx[2]]); } - return (PyObject *)MPZ_from_bytes(args[argidx[0]], is_little, is_signed); + return (PyObject *)MPZ_from_bytes(state, type, args[argidx[0]], is_little, is_signed); } static PyObject * as_integer_ratio(PyObject *self, PyObject *Py_UNUSED(args)) { - PyObject *one = get_one(NULL, NULL); + PyObject *one = get_one(self, NULL); if (!one) { return NULL; /* LCOV_EXCL_LINE */ @@ -1426,6 +1489,7 @@ __round__(PyObject *self, PyObject *const *args, Py_ssize_t nargs) } MPZ_Object *u = (MPZ_Object *)self; + gmp_state *state = get_state(Py_TYPE(self)); if (!nargs) { return plus(self); @@ -1451,16 +1515,16 @@ __round__(PyObject *self, PyObject *const *args, Py_ssize_t nargs) } Py_SETREF(ndigits, tmp); - PyObject *ten = PyLong_FromLong(10); + MPZ_Object *ten = MPZ_new(state, Py_TYPE(self), 1); - if (!ten) { + if (!ten || zz_from_i32(10, &ten->z)) { /* LCOV_EXCL_START */ Py_DECREF(ndigits); return NULL; /* LCOV_EXCL_STOP */ } - PyObject *p = power(ten, ndigits, Py_None); + PyObject *p = power((PyObject *)ten, ndigits, Py_None); Py_DECREF(ten); Py_DECREF(ndigits); @@ -1468,7 +1532,7 @@ __round__(PyObject *self, PyObject *const *args, Py_ssize_t nargs) return NULL; /* LCOV_EXCL_LINE */ } - MPZ_Object *r = MPZ_new(0); + MPZ_Object *r = MPZ_new(state, Py_TYPE(self), 0); if (!r) { /* LCOV_EXCL_START */ @@ -1484,7 +1548,7 @@ __round__(PyObject *self, PyObject *const *args, Py_ssize_t nargs) } Py_DECREF(p); - MPZ_Object *res = MPZ_new(0); + MPZ_Object *res = MPZ_new(state, Py_TYPE(self), 0); if (!res || zz_sub(&u->z, &r->z, &res->z)) { /* LCOV_EXCL_START */ @@ -1637,28 +1701,74 @@ given base. The literal can be preceded by '+' or '-' and be surrounded\n\ by whitespace. Valid bases are 0 and 2-36. Base 0 means to interpret \n\ the base from the string as an integer literal."); -PyTypeObject MPZ_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "gmp.mpz", - .tp_basicsize = sizeof(MPZ_Object), - .tp_new = new, - .tp_dealloc = dealloc, - .tp_repr = repr, - .tp_str = str, - .tp_richcompare = richcompare, - .tp_hash = hash, - .tp_as_number = &as_number, - .tp_getset = getsetters, - .tp_methods = methods, - .tp_doc = mpz_doc, - .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, - .tp_vectorcall = vectorcall, +#ifdef __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wpedantic" +#endif +static PyType_Slot mpz_slots[] = { +// {Py_tp_token, Py_TP_USE_SPEC}, + {Py_tp_dealloc, dealloc}, + {Py_tp_getattro, PyObject_GenericGetAttr}, + {Py_tp_traverse, traverse}, + {Py_tp_repr, repr}, + {Py_tp_hash, hash}, + {Py_tp_str, str}, + {Py_tp_doc, (void *)mpz_doc}, + {Py_tp_richcompare, richcompare}, + {Py_tp_methods, methods}, + {Py_tp_getset, getsetters}, + {Py_tp_new, new}, +#if PY_VERSION_HEX > 0x030E00A0 + {Py_tp_vectorcall, vectorcall}, +#endif + + /* Number protocol */ + {Py_nb_add, nb_add}, + {Py_nb_subtract, nb_sub}, + {Py_nb_multiply, nb_mul}, + {Py_nb_divmod, nb_divmod}, + {Py_nb_floor_divide, nb_quo}, + {Py_nb_true_divide, nb_truediv}, + {Py_nb_remainder, nb_rem}, + {Py_nb_power, power}, + {Py_nb_positive, plus}, + {Py_nb_negative, nb_negative}, + {Py_nb_absolute, nb_absolute}, + {Py_nb_bool, to_bool}, + {Py_nb_int, to_int}, + {Py_nb_index, to_int}, + {Py_nb_float, to_float}, + {Py_nb_invert, nb_invert}, + {Py_nb_lshift, nb_lshift}, + {Py_nb_rshift, nb_rshift}, + {Py_nb_and, nb_and}, + {Py_nb_or, nb_or}, + {Py_nb_xor, nb_xor}, + {Py_nb_int, to_int}, + {0, NULL}, +}; +#ifdef __GNUC__ +# pragma GCC diagnostic pop +#endif + +static PyType_Spec mpz_spec = { + .name = "gmp.mpz", + .basicsize = sizeof(MPZ_Object), +#if PY_VERSION_HEX >= 0x030A00B1 + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE), +#else + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_HAVE_GC), +#endif + .slots = mpz_slots, }; static PyObject * -gmp_gcd(PyObject *Py_UNUSED(module), PyObject *const *args, Py_ssize_t nargs) +gmp_gcd(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { - MPZ_Object *res = MPZ_new(0); + gmp_state *state = PyModule_GetState(module); + MPZ_Object *res = MPZ_new(state, state->MPZ_Type, 0); if (!res) { return (PyObject *)res; /* LCOV_EXCL_LINE */ @@ -1687,7 +1797,7 @@ gmp_gcd(PyObject *Py_UNUSED(module), PyObject *const *args, Py_ssize_t nargs) } static PyObject * -gmp_gcdext(PyObject *Py_UNUSED(module), PyObject *const *args, +gmp_gcdext(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { if (nargs != 2) { @@ -1695,7 +1805,8 @@ gmp_gcdext(PyObject *Py_UNUSED(module), PyObject *const *args, return NULL; } MPZ_Object *x = NULL, *y = NULL; - MPZ_Object *g = MPZ_new(0), *s = MPZ_new(0), *t = MPZ_new(0); + gmp_state *state = PyModule_GetState(module); + MPZ_Object *g = MPZ_new(state, state->MPZ_Type, 0), *s = MPZ_new(state, state->MPZ_Type, 0), *t = MPZ_new(state, state->MPZ_Type, 0); if (!g || !s || !t) { /* LCOV_EXCL_START */ @@ -1731,9 +1842,10 @@ gmp_gcdext(PyObject *Py_UNUSED(module), PyObject *const *args, } static PyObject * -gmp_lcm(PyObject *Py_UNUSED(module), PyObject *const *args, Py_ssize_t nargs) +gmp_lcm(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { - MPZ_Object *res = MPZ_new(0); + gmp_state *state = PyModule_GetState(module); + MPZ_Object *res = MPZ_new(state, state->MPZ_Type, 0); if (!res || zz_from_i64(1, &res->z)) { return PyErr_NoMemory(); /* LCOV_EXCL_LINE */ @@ -1762,9 +1874,10 @@ gmp_lcm(PyObject *Py_UNUSED(module), PyObject *const *args, Py_ssize_t nargs) } static PyObject * -gmp_isqrt(PyObject *Py_UNUSED(module), PyObject *arg) +gmp_isqrt(PyObject *module, PyObject *arg) { - MPZ_Object *x, *root = MPZ_new(0); + gmp_state *state = PyModule_GetState(module); + MPZ_Object *x, *root = MPZ_new(state, state->MPZ_Type, 0); if (!root) { return NULL; /* LCOV_EXCL_LINE */ @@ -1790,9 +1903,10 @@ gmp_isqrt(PyObject *Py_UNUSED(module), PyObject *arg) } static PyObject * -gmp_isqrt_rem(PyObject *Py_UNUSED(module), PyObject *arg) +gmp_isqrt_rem(PyObject *module, PyObject *arg) { - MPZ_Object *x, *root = MPZ_new(0), *rem = MPZ_new(0); + gmp_state *state = PyModule_GetState(module); + MPZ_Object *x, *root = MPZ_new(state, state->MPZ_Type, 0), *rem = MPZ_new(state, state->MPZ_Type, 0); PyObject *tup = NULL; if (!root || !rem) { @@ -1825,9 +1939,10 @@ gmp_isqrt_rem(PyObject *Py_UNUSED(module), PyObject *arg) #define MAKE_MPZ_UI_FUN(name) \ static PyObject * \ - gmp_##name(PyObject *Py_UNUSED(module), PyObject *arg) \ + gmp_##name(PyObject *module, PyObject *arg) \ { \ - MPZ_Object *x, *res = MPZ_new(0); \ + gmp_state *state = PyModule_GetState(module); \ + MPZ_Object *x, *res = MPZ_new(state, state->MPZ_Type, 0); \ \ if (!res) { \ return NULL; /* LCOV_EXCL_LINE */ \ @@ -1866,14 +1981,15 @@ MAKE_MPZ_UI_FUN(fac2) MAKE_MPZ_UI_FUN(fib) static PyObject * -gmp_comb(PyObject *self, PyObject *const *args, Py_ssize_t nargs) +gmp_comb(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { if (nargs != 2) { PyErr_SetString(PyExc_TypeError, "two arguments required"); return NULL; } - MPZ_Object *x, *y, *res = MPZ_new(0); + gmp_state *state = PyModule_GetState(module); + MPZ_Object *x, *y, *res = MPZ_new(state, state->MPZ_Type, 0); if (!res) { return NULL; /* LCOV_EXCL_LINE */ @@ -1912,17 +2028,18 @@ gmp_comb(PyObject *self, PyObject *const *args, Py_ssize_t nargs) } static PyObject * -gmp_perm(PyObject *self, PyObject *const *args, Py_ssize_t nargs) +gmp_perm(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { if (nargs > 2 || nargs < 1) { PyErr_SetString(PyExc_TypeError, "one or two arguments required"); return NULL; } if (nargs == 1) { - return gmp_fac(self, args[0]); + return gmp_fac(module, args[0]); } - MPZ_Object *x, *y, *res = MPZ_new(0); + gmp_state *state = PyModule_GetState(module); + MPZ_Object *x, *y, *res = MPZ_new(state, state->MPZ_Type, 0); if (!res) { return NULL; /* LCOV_EXCL_LINE */ @@ -1951,7 +2068,7 @@ gmp_perm(PyObject *self, PyObject *const *args, Py_ssize_t nargs) return (PyObject *)res; } - MPZ_Object *den = MPZ_new(0); + MPZ_Object *den = MPZ_new(state, state->MPZ_Type, 0); if (!den) { /* LCOV_EXCL_START */ @@ -2106,7 +2223,7 @@ zz_mpmath_normalize(zz_bitcnt_t prec, zz_rnd rnd, bool *negative, return ZZ_OK; } static PyObject * -gmp__mpmath_normalize(PyObject *self, PyObject *const *args, Py_ssize_t nargs) +gmp__mpmath_normalize(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { if (nargs != 6) { PyErr_SetString(PyExc_TypeError, "6 arguments required"); @@ -2118,10 +2235,11 @@ gmp__mpmath_normalize(PyObject *self, PyObject *const *args, Py_ssize_t nargs) zz_bitcnt_t bc = PyLong_AsUnsignedLongLong(args[3]); zz_bitcnt_t prec = PyLong_AsUnsignedLongLong(args[4]); PyObject *rndstr = args[5]; + gmp_state *state = PyModule_GetState(module); zz_rnd rnd = get_round_mode(rndstr); if (sign == -1 || bc == (zz_bitcnt_t)(-1) || prec == (zz_bitcnt_t)(-1) - || !MPZ_Check(args[1]) || !PyLong_Check(args[2])) + || !MPZ_Check(state, args[1]) || !PyLong_Check(args[2])) { PyErr_SetString(PyExc_TypeError, ("arguments long, MPZ_Object*, PyObject*, " @@ -2133,7 +2251,7 @@ gmp__mpmath_normalize(PyObject *self, PyObject *const *args, Py_ssize_t nargs) } MPZ_Object *man = (MPZ_Object *)plus(args[1]); - MPZ_Object *exp = MPZ_from_int(args[2]); + MPZ_Object *exp = MPZ_from_int(state, state->MPZ_Type, args[2]); if (!exp || !man || zz_mpmath_normalize(prec, rnd, &negative, &man->z, &exp->z, &bc)) @@ -2158,7 +2276,7 @@ gmp__mpmath_normalize(PyObject *self, PyObject *const *args, Py_ssize_t nargs) } static PyObject * -gmp__mpmath_create(PyObject *self, PyObject *const *args, Py_ssize_t nargs) +gmp__mpmath_create(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { if (nargs < 2 || nargs > 4) { PyErr_Format(PyExc_TypeError, @@ -2167,12 +2285,13 @@ gmp__mpmath_create(PyObject *self, PyObject *const *args, Py_ssize_t nargs) } MPZ_Object *man; + gmp_state *state = PyModule_GetState(module); - if (MPZ_Check(args[0])) { + if (MPZ_Check(state, args[0])) { man = (MPZ_Object *)plus(args[0]); } else if (PyLong_Check(args[0])) { - man = MPZ_from_int(args[0]); + man = MPZ_from_int(state, state->MPZ_Type, args[0]); if (!man) { return NULL; /* LCOV_EXCL_LINE */ } @@ -2216,7 +2335,7 @@ gmp__mpmath_create(PyObject *self, PyObject *const *args, Py_ssize_t nargs) prec = bc; } - MPZ_Object *exp = MPZ_from_int(args[1]); + MPZ_Object *exp = MPZ_from_int(state, state->MPZ_Type, args[1]); if (!exp || zz_mpmath_normalize(prec, rnd, &negative, &man->z, &exp->z, &bc)) @@ -2317,11 +2436,18 @@ static int gmp_exec(PyObject *m) { static zz_info info; + gmp_state *state = PyModule_GetState(m); if (zz_setup(&info)) { return -1; /* LCOV_EXCL_LINE */ } - if (PyModule_AddType(m, &MPZ_Type) < 0) { + state->MPZ_Type = (PyTypeObject *)PyType_FromModuleAndSpec(m, + &mpz_spec, + NULL); + if (!state->MPZ_Type) { + return -1; /* LCOV_EXCL_LINE */ + } + if (PyModule_AddType(m, state->MPZ_Type) < 0) { return -1; /* LCOV_EXCL_LINE */ } @@ -2392,6 +2518,24 @@ gmp_exec(PyObject *m) return 0; } +static int +gmp_clear(PyObject *module) +{ + gmp_state *state = PyModule_GetState(module); + + Py_CLEAR(state->MPZ_Type); + return 0; +} + +static int +gmp_traverse(PyObject *module, visitproc visit, void *arg) +{ + gmp_state *state = PyModule_GetState(module); + + Py_VISIT(state->MPZ_Type); + return 0; +} + #ifdef __GNUC__ # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wpedantic" @@ -2413,9 +2557,11 @@ static struct PyModuleDef gmp_module = { PyModuleDef_HEAD_INIT, .m_name = "gmp", .m_doc = "Bindings to the GNU GMP for Python.", - .m_size = 0, + .m_size = sizeof(gmp_state), .m_methods = gmp_functions, .m_slots = gmp_slots, + .m_clear = gmp_clear, + .m_traverse = gmp_traverse, }; PyMODINIT_FUNC diff --git a/mpz.h b/mpz.h index 716b5b17..3e44f045 100644 --- a/mpz.h +++ b/mpz.h @@ -39,9 +39,11 @@ typedef struct { zz_t z; } MPZ_Object; -extern PyTypeObject MPZ_Type; +#define MPZ_CheckExact(st, u) Py_IS_TYPE((u), (st)->MPZ_Type) +#define MPZ_Check(st, u) PyObject_TypeCheck((u), (st)->MPZ_Type) -#define MPZ_CheckExact(u) Py_IS_TYPE((u), &MPZ_Type) -#define MPZ_Check(u) PyObject_TypeCheck((u), &MPZ_Type) +typedef struct { + PyTypeObject *MPZ_Type; +} gmp_state; #endif /* MPZ_H */