aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/emacs-module.c144
-rw-r--r--src/emacs-module.h.in22
-rw-r--r--src/module-env-27.h12
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
775static void 769/*
776module_extract_big_integer (emacs_env *env, emacs_value value, 770Big integer support.
777 struct emacs_mpz *result) 771
772There are two possible ways to support big integers in the module API
773that have been discussed:
774
7751. Exposing GMP numbers (mpz_t) directly in the API.
776
7772. Isolating the API from GMP by converting to/from a custom
778 sign-magnitude representation.
779
780Approach (1) has the advantage of being faster (no import/export
781required) and requiring less code in Emacs and in modules that would
782use GMP anyway. However, (1) also couples big integer support
783directly to the current implementation in Emacs (GMP). Also (1)
784requires each module author to ensure that their module is linked to
785the same GMP library as Emacs itself; in particular, module authors
786can't link GMP statically. (1) also requires conditional compilation
787and workarounds to ensure the module interface still works if GMP
788isn't available while including emacs-module.h. It also means that
789modules written in languages such as Go and Java that support big
790integers without GMP now have to carry an otherwise unnecessary GMP
791dependency. Approach (2), on the other hand, neatly decouples the
792module interface from the GMP-based implementation. It's not
793significantly more complex than (1) either: the additional code is
794mostly straightforward. Over all, the benefits of (2) over (1) are
795large enough to prefer it here.
796
797We use a simple sign-magnitude representation for the big integers.
798For the magnitude we pick an array of an unsigned integer type similar
799to mp_limb_t instead of e.g. unsigned char. This matches in most
800cases the representation of a GMP limb. In such cases GMP picks an
801optimized algorithm for mpz_import and mpz_export that boils down to a
802single memcpy to convert the magnitude. This way we largely avoid the
803import/export overhead on most platforms.
804*/
805
806enum
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
812static bool
813module_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
785static emacs_value 895static emacs_value
786module_make_big_integer (emacs_env *env, const struct emacs_mpz *value) 896module_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/*
104struct emacs_mpz { mpz_t value; }; 101Implementation note: We define emacs_limb_t so that it is likely to
102match the GMP mp_limb_t type. If the types match, GMP can use an
103optimization for mpz_import and mpz_export that boils down to a
104memcpy. According to https://gmplib.org/manual/ABI-and-ISA.html GMP
105will prefer a 64-bit limb and will default to unsigned long if that is
106wide enough. Note that this is an internal micro-optimization. Users
107shouldn't rely on the exact size of emacs_limb_t.
108*/
109#if ULONG_MAX == 0xFFFFFFFF
110typedef unsigned long long emacs_limb_t;
111# define EMACS_LIMB_MAX ULLONG_MAX
105#else 112#else
106struct emacs_mpz; /* no definition */ 113typedef unsigned long emacs_limb_t;
114# define EMACS_LIMB_MAX ULONG_MAX
107#endif 115#endif
108 116
109struct emacs_env_25 117struct 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);