diff options
author | Joel Brobecker <brobecker@adacore.com> | 2020-11-15 03:09:44 -0500 |
---|---|---|
committer | Joel Brobecker <brobecker@adacore.com> | 2020-11-15 03:09:44 -0500 |
commit | b34c74ab9a6b8dc0ace3d0cc67bf62de8a74ea00 (patch) | |
tree | cebf5c4bc8bee8820f497d92258416be8e997564 /gdb/gmp-utils.c | |
parent | 1b4ac058f7daeb9bac9ab0e63a7e73535208dfef (diff) | |
download | gdb-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.c | 172 |
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); +} |