123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243 |
- /* Copyright (C) 2019-2022 Free Software Foundation, Inc.
- This file is part of GDB.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
- #include "gmp-utils.h"
- /* See gmp-utils.h. */
- std::string
- gmp_string_printf (const char *fmt, ...)
- {
- va_list vp;
- va_start (vp, fmt);
- int size = gmp_vsnprintf (NULL, 0, fmt, vp);
- va_end (vp);
- std::string str (size, '\0');
- /* C++11 and later guarantee std::string uses contiguous memory and
- always includes the terminating '\0'. */
- va_start (vp, fmt);
- gmp_vsprintf (&str[0], fmt, vp);
- va_end (vp);
- return str;
- }
- /* See gmp-utils.h. */
- void
- gdb_mpz::read (gdb::array_view<const gdb_byte> buf, enum bfd_endian byte_order,
- bool unsigned_p)
- {
- mpz_import (val, 1 /* count */, -1 /* order */, buf.size () /* size */,
- byte_order == BFD_ENDIAN_BIG ? 1 : -1 /* endian */,
- 0 /* nails */, buf.data () /* op */);
- if (!unsigned_p)
- {
- /* The value was imported as if it was a positive value,
- as mpz_import does not handle signs. If the original value
- was in fact negative, we need to adjust VAL accordingly. */
- gdb_mpz max;
- mpz_ui_pow_ui (max.val, 2, buf.size () * HOST_CHAR_BIT - 1);
- if (mpz_cmp (val, max.val) >= 0)
- mpz_submul_ui (val, max.val, 2);
- }
- }
- /* See gmp-utils.h. */
- void
- gdb_mpz::write (gdb::array_view<gdb_byte> buf, enum bfd_endian byte_order,
- bool unsigned_p) const
- {
- this->safe_export
- (buf, byte_order == BFD_ENDIAN_BIG ? 1 : -1 /* endian */, unsigned_p);
- }
- /* See gmp-utils.h. */
- void
- gdb_mpz::safe_export (gdb::array_view<gdb_byte> buf,
- int endian, bool unsigned_p) const
- {
- gdb_assert (buf.size () > 0);
- if (mpz_sgn (val) == 0)
- {
- /* Our value is zero, so no need to call mpz_export to do the work,
- especially since mpz_export's documentation explicitly says
- that the function is a noop in this case. Just write zero to
- BUF ourselves. */
- memset (buf.data (), 0, buf.size ());
- return;
- }
- /* Determine the maximum range of values that our buffer can hold,
- and verify that VAL is within that range. */
- gdb_mpz lo, hi;
- const size_t max_usable_bits = buf.size () * HOST_CHAR_BIT;
- if (unsigned_p)
- {
- lo = 0;
- mpz_ui_pow_ui (hi.val, 2, max_usable_bits);
- mpz_sub_ui (hi.val, hi.val, 1);
- }
- else
- {
- mpz_ui_pow_ui (lo.val, 2, max_usable_bits - 1);
- mpz_neg (lo.val, lo.val);
- mpz_ui_pow_ui (hi.val, 2, max_usable_bits - 1);
- mpz_sub_ui (hi.val, hi.val, 1);
- }
- if (mpz_cmp (val, lo.val) < 0 || mpz_cmp (val, hi.val) > 0)
- error (_("Cannot export value %s as %zu-bits %s integer"
- " (must be between %s and %s)"),
- this->str ().c_str (),
- max_usable_bits,
- unsigned_p ? _("unsigned") : _("signed"),
- lo.str ().c_str (),
- hi.str ().c_str ());
- gdb_mpz exported_val (val);
- if (mpz_cmp_ui (exported_val.val, 0) < 0)
- {
- /* mpz_export does not handle signed values, so create a positive
- value whose bit representation as an unsigned of the same length
- would be the same as our negative value. */
- gdb_mpz neg_offset;
- mpz_ui_pow_ui (neg_offset.val, 2, buf.size () * HOST_CHAR_BIT);
- mpz_add (exported_val.val, exported_val.val, neg_offset.val);
- }
- /* Do the export into a buffer allocated by GMP itself; that way,
- we can detect cases where BUF is not large enough to export
- our value, and thus avoid a buffer overlow. Normally, this should
- never happen, since we verified earlier that the buffer is large
- enough to accomodate our value, but doing this allows us to be
- extra safe with the export.
- After verification that the export behaved as expected, we will
- copy the data over to BUF. */
- size_t word_countp;
- gdb::unique_xmalloc_ptr<void> exported
- (mpz_export (NULL, &word_countp, -1 /* order */, buf.size () /* size */,
- endian, 0 /* nails */, exported_val.val));
- gdb_assert (word_countp == 1);
- memcpy (buf.data (), exported.get (), buf.size ());
- }
- /* See gmp-utils.h. */
- gdb_mpz
- gdb_mpq::get_rounded () const
- {
- /* Work with a positive number so as to make the "floor" rounding
- always round towards zero. */
- gdb_mpq abs_val (val);
- mpq_abs (abs_val.val, abs_val.val);
- /* Convert our rational number into a quotient and remainder,
- with "floor" rounding, which in our case means rounding
- towards zero. */
- gdb_mpz quotient, remainder;
- mpz_fdiv_qr (quotient.val, remainder.val,
- mpq_numref (abs_val.val), mpq_denref (abs_val.val));
- /* Multiply the remainder by 2, and see if it is greater or equal
- to abs_val's denominator. If yes, round to the next integer. */
- mpz_mul_ui (remainder.val, remainder.val, 2);
- if (mpz_cmp (remainder.val, mpq_denref (abs_val.val)) >= 0)
- mpz_add_ui (quotient.val, quotient.val, 1);
- /* Re-apply the sign if needed. */
- if (mpq_sgn (val) < 0)
- mpz_neg (quotient.val, quotient.val);
- return quotient;
- }
- /* See gmp-utils.h. */
- void
- gdb_mpq::read_fixed_point (gdb::array_view<const gdb_byte> buf,
- enum bfd_endian byte_order, bool unsigned_p,
- const gdb_mpq &scaling_factor)
- {
- gdb_mpz vz;
- vz.read (buf, byte_order, unsigned_p);
- mpq_set_z (val, vz.val);
- mpq_mul (val, val, scaling_factor.val);
- }
- /* See gmp-utils.h. */
- void
- gdb_mpq::write_fixed_point (gdb::array_view<gdb_byte> buf,
- enum bfd_endian byte_order, bool unsigned_p,
- const gdb_mpq &scaling_factor) const
- {
- gdb_mpq unscaled (val);
- mpq_div (unscaled.val, unscaled.val, scaling_factor.val);
- gdb_mpz unscaled_z = unscaled.get_rounded ();
- unscaled_z.write (buf, byte_order, unsigned_p);
- }
- /* A wrapper around xrealloc that we can then register with GMP
- as the "realloc" function. */
- static void *
- xrealloc_for_gmp (void *ptr, size_t old_size, size_t new_size)
- {
- return xrealloc (ptr, new_size);
- }
- /* A wrapper around xfree that we can then register with GMP
- as the "free" function. */
- static void
- xfree_for_gmp (void *ptr, size_t size)
- {
- xfree (ptr);
- }
- void _initialize_gmp_utils ();
- void
- _initialize_gmp_utils ()
- {
- /* Tell GMP to use GDB's memory management routines. */
- mp_set_memory_functions (xmalloc, xrealloc_for_gmp, xfree_for_gmp);
- }
|