diff options
| author | Paul Eggert | 2018-08-31 00:25:07 -0700 |
|---|---|---|
| committer | Paul Eggert | 2018-08-31 00:28:58 -0700 |
| commit | db2fed3bdfb351c3283e481829ce687931d27a3d (patch) | |
| tree | 4f2674ec4f4fe450fd483132b9ddcca48f9eaf81 /src | |
| parent | a451c6ec12b7b024f347364becb10c49807513ed (diff) | |
| download | emacs-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.c | 37 | ||||
| -rw-r--r-- | src/editfns.c | 359 | ||||
| -rw-r--r-- | src/lisp.h | 5 | ||||
| -rw-r--r-- | src/print.c | 9 |
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. */ |
| 27 | double | 29 | double |
| 28 | bignum_to_double (Lisp_Object n) | 30 | bignum_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. */ | ||
| 231 | ptrdiff_t | ||
| 232 | bignum_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)). */ | ||
| 241 | ptrdiff_t | ||
| 242 | bignum_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 | ||
| 228 | Lisp_Object | 253 | Lisp_Object |
| 229 | bignum_to_string (Lisp_Object num, int base) | 254 | bignum_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) */) | |||
| 4232 | static Lisp_Object | 4232 | static Lisp_Object |
| 4233 | styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) | 4233 | styled_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. */ | ||
| 3282 | extern intmax_t bignum_to_intmax (Lisp_Object); | 3283 | extern intmax_t bignum_to_intmax (Lisp_Object); |
| 3283 | extern uintmax_t bignum_to_uintmax (Lisp_Object); | 3284 | extern uintmax_t bignum_to_uintmax (Lisp_Object); |
| 3285 | extern ptrdiff_t bignum_bufsize (Lisp_Object, int); | ||
| 3286 | extern ptrdiff_t bignum_to_c_string (char *, ptrdiff_t, Lisp_Object, int); | ||
| 3284 | extern Lisp_Object bignum_to_string (Lisp_Object, int); | 3287 | extern Lisp_Object bignum_to_string (Lisp_Object, int); |
| 3285 | extern Lisp_Object make_bignum_str (char const *, int); | 3288 | extern Lisp_Object make_bignum_str (char const *, int); |
| 3286 | extern Lisp_Object double_to_bignum (double); | 3289 | extern 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; |