diff options
Diffstat (limited to 'src')
| -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 |
3 files changed, 151 insertions, 27 deletions
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); |