diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 676e0320..f02d1440 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -9,7 +9,7 @@ jobs: strategy: matrix: os: [ubuntu-24.04] - python-version: [pypy3.11, "graalpy-25.0", "3.x"] + python-version: [pypy3.11-nightly, "graalpy-25.0", "3.x"] steps: - uses: actions/checkout@v6 with: diff --git a/fmt.c b/fmt.c index ea5deee9..81b8af9e 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 dec32c6f..e9af412e 100644 --- a/gmp.c +++ b/gmp.c @@ -27,11 +27,11 @@ _Thread_local gmp_global global = { uint8_t bits_per_digit; static MPZ_Object * -MPZ_new(void) +MPZ_new(gmp_state * state, PyTypeObject *type) { MPZ_Object *res; - if (global.gmp_cache_size) { + if (global.gmp_cache_size && type == state->MPZ_Type) { res = global.gmp_cache[--(global.gmp_cache_size)]; if (zz_set(0, &res->z)) { /* LCOV_EXCL_START */ @@ -42,13 +42,21 @@ MPZ_new(void) Py_XINCREF((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)) { return (MPZ_Object *)PyErr_NoMemory(); /* LCOV_EXCL_LINE */ } + if (type == state->MPZ_Type) { + PyObject_GC_Track((PyObject *)res); + } } res->hash_cache = -1; return res; @@ -138,7 +146,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; const char *str = PyUnicode_AsUTF8AndSize(obj, &len); @@ -150,7 +158,7 @@ MPZ_from_str(PyObject *obj, int base) goto bad_base; } - MPZ_Object *res = MPZ_new(); + MPZ_Object *res = MPZ_new(state, type); if (!res) { return (MPZ_Object *)PyErr_NoMemory(); /* LCOV_EXCL_LINE */ @@ -245,7 +253,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) @@ -257,7 +265,7 @@ MPZ_from_int(PyObject *obj) return res; /* LCOV_EXCL_LINE */ } if (long_export.digits) { - res = MPZ_new(); + res = MPZ_new(state, type); if (!res || zz_import((size_t)long_export.ndigits, long_export.digits, *int_layout, &res->z)) { @@ -269,7 +277,7 @@ MPZ_from_int(PyObject *obj) PyLong_FreeExport(&long_export); } else { - res = MPZ_new(); + res = MPZ_new(state, type); if (res && zz_set(long_export.value, &res->z)) { PyErr_NoMemory(); /* LCOV_EXCL_LINE */ } @@ -279,7 +287,7 @@ MPZ_from_int(PyObject *obj) int64_t value; if (!PyLong_AsInt64(obj, &value)) { - MPZ_Object *res = MPZ_new(); + MPZ_Object *res = MPZ_new(state, type); if (res && zz_set(value, &res->z)) { PyErr_NoMemory(); /* LCOV_EXCL_LINE */ @@ -294,7 +302,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; @@ -479,7 +487,7 @@ zz_set_bytes(const unsigned char *buffer, size_t length, bool 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); unsigned char *buffer; @@ -508,7 +516,7 @@ MPZ_from_bytes(PyObject *obj, int is_little, int is_signed) buffer = tmp; } - MPZ_Object *res = MPZ_new(); + MPZ_Object *res = MPZ_new(state, type); if (!res || zz_set_bytes(buffer, (size_t)length, is_signed, &res->z)) { /* LCOV_EXCL_START */ @@ -527,15 +535,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)) { @@ -573,7 +581,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; } } @@ -593,7 +602,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; @@ -614,7 +623,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; @@ -630,50 +639,37 @@ new_impl(PyTypeObject *Py_UNUSED(type), PyObject *arg, PyObject *base_arg) return NULL; } +static struct PyModuleDef gmp_module; + +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 (type != &MPZ_Type) { - MPZ_Object *tmp = (MPZ_Object *)new(&MPZ_Type, args, keywds); - - if (!tmp) { - return NULL; /* LCOV_EXCL_LINE */ - } - - MPZ_Object *newobj = (MPZ_Object *)type->tp_alloc(type, 0); - - if (!newobj) { - /* LCOV_EXCL_START */ - Py_DECREF(tmp); - return NULL; - /* LCOV_EXCL_STOP */ - } - if (zz_init(&newobj->z) || zz_pos(&tmp->z, &newobj->z)) { - /* LCOV_EXCL_START */ - Py_DECREF(tmp); - return PyErr_NoMemory(); - /* LCOV_EXCL_STOP */ - } - Py_DECREF(tmp); - return (PyObject *)newobj; - } if (argc == 0) { - return (PyObject *)MPZ_new(); + return (PyObject *)MPZ_new(state, type); } 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 @@ -681,19 +677,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_DIGITS - && 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) @@ -712,17 +719,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(); + return (PyObject *)MPZ_new(state, (PyTypeObject *)type); } } +#endif static PyObject * repr(PyObject *self) @@ -738,22 +749,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 * @@ -771,6 +782,20 @@ 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 inline int64_t PyLong_AsSdigit_t(PyObject *obj, int *error) { @@ -790,9 +815,11 @@ static PyObject * richcompare(PyObject *self, PyObject *other, int op) { MPZ_Object *u = (MPZ_Object *)self; + gmp_state *state = get_state(Py_TYPE(self)); zz_ord r; - if (MPZ_Check(other)) { + assert(MPZ_Check(state, self)); + if (MPZ_Check(state, other)) { r = zz_cmp(&u->z, &((MPZ_Object *)other)->z); } else if (PyLong_Check(other)) { @@ -803,7 +830,7 @@ richcompare(PyObject *self, PyObject *other, int op) r = zz_cmp(&u->z, temp); } else { - MPZ_Object *v = MPZ_from_int(other); + MPZ_Object *v = MPZ_from_int(state, state->MPZ_Type, other); if (!v) { goto end; /* LCOV_EXCL_LINE */ @@ -876,17 +903,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(); \ - \ - 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)); \ + \ + if (res && zz_##suff(&u->z, &res->z)) { \ + PyErr_NoMemory(); /* LCOV_EXCL_LINE */ \ + } \ + return (PyObject *)res; \ } UNOP(pos, plus) @@ -907,7 +935,7 @@ to_bool(PyObject *self) } #define CHECK_OPv2(u, a) \ - if (MPZ_Check(a)) { \ + if (MPZ_Check(state, a)) { \ u = (MPZ_Object *)a; \ Py_INCREF(u); \ } \ @@ -926,11 +954,12 @@ to_bool(PyObject *self) 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_OPv2(u, self); \ CHECK_OPv2(v, other); \ \ - res = MPZ_new(); \ + res = MPZ_new(state, state->MPZ_Type); \ if (!res) { \ goto end; \ } \ @@ -945,7 +974,7 @@ to_bool(PyObject *self) ret = zz_##suff(temp, &v->z, &res->z); \ goto done; \ } \ - u = MPZ_from_int(self); \ + u = MPZ_from_int(state, state->MPZ_Type, self); \ if (!u) { \ goto end; \ } \ @@ -958,7 +987,7 @@ to_bool(PyObject *self) ret = zz_##suff(&u->z, temp, &res->z); \ goto done; \ } \ - v = MPZ_from_int(other); \ + v = MPZ_from_int(state, state->MPZ_Type, other); \ if (!v) { \ goto end; \ } \ @@ -1033,6 +1062,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 */ @@ -1040,8 +1070,8 @@ nb_divmod(PyObject *self, PyObject *other) CHECK_OP(u, self); CHECK_OP(v, other); - MPZ_Object *q = MPZ_new(); - MPZ_Object *r = MPZ_new(); + MPZ_Object *q = MPZ_new(state, state->MPZ_Type); + MPZ_Object *r = MPZ_new(state, state->MPZ_Type); if (!q || !r) { /* LCOV_EXCL_START */ @@ -1255,6 +1285,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); @@ -1320,20 +1351,20 @@ nb_truediv(PyObject *self, PyObject *other) return res; } -#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; \ + } \ + } \ #define CHECK_OP_INTv2(u, a) \ - if (MPZ_Check(a)) { \ + if (MPZ_Check(state, a)) { \ u = (MPZ_Object *)a; \ Py_INCREF(u); \ } \ @@ -1349,11 +1380,12 @@ nb_truediv(PyObject *self, PyObject *other) 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_INT(u, self); \ CHECK_OP_INT(v, other); \ \ - res = MPZ_new(); \ + res = MPZ_new(state, state->MPZ_Type); \ zz_err ret = ZZ_OK; \ \ if (!res || (ret = zz_##suff(&u->z, &v->z, &res->z))) { \ @@ -1383,11 +1415,12 @@ nb_truediv(PyObject *self, PyObject *other) 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_INTv2(u, self); \ CHECK_OP_INTv2(v, other); \ \ - res = MPZ_new(); \ + res = MPZ_new(state, state->MPZ_Type); \ if (!res) { \ goto end; \ } \ @@ -1405,7 +1438,7 @@ nb_truediv(PyObject *self, PyObject *other) goto done; \ } \ } \ - u = MPZ_from_int(self); \ + u = MPZ_from_int(state, state->MPZ_Type, self); \ if (!u) { \ goto end; \ } \ @@ -1421,7 +1454,7 @@ nb_truediv(PyObject *self, PyObject *other) goto done; \ } \ } \ - v = MPZ_from_int(other); \ + v = MPZ_from_int(state, state->MPZ_Type, other); \ if (!v) { \ goto end; \ } \ @@ -1503,7 +1536,23 @@ 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)) { @@ -1527,7 +1576,7 @@ power(PyObject *self, PyObject *other, PyObject *module) Py_DECREF(vf); return resf; } - res = MPZ_new(); + res = MPZ_new(state, state->MPZ_Type); int64_t exp; @@ -1550,7 +1599,7 @@ power(PyObject *self, PyObject *other, PyObject *module) zz_err ret = ZZ_OK; - res = MPZ_new(); + res = MPZ_new(state, state->MPZ_Type); if (!res || (ret = zz_powm(&u->z, &v->z, &w->z, &res->z))) { /* LCOV_EXCL_START */ if (ret == ZZ_VAL) { @@ -1608,30 +1657,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)) { @@ -1639,9 +1664,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(); + gmp_state *state = get_state(Py_TYPE(self)); + MPZ_Object *res = MPZ_new(state, Py_TYPE(self)); if (res && zz_set(1, &res->z)) { PyErr_NoMemory(); /* LCOV_EXCL_LINE */ @@ -1650,9 +1676,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(); + gmp_state *state = get_state(Py_TYPE(self)); + + return (PyObject *)MPZ_new(state, Py_TYPE(self)); } static PyGetSetDef getsetters[] = { @@ -1757,13 +1785,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"}; @@ -1775,6 +1806,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; @@ -1814,13 +1846,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 */ @@ -1844,6 +1876,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) { noop: @@ -1887,7 +1920,7 @@ __round__(PyObject *self, PyObject *const *args, Py_ssize_t nargs) zz_clear(&exp); zz_clear(&ten); - MPZ_Object *res = MPZ_new(); + MPZ_Object *res = MPZ_new(state, Py_TYPE(self)); if (!res) { /* LCOV_EXCL_START */ @@ -2054,28 +2087,71 @@ 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), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | +#if PY_VERSION_HEX > 0x030E00A0 + Py_TPFLAGS_HAVE_VECTORCALL | +#endif + Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE), + .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(); + gmp_state *state = PyModule_GetState(module); + MPZ_Object *res = MPZ_new(state, state->MPZ_Type); if (!res) { return (PyObject *)res; /* LCOV_EXCL_LINE */ @@ -2104,7 +2180,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) { @@ -2112,7 +2188,8 @@ gmp_gcdext(PyObject *Py_UNUSED(module), PyObject *const *args, return NULL; } MPZ_Object *x = NULL, *y = NULL; - MPZ_Object *g = MPZ_new(), *s = MPZ_new(), *t = MPZ_new(); + gmp_state *state = PyModule_GetState(module); + MPZ_Object *g = MPZ_new(state, state->MPZ_Type), *s = MPZ_new(state, state->MPZ_Type), *t = MPZ_new(state, state->MPZ_Type); if (!g || !s || !t) { /* LCOV_EXCL_START */ @@ -2148,9 +2225,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(); + gmp_state *state = PyModule_GetState(module); + MPZ_Object *res = MPZ_new(state, state->MPZ_Type); if (!res || zz_set(1, &res->z)) { return PyErr_NoMemory(); /* LCOV_EXCL_LINE */ @@ -2179,9 +2257,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(); + gmp_state *state = PyModule_GetState(module); + MPZ_Object *x, *root = MPZ_new(state, state->MPZ_Type); if (!root) { return NULL; /* LCOV_EXCL_LINE */ @@ -2207,9 +2286,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(), *rem = MPZ_new(); + gmp_state *state = PyModule_GetState(module); + MPZ_Object *x, *root = MPZ_new(state, state->MPZ_Type), *rem = MPZ_new(state, state->MPZ_Type); PyObject *tup = NULL; if (!root || !rem) { @@ -2241,9 +2321,10 @@ gmp_isqrt_rem(PyObject *Py_UNUSED(module), PyObject *arg) } static PyObject * -gmp_fac(PyObject *Py_UNUSED(module), PyObject *arg) +gmp_fac(PyObject *module, PyObject *arg) { - MPZ_Object *x, *res = MPZ_new(); + gmp_state *state = PyModule_GetState(module); + MPZ_Object *x, *res = MPZ_new(state, state->MPZ_Type); if (!res) { return NULL; /* LCOV_EXCL_LINE */ @@ -2278,14 +2359,15 @@ gmp_fac(PyObject *Py_UNUSED(module), PyObject *arg) } 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(); + gmp_state *state = PyModule_GetState(module); + MPZ_Object *x, *y, *res = MPZ_new(state, state->MPZ_Type); if (!res) { return NULL; /* LCOV_EXCL_LINE */ @@ -2324,17 +2406,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(); + gmp_state *state = PyModule_GetState(module); + MPZ_Object *x, *y, *res = MPZ_new(state, state->MPZ_Type); if (!res) { return NULL; /* LCOV_EXCL_LINE */ @@ -2363,7 +2446,7 @@ gmp_perm(PyObject *self, PyObject *const *args, Py_ssize_t nargs) return (PyObject *)res; } - MPZ_Object *den = MPZ_new(); + MPZ_Object *den = MPZ_new(state, state->MPZ_Type); if (!den) { /* LCOV_EXCL_START */ @@ -2526,7 +2609,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"); @@ -2538,10 +2621,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*, " @@ -2553,7 +2637,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)) @@ -2578,7 +2662,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, @@ -2587,12 +2671,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 */ } @@ -2636,7 +2721,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)) @@ -2668,8 +2753,10 @@ gmp__free_cache(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) PyObject *self = (PyObject *)u; PyTypeObject *type = Py_TYPE(self); + PyObject_GC_UnTrack(self); zz_clear(&u->z); type->tp_free(self); + Py_DECREF(type); } global.gmp_cache_size = 0; Py_RETURN_NONE; @@ -2725,10 +2812,18 @@ static PyStructSequence_Desc mpz_info_desc = { static int gmp_exec(PyObject *m) { + gmp_state *state = PyModule_GetState(m); + if (zz_setup()) { 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 */ } @@ -2795,6 +2890,25 @@ 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); + + gmp__free_cache(module, NULL); /* XXX: to make cache work on f-t builds */ + Py_VISIT(state->MPZ_Type); + return 0; +} + #ifdef __GNUC__ # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wpedantic" @@ -2816,9 +2930,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 294aa354..091fe58b 100644 --- a/mpz.h +++ b/mpz.h @@ -37,9 +37,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 */ diff --git a/pyproject.toml b/pyproject.toml index 42706960..743ccbe9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -65,7 +65,7 @@ select = ["E", "F", "I", "PT", "W", "Q", "SIM"] [tool.cibuildwheel] build-frontend = {name="build", args=["--verbose", "-Csetup-args=--vsenv"]} -enable = "pypy cpython-prerelease cpython-freethreading graalpy" +enable = "cpython-prerelease cpython-freethreading graalpy" skip = "gp311_242* gp3*win*amd64*" before-all = "sh scripts/cibw_before_all.sh"