aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPaul Eggert2018-08-31 00:25:07 -0700
committerPaul Eggert2018-08-31 00:28:58 -0700
commitdb2fed3bdfb351c3283e481829ce687931d27a3d (patch)
tree4f2674ec4f4fe450fd483132b9ddcca48f9eaf81 /src
parenta451c6ec12b7b024f347364becb10c49807513ed (diff)
downloademacs-db2fed3bdfb351c3283e481829ce687931d27a3d.tar.gz
emacs-db2fed3bdfb351c3283e481829ce687931d27a3d.zip
Several fixes for formatting bignums
* src/bignum.c: Include stdlib.h, for abs. (bignum_bufsize, bignum_to_c_string): New functions. * src/bignum.c (bignum_to_string): * src/print.c (print_vectorlike): Use them. * src/editfns.c (styled_format): Instead of having a separate buffer for sprintf (which does not work for bignums), just append to the main buffer. When formatting bignums, add support for the standard integer flags -, #, 0, + and space. Fix some comments. Capitalize properly when formatting bignums with %X. Use functions like c_isdigit rather than reinventing the wheel. Simplify computation of excess precision. * src/print.c: Do not include bignum.h; no longer needed. (print_vectorlike): Avoid recalculating string length. * test/src/editfns-tests.el (format-bignum): Test some of the above fixes.
Diffstat (limited to 'src')
-rw-r--r--src/bignum.c37
-rw-r--r--src/editfns.c359
-rw-r--r--src/lisp.h5
-rw-r--r--src/print.c9
4 files changed, 233 insertions, 177 deletions
diff --git a/src/bignum.c b/src/bignum.c
index 5dbfdb9319a..b18ceccb59d 100644
--- a/src/bignum.c
+++ b/src/bignum.c
@@ -23,6 +23,8 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
23 23
24#include "lisp.h" 24#include "lisp.h"
25 25
26#include <stdlib.h>
27
26/* Return the value of the Lisp bignum N, as a double. */ 28/* Return the value of the Lisp bignum N, as a double. */
27double 29double
28bignum_to_double (Lisp_Object n) 30bignum_to_double (Lisp_Object n)
@@ -223,18 +225,39 @@ bignum_to_uintmax (Lisp_Object x)
223 return v; 225 return v;
224} 226}
225 227
226/* Convert NUM to a base-BASE Lisp string. */ 228/* Yield an upper bound on the buffer size needed to contain a C
229 string representing the bignum NUM in base BASE. This includes any
230 preceding '-' and the terminating null. */
231ptrdiff_t
232bignum_bufsize (Lisp_Object num, int base)
233{
234 return mpz_sizeinbase (XBIGNUM (num)->value, base) + 2;
235}
236
237/* Store into BUF (of size SIZE) the value of NUM as a base-BASE string.
238 If BASE is negative, use upper-case digits in base -BASE.
239 Return the string's length.
240 SIZE must equal bignum_bufsize (NUM, abs (BASE)). */
241ptrdiff_t
242bignum_to_c_string (char *buf, ptrdiff_t size, Lisp_Object num, int base)
243{
244 eassert (bignum_bufsize (num, abs (base)) == size);
245 mpz_get_str (buf, base, XBIGNUM (num)->value);
246 ptrdiff_t n = size - 2;
247 return !buf[n - 1] ? n - 1 : n + !!buf[n];
248}
249
250/* Convert NUM to a base-BASE Lisp string.
251 If BASE is negative, use upper-case digits in base -BASE. */
227 252
228Lisp_Object 253Lisp_Object
229bignum_to_string (Lisp_Object num, int base) 254bignum_to_string (Lisp_Object num, int base)
230{ 255{
231 ptrdiff_t n = mpz_sizeinbase (XBIGNUM (num)->value, base) - 1; 256 ptrdiff_t size = bignum_bufsize (num, abs (base));
232 USE_SAFE_ALLOCA; 257 USE_SAFE_ALLOCA;
233 char *str = SAFE_ALLOCA (n + 3); 258 char *str = SAFE_ALLOCA (size);
234 mpz_get_str (str, base, XBIGNUM (num)->value); 259 ptrdiff_t len = bignum_to_c_string (str, size, num, base);
235 while (str[n]) 260 Lisp_Object result = make_unibyte_string (str, len);
236 n++;
237 Lisp_Object result = make_unibyte_string (str, n);
238 SAFE_FREE (); 261 SAFE_FREE ();
239 return result; 262 return result;
240} 263}
diff --git a/src/editfns.c b/src/editfns.c
index b4c597feda1..3b1c21a1781 100644
--- a/src/editfns.c
+++ b/src/editfns.c
@@ -4232,8 +4232,26 @@ usage: (format-message STRING &rest OBJECTS) */)
4232static Lisp_Object 4232static Lisp_Object
4233styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) 4233styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4234{ 4234{
4235 enum
4236 {
4237 /* Maximum precision for a %f conversion such that the trailing
4238 output digit might be nonzero. Any precision larger than this
4239 will not yield useful information. */
4240 USEFUL_PRECISION_MAX = ((1 - LDBL_MIN_EXP)
4241 * (FLT_RADIX == 2 || FLT_RADIX == 10 ? 1
4242 : FLT_RADIX == 16 ? 4
4243 : -1)),
4244
4245 /* Maximum number of bytes (including terminating null) generated
4246 by any format, if precision is no more than USEFUL_PRECISION_MAX.
4247 On all practical hosts, %Lf is the worst case. */
4248 SPRINTF_BUFSIZE = (sizeof "-." + (LDBL_MAX_10_EXP + 1)
4249 + USEFUL_PRECISION_MAX)
4250 };
4251 verify (USEFUL_PRECISION_MAX > 0);
4252
4235 ptrdiff_t n; /* The number of the next arg to substitute. */ 4253 ptrdiff_t n; /* The number of the next arg to substitute. */
4236 char initial_buffer[4000]; 4254 char initial_buffer[1000 + SPRINTF_BUFSIZE];
4237 char *buf = initial_buffer; 4255 char *buf = initial_buffer;
4238 ptrdiff_t bufsize = sizeof initial_buffer; 4256 ptrdiff_t bufsize = sizeof initial_buffer;
4239 ptrdiff_t max_bufsize = STRING_BYTES_BOUND + 1; 4257 ptrdiff_t max_bufsize = STRING_BYTES_BOUND + 1;
@@ -4338,8 +4356,14 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4338 char const *convsrc = format; 4356 char const *convsrc = format;
4339 unsigned char format_char = *format++; 4357 unsigned char format_char = *format++;
4340 4358
4341 /* Bytes needed to represent the output of this conversion. */ 4359 /* Number of bytes to be preallocated for the next directive's
4360 output. At the end of each iteration this is at least
4361 CONVBYTES_ROOM, and is greater if the current directive
4362 output was so large that it will be retried after buffer
4363 reallocation. */
4342 ptrdiff_t convbytes = 1; 4364 ptrdiff_t convbytes = 1;
4365 enum { CONVBYTES_ROOM = SPRINTF_BUFSIZE - 1 };
4366 eassert (p <= buf + bufsize - SPRINTF_BUFSIZE);
4343 4367
4344 if (format_char == '%') 4368 if (format_char == '%')
4345 { 4369 {
@@ -4473,23 +4497,6 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4473 conversion = 's'; 4497 conversion = 's';
4474 zero_flag = false; 4498 zero_flag = false;
4475 } 4499 }
4476 else if ((conversion == 'd' || conversion == 'i'
4477 || conversion == 'o' || conversion == 'x'
4478 || conversion == 'X')
4479 && BIGNUMP (arg))
4480 {
4481 int base = 10;
4482
4483 if (conversion == 'o')
4484 base = 8;
4485 else if (conversion == 'x')
4486 base = 16;
4487 else if (conversion == 'X')
4488 base = -16;
4489
4490 arg = bignum_to_string (arg, base);
4491 conversion = 's';
4492 }
4493 4500
4494 if (SYMBOLP (arg)) 4501 if (SYMBOLP (arg))
4495 { 4502 {
@@ -4592,7 +4599,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4592 spec->intervals = arg_intervals = true; 4599 spec->intervals = arg_intervals = true;
4593 4600
4594 new_result = true; 4601 new_result = true;
4595 continue; 4602 convbytes = CONVBYTES_ROOM;
4596 } 4603 }
4597 } 4604 }
4598 else if (! (conversion == 'c' || conversion == 'd' 4605 else if (! (conversion == 'c' || conversion == 'd'
@@ -4606,28 +4613,8 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4606 error ("Format specifier doesn't match argument type"); 4613 error ("Format specifier doesn't match argument type");
4607 else 4614 else
4608 { 4615 {
4609 enum 4616 /* Length of pM (that is, of pMd without the trailing "d"). */
4610 { 4617 enum { pMlen = sizeof pMd - 2 };
4611 /* Maximum precision for a %f conversion such that the
4612 trailing output digit might be nonzero. Any precision
4613 larger than this will not yield useful information. */
4614 USEFUL_PRECISION_MAX =
4615 ((1 - LDBL_MIN_EXP)
4616 * (FLT_RADIX == 2 || FLT_RADIX == 10 ? 1
4617 : FLT_RADIX == 16 ? 4
4618 : -1)),
4619
4620 /* Maximum number of bytes generated by any format, if
4621 precision is no more than USEFUL_PRECISION_MAX.
4622 On all practical hosts, %f is the worst case. */
4623 SPRINTF_BUFSIZE =
4624 sizeof "-." + (LDBL_MAX_10_EXP + 1) + USEFUL_PRECISION_MAX,
4625
4626 /* Length of pM (that is, of pMd without the
4627 trailing "d"). */
4628 pMlen = sizeof pMd - 2
4629 };
4630 verify (USEFUL_PRECISION_MAX > 0);
4631 4618
4632 /* Avoid undefined behavior in underlying sprintf. */ 4619 /* Avoid undefined behavior in underlying sprintf. */
4633 if (conversion == 'd' || conversion == 'i') 4620 if (conversion == 'd' || conversion == 'i')
@@ -4660,18 +4647,24 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4660 if (precision_given) 4647 if (precision_given)
4661 prec = min (precision, USEFUL_PRECISION_MAX); 4648 prec = min (precision, USEFUL_PRECISION_MAX);
4662 4649
4663 /* Use sprintf to format this number into sprintf_buf. Omit 4650 /* Characters to be inserted after spaces and before
4651 leading zeros. This can occur with bignums, since
4652 string_to_bignum does only leading '-'. */
4653 char prefix[sizeof "-0x" - 1];
4654 int prefixlen = 0;
4655
4656 /* Use sprintf or bignum_to_string to format this number. Omit
4664 padding and excess precision, though, because sprintf limits 4657 padding and excess precision, though, because sprintf limits
4665 output length to INT_MAX. 4658 output length to INT_MAX and bignum_to_string doesn't
4659 do padding or precision.
4666 4660
4667 There are four types of conversion: double, unsigned 4661 Use five sprintf conversions: double, long double, unsigned
4668 char (passed as int), wide signed int, and wide 4662 char (passed as int), wide signed int, and wide
4669 unsigned int. Treat them separately because the 4663 unsigned int. Treat them separately because the
4670 sprintf ABI is sensitive to which type is passed. Be 4664 sprintf ABI is sensitive to which type is passed. Be
4671 careful about integer overflow, NaNs, infinities, and 4665 careful about integer overflow, NaNs, infinities, and
4672 conversions; for example, the min and max macros are 4666 conversions; for example, the min and max macros are
4673 not suitable here. */ 4667 not suitable here. */
4674 char sprintf_buf[SPRINTF_BUFSIZE];
4675 ptrdiff_t sprintf_bytes; 4668 ptrdiff_t sprintf_bytes;
4676 if (float_conversion) 4669 if (float_conversion)
4677 { 4670 {
@@ -4729,26 +4722,43 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4729 f[-1] = 'L'; 4722 f[-1] = 'L';
4730 *f++ = conversion; 4723 *f++ = conversion;
4731 *f = '\0'; 4724 *f = '\0';
4732 sprintf_bytes = sprintf (sprintf_buf, convspec, prec, 4725 sprintf_bytes = sprintf (p, convspec, prec, ldarg);
4733 ldarg);
4734 } 4726 }
4735 else 4727 else
4736 sprintf_bytes = sprintf (sprintf_buf, convspec, prec, 4728 sprintf_bytes = sprintf (p, convspec, prec, darg);
4737 darg);
4738 } 4729 }
4739 else if (conversion == 'c') 4730 else if (conversion == 'c')
4740 { 4731 {
4741 /* Don't use sprintf here, as it might mishandle prec. */ 4732 /* Don't use sprintf here, as it might mishandle prec. */
4742 sprintf_buf[0] = XFIXNUM (arg); 4733 p[0] = XFIXNUM (arg);
4734 p[1] = '\0';
4743 sprintf_bytes = prec != 0; 4735 sprintf_bytes = prec != 0;
4744 sprintf_buf[sprintf_bytes] = '\0'; 4736 }
4737 else if (BIGNUMP (arg))
4738 {
4739 int base = ((conversion == 'd' || conversion == 'i') ? 10
4740 : conversion == 'o' ? 8 : 16);
4741 sprintf_bytes = bignum_bufsize (arg, base);
4742 if (sprintf_bytes <= buf + bufsize - p)
4743 {
4744 int signedbase = conversion == 'X' ? -base : base;
4745 sprintf_bytes = bignum_to_c_string (p, sprintf_bytes,
4746 arg, signedbase);
4747 bool negative = p[0] == '-';
4748 prec = min (precision, sprintf_bytes - prefixlen);
4749 prefix[prefixlen] = plus_flag ? '+' : ' ';
4750 prefixlen += (plus_flag | space_flag) & !negative;
4751 prefix[prefixlen] = '0';
4752 prefix[prefixlen + 1] = conversion;
4753 prefixlen += sharp_flag && base == 16 ? 2 : 0;
4754 }
4745 } 4755 }
4746 else if (conversion == 'd' || conversion == 'i') 4756 else if (conversion == 'd' || conversion == 'i')
4747 { 4757 {
4748 if (FIXNUMP (arg)) 4758 if (FIXNUMP (arg))
4749 { 4759 {
4750 printmax_t x = XFIXNUM (arg); 4760 printmax_t x = XFIXNUM (arg);
4751 sprintf_bytes = sprintf (sprintf_buf, convspec, prec, x); 4761 sprintf_bytes = sprintf (p, convspec, prec, x);
4752 } 4762 }
4753 else 4763 else
4754 { 4764 {
@@ -4760,9 +4770,8 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4760 x = trunc (x); 4770 x = trunc (x);
4761 x = x ? x : 0; 4771 x = x ? x : 0;
4762 4772
4763 sprintf_bytes = sprintf (sprintf_buf, convspec, 0, x); 4773 sprintf_bytes = sprintf (p, convspec, 0, x);
4764 char c0 = sprintf_buf[0]; 4774 bool signedp = ! c_isdigit (p[0]);
4765 bool signedp = ! ('0' <= c0 && c0 <= '9');
4766 prec = min (precision, sprintf_bytes - signedp); 4775 prec = min (precision, sprintf_bytes - signedp);
4767 } 4776 }
4768 } 4777 }
@@ -4793,10 +4802,9 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4793 x = d; 4802 x = d;
4794 negative = false; 4803 negative = false;
4795 } 4804 }
4796 sprintf_buf[0] = negative ? '-' : plus_flag ? '+' : ' '; 4805 p[0] = negative ? '-' : plus_flag ? '+' : ' ';
4797 bool signedp = negative | plus_flag | space_flag; 4806 bool signedp = negative | plus_flag | space_flag;
4798 sprintf_bytes = sprintf (sprintf_buf + signedp, 4807 sprintf_bytes = sprintf (p + signedp, convspec, prec, x);
4799 convspec, prec, x);
4800 sprintf_bytes += signedp; 4808 sprintf_bytes += signedp;
4801 } 4809 }
4802 4810
@@ -4804,112 +4812,126 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4804 padding and excess precision. Deal with excess precision 4812 padding and excess precision. Deal with excess precision
4805 first. This happens when the format specifies ridiculously 4813 first. This happens when the format specifies ridiculously
4806 large precision, or when %d or %i formats a float that would 4814 large precision, or when %d or %i formats a float that would
4807 ordinarily need fewer digits than a specified precision. */ 4815 ordinarily need fewer digits than a specified precision,
4816 or when a bignum is formatted using an integer format
4817 with enough precision. */
4808 ptrdiff_t excess_precision 4818 ptrdiff_t excess_precision
4809 = precision_given ? precision - prec : 0; 4819 = precision_given ? precision - prec : 0;
4810 ptrdiff_t leading_zeros = 0, trailing_zeros = 0; 4820 ptrdiff_t trailing_zeros = 0;
4811 if (excess_precision) 4821 if (excess_precision != 0 && float_conversion)
4812 { 4822 {
4813 if (float_conversion) 4823 if (! c_isdigit (p[sprintf_bytes - 1])
4814 { 4824 || (conversion == 'g'
4815 if ((conversion == 'g' && ! sharp_flag) 4825 && ! (sharp_flag && strchr (p, '.'))))
4816 || ! ('0' <= sprintf_buf[sprintf_bytes - 1] 4826 excess_precision = 0;
4817 && sprintf_buf[sprintf_bytes - 1] <= '9')) 4827 trailing_zeros = excess_precision;
4818 excess_precision = 0;
4819 else
4820 {
4821 if (conversion == 'g')
4822 {
4823 char *dot = strchr (sprintf_buf, '.');
4824 if (!dot)
4825 excess_precision = 0;
4826 }
4827 }
4828 trailing_zeros = excess_precision;
4829 }
4830 else
4831 leading_zeros = excess_precision;
4832 } 4828 }
4829 ptrdiff_t leading_zeros = excess_precision - trailing_zeros;
4833 4830
4834 /* Compute the total bytes needed for this item, including 4831 /* Compute the total bytes needed for this item, including
4835 excess precision and padding. */ 4832 excess precision and padding. */
4836 ptrdiff_t numwidth; 4833 ptrdiff_t numwidth;
4837 if (INT_ADD_WRAPV (sprintf_bytes, excess_precision, &numwidth)) 4834 if (INT_ADD_WRAPV (prefixlen + sprintf_bytes, excess_precision,
4835 &numwidth))
4838 numwidth = PTRDIFF_MAX; 4836 numwidth = PTRDIFF_MAX;
4839 ptrdiff_t padding 4837 ptrdiff_t padding
4840 = numwidth < field_width ? field_width - numwidth : 0; 4838 = numwidth < field_width ? field_width - numwidth : 0;
4841 if (max_bufsize - sprintf_bytes <= excess_precision 4839 if (max_bufsize - (prefixlen + sprintf_bytes) <= excess_precision
4842 || max_bufsize - padding <= numwidth) 4840 || max_bufsize - padding <= numwidth)
4843 string_overflow (); 4841 string_overflow ();
4844 convbytes = numwidth + padding; 4842 convbytes = numwidth + padding;
4845 4843
4846 if (convbytes <= buf + bufsize - p) 4844 if (convbytes <= buf + bufsize - p)
4847 { 4845 {
4848 /* Copy the formatted item from sprintf_buf into buf, 4846 bool signedp = p[0] == '-' || p[0] == '+' || p[0] == ' ';
4849 inserting padding and excess-precision zeros. */ 4847 int beglen = (signedp
4850 4848 + ((p[signedp] == '0'
4851 char *src = sprintf_buf; 4849 && (p[signedp + 1] == 'x'
4852 char src0 = src[0]; 4850 || p[signedp + 1] == 'X'))
4853 int exponent_bytes = 0; 4851 ? 2 : 0));
4854 bool signedp = src0 == '-' || src0 == '+' || src0 == ' '; 4852 eassert (prefixlen == 0 || beglen == 0
4855 int prefix_bytes = (signedp 4853 || (beglen == 1 && p[0] == '-'
4856 + ((src[signedp] == '0' 4854 && ! (prefix[0] == '-' || prefix[0] == '+'
4857 && (src[signedp + 1] == 'x' 4855 || prefix[0] == ' ')));
4858 || src[signedp + 1] == 'X')) 4856 if (zero_flag && 0 <= char_hexdigit (p[beglen]))
4859 ? 2 : 0));
4860 if (zero_flag)
4861 { 4857 {
4862 unsigned char after_prefix = src[prefix_bytes]; 4858 leading_zeros += padding;
4863 if (0 <= char_hexdigit (after_prefix)) 4859 padding = 0;
4864 { 4860 }
4865 leading_zeros += padding; 4861 if (leading_zeros == 0 && sharp_flag && conversion == 'o'
4866 padding = 0; 4862 && p[beglen] != '0')
4867 } 4863 {
4864 leading_zeros++;
4865 padding -= padding != 0;
4868 } 4866 }
4869 4867
4870 if (excess_precision 4868 int endlen = 0;
4869 if (trailing_zeros
4871 && (conversion == 'e' || conversion == 'g')) 4870 && (conversion == 'e' || conversion == 'g'))
4872 { 4871 {
4873 char *e = strchr (src, 'e'); 4872 char *e = strchr (p, 'e');
4874 if (e) 4873 if (e)
4875 exponent_bytes = src + sprintf_bytes - e; 4874 endlen = p + sprintf_bytes - e;
4876 } 4875 }
4877 4876
4878 spec->start = nchars; 4877 ptrdiff_t midlen = sprintf_bytes - beglen - endlen;
4879 if (! minus_flag) 4878 ptrdiff_t leading_padding = minus_flag ? 0 : padding;
4880 { 4879 ptrdiff_t trailing_padding = padding - leading_padding;
4881 memset (p, ' ', padding);
4882 p += padding;
4883 nchars += padding;
4884 }
4885 4880
4886 memcpy (p, src, prefix_bytes); 4881 /* Insert padding and excess-precision zeros. The output
4887 p += prefix_bytes; 4882 contains the following components, in left-to-right order:
4888 src += prefix_bytes;
4889 memset (p, '0', leading_zeros);
4890 p += leading_zeros;
4891 int significand_bytes
4892 = sprintf_bytes - prefix_bytes - exponent_bytes;
4893 memcpy (p, src, significand_bytes);
4894 p += significand_bytes;
4895 src += significand_bytes;
4896 memset (p, '0', trailing_zeros);
4897 p += trailing_zeros;
4898 memcpy (p, src, exponent_bytes);
4899 p += exponent_bytes;
4900
4901 nchars += leading_zeros + sprintf_bytes + trailing_zeros;
4902 4883
4903 if (minus_flag) 4884 LEADING_PADDING spaces.
4885 BEGLEN bytes taken from the start of sprintf output.
4886 PREFIXLEN bytes taken from the start of the prefix array.
4887 LEADING_ZEROS zeros.
4888 MIDLEN bytes taken from the middle of sprintf output.
4889 TRAILING_ZEROS zeros.
4890 ENDLEN bytes taken from the end of sprintf output.
4891 TRAILING_PADDING spaces.
4892
4893 The sprintf output is taken from the buffer starting at
4894 P and continuing for SPRINTF_BYTES bytes. */
4895
4896 ptrdiff_t incr
4897 = (padding + leading_zeros + prefixlen
4898 + sprintf_bytes + trailing_zeros);
4899
4900 /* Optimize for the typical case with padding or zeros. */
4901 if (incr != sprintf_bytes)
4904 { 4902 {
4905 memset (p, ' ', padding); 4903 /* Move data to make room to insert spaces and '0's.
4906 p += padding; 4904 As this may entail overlapping moves, process
4907 nchars += padding; 4905 the output right-to-left and use memmove.
4906 With any luck this code is rarely executed. */
4907 char *src = p + sprintf_bytes;
4908 char *dst = p + incr;
4909 dst -= trailing_padding;
4910 memset (dst, ' ', trailing_padding);
4911 src -= endlen;
4912 dst -= endlen;
4913 memmove (dst, src, endlen);
4914 dst -= trailing_zeros;
4915 memset (dst, '0', trailing_zeros);
4916 src -= midlen;
4917 dst -= midlen;
4918 memmove (dst, src, midlen);
4919 dst -= leading_zeros;
4920 memset (dst, '0', leading_zeros);
4921 dst -= prefixlen;
4922 memcpy (dst, prefix, prefixlen);
4923 src -= beglen;
4924 dst -= beglen;
4925 memmove (dst, src, beglen);
4926 dst -= leading_padding;
4927 memset (dst, ' ', leading_padding);
4908 } 4928 }
4909 spec->end = nchars;
4910 4929
4930 p += incr;
4931 spec->start = nchars;
4932 spec->end = nchars += incr;
4911 new_result = true; 4933 new_result = true;
4912 continue; 4934 convbytes = CONVBYTES_ROOM;
4913 } 4935 }
4914 } 4936 }
4915 } 4937 }
@@ -4962,42 +4984,51 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4962 } 4984 }
4963 4985
4964 copy_char: 4986 copy_char:
4965 if (convbytes <= buf + bufsize - p) 4987 memcpy (p, convsrc, convbytes);
4966 { 4988 p += convbytes;
4967 memcpy (p, convsrc, convbytes); 4989 nchars++;
4968 p += convbytes; 4990 convbytes = CONVBYTES_ROOM;
4969 nchars++;
4970 continue;
4971 }
4972 } 4991 }
4973 4992
4974 /* There wasn't enough room to store this conversion or single
4975 character. CONVBYTES says how much room is needed. Allocate
4976 enough room (and then some) and do it again. */
4977
4978 ptrdiff_t used = p - buf; 4993 ptrdiff_t used = p - buf;
4979 if (max_bufsize - used < convbytes) 4994 ptrdiff_t buflen_needed;
4995 if (INT_ADD_WRAPV (used, convbytes, &buflen_needed))
4980 string_overflow (); 4996 string_overflow ();
4981 bufsize = used + convbytes; 4997 if (bufsize <= buflen_needed)
4982 bufsize = bufsize < max_bufsize / 2 ? bufsize * 2 : max_bufsize;
4983
4984 if (buf == initial_buffer)
4985 { 4998 {
4986 buf = xmalloc (bufsize); 4999 if (max_bufsize <= buflen_needed)
4987 buf_save_value_index = SPECPDL_INDEX (); 5000 string_overflow ();
4988 record_unwind_protect_ptr (xfree, buf);
4989 memcpy (buf, initial_buffer, used);
4990 }
4991 else
4992 {
4993 buf = xrealloc (buf, bufsize);
4994 set_unwind_protect_ptr (buf_save_value_index, xfree, buf);
4995 }
4996 5001
4997 p = buf + used; 5002 /* Either there wasn't enough room to store this conversion,
4998 format = format0; 5003 or there won't be enough room to do a sprintf the next
4999 n = n0; 5004 time through the loop. Allocate enough room (and then some). */
5000 ispec = ispec0; 5005
5006 bufsize = (buflen_needed <= max_bufsize / 2
5007 ? buflen_needed * 2 : max_bufsize);
5008
5009 if (buf == initial_buffer)
5010 {
5011 buf = xmalloc (bufsize);
5012 buf_save_value_index = SPECPDL_INDEX ();
5013 record_unwind_protect_ptr (xfree, buf);
5014 memcpy (buf, initial_buffer, used);
5015 }
5016 else
5017 {
5018 buf = xrealloc (buf, bufsize);
5019 set_unwind_protect_ptr (buf_save_value_index, xfree, buf);
5020 }
5021
5022 p = buf + used;
5023 if (convbytes != CONVBYTES_ROOM)
5024 {
5025 /* There wasn't enough room for this conversion; do it over. */
5026 eassert (CONVBYTES_ROOM < convbytes);
5027 format = format0;
5028 n = n0;
5029 ispec = ispec0;
5030 }
5031 }
5001 } 5032 }
5002 5033
5003 if (bufsize < p - buf) 5034 if (bufsize < p - buf)
diff --git a/src/lisp.h b/src/lisp.h
index c5b51ba3b35..36ca32c3c05 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -3278,9 +3278,12 @@ set_sub_char_table_contents (Lisp_Object table, ptrdiff_t idx, Lisp_Object val)
3278 XSUB_CHAR_TABLE (table)->contents[idx] = val; 3278 XSUB_CHAR_TABLE (table)->contents[idx] = val;
3279} 3279}
3280 3280
3281/* Defined in bignum.c. */ 3281/* Defined in bignum.c. This part of bignum.c's API does not require
3282 the caller to access bignum internals; see bignum.h for that. */
3282extern intmax_t bignum_to_intmax (Lisp_Object); 3283extern intmax_t bignum_to_intmax (Lisp_Object);
3283extern uintmax_t bignum_to_uintmax (Lisp_Object); 3284extern uintmax_t bignum_to_uintmax (Lisp_Object);
3285extern ptrdiff_t bignum_bufsize (Lisp_Object, int);
3286extern ptrdiff_t bignum_to_c_string (char *, ptrdiff_t, Lisp_Object, int);
3284extern Lisp_Object bignum_to_string (Lisp_Object, int); 3287extern Lisp_Object bignum_to_string (Lisp_Object, int);
3285extern Lisp_Object make_bignum_str (char const *, int); 3288extern Lisp_Object make_bignum_str (char const *, int);
3286extern Lisp_Object double_to_bignum (double); 3289extern Lisp_Object double_to_bignum (double);
diff --git a/src/print.c b/src/print.c
index 49d9e38e7d3..c0c90bc7e9a 100644
--- a/src/print.c
+++ b/src/print.c
@@ -23,7 +23,6 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
23#include "sysstdio.h" 23#include "sysstdio.h"
24 24
25#include "lisp.h" 25#include "lisp.h"
26#include "bignum.h"
27#include "character.h" 26#include "character.h"
28#include "coding.h" 27#include "coding.h"
29#include "buffer.h" 28#include "buffer.h"
@@ -1370,11 +1369,11 @@ print_vectorlike (Lisp_Object obj, Lisp_Object printcharfun, bool escapeflag,
1370 { 1369 {
1371 case PVEC_BIGNUM: 1370 case PVEC_BIGNUM:
1372 { 1371 {
1372 ptrdiff_t size = bignum_bufsize (obj, 10);
1373 USE_SAFE_ALLOCA; 1373 USE_SAFE_ALLOCA;
1374 char *str = SAFE_ALLOCA (mpz_sizeinbase (XBIGNUM (obj)->value, 10) 1374 char *str = SAFE_ALLOCA (size);
1375 + 2); 1375 ptrdiff_t len = bignum_to_c_string (str, size, obj, 10);
1376 mpz_get_str (str, 10, XBIGNUM (obj)->value); 1376 strout (str, len, len, printcharfun);
1377 print_c_string (str, printcharfun);
1378 SAFE_FREE (); 1377 SAFE_FREE ();
1379 } 1378 }
1380 break; 1379 break;