1 /* Copyright (C) 2019-2022 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/>. */
18 #include "gmp-utils.h"
20 /* See gmp-utils.h. */
23 gmp_string_printf (const char *fmt
, ...)
28 int size
= gmp_vsnprintf (NULL
, 0, fmt
, vp
);
31 std::string
str (size
, '\0');
33 /* C++11 and later guarantee std::string uses contiguous memory and
34 always includes the terminating '\0'. */
36 gmp_vsprintf (&str
[0], fmt
, vp
);
42 /* See gmp-utils.h. */
45 gdb_mpz::read (gdb::array_view
<const gdb_byte
> buf
, enum bfd_endian byte_order
,
48 mpz_import (val
, 1 /* count */, -1 /* order */, buf
.size () /* size */,
49 byte_order
== BFD_ENDIAN_BIG
? 1 : -1 /* endian */,
50 0 /* nails */, buf
.data () /* op */);
54 /* The value was imported as if it was a positive value,
55 as mpz_import does not handle signs. If the original value
56 was in fact negative, we need to adjust VAL accordingly. */
59 mpz_ui_pow_ui (max
.val
, 2, buf
.size () * HOST_CHAR_BIT
- 1);
60 if (mpz_cmp (val
, max
.val
) >= 0)
61 mpz_submul_ui (val
, max
.val
, 2);
65 /* See gmp-utils.h. */
68 gdb_mpz::write (gdb::array_view
<gdb_byte
> buf
, enum bfd_endian byte_order
,
69 bool unsigned_p
) const
72 (buf
, byte_order
== BFD_ENDIAN_BIG
? 1 : -1 /* endian */, unsigned_p
);
75 /* See gmp-utils.h. */
78 gdb_mpz::safe_export (gdb::array_view
<gdb_byte
> buf
,
79 int endian
, bool unsigned_p
) const
81 gdb_assert (buf
.size () > 0);
83 if (mpz_sgn (val
) == 0)
85 /* Our value is zero, so no need to call mpz_export to do the work,
86 especially since mpz_export's documentation explicitly says
87 that the function is a noop in this case. Just write zero to
89 memset (buf
.data (), 0, buf
.size ());
93 /* Determine the maximum range of values that our buffer can hold,
94 and verify that VAL is within that range. */
97 const size_t max_usable_bits
= buf
.size () * HOST_CHAR_BIT
;
102 mpz_ui_pow_ui (hi
.val
, 2, max_usable_bits
);
103 mpz_sub_ui (hi
.val
, hi
.val
, 1);
107 mpz_ui_pow_ui (lo
.val
, 2, max_usable_bits
- 1);
108 mpz_neg (lo
.val
, lo
.val
);
110 mpz_ui_pow_ui (hi
.val
, 2, max_usable_bits
- 1);
111 mpz_sub_ui (hi
.val
, hi
.val
, 1);
114 if (mpz_cmp (val
, lo
.val
) < 0 || mpz_cmp (val
, hi
.val
) > 0)
115 error (_("Cannot export value %s as %zu-bits %s integer"
116 " (must be between %s and %s)"),
117 this->str ().c_str (),
119 unsigned_p
? _("unsigned") : _("signed"),
123 gdb_mpz
exported_val (val
);
125 if (mpz_cmp_ui (exported_val
.val
, 0) < 0)
127 /* mpz_export does not handle signed values, so create a positive
128 value whose bit representation as an unsigned of the same length
129 would be the same as our negative value. */
132 mpz_ui_pow_ui (neg_offset
.val
, 2, buf
.size () * HOST_CHAR_BIT
);
133 mpz_add (exported_val
.val
, exported_val
.val
, neg_offset
.val
);
136 /* Do the export into a buffer allocated by GMP itself; that way,
137 we can detect cases where BUF is not large enough to export
138 our value, and thus avoid a buffer overlow. Normally, this should
139 never happen, since we verified earlier that the buffer is large
140 enough to accomodate our value, but doing this allows us to be
141 extra safe with the export.
143 After verification that the export behaved as expected, we will
144 copy the data over to BUF. */
147 gdb::unique_xmalloc_ptr
<void> exported
148 (mpz_export (NULL
, &word_countp
, -1 /* order */, buf
.size () /* size */,
149 endian
, 0 /* nails */, exported_val
.val
));
151 gdb_assert (word_countp
== 1);
153 memcpy (buf
.data (), exported
.get (), buf
.size ());
156 /* See gmp-utils.h. */
159 gdb_mpq::get_rounded () const
161 /* Work with a positive number so as to make the "floor" rounding
162 always round towards zero. */
164 gdb_mpq
abs_val (val
);
165 mpq_abs (abs_val
.val
, abs_val
.val
);
167 /* Convert our rational number into a quotient and remainder,
168 with "floor" rounding, which in our case means rounding
171 gdb_mpz quotient
, remainder
;
172 mpz_fdiv_qr (quotient
.val
, remainder
.val
,
173 mpq_numref (abs_val
.val
), mpq_denref (abs_val
.val
));
175 /* Multiply the remainder by 2, and see if it is greater or equal
176 to abs_val's denominator. If yes, round to the next integer. */
178 mpz_mul_ui (remainder
.val
, remainder
.val
, 2);
179 if (mpz_cmp (remainder
.val
, mpq_denref (abs_val
.val
)) >= 0)
180 mpz_add_ui (quotient
.val
, quotient
.val
, 1);
182 /* Re-apply the sign if needed. */
183 if (mpq_sgn (val
) < 0)
184 mpz_neg (quotient
.val
, quotient
.val
);
189 /* See gmp-utils.h. */
192 gdb_mpq::read_fixed_point (gdb::array_view
<const gdb_byte
> buf
,
193 enum bfd_endian byte_order
, bool unsigned_p
,
194 const gdb_mpq
&scaling_factor
)
197 vz
.read (buf
, byte_order
, unsigned_p
);
199 mpq_set_z (val
, vz
.val
);
200 mpq_mul (val
, val
, scaling_factor
.val
);
203 /* See gmp-utils.h. */
206 gdb_mpq::write_fixed_point (gdb::array_view
<gdb_byte
> buf
,
207 enum bfd_endian byte_order
, bool unsigned_p
,
208 const gdb_mpq
&scaling_factor
) const
210 gdb_mpq
unscaled (val
);
212 mpq_div (unscaled
.val
, unscaled
.val
, scaling_factor
.val
);
214 gdb_mpz unscaled_z
= unscaled
.get_rounded ();
215 unscaled_z
.write (buf
, byte_order
, unsigned_p
);
218 /* A wrapper around xrealloc that we can then register with GMP
219 as the "realloc" function. */
222 xrealloc_for_gmp (void *ptr
, size_t old_size
, size_t new_size
)
224 return xrealloc (ptr
, new_size
);
227 /* A wrapper around xfree that we can then register with GMP
228 as the "free" function. */
231 xfree_for_gmp (void *ptr
, size_t size
)
236 void _initialize_gmp_utils ();
239 _initialize_gmp_utils ()
241 /* Tell GMP to use GDB's memory management routines. */
242 mp_set_memory_functions (xmalloc
, xrealloc_for_gmp
, xfree_for_gmp
);