aboutsummaryrefslogtreecommitdiffstats
path: root/src/bignum.c
diff options
context:
space:
mode:
authorPaul Eggert2019-11-13 12:10:31 -0800
committerPaul Eggert2019-11-13 13:10:09 -0800
commit02e637ecca3b1419d2a6c433eca72c5728c65051 (patch)
treef3a213c730c7893513a69b5f9e2e69a429f837da /src/bignum.c
parentff10e9517d98aa606e007d3aa4d5aef1c423ab77 (diff)
downloademacs-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.c78
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. */
282enum { 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. */
289enum
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
303static int
304emacs_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
317void
318emacs_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
325void
326emacs_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
339void
340emacs_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. */