1 /* Copyright (C) 2019-2023 Free Software Foundation, Inc.
3 This file is part of GDB.
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 #include "gmp-utils.h"
21 /* See gmp-utils.h. */
24 gmp_string_printf (const char *fmt
, ...)
29 int size
= gmp_vsnprintf (NULL
, 0, fmt
, vp
);
32 std::string
str (size
, '\0');
34 /* C++11 and later guarantee std::string uses contiguous memory and
35 always includes the terminating '\0'. */
37 gmp_vsprintf (&str
[0], fmt
, vp
);
43 /* See gmp-utils.h. */
46 gdb_mpz::read (gdb::array_view
<const gdb_byte
> buf
, enum bfd_endian byte_order
,
49 mpz_import (m_val
, 1 /* count */, -1 /* order */, buf
.size () /* size */,
50 byte_order
== BFD_ENDIAN_BIG
? 1 : -1 /* endian */,
51 0 /* nails */, buf
.data () /* op */);
55 /* The value was imported as if it was a positive value,
56 as mpz_import does not handle signs. If the original value
57 was in fact negative, we need to adjust VAL accordingly. */
60 mpz_ui_pow_ui (max
.m_val
, 2, buf
.size () * HOST_CHAR_BIT
- 1);
61 if (mpz_cmp (m_val
, max
.m_val
) >= 0)
62 mpz_submul_ui (m_val
, max
.m_val
, 2);
66 /* See gmp-utils.h. */
69 gdb_mpz::export_bits (gdb::array_view
<gdb_byte
> buf
, int endian
, bool unsigned_p
,
72 int sign
= mpz_sgn (m_val
);
75 /* Our value is zero, so no need to call mpz_export to do the work,
76 especially since mpz_export's documentation explicitly says
77 that the function is a noop in this case. Just write zero to
78 BUF ourselves, if it is non-empty. In some languages, a
79 zero-bit type can exist and this is also fine. */
81 memset (buf
.data (), 0, buf
.size ());
85 gdb_assert (buf
.size () > 0);
89 /* Determine the maximum range of values that our buffer can
90 hold, and verify that VAL is within that range. */
93 const size_t max_usable_bits
= buf
.size () * HOST_CHAR_BIT
;
98 mpz_ui_pow_ui (hi
.m_val
, 2, max_usable_bits
);
99 mpz_sub_ui (hi
.m_val
, hi
.m_val
, 1);
103 mpz_ui_pow_ui (lo
.m_val
, 2, max_usable_bits
- 1);
104 mpz_neg (lo
.m_val
, lo
.m_val
);
106 mpz_ui_pow_ui (hi
.m_val
, 2, max_usable_bits
- 1);
107 mpz_sub_ui (hi
.m_val
, hi
.m_val
, 1);
110 if (mpz_cmp (m_val
, lo
.m_val
) < 0 || mpz_cmp (m_val
, hi
.m_val
) > 0)
111 error (_("Cannot export value %s as %zu-bits %s integer"
112 " (must be between %s and %s)"),
113 this->str ().c_str (),
115 unsigned_p
? _("unsigned") : _("signed"),
120 const gdb_mpz
*exported_val
= this;
124 /* mpz_export does not handle signed values, so create a positive
125 value whose bit representation as an unsigned of the same length
126 would be the same as our negative value. */
127 gdb_mpz neg_offset
= gdb_mpz::pow (2, buf
.size () * HOST_CHAR_BIT
);
128 un_signed
= *exported_val
+ neg_offset
;
129 exported_val
= &un_signed
;
132 /* If the value is too large, truncate it. */
134 && mpz_sizeinbase (exported_val
->m_val
, 2) > buf
.size () * HOST_CHAR_BIT
)
136 /* If we don't already have a copy, make it now. */
137 if (exported_val
!= &un_signed
)
139 un_signed
= *exported_val
;
140 exported_val
= &un_signed
;
143 un_signed
.mask (buf
.size () * HOST_CHAR_BIT
);
146 /* It's possible that one of the above results in zero, which has to
147 be handled specially. */
148 if (exported_val
->sgn () == 0)
150 memset (buf
.data (), 0, buf
.size ());
154 /* Do the export into a buffer allocated by GMP itself; that way,
155 we can detect cases where BUF is not large enough to export
156 our value, and thus avoid a buffer overflow. Normally, this should
157 never happen, since we verified earlier that the buffer is large
158 enough to accommodate our value, but doing this allows us to be
159 extra safe with the export.
161 After verification that the export behaved as expected, we will
162 copy the data over to BUF. */
165 gdb::unique_xmalloc_ptr
<void> exported
166 (mpz_export (NULL
, &word_countp
, -1 /* order */, buf
.size () /* size */,
167 endian
, 0 /* nails */, exported_val
->m_val
));
169 gdb_assert (word_countp
== 1);
171 memcpy (buf
.data (), exported
.get (), buf
.size ());
174 /* See gmp-utils.h. */
177 gdb_mpq::get_rounded () const
179 /* Work with a positive number so as to make the "floor" rounding
180 always round towards zero. */
182 gdb_mpq
abs_val (m_val
);
183 mpq_abs (abs_val
.m_val
, abs_val
.m_val
);
185 /* Convert our rational number into a quotient and remainder,
186 with "floor" rounding, which in our case means rounding
189 gdb_mpz quotient
, remainder
;
190 mpz_fdiv_qr (quotient
.m_val
, remainder
.m_val
,
191 mpq_numref (abs_val
.m_val
), mpq_denref (abs_val
.m_val
));
193 /* Multiply the remainder by 2, and see if it is greater or equal
194 to abs_val's denominator. If yes, round to the next integer. */
196 mpz_mul_ui (remainder
.m_val
, remainder
.m_val
, 2);
197 if (mpz_cmp (remainder
.m_val
, mpq_denref (abs_val
.m_val
)) >= 0)
198 mpz_add_ui (quotient
.m_val
, quotient
.m_val
, 1);
200 /* Re-apply the sign if needed. */
201 if (mpq_sgn (m_val
) < 0)
202 mpz_neg (quotient
.m_val
, quotient
.m_val
);
207 /* See gmp-utils.h. */
210 gdb_mpq::read_fixed_point (gdb::array_view
<const gdb_byte
> buf
,
211 enum bfd_endian byte_order
, bool unsigned_p
,
212 const gdb_mpq
&scaling_factor
)
215 vz
.read (buf
, byte_order
, unsigned_p
);
217 mpq_set_z (m_val
, vz
.m_val
);
218 mpq_mul (m_val
, m_val
, scaling_factor
.m_val
);
221 /* See gmp-utils.h. */
224 gdb_mpq::write_fixed_point (gdb::array_view
<gdb_byte
> buf
,
225 enum bfd_endian byte_order
, bool unsigned_p
,
226 const gdb_mpq
&scaling_factor
) const
228 gdb_mpq
unscaled (m_val
);
230 mpq_div (unscaled
.m_val
, unscaled
.m_val
, scaling_factor
.m_val
);
232 gdb_mpz unscaled_z
= unscaled
.get_rounded ();
233 unscaled_z
.write (buf
, byte_order
, unsigned_p
);
236 /* A wrapper around xrealloc that we can then register with GMP
237 as the "realloc" function. */
240 xrealloc_for_gmp (void *ptr
, size_t old_size
, size_t new_size
)
242 return xrealloc (ptr
, new_size
);
245 /* A wrapper around xfree that we can then register with GMP
246 as the "free" function. */
249 xfree_for_gmp (void *ptr
, size_t size
)
254 void _initialize_gmp_utils ();
257 _initialize_gmp_utils ()
259 /* Tell GMP to use GDB's memory management routines. */
260 mp_set_memory_functions (xmalloc
, xrealloc_for_gmp
, xfree_for_gmp
);