Remove path name from test case
[binutils-gdb.git] / gdb / gmp-utils.c
1 /* Copyright (C) 2019-2023 Free Software Foundation, Inc.
2
3 This file is part of GDB.
4
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.
9
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.
14
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/>. */
17
18 #include "defs.h"
19 #include "gmp-utils.h"
20
21 /* See gmp-utils.h. */
22
23 std::string
24 gmp_string_printf (const char *fmt, ...)
25 {
26 va_list vp;
27
28 va_start (vp, fmt);
29 int size = gmp_vsnprintf (NULL, 0, fmt, vp);
30 va_end (vp);
31
32 std::string str (size, '\0');
33
34 /* C++11 and later guarantee std::string uses contiguous memory and
35 always includes the terminating '\0'. */
36 va_start (vp, fmt);
37 gmp_vsprintf (&str[0], fmt, vp);
38 va_end (vp);
39
40 return str;
41 }
42
43 /* See gmp-utils.h. */
44
45 void
46 gdb_mpz::read (gdb::array_view<const gdb_byte> buf, enum bfd_endian byte_order,
47 bool unsigned_p)
48 {
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 */);
52
53 if (!unsigned_p)
54 {
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. */
58 gdb_mpz max;
59
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);
63 }
64 }
65
66 /* See gmp-utils.h. */
67
68 void
69 gdb_mpz::export_bits (gdb::array_view<gdb_byte> buf, int endian, bool unsigned_p,
70 bool safe) const
71 {
72 int sign = mpz_sgn (m_val);
73 if (sign == 0)
74 {
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. */
80 if (buf.size () > 0)
81 memset (buf.data (), 0, buf.size ());
82 return;
83 }
84
85 gdb_assert (buf.size () > 0);
86
87 if (safe)
88 {
89 /* Determine the maximum range of values that our buffer can
90 hold, and verify that VAL is within that range. */
91
92 gdb_mpz lo, hi;
93 const size_t max_usable_bits = buf.size () * HOST_CHAR_BIT;
94 if (unsigned_p)
95 {
96 lo = 0;
97
98 mpz_ui_pow_ui (hi.m_val, 2, max_usable_bits);
99 mpz_sub_ui (hi.m_val, hi.m_val, 1);
100 }
101 else
102 {
103 mpz_ui_pow_ui (lo.m_val, 2, max_usable_bits - 1);
104 mpz_neg (lo.m_val, lo.m_val);
105
106 mpz_ui_pow_ui (hi.m_val, 2, max_usable_bits - 1);
107 mpz_sub_ui (hi.m_val, hi.m_val, 1);
108 }
109
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 (),
114 max_usable_bits,
115 unsigned_p ? _("unsigned") : _("signed"),
116 lo.str ().c_str (),
117 hi.str ().c_str ());
118 }
119
120 const gdb_mpz *exported_val = this;
121 gdb_mpz un_signed;
122 if (sign < 0)
123 {
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;
130 }
131
132 /* If the value is too large, truncate it. */
133 if (!safe
134 && mpz_sizeinbase (exported_val->m_val, 2) > buf.size () * HOST_CHAR_BIT)
135 {
136 /* If we don't already have a copy, make it now. */
137 if (exported_val != &un_signed)
138 {
139 un_signed = *exported_val;
140 exported_val = &un_signed;
141 }
142
143 un_signed.mask (buf.size () * HOST_CHAR_BIT);
144 }
145
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)
149 {
150 memset (buf.data (), 0, buf.size ());
151 return;
152 }
153
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.
160
161 After verification that the export behaved as expected, we will
162 copy the data over to BUF. */
163
164 size_t word_countp;
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));
168
169 gdb_assert (word_countp == 1);
170
171 memcpy (buf.data (), exported.get (), buf.size ());
172 }
173
174 /* See gmp-utils.h. */
175
176 gdb_mpz
177 gdb_mpq::get_rounded () const
178 {
179 /* Work with a positive number so as to make the "floor" rounding
180 always round towards zero. */
181
182 gdb_mpq abs_val (m_val);
183 mpq_abs (abs_val.m_val, abs_val.m_val);
184
185 /* Convert our rational number into a quotient and remainder,
186 with "floor" rounding, which in our case means rounding
187 towards zero. */
188
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));
192
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. */
195
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);
199
200 /* Re-apply the sign if needed. */
201 if (mpq_sgn (m_val) < 0)
202 mpz_neg (quotient.m_val, quotient.m_val);
203
204 return quotient;
205 }
206
207 /* See gmp-utils.h. */
208
209 void
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)
213 {
214 gdb_mpz vz;
215 vz.read (buf, byte_order, unsigned_p);
216
217 mpq_set_z (m_val, vz.m_val);
218 mpq_mul (m_val, m_val, scaling_factor.m_val);
219 }
220
221 /* See gmp-utils.h. */
222
223 void
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
227 {
228 gdb_mpq unscaled (m_val);
229
230 mpq_div (unscaled.m_val, unscaled.m_val, scaling_factor.m_val);
231
232 gdb_mpz unscaled_z = unscaled.get_rounded ();
233 unscaled_z.write (buf, byte_order, unsigned_p);
234 }
235
236 /* A wrapper around xrealloc that we can then register with GMP
237 as the "realloc" function. */
238
239 static void *
240 xrealloc_for_gmp (void *ptr, size_t old_size, size_t new_size)
241 {
242 return xrealloc (ptr, new_size);
243 }
244
245 /* A wrapper around xfree that we can then register with GMP
246 as the "free" function. */
247
248 static void
249 xfree_for_gmp (void *ptr, size_t size)
250 {
251 xfree (ptr);
252 }
253
254 void _initialize_gmp_utils ();
255
256 void
257 _initialize_gmp_utils ()
258 {
259 /* Tell GMP to use GDB's memory management routines. */
260 mp_set_memory_functions (xmalloc, xrealloc_for_gmp, xfree_for_gmp);
261 }