]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blob - gdb/gmp-utils.c
gmp-utils: New API to simply use of GMP's integer/rational/float objects
[thirdparty/binutils-gdb.git] / gdb / gmp-utils.c
1 /* Copyright (C) 2019-2020 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 "gmp-utils.h"
19
20 /* See gmp-utils.h. */
21
22 gdb::unique_xmalloc_ptr<char>
23 gmp_string_asprintf (const char *fmt, ...)
24 {
25 va_list vp;
26 char *buf;
27
28 va_start (vp, fmt);
29 gmp_vasprintf (&buf, fmt, vp);
30 va_end (vp);
31
32 return gdb::unique_xmalloc_ptr<char> (buf);
33 }
34
35 /* See gmp-utils.h. */
36
37 void
38 gdb_mpz::read (const gdb_byte *buf, int len, enum bfd_endian byte_order,
39 bool unsigned_p)
40 {
41 mpz_import (val, 1 /* count */, -1 /* order */, len /* size */,
42 byte_order == BFD_ENDIAN_BIG ? 1 : -1 /* endian */,
43 0 /* nails */, buf /* op */);
44
45 if (!unsigned_p)
46 {
47 /* The value was imported as if it was a positive value,
48 as mpz_import does not handle signs. If the original value
49 was in fact negative, we need to adjust VAL accordingly. */
50 gdb_mpz max;
51
52 mpz_ui_pow_ui (max.val, 2, len * TARGET_CHAR_BIT - 1);
53 if (mpz_cmp (val, max.val) >= 0)
54 mpz_submul_ui (val, max.val, 2);
55 }
56 }
57
58 /* See gmp-utils.h. */
59
60 void
61 gdb_mpz::write (gdb_byte *buf, int len, enum bfd_endian byte_order,
62 bool unsigned_p) const
63 {
64 gdb_mpz exported_val (val);
65
66 if (mpz_cmp_ui (val, 0) < 0)
67 {
68 /* mpz_export does not handle signed values, so create a positive
69 value whose bit representation as an unsigned of the same length
70 would be the same as our negative value. */
71 gdb_mpz neg_offset;
72
73 mpz_ui_pow_ui (neg_offset.val, 2, len * TARGET_CHAR_BIT);
74 mpz_add (exported_val.val, exported_val.val, neg_offset.val);
75 }
76
77 /* Start by clearing the buffer, as mpz_export only writes as many
78 bytes as it needs (including none, if the value to export is zero. */
79 memset (buf, 0, len);
80 mpz_export (buf, NULL /* count */, -1 /* order */, len /* size */,
81 byte_order == BFD_ENDIAN_BIG ? 1 : -1 /* endian */,
82 0 /* nails */, exported_val.val);
83 }
84
85 /* See gmp-utils.h. */
86
87 gdb_mpz
88 gdb_mpq::get_rounded () const
89 {
90 /* Work with a positive number so as to make the "floor" rounding
91 always round towards zero. */
92
93 gdb_mpq abs_val (val);
94 mpq_abs (abs_val.val, abs_val.val);
95
96 /* Convert our rational number into a quotient and remainder,
97 with "floor" rounding, which in our case means rounding
98 towards zero. */
99
100 gdb_mpz quotient, remainder;
101 mpz_fdiv_qr (quotient.val, remainder.val,
102 mpq_numref (abs_val.val), mpq_denref (abs_val.val));
103
104 /* Multiply the remainder by 2, and see if it is greater or equal
105 to abs_val's denominator. If yes, round to the next integer. */
106
107 mpz_mul_ui (remainder.val, remainder.val, 2);
108 if (mpz_cmp (remainder.val, mpq_denref (abs_val.val)) >= 0)
109 mpz_add_ui (quotient.val, quotient.val, 1);
110
111 /* Re-apply the sign if needed. */
112 if (mpq_sgn (val) < 0)
113 mpz_neg (quotient.val, quotient.val);
114
115 return quotient;
116 }
117
118 /* See gmp-utils.h. */
119
120 void
121 gdb_mpq::read_fixed_point (const gdb_byte *buf, int len,
122 enum bfd_endian byte_order, bool unsigned_p,
123 const gdb_mpq &scaling_factor)
124 {
125 gdb_mpz vz;
126 vz.read (buf, len, byte_order, unsigned_p);
127
128 mpq_set_z (val, vz.val);
129 mpq_mul (val, val, scaling_factor.val);
130 }
131
132 /* See gmp-utils.h. */
133
134 void
135 gdb_mpq::write_fixed_point (gdb_byte *buf, int len,
136 enum bfd_endian byte_order, bool unsigned_p,
137 const gdb_mpq &scaling_factor) const
138 {
139 gdb_mpq unscaled (val);
140
141 mpq_div (unscaled.val, unscaled.val, scaling_factor.val);
142
143 gdb_mpz unscaled_z = unscaled.get_rounded ();
144 unscaled_z.write (buf, len, byte_order, unsigned_p);
145 }
146
147 /* A wrapper around xrealloc that we can then register with GMP
148 as the "realloc" function. */
149
150 static void *
151 xrealloc_for_gmp (void *ptr, size_t old_size, size_t new_size)
152 {
153 return xrealloc (ptr, new_size);
154 }
155
156 /* A wrapper around xfree that we can then register with GMP
157 as the "free" function. */
158
159 static void
160 xfree_for_gmp (void *ptr, size_t size)
161 {
162 xfree (ptr);
163 }
164
165 void _initialize_gmp_utils ();
166
167 void
168 _initialize_gmp_utils ()
169 {
170 /* Tell GMP to use GDB's memory management routines. */
171 mp_set_memory_functions (xmalloc, xrealloc_for_gmp, xfree_for_gmp);
172 }