Skip to content

Commit 458b2b2

Browse files
committed
Support python 3.12
1 parent f7f0d5e commit 458b2b2

File tree

4 files changed

+119
-27
lines changed

4 files changed

+119
-27
lines changed

src/fpylll/gmp/pycore_long.h

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
#include "Python.h"
2+
#include <stdbool.h>
3+
4+
#if PY_VERSION_HEX >= 0x030C00A5
5+
#define ob_digit(o) (((PyLongObject*)o)->long_value.ob_digit)
6+
#else
7+
#define ob_digit(o) (((PyLongObject*)o)->ob_digit)
8+
#endif
9+
10+
#if PY_VERSION_HEX >= 0x030C00A7
11+
// taken from cpython:Include/internal/pycore_long.h @ 3.12
12+
13+
/* Long value tag bits:
14+
* 0-1: Sign bits value = (1-sign), ie. negative=2, positive=0, zero=1.
15+
* 2: Reserved for immortality bit
16+
* 3+ Unsigned digit count
17+
*/
18+
#define SIGN_MASK 3
19+
#define SIGN_ZERO 1
20+
#define SIGN_NEGATIVE 2
21+
#define NON_SIZE_BITS 3
22+
23+
static inline bool
24+
_PyLong_IsZero(const PyLongObject *op)
25+
{
26+
return (op->long_value.lv_tag & SIGN_MASK) == SIGN_ZERO;
27+
}
28+
29+
static inline bool
30+
_PyLong_IsNegative(const PyLongObject *op)
31+
{
32+
return (op->long_value.lv_tag & SIGN_MASK) == SIGN_NEGATIVE;
33+
}
34+
35+
static inline bool
36+
_PyLong_IsPositive(const PyLongObject *op)
37+
{
38+
return (op->long_value.lv_tag & SIGN_MASK) == 0;
39+
}
40+
41+
static inline Py_ssize_t
42+
_PyLong_DigitCount(const PyLongObject *op)
43+
{
44+
assert(PyLong_Check(op));
45+
return op->long_value.lv_tag >> NON_SIZE_BITS;
46+
}
47+
48+
#define TAG_FROM_SIGN_AND_SIZE(sign, size) ((1 - (sign)) | ((size) << NON_SIZE_BITS))
49+
50+
static inline void
51+
_PyLong_SetSignAndDigitCount(PyLongObject *op, int sign, Py_ssize_t size)
52+
{
53+
assert(size >= 0);
54+
assert(-1 <= sign && sign <= 1);
55+
assert(sign != 0 || size == 0);
56+
op->long_value.lv_tag = TAG_FROM_SIGN_AND_SIZE(sign, (size_t)size);
57+
}
58+
59+
#else
60+
// fallback for < 3.12
61+
62+
static inline bool
63+
_PyLong_IsZero(const PyLongObject *op)
64+
{
65+
return Py_SIZE(op) == 0;
66+
}
67+
68+
static inline bool
69+
_PyLong_IsNegative(const PyLongObject *op)
70+
{
71+
return Py_SIZE(op) < 0;
72+
}
73+
74+
static inline bool
75+
_PyLong_IsPositive(const PyLongObject *op)
76+
{
77+
return Py_SIZE(op) > 0;
78+
}
79+
80+
static inline Py_ssize_t
81+
_PyLong_DigitCount(const PyLongObject *op)
82+
{
83+
Py_ssize_t size = Py_SIZE(op);
84+
return size < 0 ? -size : size;
85+
}
86+
87+
static inline void
88+
_PyLong_SetSignAndDigitCount(PyLongObject *op, int sign, Py_ssize_t size)
89+
{
90+
#if (PY_MAJOR_VERSION == 3) && (PY_MINOR_VERSION < 9)
91+
// The function Py_SET_SIZE is defined starting with python 3.9.
92+
Py_SIZE(o) = size;
93+
#else
94+
Py_SET_SIZE(op, sign < 0 ? -size : size);
95+
#endif
96+
}
97+
98+
#endif

src/fpylll/gmp/pycore_long.pxd

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from cpython.longintrepr cimport py_long, digit
2+
3+
cdef extern from "pycore_long.h":
4+
digit* ob_digit(py_long o)
5+
bint _PyLong_IsZero(py_long o)
6+
bint _PyLong_IsNegative(py_long o)
7+
bint _PyLong_IsPositive(py_long o)
8+
Py_ssize_t _PyLong_DigitCount(py_long o)
9+
void _PyLong_SetSignAndDigitCount(py_long o, int sign, Py_ssize_t size)

src/fpylll/gmp/pylong.pxd

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,10 @@
33
Various functions to deal with conversion mpz <-> Python int/long
44
"""
55

6-
cdef extern from "Python.h":
7-
cdef _PyLong_New(Py_ssize_t s)
8-
cdef long PyLong_SHIFT
9-
ctypedef unsigned int digit
10-
ctypedef struct PyLongObject:
11-
digit* ob_digit
12-
13-
ctypedef struct PyObject:
14-
pass
15-
16-
ctypedef struct PyVarObject:
17-
PyObject ob_base
18-
Py_ssize_t ob_size
19-
6+
from cpython.longintrepr cimport py_long
207
from fpylll.gmp.types cimport *
218

229
cdef mpz_get_pylong(mpz_srcptr z)
2310
cdef mpz_get_pyintlong(mpz_srcptr z)
24-
cdef int mpz_set_pylong(mpz_ptr z, L) except -1
11+
cdef int mpz_set_pylong(mpz_ptr z, py_long L) except -1
2512
cdef Py_hash_t mpz_pythonhash(mpz_srcptr z)

src/fpylll/gmp/pylong.pyx

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ AUTHORS:
2828

2929
from cpython.int cimport PyInt_FromLong
3030
from cpython.long cimport PyLong_CheckExact, PyLong_FromLong
31+
from cpython.longintrepr cimport _PyLong_New, digit, PyLong_SHIFT
32+
from .pycore_long cimport (ob_digit, _PyLong_IsZero, _PyLong_IsNegative,
33+
_PyLong_IsPositive, _PyLong_DigitCount, _PyLong_SetSignAndDigitCount)
3134
from .mpz cimport *
3235

3336
# Unused bits in every PyLong digit
@@ -40,11 +43,9 @@ cdef mpz_get_pylong_large(mpz_srcptr z):
4043
"""
4144
cdef size_t nbits = mpz_sizeinbase(z, 2)
4245
cdef size_t pylong_size = (nbits + PyLong_SHIFT - 1) // PyLong_SHIFT
43-
L = _PyLong_New(pylong_size)
44-
mpz_export((<PyLongObject*>L).ob_digit, NULL,
45-
-1, sizeof(digit), 0, PyLong_nails, z)
46-
if mpz_sgn(z) < 0:
47-
(<PyVarObject*>L).ob_size = -(<PyVarObject*>L).ob_size
46+
cdef py_long L = _PyLong_New(pylong_size)
47+
mpz_export(ob_digit(L), NULL, -1, sizeof(digit), 0, PyLong_nails, z)
48+
_PyLong_SetSignAndDigitCount(L, mpz_sgn(z), pylong_size)
4849
return L
4950

5051

@@ -67,16 +68,13 @@ cdef mpz_get_pyintlong(mpz_srcptr z):
6768
return mpz_get_pylong_large(z)
6869

6970

70-
cdef int mpz_set_pylong(mpz_ptr z, L) except -1:
71+
cdef int mpz_set_pylong(mpz_ptr z, py_long L) except -1:
7172
"""
7273
Convert a Python ``long`` `L` to an ``mpz``.
7374
"""
74-
cdef Py_ssize_t pylong_size = (<PyVarObject*>L).ob_size
75-
if pylong_size < 0:
76-
pylong_size = -pylong_size
77-
mpz_import(z, pylong_size, -1, sizeof(digit), 0, PyLong_nails,
78-
(<PyLongObject*>L).ob_digit)
79-
if (<PyVarObject*>L).ob_size < 0:
75+
cdef Py_ssize_t pylong_size = _PyLong_DigitCount(L)
76+
mpz_import(z, pylong_size, -1, sizeof(digit), 0, PyLong_nails, ob_digit(L))
77+
if _PyLong_IsNegative(L):
8078
mpz_neg(z, z)
8179

8280

0 commit comments

Comments
 (0)