diff options
| author | Paul Eggert | 2019-11-13 12:10:31 -0800 |
|---|---|---|
| committer | Paul Eggert | 2019-11-13 13:10:09 -0800 |
| commit | 02e637ecca3b1419d2a6c433eca72c5728c65051 (patch) | |
| tree | f3a213c730c7893513a69b5f9e2e69a429f837da /src/bignum.c | |
| parent | ff10e9517d98aa606e007d3aa4d5aef1c423ab77 (diff) | |
| download | emacs-02e637ecca3b1419d2a6c433eca72c5728c65051.tar.gz emacs-02e637ecca3b1419d2a6c433eca72c5728c65051.zip | |
Refactor bignum multiplication, exponentiation
This doesn’t alter behavior, and simplifies the next commit.
* src/bignum.c (GMP_NLIMBS_MAX, NLIMBS_LIMIT, emacs_mpz_size)
(emacs_mpz_mul, emacs_mpz_mul_2exp, emacs_mpz_pow_ui): Move here ...
* src/data.c: ... from here.
Diffstat (limited to 'src/bignum.c')
| -rw-r--r-- | src/bignum.c | 78 |
1 files changed, 78 insertions, 0 deletions
diff --git a/src/bignum.c b/src/bignum.c index 167b73eee02..c31cf3d59a4 100644 --- a/src/bignum.c +++ b/src/bignum.c | |||
| @@ -273,6 +273,84 @@ bignum_to_uintmax (Lisp_Object x) | |||
| 273 | return mpz_to_uintmax (*xbignum_val (x), &i) ? i : 0; | 273 | return mpz_to_uintmax (*xbignum_val (x), &i) ? i : 0; |
| 274 | } | 274 | } |
| 275 | 275 | ||
| 276 | |||
| 277 | /* Multiply and exponentiate mpz_t values without aborting due to size | ||
| 278 | limits. */ | ||
| 279 | |||
| 280 | /* GMP tests for this value and aborts (!) if it is exceeded. | ||
| 281 | This is as of GMP 6.1.2 (2016); perhaps future versions will differ. */ | ||
| 282 | enum { GMP_NLIMBS_MAX = min (INT_MAX, ULONG_MAX / GMP_NUMB_BITS) }; | ||
| 283 | |||
| 284 | /* An upper bound on limb counts, needed to prevent libgmp and/or | ||
| 285 | Emacs from aborting or otherwise misbehaving. This bound applies | ||
| 286 | to estimates of mpz_t sizes before the mpz_t objects are created, | ||
| 287 | as opposed to integer-width which operates on mpz_t values after | ||
| 288 | creation and before conversion to Lisp bignums. */ | ||
| 289 | enum | ||
| 290 | { | ||
| 291 | NLIMBS_LIMIT = min (min (/* libgmp needs to store limb counts. */ | ||
| 292 | GMP_NLIMBS_MAX, | ||
| 293 | |||
| 294 | /* Size calculations need to work. */ | ||
| 295 | min (PTRDIFF_MAX, SIZE_MAX) / sizeof (mp_limb_t)), | ||
| 296 | |||
| 297 | /* Emacs puts bit counts into fixnums. */ | ||
| 298 | MOST_POSITIVE_FIXNUM / GMP_NUMB_BITS) | ||
| 299 | }; | ||
| 300 | |||
| 301 | /* Like mpz_size, but tell the compiler the result is a nonnegative int. */ | ||
| 302 | |||
| 303 | static int | ||
| 304 | emacs_mpz_size (mpz_t const op) | ||
| 305 | { | ||
| 306 | mp_size_t size = mpz_size (op); | ||
| 307 | eassume (0 <= size && size <= INT_MAX); | ||
| 308 | return size; | ||
| 309 | } | ||
| 310 | |||
| 311 | /* Wrappers to work around GMP limitations. As of GMP 6.1.2 (2016), | ||
| 312 | the library code aborts when a number is too large. These wrappers | ||
| 313 | avoid the problem for functions that can return numbers much larger | ||
| 314 | than their arguments. For slowly-growing numbers, the integer | ||
| 315 | width checks in bignum.c should suffice. */ | ||
| 316 | |||
| 317 | void | ||
| 318 | emacs_mpz_mul (mpz_t rop, mpz_t const op1, mpz_t const op2) | ||
| 319 | { | ||
| 320 | if (NLIMBS_LIMIT - emacs_mpz_size (op1) < emacs_mpz_size (op2)) | ||
| 321 | overflow_error (); | ||
| 322 | mpz_mul (rop, op1, op2); | ||
| 323 | } | ||
| 324 | |||
| 325 | void | ||
| 326 | emacs_mpz_mul_2exp (mpz_t rop, mpz_t const op1, EMACS_INT op2) | ||
| 327 | { | ||
| 328 | /* Fudge factor derived from GMP 6.1.2, to avoid an abort in | ||
| 329 | mpz_mul_2exp (look for the '+ 1' in its source code). */ | ||
| 330 | enum { mul_2exp_extra_limbs = 1 }; | ||
| 331 | enum { lim = min (NLIMBS_LIMIT, GMP_NLIMBS_MAX - mul_2exp_extra_limbs) }; | ||
| 332 | |||
| 333 | EMACS_INT op2limbs = op2 / GMP_NUMB_BITS; | ||
| 334 | if (lim - emacs_mpz_size (op1) < op2limbs) | ||
| 335 | overflow_error (); | ||
| 336 | mpz_mul_2exp (rop, op1, op2); | ||
| 337 | } | ||
| 338 | |||
| 339 | void | ||
| 340 | emacs_mpz_pow_ui (mpz_t rop, mpz_t const base, unsigned long exp) | ||
| 341 | { | ||
| 342 | /* This fudge factor is derived from GMP 6.1.2, to avoid an abort in | ||
| 343 | mpz_n_pow_ui (look for the '5' in its source code). */ | ||
| 344 | enum { pow_ui_extra_limbs = 5 }; | ||
| 345 | enum { lim = min (NLIMBS_LIMIT, GMP_NLIMBS_MAX - pow_ui_extra_limbs) }; | ||
| 346 | |||
| 347 | int nbase = emacs_mpz_size (base), n; | ||
| 348 | if (INT_MULTIPLY_WRAPV (nbase, exp, &n) || lim < n) | ||
| 349 | overflow_error (); | ||
| 350 | mpz_pow_ui (rop, base, exp); | ||
| 351 | } | ||
| 352 | |||
| 353 | |||
| 276 | /* Yield an upper bound on the buffer size needed to contain a C | 354 | /* Yield an upper bound on the buffer size needed to contain a C |
| 277 | string representing the NUM in base BASE. This includes any | 355 | string representing the NUM in base BASE. This includes any |
| 278 | preceding '-' and the terminating NUL. */ | 356 | preceding '-' and the terminating NUL. */ |