/* Copyright (C) 2019-2020 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 . */ #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 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 () * TARGET_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 buf, enum bfd_endian byte_order, bool unsigned_p) const { gdb_mpz exported_val (val); if (mpz_cmp_ui (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 () * TARGET_CHAR_BIT); mpz_add (exported_val.val, exported_val.val, neg_offset.val); } /* Start by clearing the buffer, as mpz_export only writes as many bytes as it needs (including none, if the value to export is zero. */ memset (buf.data (), 0, buf.size ()); mpz_export (buf.data (), NULL /* count */, -1 /* order */, buf.size () /* size */, byte_order == BFD_ENDIAN_BIG ? 1 : -1 /* endian */, 0 /* nails */, exported_val.val); } /* 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 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 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); }