>From 20d92047cf82318c093ff03dbb07a0631a2046aa Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Mon, 1 Oct 2018 15:31:53 -0700 Subject: [PATCH 3/4] Export converting mpz to [u]intmax This refactoring will help improve timestamp handling later. * src/bignum.c (mpz_set_uintmax): Move to bignum.h, and make inline. (mpz_set_uintmax_slow): Now extern. (mpz_to_intmax, mpz_to_uintmax): New functions, with implementation taken from the old bignum_to_intmax and bignum_to_uintmax. (bignum_to_intmax, bignum_to_uintmax): Use them. --- src/bignum.c | 91 +++++++++++++++++++++++++++++----------------------- src/bignum.h | 11 +++++++ 2 files changed, 61 insertions(+), 41 deletions(-) diff --git a/src/bignum.c b/src/bignum.c index 1e78d981b7..5d8ab670f2 100644 --- a/src/bignum.c +++ b/src/bignum.c @@ -101,18 +101,6 @@ make_bignum (void) return make_bignum_bits (mpz_sizeinbase (mpz[0], 2)); } -static void mpz_set_uintmax_slow (mpz_t, uintmax_t); - -/* Set RESULT to V. */ -static void -mpz_set_uintmax (mpz_t result, uintmax_t v) -{ - if (v <= ULONG_MAX) - mpz_set_ui (result, v); - else - mpz_set_uintmax_slow (result, v); -} - /* Return a Lisp integer equal to N, which must not be in fixnum range. */ Lisp_Object make_bigint (intmax_t n) @@ -183,7 +171,7 @@ mpz_set_intmax_slow (mpz_t result, intmax_t v) mpz_limbs_finish (result, negative ? -n : n); } -static void +void mpz_set_uintmax_slow (mpz_t result, uintmax_t v) { int maxlimbs = (UINTMAX_WIDTH + GMP_NUMB_BITS - 1) / GMP_NUMB_BITS; @@ -200,13 +188,13 @@ mpz_set_uintmax_slow (mpz_t result, uintmax_t v) mpz_limbs_finish (result, n); } -/* Return the value of the bignum X if it fits, 0 otherwise. - A bignum cannot be zero, so 0 indicates failure reliably. */ -intmax_t -bignum_to_intmax (Lisp_Object x) +/* If Z fits into *PI, store its value there and return true. + Return false otherwise. */ +bool +mpz_to_intmax (mpz_t const z, intmax_t *pi) { - ptrdiff_t bits = mpz_sizeinbase (XBIGNUM (x)->value, 2); - bool negative = mpz_sgn (XBIGNUM (x)->value) < 0; + ptrdiff_t bits = mpz_sizeinbase (z, 2); + bool negative = mpz_sgn (z) < 0; if (bits < INTMAX_WIDTH) { @@ -215,39 +203,60 @@ bignum_to_intmax (Lisp_Object x) do { - intmax_t limb = mpz_getlimbn (XBIGNUM (x)->value, i++); + intmax_t limb = mpz_getlimbn (z, i++); v += limb << shift; shift += GMP_NUMB_BITS; } while (shift < bits); - return negative ? -v : v; + *pi = negative ? -v : v; + return true; + } + if (bits == INTMAX_WIDTH && INTMAX_MIN < -INTMAX_MAX && negative + && mpz_scan1 (z, 0) == INTMAX_WIDTH - 1) + { + *pi = INTMAX_MIN; + return true; } - return ((bits == INTMAX_WIDTH && INTMAX_MIN < -INTMAX_MAX && negative - && mpz_scan1 (XBIGNUM (x)->value, 0) == INTMAX_WIDTH - 1) - ? INTMAX_MIN : 0); + return false; } -uintmax_t -bignum_to_uintmax (Lisp_Object x) +bool +mpz_to_uintmax (mpz_t const z, uintmax_t *pi) { + if (mpz_sgn (z) < 0) + return false; + ptrdiff_t bits = mpz_sizeinbase (z, 2); + if (UINTMAX_WIDTH < bits) + return false; + uintmax_t v = 0; - if (0 <= mpz_sgn (XBIGNUM (x)->value)) + int i = 0, shift = 0; + + do { - ptrdiff_t bits = mpz_sizeinbase (XBIGNUM (x)->value, 2); - if (bits <= UINTMAX_WIDTH) - { - int i = 0, shift = 0; - - do - { - uintmax_t limb = mpz_getlimbn (XBIGNUM (x)->value, i++); - v += limb << shift; - shift += GMP_NUMB_BITS; - } - while (shift < bits); - } + uintmax_t limb = mpz_getlimbn (z, i++); + v += limb << shift; + shift += GMP_NUMB_BITS; } - return v; + while (shift < bits); + + *pi = v; + return true; +} + +/* Return the value of the bignum X if it fits, 0 otherwise. + A bignum cannot be zero, so 0 indicates failure reliably. */ +intmax_t +bignum_to_intmax (Lisp_Object x) +{ + intmax_t i; + return mpz_to_intmax (XBIGNUM (x)->value, &i) ? i : 0; +} +uintmax_t +bignum_to_uintmax (Lisp_Object x) +{ + uintmax_t i; + return mpz_to_uintmax (XBIGNUM (x)->value, &i) ? i : 0; } /* Yield an upper bound on the buffer size needed to contain a C diff --git a/src/bignum.h b/src/bignum.h index e9cd5c0763..fd035e6e14 100644 --- a/src/bignum.h +++ b/src/bignum.h @@ -45,7 +45,10 @@ extern mpz_t mpz[4]; extern void init_bignum (void); extern Lisp_Object make_integer_mpz (void); +extern bool mpz_to_intmax (mpz_t const, intmax_t *) ARG_NONNULL ((1, 2)); +extern bool mpz_to_uintmax (mpz_t const, uintmax_t *) ARG_NONNULL ((1, 2)); extern void mpz_set_intmax_slow (mpz_t, intmax_t) ARG_NONNULL ((1)); +extern void mpz_set_uintmax_slow (mpz_t, uintmax_t) ARG_NONNULL ((1)); extern double mpz_get_d_rounded (mpz_t const); INLINE_HEADER_BEGIN @@ -68,6 +71,14 @@ mpz_set_intmax (mpz_t result, intmax_t v) else mpz_set_intmax_slow (result, v); } +INLINE void ARG_NONNULL ((1)) +mpz_set_uintmax (mpz_t result, uintmax_t v) +{ + if (v <= ULONG_MAX) + mpz_set_ui (result, v); + else + mpz_set_uintmax_slow (result, v); +} /* Return a pointer to an mpz_t that is equal to the Lisp integer I. If I is a bignum this returns a pointer to I's representation; -- 2.17.1