diff options
| -rw-r--r-- | doc/lispref/internals.texi | 218 | ||||
| -rw-r--r-- | src/emacs-module.c | 144 | ||||
| -rw-r--r-- | src/emacs-module.h.in | 22 | ||||
| -rw-r--r-- | src/module-env-27.h | 12 | ||||
| -rw-r--r-- | test/data/emacs-module/mod-test.c | 111 |
5 files changed, 406 insertions, 101 deletions
diff --git a/doc/lispref/internals.texi b/doc/lispref/internals.texi index e870d6e06e8..f1062a2f4d0 100644 --- a/doc/lispref/internals.texi +++ b/doc/lispref/internals.texi | |||
| @@ -1475,6 +1475,42 @@ the widest integral data type supported by the C compiler, typically | |||
| 1475 | @code{overflow-error}. | 1475 | @code{overflow-error}. |
| 1476 | @end deftypefn | 1476 | @end deftypefn |
| 1477 | 1477 | ||
| 1478 | @deftypefn Function bool extract_big_integer (emacs_env *@var{env}, emacs_value @var{arg}, int *@var{sign}, ptrdiff_t *@var{count}, emacs_limb_t *@var{magnitude}) | ||
| 1479 | This function, which is available since Emacs 27, extracts the | ||
| 1480 | integral value of @var{arg}. The value of @var{arg} must be an | ||
| 1481 | integer (fixnum or bignum). If @var{sign} is not @code{NULL}, it | ||
| 1482 | stores the sign of @var{arg} (-1, 0, or +1) into @code{*sign}. The | ||
| 1483 | magnitude is stored into @var{magnitude} as follows. If @var{count} | ||
| 1484 | and @var{magnitude} are bot non-@code{NULL}, then @var{magnitude} must | ||
| 1485 | point to an array of at least @code{*count} @code{unsigned long} | ||
| 1486 | elements. If @var{magnitude} is large enough to hold the magnitude of | ||
| 1487 | @var{arg}, then this function writes the magnitude into the | ||
| 1488 | @var{magnitude} array in little-endian form, stores the number of | ||
| 1489 | array elements written into @code{*count}, and returns @code{true}. | ||
| 1490 | If @var{magnitude} is not large enough, it stores the required array | ||
| 1491 | size into @code{*count}, signals an error, and returns @code{false}. | ||
| 1492 | If @var{count} is not @code{NULL} and @var{magnitude} is @code{NULL}, | ||
| 1493 | then the function stores the required array size into @code{*count} | ||
| 1494 | and returns @code{true}. | ||
| 1495 | |||
| 1496 | Emacs guarantees that the maximum required value of @code{*count} | ||
| 1497 | never exceeds @code{min (PTRDIFF_MAX, SIZE_MAX) / sizeof | ||
| 1498 | (emacs_limb_t)}. This implies that you can use e.g. @code{malloc | ||
| 1499 | ((size_t) (*count * sizeof (emacs_limb_t)))} to allocate the | ||
| 1500 | @code{magnitude} array without integer overflow. | ||
| 1501 | @end deftypefn | ||
| 1502 | |||
| 1503 | @deftp {Type alias} emacs_limb_t | ||
| 1504 | This type is an alias to an otherwise unspecified unsigned integral | ||
| 1505 | type. It is used as element type for the magnitude arrays for the big | ||
| 1506 | integer conversion functions. | ||
| 1507 | @end deftp | ||
| 1508 | |||
| 1509 | @defvr Macro EMACS_LIMB_MAX | ||
| 1510 | This macro expands to an integer literal specifying the maximum | ||
| 1511 | possible value for an @code{emacs_limb_t} object. | ||
| 1512 | @end defvr | ||
| 1513 | |||
| 1478 | @deftypefn Function double extract_float (emacs_env *@var{env}, emacs_value @var{arg}) | 1514 | @deftypefn Function double extract_float (emacs_env *@var{env}, emacs_value @var{arg}) |
| 1479 | This function returns the value of a Lisp float specified by | 1515 | This function returns the value of a Lisp float specified by |
| 1480 | @var{arg}, as a C @code{double} value. | 1516 | @var{arg}, as a C @code{double} value. |
| @@ -1572,6 +1608,128 @@ limits set by @code{most-negative-fixnum} and | |||
| 1572 | @code{most-positive-fixnum} (@pxref{Integer Basics}). | 1608 | @code{most-positive-fixnum} (@pxref{Integer Basics}). |
| 1573 | @end deftypefn | 1609 | @end deftypefn |
| 1574 | 1610 | ||
| 1611 | @deftypefn Function emacs_value make_big_integer (emacs_env *@var{env}, int sign, ptrdiff_t count, const emacs_limb_t *magnitude) | ||
| 1612 | This function, which is available since Emacs 27, takes an | ||
| 1613 | arbitrary-sized integer argument and returns a corresponding | ||
| 1614 | @code{emacs_value} object. The @var{sign} argument gives the sign of | ||
| 1615 | the return value. If @var{sign} is nonzero, then @var{magnitude} must | ||
| 1616 | point to an array of at least @var{count} elements specifying the | ||
| 1617 | little-endian magnitude of the return value. | ||
| 1618 | @end deftypefn | ||
| 1619 | |||
| 1620 | The following example uses the GNU Multiprecision Library (GMP) to | ||
| 1621 | calculate the next probable prime after a given integer. | ||
| 1622 | @xref{Top,,,gmp} for a general overview of GMP, and @pxref{Integer | ||
| 1623 | Import and Export,,,gmp} for how to convert the @code{magnitude} array | ||
| 1624 | to and from GMP @code{mpz_t} values. | ||
| 1625 | |||
| 1626 | @example | ||
| 1627 | #include <assert.h> | ||
| 1628 | #include <limits.h> | ||
| 1629 | #include <stdint.h> | ||
| 1630 | #include <stdlib.h> | ||
| 1631 | #include <string.h> | ||
| 1632 | |||
| 1633 | #include <gmp.h> | ||
| 1634 | |||
| 1635 | #include <emacs-module.h> | ||
| 1636 | |||
| 1637 | static void | ||
| 1638 | memory_full (emacs_env *env) | ||
| 1639 | @{ | ||
| 1640 | const char *message = "Memory exhausted"; | ||
| 1641 | emacs_value data = env->make_string (env, message, strlen (message)); | ||
| 1642 | env->non_local_exit_signal (env, env->intern (env, "error"), | ||
| 1643 | env->funcall (env, env->intern (env, "list"), 1, | ||
| 1644 | &data)); | ||
| 1645 | @} | ||
| 1646 | |||
| 1647 | enum | ||
| 1648 | @{ | ||
| 1649 | max_count = ((SIZE_MAX < PTRDIFF_MAX ? SIZE_MAX : PTRDIFF_MAX) | ||
| 1650 | / sizeof (emacs_limb_t)) | ||
| 1651 | @}; | ||
| 1652 | |||
| 1653 | static bool | ||
| 1654 | extract_big_integer (emacs_env *env, emacs_value arg, mpz_t result) | ||
| 1655 | @{ | ||
| 1656 | int sign; | ||
| 1657 | ptrdiff_t count; | ||
| 1658 | bool success = env->extract_big_integer (env, arg, &sign, &count, NULL); | ||
| 1659 | if (!success) | ||
| 1660 | return false; | ||
| 1661 | if (sign == 0) | ||
| 1662 | @{ | ||
| 1663 | mpz_set_ui (result, 0); | ||
| 1664 | return true; | ||
| 1665 | @} | ||
| 1666 | enum @{ order = -1, size = sizeof (emacs_limb_t), endian = 0, nails = 0 @}; | ||
| 1667 | assert (0 < count && count <= max_count); | ||
| 1668 | emacs_limb_t *magnitude = malloc ((size_t) (count * size)); | ||
| 1669 | if (magnitude == NULL) | ||
| 1670 | @{ | ||
| 1671 | memory_full (env); | ||
| 1672 | return false; | ||
| 1673 | @} | ||
| 1674 | success = env->extract_big_integer (env, arg, NULL, &count, magnitude); | ||
| 1675 | assert (success); | ||
| 1676 | mpz_import (result, count, order, size, endian, nails, magnitude); | ||
| 1677 | free (magnitude); | ||
| 1678 | if (sign < 0) | ||
| 1679 | mpz_neg (result, result); | ||
| 1680 | return true; | ||
| 1681 | @} | ||
| 1682 | |||
| 1683 | static emacs_value | ||
| 1684 | make_big_integer (emacs_env *env, const mpz_t value) | ||
| 1685 | @{ | ||
| 1686 | if (mpz_sgn (value) == 0) | ||
| 1687 | return env->make_integer (env, 0); | ||
| 1688 | enum | ||
| 1689 | @{ | ||
| 1690 | order = -1, | ||
| 1691 | size = sizeof (emacs_limb_t), | ||
| 1692 | endian = 0, | ||
| 1693 | nails = 0, | ||
| 1694 | numb = 8 * size - nails | ||
| 1695 | @}; | ||
| 1696 | size_t count = (mpz_sizeinbase (value, 2) + numb - 1) / numb; | ||
| 1697 | if (max_count < count) | ||
| 1698 | @{ | ||
| 1699 | memory_full (env); | ||
| 1700 | return NULL; | ||
| 1701 | @} | ||
| 1702 | emacs_limb_t *magnitude = malloc (count * size); | ||
| 1703 | if (magnitude == NULL) | ||
| 1704 | @{ | ||
| 1705 | memory_full (env); | ||
| 1706 | return NULL; | ||
| 1707 | @} | ||
| 1708 | size_t written; | ||
| 1709 | mpz_export (magnitude, &written, order, size, endian, nails, value); | ||
| 1710 | assert (written == count); | ||
| 1711 | assert (count <= PTRDIFF_MAX); | ||
| 1712 | emacs_value result = env->make_big_integer (env, mpz_sgn (value), | ||
| 1713 | (ptrdiff_t) count, magnitude); | ||
| 1714 | free (magnitude); | ||
| 1715 | return result; | ||
| 1716 | @} | ||
| 1717 | |||
| 1718 | static emacs_value | ||
| 1719 | next_prime (emacs_env *env, ptrdiff_t nargs, emacs_value *args, | ||
| 1720 | void *data) | ||
| 1721 | @{ | ||
| 1722 | assert (nargs == 1); | ||
| 1723 | emacs_mpz p; | ||
| 1724 | mpz_init (p); | ||
| 1725 | extract_big_integer (env, args[0], p); | ||
| 1726 | mpz_nextprime (p, p); | ||
| 1727 | emacs_value result = make_big_integer (env, p); | ||
| 1728 | mpz_clear (p); | ||
| 1729 | return result; | ||
| 1730 | @} | ||
| 1731 | @end example | ||
| 1732 | |||
| 1575 | @deftypefn Function emacs_value make_float (emacs_env *@var{env}, double @var{d}) | 1733 | @deftypefn Function emacs_value make_float (emacs_env *@var{env}, double @var{d}) |
| 1576 | This function takes a @code{double} argument @var{d} and returns the | 1734 | This function takes a @code{double} argument @var{d} and returns the |
| 1577 | corresponding Emacs floating-point value. | 1735 | corresponding Emacs floating-point value. |
| @@ -1601,66 +1759,6 @@ function raises the @code{overflow-error} error condition if | |||
| 1601 | string. | 1759 | string. |
| 1602 | @end deftypefn | 1760 | @end deftypefn |
| 1603 | 1761 | ||
| 1604 | If you define the preprocessor macro @code{EMACS_MODULE_GMP} before | ||
| 1605 | including the header @file{emacs-module.h}, you can also convert | ||
| 1606 | between Emacs integers and GMP @code{mpz_t} values. @xref{GMP | ||
| 1607 | Basics,,,gmp}. If @code{EMACS_MODULE_GMP} is defined, | ||
| 1608 | @file{emacs-module.h} wraps @code{mpz_t} in the following structure: | ||
| 1609 | |||
| 1610 | @deftp struct emacs_mpz value | ||
| 1611 | struct emacs_mpz @{ mpz_t value; @}; | ||
| 1612 | @end deftp | ||
| 1613 | |||
| 1614 | @noindent | ||
| 1615 | Then you can use the following additional functions: | ||
| 1616 | |||
| 1617 | @deftypefn Function bool extract_big_integer (emacs_env *@var{env}, emacs_value @var{arg}, struct emacs_mpz *@var{result}) | ||
| 1618 | This function, which is available since Emacs 27, extracts the | ||
| 1619 | integral value of @var{arg} into @var{result}. @var{result} must not | ||
| 1620 | be @code{NULL}. @code{@var{result}->value} must be an initialized | ||
| 1621 | @code{mpz_t} object. @xref{Initializing Integers,,,gmp}. If | ||
| 1622 | @var{arg} is an integer, Emacs will store its value into | ||
| 1623 | @code{@var{result}->value}. After you have finished using | ||
| 1624 | @code{@var{result}->value}, you should free it using @code{mpz_clear} | ||
| 1625 | or similar. | ||
| 1626 | @end deftypefn | ||
| 1627 | |||
| 1628 | @deftypefn Function emacs_value make_big_integer (emacs_env *@var{env}, const struct emacs_mpz *@var{value}) | ||
| 1629 | This function, which is available since Emacs 27, takes an | ||
| 1630 | arbitrary-sized integer argument and returns a corresponding | ||
| 1631 | @code{emacs_value} object. @var{value} must not be @code{NULL}. | ||
| 1632 | @code{@var{value}->value} must be an initialized @code{mpz_t} object. | ||
| 1633 | @xref{Initializing Integers,,,gmp}. Emacs will return a corresponding | ||
| 1634 | integral object. After you have finished using | ||
| 1635 | @code{@var{value}->value}, you should free it using @code{mpz_clear} | ||
| 1636 | or similar. | ||
| 1637 | @end deftypefn | ||
| 1638 | |||
| 1639 | The following example uses GMP to calculate the next probable prime | ||
| 1640 | after a given integer: | ||
| 1641 | |||
| 1642 | @example | ||
| 1643 | #include <assert.h> | ||
| 1644 | #include <gmp.h> | ||
| 1645 | |||
| 1646 | #define EMACS_MODULE_GMP | ||
| 1647 | #include <emacs-module.h> | ||
| 1648 | |||
| 1649 | static emacs_value | ||
| 1650 | next_prime (emacs_env *env, ptrdiff_t nargs, emacs_value *args, | ||
| 1651 | void *data) | ||
| 1652 | @{ | ||
| 1653 | assert (nargs == 1); | ||
| 1654 | emacs_mpz p; | ||
| 1655 | mpz_init (p.value); | ||
| 1656 | env->extract_big_integer (env, args[0], &p); | ||
| 1657 | mpz_nextprime (p.value, p.value); | ||
| 1658 | emacs_value result = env->make_big_integer (env, &p); | ||
| 1659 | mpz_clear (p.value); | ||
| 1660 | return result; | ||
| 1661 | @} | ||
| 1662 | @end example | ||
| 1663 | |||
| 1664 | The @acronym{API} does not provide functions to manipulate Lisp data | 1762 | The @acronym{API} does not provide functions to manipulate Lisp data |
| 1665 | structures, for example, create lists with @code{cons} and @code{list} | 1763 | structures, for example, create lists with @code{cons} and @code{list} |
| 1666 | (@pxref{Building Lists}), extract list members with @code{car} and | 1764 | (@pxref{Building Lists}), extract list members with @code{car} and |
diff --git a/src/emacs-module.c b/src/emacs-module.c index 4b991a1c744..e5c88fd814a 100644 --- a/src/emacs-module.c +++ b/src/emacs-module.c | |||
| @@ -70,12 +70,6 @@ To add a new module function, proceed as follows: | |||
| 70 | 70 | ||
| 71 | #include <config.h> | 71 | #include <config.h> |
| 72 | 72 | ||
| 73 | #ifndef HAVE_GMP | ||
| 74 | #include "mini-gmp.h" | ||
| 75 | #define EMACS_MODULE_HAVE_MPZ_T | ||
| 76 | #endif | ||
| 77 | |||
| 78 | #define EMACS_MODULE_GMP | ||
| 79 | #include "emacs-module.h" | 73 | #include "emacs-module.h" |
| 80 | 74 | ||
| 81 | #include <stdarg.h> | 75 | #include <stdarg.h> |
| @@ -772,21 +766,143 @@ module_make_time (emacs_env *env, struct timespec time) | |||
| 772 | return lisp_to_value (env, timespec_to_lisp (time)); | 766 | return lisp_to_value (env, timespec_to_lisp (time)); |
| 773 | } | 767 | } |
| 774 | 768 | ||
| 775 | static void | 769 | /* |
| 776 | module_extract_big_integer (emacs_env *env, emacs_value value, | 770 | Big integer support. |
| 777 | struct emacs_mpz *result) | 771 | |
| 772 | There are two possible ways to support big integers in the module API | ||
| 773 | that have been discussed: | ||
| 774 | |||
| 775 | 1. Exposing GMP numbers (mpz_t) directly in the API. | ||
| 776 | |||
| 777 | 2. Isolating the API from GMP by converting to/from a custom | ||
| 778 | sign-magnitude representation. | ||
| 779 | |||
| 780 | Approach (1) has the advantage of being faster (no import/export | ||
| 781 | required) and requiring less code in Emacs and in modules that would | ||
| 782 | use GMP anyway. However, (1) also couples big integer support | ||
| 783 | directly to the current implementation in Emacs (GMP). Also (1) | ||
| 784 | requires each module author to ensure that their module is linked to | ||
| 785 | the same GMP library as Emacs itself; in particular, module authors | ||
| 786 | can't link GMP statically. (1) also requires conditional compilation | ||
| 787 | and workarounds to ensure the module interface still works if GMP | ||
| 788 | isn't available while including emacs-module.h. It also means that | ||
| 789 | modules written in languages such as Go and Java that support big | ||
| 790 | integers without GMP now have to carry an otherwise unnecessary GMP | ||
| 791 | dependency. Approach (2), on the other hand, neatly decouples the | ||
| 792 | module interface from the GMP-based implementation. It's not | ||
| 793 | significantly more complex than (1) either: the additional code is | ||
| 794 | mostly straightforward. Over all, the benefits of (2) over (1) are | ||
| 795 | large enough to prefer it here. | ||
| 796 | |||
| 797 | We use a simple sign-magnitude representation for the big integers. | ||
| 798 | For the magnitude we pick an array of an unsigned integer type similar | ||
| 799 | to mp_limb_t instead of e.g. unsigned char. This matches in most | ||
| 800 | cases the representation of a GMP limb. In such cases GMP picks an | ||
| 801 | optimized algorithm for mpz_import and mpz_export that boils down to a | ||
| 802 | single memcpy to convert the magnitude. This way we largely avoid the | ||
| 803 | import/export overhead on most platforms. | ||
| 804 | */ | ||
| 805 | |||
| 806 | enum | ||
| 778 | { | 807 | { |
| 779 | MODULE_FUNCTION_BEGIN (); | 808 | /* Documented maximum count of magnitude elements. */ |
| 780 | Lisp_Object o = value_to_lisp (value); | 809 | module_bignum_count_max = min (SIZE_MAX, PTRDIFF_MAX) / sizeof (emacs_limb_t) |
| 810 | }; | ||
| 811 | |||
| 812 | static bool | ||
| 813 | module_extract_big_integer (emacs_env *env, emacs_value arg, int *sign, | ||
| 814 | ptrdiff_t *count, emacs_limb_t *magnitude) | ||
| 815 | { | ||
| 816 | MODULE_FUNCTION_BEGIN (false); | ||
| 817 | Lisp_Object o = value_to_lisp (arg); | ||
| 781 | CHECK_INTEGER (o); | 818 | CHECK_INTEGER (o); |
| 782 | mpz_set_integer (result->value, o); | 819 | int dummy; |
| 820 | if (sign == NULL) | ||
| 821 | sign = &dummy; | ||
| 822 | /* See | ||
| 823 | https://gmplib.org/manual/Integer-Import-and-Export.html#index-Export. */ | ||
| 824 | enum | ||
| 825 | { | ||
| 826 | order = -1, | ||
| 827 | size = sizeof *magnitude, | ||
| 828 | bits = size * CHAR_BIT, | ||
| 829 | endian = 0, | ||
| 830 | nails = 0, | ||
| 831 | numb = 8 * size - nails | ||
| 832 | }; | ||
| 833 | if (FIXNUMP (o)) | ||
| 834 | { | ||
| 835 | EMACS_INT x = XFIXNUM (o); | ||
| 836 | *sign = (0 < x) - (x < 0); | ||
| 837 | if (x == 0 || count == NULL) | ||
| 838 | return true; | ||
| 839 | /* As a simplification we don't check how many array elements | ||
| 840 | are exactly required, but use a reasonable static upper | ||
| 841 | bound. For most architectures exactly one element should | ||
| 842 | suffice. */ | ||
| 843 | EMACS_UINT u; | ||
| 844 | enum { required = (sizeof u + size - 1) / size }; | ||
| 845 | verify (0 < required && required <= module_bignum_count_max); | ||
| 846 | if (magnitude == NULL) | ||
| 847 | { | ||
| 848 | *count = required; | ||
| 849 | return true; | ||
| 850 | } | ||
| 851 | if (*count < required) | ||
| 852 | { | ||
| 853 | ptrdiff_t actual = *count; | ||
| 854 | *count = required; | ||
| 855 | args_out_of_range_3 (INT_TO_INTEGER (actual), | ||
| 856 | INT_TO_INTEGER (required), | ||
| 857 | INT_TO_INTEGER (module_bignum_count_max)); | ||
| 858 | } | ||
| 859 | /* Set u = abs(x). See https://stackoverflow.com/a/17313717. */ | ||
| 860 | if (0 < x) | ||
| 861 | u = (EMACS_UINT) x; | ||
| 862 | else | ||
| 863 | u = -(EMACS_UINT) x; | ||
| 864 | verify (required * bits < PTRDIFF_MAX); | ||
| 865 | for (ptrdiff_t i = 0; i < required; ++i) | ||
| 866 | magnitude[i] = (emacs_limb_t) (u >> (i * bits)); | ||
| 867 | return true; | ||
| 868 | } | ||
| 869 | const mpz_t *x = xbignum_val (o); | ||
| 870 | *sign = mpz_sgn (*x); | ||
| 871 | if (count == NULL) | ||
| 872 | return true; | ||
| 873 | size_t required_size = (mpz_sizeinbase (*x, 2) + numb - 1) / numb; | ||
| 874 | eassert (required_size <= PTRDIFF_MAX); | ||
| 875 | ptrdiff_t required = (ptrdiff_t) required_size; | ||
| 876 | eassert (required <= module_bignum_count_max); | ||
| 877 | if (magnitude == NULL) | ||
| 878 | { | ||
| 879 | *count = required; | ||
| 880 | return true; | ||
| 881 | } | ||
| 882 | if (*count < required) | ||
| 883 | { | ||
| 884 | ptrdiff_t actual = *count; | ||
| 885 | *count = required; | ||
| 886 | args_out_of_range_3 (INT_TO_INTEGER (actual), INT_TO_INTEGER (required), | ||
| 887 | INT_TO_INTEGER (module_bignum_count_max)); | ||
| 888 | } | ||
| 889 | size_t written; | ||
| 890 | mpz_export (magnitude, &written, order, size, endian, nails, *x); | ||
| 891 | eassert (written == required_size); | ||
| 892 | return true; | ||
| 783 | } | 893 | } |
| 784 | 894 | ||
| 785 | static emacs_value | 895 | static emacs_value |
| 786 | module_make_big_integer (emacs_env *env, const struct emacs_mpz *value) | 896 | module_make_big_integer (emacs_env *env, int sign, |
| 897 | ptrdiff_t count, const unsigned long *magnitude) | ||
| 787 | { | 898 | { |
| 788 | MODULE_FUNCTION_BEGIN (NULL); | 899 | MODULE_FUNCTION_BEGIN (NULL); |
| 789 | mpz_set (mpz[0], value->value); | 900 | if (sign == 0) |
| 901 | return lisp_to_value (env, make_fixed_natnum (0)); | ||
| 902 | enum { order = -1, size = sizeof *magnitude, endian = 0, nails = 0 }; | ||
| 903 | mpz_import (mpz[0], count, order, size, endian, nails, magnitude); | ||
| 904 | if (sign < 0) | ||
| 905 | mpz_neg (mpz[0], mpz[0]); | ||
| 790 | return lisp_to_value (env, make_integer_mpz ()); | 906 | return lisp_to_value (env, make_integer_mpz ()); |
| 791 | } | 907 | } |
| 792 | 908 | ||
diff --git a/src/emacs-module.h.in b/src/emacs-module.h.in index 9955e30eb7a..800c0188ff5 100644 --- a/src/emacs-module.h.in +++ b/src/emacs-module.h.in | |||
| @@ -20,6 +20,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | |||
| 20 | #ifndef EMACS_MODULE_H | 20 | #ifndef EMACS_MODULE_H |
| 21 | #define EMACS_MODULE_H | 21 | #define EMACS_MODULE_H |
| 22 | 22 | ||
| 23 | #include <limits.h> | ||
| 23 | #include <stdint.h> | 24 | #include <stdint.h> |
| 24 | #include <stddef.h> | 25 | #include <stddef.h> |
| 25 | #include <time.h> | 26 | #include <time.h> |
| @@ -28,10 +29,6 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | |||
| 28 | #include <stdbool.h> | 29 | #include <stdbool.h> |
| 29 | #endif | 30 | #endif |
| 30 | 31 | ||
| 31 | #if defined EMACS_MODULE_GMP && !defined EMACS_MODULE_HAVE_MPZ_T | ||
| 32 | #include <gmp.h> | ||
| 33 | #endif | ||
| 34 | |||
| 35 | #define EMACS_MAJOR_VERSION @emacs_major_version@ | 32 | #define EMACS_MAJOR_VERSION @emacs_major_version@ |
| 36 | 33 | ||
| 37 | #if defined __cplusplus && __cplusplus >= 201103L | 34 | #if defined __cplusplus && __cplusplus >= 201103L |
| @@ -100,10 +97,21 @@ enum emacs_process_input_result | |||
| 100 | emacs_process_input_quit = 1 | 97 | emacs_process_input_quit = 1 |
| 101 | }; | 98 | }; |
| 102 | 99 | ||
| 103 | #ifdef EMACS_MODULE_GMP | 100 | /* |
| 104 | struct emacs_mpz { mpz_t value; }; | 101 | Implementation note: We define emacs_limb_t so that it is likely to |
| 102 | match the GMP mp_limb_t type. If the types match, GMP can use an | ||
| 103 | optimization for mpz_import and mpz_export that boils down to a | ||
| 104 | memcpy. According to https://gmplib.org/manual/ABI-and-ISA.html GMP | ||
| 105 | will prefer a 64-bit limb and will default to unsigned long if that is | ||
| 106 | wide enough. Note that this is an internal micro-optimization. Users | ||
| 107 | shouldn't rely on the exact size of emacs_limb_t. | ||
| 108 | */ | ||
| 109 | #if ULONG_MAX == 0xFFFFFFFF | ||
| 110 | typedef unsigned long long emacs_limb_t; | ||
| 111 | # define EMACS_LIMB_MAX ULLONG_MAX | ||
| 105 | #else | 112 | #else |
| 106 | struct emacs_mpz; /* no definition */ | 113 | typedef unsigned long emacs_limb_t; |
| 114 | # define EMACS_LIMB_MAX ULONG_MAX | ||
| 107 | #endif | 115 | #endif |
| 108 | 116 | ||
| 109 | struct emacs_env_25 | 117 | struct emacs_env_25 |
diff --git a/src/module-env-27.h b/src/module-env-27.h index 00de3009007..da8ac0e7479 100644 --- a/src/module-env-27.h +++ b/src/module-env-27.h | |||
| @@ -9,10 +9,10 @@ | |||
| 9 | emacs_value (*make_time) (emacs_env *env, struct timespec time) | 9 | emacs_value (*make_time) (emacs_env *env, struct timespec time) |
| 10 | EMACS_ATTRIBUTE_NONNULL (1); | 10 | EMACS_ATTRIBUTE_NONNULL (1); |
| 11 | 11 | ||
| 12 | void (*extract_big_integer) (emacs_env *env, emacs_value value, | 12 | bool (*extract_big_integer) (emacs_env *env, emacs_value arg, int *sign, |
| 13 | struct emacs_mpz *result) | 13 | ptrdiff_t *count, unsigned long *magnitude) |
| 14 | EMACS_ATTRIBUTE_NONNULL (1, 3); | 14 | EMACS_ATTRIBUTE_NONNULL (1); |
| 15 | 15 | ||
| 16 | emacs_value (*make_big_integer) (emacs_env *env, | 16 | emacs_value (*make_big_integer) (emacs_env *env, int sign, ptrdiff_t count, |
| 17 | const struct emacs_mpz *value) | 17 | const unsigned long *magnitude) |
| 18 | EMACS_ATTRIBUTE_NONNULL (1, 2); | 18 | EMACS_ATTRIBUTE_NONNULL (1); |
diff --git a/test/data/emacs-module/mod-test.c b/test/data/emacs-module/mod-test.c index 2891b73c1a0..b579c8a6278 100644 --- a/test/data/emacs-module/mod-test.c +++ b/test/data/emacs-module/mod-test.c | |||
| @@ -33,10 +33,8 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | |||
| 33 | #include <gmp.h> | 33 | #include <gmp.h> |
| 34 | #else | 34 | #else |
| 35 | #include "mini-gmp.h" | 35 | #include "mini-gmp.h" |
| 36 | #define EMACS_MODULE_HAVE_MPZ_T | ||
| 37 | #endif | 36 | #endif |
| 38 | 37 | ||
| 39 | #define EMACS_MODULE_GMP | ||
| 40 | #include <emacs-module.h> | 38 | #include <emacs-module.h> |
| 41 | 39 | ||
| 42 | #include "timespec.h" | 40 | #include "timespec.h" |
| @@ -66,6 +64,8 @@ int plugin_is_GPL_compatible; | |||
| 66 | # error "INTPTR_MAX too large" | 64 | # error "INTPTR_MAX too large" |
| 67 | #endif | 65 | #endif |
| 68 | 66 | ||
| 67 | /* Smoke test to verify that EMACS_LIMB_MAX is defined. */ | ||
| 68 | _Static_assert (0 < EMACS_LIMB_MAX, "EMACS_LIMB_MAX missing or incorrect"); | ||
| 69 | 69 | ||
| 70 | /* Always return symbol 't'. */ | 70 | /* Always return symbol 't'. */ |
| 71 | static emacs_value | 71 | static emacs_value |
| @@ -372,23 +372,106 @@ Fmod_test_add_nanosecond (emacs_env *env, ptrdiff_t nargs, emacs_value *args, | |||
| 372 | return env->make_time (env, time); | 372 | return env->make_time (env, time); |
| 373 | } | 373 | } |
| 374 | 374 | ||
| 375 | static void | ||
| 376 | memory_full (emacs_env *env) | ||
| 377 | { | ||
| 378 | const char *message = "Memory exhausted"; | ||
| 379 | emacs_value data = env->make_string (env, message, strlen (message)); | ||
| 380 | env->non_local_exit_signal (env, env->intern (env, "error"), | ||
| 381 | env->funcall (env, env->intern (env, "list"), 1, | ||
| 382 | &data)); | ||
| 383 | } | ||
| 384 | |||
| 385 | enum | ||
| 386 | { | ||
| 387 | max_count = ((SIZE_MAX < PTRDIFF_MAX ? SIZE_MAX : PTRDIFF_MAX) | ||
| 388 | / sizeof (emacs_limb_t)) | ||
| 389 | }; | ||
| 390 | |||
| 391 | static bool | ||
| 392 | extract_big_integer (emacs_env *env, emacs_value arg, mpz_t result) | ||
| 393 | { | ||
| 394 | int sign; | ||
| 395 | ptrdiff_t count; | ||
| 396 | bool success = env->extract_big_integer (env, arg, &sign, &count, NULL); | ||
| 397 | if (!success) | ||
| 398 | return false; | ||
| 399 | if (sign == 0) | ||
| 400 | { | ||
| 401 | mpz_set_ui (result, 0); | ||
| 402 | return true; | ||
| 403 | } | ||
| 404 | enum { order = -1, size = sizeof (unsigned long), endian = 0, nails = 0 }; | ||
| 405 | assert (0 < count && count <= max_count); | ||
| 406 | emacs_limb_t *magnitude = malloc (count * size); | ||
| 407 | if (magnitude == NULL) | ||
| 408 | { | ||
| 409 | memory_full (env); | ||
| 410 | return false; | ||
| 411 | } | ||
| 412 | success = env->extract_big_integer (env, arg, NULL, &count, magnitude); | ||
| 413 | assert (success); | ||
| 414 | mpz_import (result, count, order, size, endian, nails, magnitude); | ||
| 415 | free (magnitude); | ||
| 416 | if (sign < 0) | ||
| 417 | mpz_neg (result, result); | ||
| 418 | return true; | ||
| 419 | } | ||
| 420 | |||
| 421 | static emacs_value | ||
| 422 | make_big_integer (emacs_env *env, const mpz_t value) | ||
| 423 | { | ||
| 424 | if (mpz_sgn (value) == 0) | ||
| 425 | return env->make_integer (env, 0); | ||
| 426 | /* See | ||
| 427 | https://gmplib.org/manual/Integer-Import-and-Export.html#index-Export. */ | ||
| 428 | enum | ||
| 429 | { | ||
| 430 | order = -1, | ||
| 431 | size = sizeof (emacs_limb_t), | ||
| 432 | endian = 0, | ||
| 433 | nails = 0, | ||
| 434 | numb = 8 * size - nails | ||
| 435 | }; | ||
| 436 | size_t count = (mpz_sizeinbase (value, 2) + numb - 1) / numb; | ||
| 437 | if (max_count < count) | ||
| 438 | { | ||
| 439 | memory_full (env); | ||
| 440 | return NULL; | ||
| 441 | } | ||
| 442 | emacs_limb_t *magnitude = malloc (count * size); | ||
| 443 | if (magnitude == NULL) | ||
| 444 | { | ||
| 445 | memory_full (env); | ||
| 446 | return NULL; | ||
| 447 | } | ||
| 448 | size_t written; | ||
| 449 | mpz_export (magnitude, &written, order, size, endian, nails, value); | ||
| 450 | assert (written == count); | ||
| 451 | assert (count <= PTRDIFF_MAX); | ||
| 452 | emacs_value result = env->make_big_integer (env, mpz_sgn (value), | ||
| 453 | (ptrdiff_t) count, magnitude); | ||
| 454 | free (magnitude); | ||
| 455 | return result; | ||
| 456 | } | ||
| 457 | |||
| 375 | static emacs_value | 458 | static emacs_value |
| 376 | Fmod_test_nanoseconds (emacs_env *env, ptrdiff_t nargs, emacs_value *args, void *data) { | 459 | Fmod_test_nanoseconds (emacs_env *env, ptrdiff_t nargs, emacs_value *args, void *data) { |
| 377 | assert (nargs == 1); | 460 | assert (nargs == 1); |
| 378 | struct timespec time = env->extract_time (env, args[0]); | 461 | struct timespec time = env->extract_time (env, args[0]); |
| 379 | struct emacs_mpz nanoseconds; | 462 | mpz_t nanoseconds; |
| 380 | assert (LONG_MIN <= time.tv_sec && time.tv_sec <= LONG_MAX); | 463 | assert (LONG_MIN <= time.tv_sec && time.tv_sec <= LONG_MAX); |
| 381 | mpz_init_set_si (nanoseconds.value, time.tv_sec); | 464 | mpz_init_set_si (nanoseconds, time.tv_sec); |
| 382 | #ifdef __MINGW32__ | 465 | #ifdef __MINGW32__ |
| 383 | _Static_assert (1000000000 <= ULONG_MAX, "unsupported architecture"); | 466 | _Static_assert (1000000000 <= ULONG_MAX, "unsupported architecture"); |
| 384 | #else | 467 | #else |
| 385 | static_assert (1000000000 <= ULONG_MAX, "unsupported architecture"); | 468 | static_assert (1000000000 <= ULONG_MAX, "unsupported architecture"); |
| 386 | #endif | 469 | #endif |
| 387 | mpz_mul_ui (nanoseconds.value, nanoseconds.value, 1000000000); | 470 | mpz_mul_ui (nanoseconds, nanoseconds, 1000000000); |
| 388 | assert (0 <= time.tv_nsec && time.tv_nsec <= ULONG_MAX); | 471 | assert (0 <= time.tv_nsec && time.tv_nsec <= ULONG_MAX); |
| 389 | mpz_add_ui (nanoseconds.value, nanoseconds.value, time.tv_nsec); | 472 | mpz_add_ui (nanoseconds, nanoseconds, time.tv_nsec); |
| 390 | emacs_value result = env->make_big_integer (env, &nanoseconds); | 473 | emacs_value result = make_big_integer (env, nanoseconds); |
| 391 | mpz_clear (nanoseconds.value); | 474 | mpz_clear (nanoseconds); |
| 392 | return result; | 475 | return result; |
| 393 | } | 476 | } |
| 394 | 477 | ||
| @@ -398,12 +481,12 @@ Fmod_test_double (emacs_env *env, ptrdiff_t nargs, emacs_value *args, | |||
| 398 | { | 481 | { |
| 399 | assert (nargs == 1); | 482 | assert (nargs == 1); |
| 400 | emacs_value arg = args[0]; | 483 | emacs_value arg = args[0]; |
| 401 | struct emacs_mpz value; | 484 | mpz_t value; |
| 402 | mpz_init (value.value); | 485 | mpz_init (value); |
| 403 | env->extract_big_integer (env, arg, &value); | 486 | extract_big_integer (env, arg, value); |
| 404 | mpz_mul_ui (value.value, value.value, 2); | 487 | mpz_mul_ui (value, value, 2); |
| 405 | emacs_value result = env->make_big_integer (env, &value); | 488 | emacs_value result = make_big_integer (env, value); |
| 406 | mpz_clear (value.value); | 489 | mpz_clear (value); |
| 407 | return result; | 490 | return result; |
| 408 | } | 491 | } |
| 409 | 492 | ||