Skip to content

Commit edf7e54

Browse files
Merge pull request #436 from UriKH/fix/mpoly-str-leak
Fix memory leak in str() of fmpz_mpoly, fmpq_mpoly, fmpz_mod_mpoly, nmod_mpoly
2 parents a1034a0 + 68df3bb commit edf7e54

5 files changed

Lines changed: 34 additions & 8 deletions

File tree

‎README.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,9 +170,15 @@ Contributors (0.10.0):
170170
- Vitaly Magerya (VM)
171171
- Oscar Benjamin (OB)
172172
- Ilia Gradina (IG)
173+
- Uri K.H. (UKH)
173174

174175
Changes (0.10.0):
175176

177+
- [gh-435](https://github.com/flintlib/python-flint/issues/435),
178+
Fix a memory leak in `str()` (and so in `repr()`, which uses it unless
179+
`flint.ctx.pretty` is `False`) of `fmpz_mpoly`, `fmpq_mpoly`,
180+
`fmpz_mod_mpoly` and `nmod_mpoly`: the string returned by FLINT was
181+
copied into a Python `bytes` object but never freed. (UKH)
176182
- [gh-433](https://github.com/flintlib/python-flint/pull/433),
177183
Define the input type aliases in the type stubs (such as `iarb` and
178184
`ifmpz`) after the classes they reference, so that the ty type checker

‎src/flint/types/fmpq_mpoly.pyx‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ from flint.flint_base.flint_base cimport (
1010
)
1111

1212
from flint.utils.typecheck cimport typecheck
13+
from flint.flintlib.functions.flint cimport flint_free
1314
from flint.utils.flint_exceptions import DomainError, IncompatibleContextError
1415

1516
from flint.types.fmpq cimport fmpq, any_as_fmpq
@@ -760,9 +761,13 @@ cdef class fmpq_mpoly(flint_mpoly):
760761
return f"{self.ctx}.from_dict({self.to_dict()})"
761762

762763
def str(self):
763-
cdef bytes s = <bytes> fmpq_mpoly_get_str_pretty(self.val, self.ctx.c_names, self.ctx.val)
764+
cdef char * cs = fmpq_mpoly_get_str_pretty(self.val, self.ctx.c_names, self.ctx.val)
765+
try:
766+
s = (<bytes> cs).decode()
767+
finally:
768+
flint_free(cs) # FLINT malloc'ed the string; the bytes cast copies it
764769
# res = s.decode().replace("+", " + ").replace("-", " - ")
765-
res = s.decode()
770+
res = s
766771
if res.startswith("- "):
767772
res = "-" + res[3:]
768773
return res

‎src/flint/types/fmpz_mod_mpoly.pyx‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ from flint.flint_base.flint_base cimport (
1010
)
1111

1212
from flint.utils.typecheck cimport typecheck
13+
from flint.flintlib.functions.flint cimport flint_free
1314
from flint.utils.flint_exceptions import DomainError, IncompatibleContextError
1415

1516
from flint.types.fmpz cimport any_as_fmpz, fmpz
@@ -746,8 +747,12 @@ cdef class fmpz_mod_mpoly(flint_mpoly):
746747
return f"{self.ctx}.from_dict({self.to_dict()})"
747748

748749
def str(self):
749-
cdef bytes s = <bytes> fmpz_mod_mpoly_get_str_pretty(self.val, self.ctx.c_names, self.ctx.val)
750-
res = s.decode().replace("+", " + ").replace("-", " - ")
750+
cdef char * cs = fmpz_mod_mpoly_get_str_pretty(self.val, self.ctx.c_names, self.ctx.val)
751+
try:
752+
s = (<bytes> cs).decode()
753+
finally:
754+
flint_free(cs) # FLINT malloc'ed the string; the bytes cast copies it
755+
res = s.replace("+", " + ").replace("-", " - ")
751756
if res.startswith(" - "):
752757
res = "-" + res[3:]
753758
return res

‎src/flint/types/fmpz_mpoly.pyx‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ from flint.flint_base.flint_base cimport (
1010
)
1111

1212
from flint.utils.typecheck cimport typecheck
13+
from flint.flintlib.functions.flint cimport flint_free
1314
from flint.utils.flint_exceptions import DomainError, IncompatibleContextError
1415

1516
from flint.types.fmpz cimport any_as_fmpz, fmpz
@@ -734,8 +735,12 @@ cdef class fmpz_mpoly(flint_mpoly):
734735
return f"{self.ctx}.from_dict({self.to_dict()})"
735736

736737
def str(self):
737-
cdef bytes s = <bytes> fmpz_mpoly_get_str_pretty(self.val, self.ctx.c_names, self.ctx.val)
738-
res = s.decode().replace("+", " + ").replace("-", " - ")
738+
cdef char * cs = fmpz_mpoly_get_str_pretty(self.val, self.ctx.c_names, self.ctx.val)
739+
try:
740+
s = (<bytes> cs).decode()
741+
finally:
742+
flint_free(cs) # FLINT malloc'ed the string; the bytes cast copies it
743+
res = s.replace("+", " + ").replace("-", " - ")
739744
if res.startswith(" - "):
740745
res = "-" + res[3:]
741746
return res

‎src/flint/types/nmod_mpoly.pyx‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ from flint.flint_base.flint_base cimport (
88
)
99

1010
from flint.utils.typecheck cimport typecheck
11+
from flint.flintlib.functions.flint cimport flint_free
1112
from flint.utils.flint_exceptions import DomainError, IncompatibleContextError
1213

1314
from flint.types.fmpz cimport fmpz
@@ -720,8 +721,12 @@ cdef class nmod_mpoly(flint_mpoly):
720721
return f"{self.ctx}.from_dict({self.to_dict()})"
721722

722723
def str(self):
723-
cdef bytes s = <bytes> nmod_mpoly_get_str_pretty(self.val, self.ctx.c_names, self.ctx.val)
724-
res = s.decode().replace("+", " + ").replace("-", " - ")
724+
cdef char * cs = nmod_mpoly_get_str_pretty(self.val, self.ctx.c_names, self.ctx.val)
725+
try:
726+
s = (<bytes> cs).decode()
727+
finally:
728+
flint_free(cs) # FLINT malloc'ed the string; the bytes cast copies it
729+
res = s.replace("+", " + ").replace("-", " - ")
725730
if res.startswith(" - "):
726731
res = "-" + res[3:]
727732
return res

0 commit comments

Comments
 (0)