aboutsummaryrefslogtreecommitdiff
path: root/gdb/gmp-utils.c
diff options
context:
space:
mode:
authorJoel Brobecker <brobecker@adacore.com>2020-11-15 03:09:44 -0500
committerJoel Brobecker <brobecker@adacore.com>2020-11-15 03:09:44 -0500
commitb34c74ab9a6b8dc0ace3d0cc67bf62de8a74ea00 (patch)
treecebf5c4bc8bee8820f497d92258416be8e997564 /gdb/gmp-utils.c
parent1b4ac058f7daeb9bac9ab0e63a7e73535208dfef (diff)
downloadgdb-b34c74ab9a6b8dc0ace3d0cc67bf62de8a74ea00.zip
gdb-b34c74ab9a6b8dc0ace3d0cc67bf62de8a74ea00.tar.gz
gdb-b34c74ab9a6b8dc0ace3d0cc67bf62de8a74ea00.tar.bz2
gmp-utils: New API to simply use of GMP's integer/rational/float objects
This API was motivated by a number of reasons: - GMP's API does not handle "long long" and "unsigned long long", so using LONGEST and ULONGEST is not straightforward; - Automate the need to initialize GMP objects before use, and clear them when no longer used. However, this API grew also to help with similar matter such as formatting to a string, and also reading/writing fixed-point values from byte buffers. Dedicated unit testing is also added. gdb/ChangeLog: * gmp-utils.h, gmp-utils.h: New file. * unittests/gmp-utils-selftests.c: New file. * Makefile.in (SUBDIR_UNITTESTS_SRCS): Add unittests/gmp-utils-selftests.c. (COMMON_SFILES) Add gmp-utils.c. (HFILES_NO_SRCDIR): Add gmp-utils.h.
Diffstat (limited to 'gdb/gmp-utils.c')
-rw-r--r--gdb/gmp-utils.c172
1 files changed, 172 insertions, 0 deletions
diff --git a/gdb/gmp-utils.c b/gdb/gmp-utils.c
new file mode 100644
index 0000000..db92e57
--- /dev/null
+++ b/gdb/gmp-utils.c
@@ -0,0 +1,172 @@
+/* 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 <http://www.gnu.org/licenses/>. */
+
+#include "gmp-utils.h"
+
+/* See gmp-utils.h. */
+
+gdb::unique_xmalloc_ptr<char>
+gmp_string_asprintf (const char *fmt, ...)
+{
+ va_list vp;
+ char *buf;
+
+ va_start (vp, fmt);
+ gmp_vasprintf (&buf, fmt, vp);
+ va_end (vp);
+
+ return gdb::unique_xmalloc_ptr<char> (buf);
+}
+
+/* See gmp-utils.h. */
+
+void
+gdb_mpz::read (const gdb_byte *buf, int len, enum bfd_endian byte_order,
+ bool unsigned_p)
+{
+ mpz_import (val, 1 /* count */, -1 /* order */, len /* size */,
+ byte_order == BFD_ENDIAN_BIG ? 1 : -1 /* endian */,
+ 0 /* nails */, buf /* 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, len * 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_byte *buf, int len, 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, len * 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, 0, len);
+ mpz_export (buf, NULL /* count */, -1 /* order */, len /* 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 (const gdb_byte *buf, int len,
+ enum bfd_endian byte_order, bool unsigned_p,
+ const gdb_mpq &scaling_factor)
+{
+ gdb_mpz vz;
+ vz.read (buf, len, 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_byte *buf, int len,
+ 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, len, 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);
+}