From e290a7d1730c99010272bbff7f497c3041cef46d Mon Sep 17 00:00:00 2001 From: Philipp Stephani Date: Thu, 18 Apr 2019 22:38:29 +0200 Subject: [PATCH] Add module functions to convert from and to big integers. * src/module-env-27.h: Add new module functions to convert big integers. * src/emacs-module.h.in (emacs_mpz): Define if GMP is available. * src/emacs-module.c (module_extract_big_integer) (module_make_big_integer): New functions. (initialize_environment): Use them. * test/data/emacs-module/mod-test.c (Fmod_test_double): New test function. (emacs_module_init): Define it. * test/src/emacs-module-tests.el (mod-test-double): New unit test. * doc/lispref/internals.texi (Module Values): Document new functions. --- doc/lispref/internals.texi | 35 +++++++++++++++++++++++++++++++ etc/NEWS | 4 ++++ src/emacs-module.c | 27 ++++++++++++++++++++++++ src/emacs-module.h.in | 10 +++++++++ src/lisp.h | 1 + src/module-env-27.h | 8 +++++++ test/data/emacs-module/mod-test.c | 19 +++++++++++++++++ test/src/emacs-module-tests.el | 7 +++++++ 8 files changed, 111 insertions(+) diff --git a/doc/lispref/internals.texi b/doc/lispref/internals.texi index 0e7a1339e76..10f49c569fa 100644 --- a/doc/lispref/internals.texi +++ b/doc/lispref/internals.texi @@ -1508,6 +1508,41 @@ function raises the @code{overflow-error} error condition if string. @end deftypefn +If you define the preprocessor macro @code{EMACS_MODULE_GMP} before +including the header @file{emacs-module.h}, you can also convert +between Emacs integers and GMP @code{mpz_t} values. @xref{GMP +Basics,,,gmp}. If @code{EMACS_MODULE_GMP} is defined, +@file{emacs-module.h} wraps @code{mpz_t} in the following structure: + +@deftp struct emacs_mpz value +struct emacs_mpz @{ mpz_t value; @}; +@end deftp + +@noindent +Then you can use the following additional functions: + +@deftypefn Function bool extract_big_integer (emacs_env *@var{env}, emacs_value @var{arg}, struct emacs_mpz *@var{result}) +This function, which is available since Emacs 27, extracts the +integral value of @var{arg} into @var{result}. @var{result} must not +be @code{NULL}. @code{@var{result}->value} must be an initialized +@code{mpz_t} object. @xref{Initializing Integers,,,gmp}. If +@var{arg} is an integer, Emacs will store its value into +@code{@var{result}->value}. After you have finished using +@code{@var{result}->value}, you should free it using @code{mpz_clear} +or similar. +@end deftypefn + +@deftypefn Function emacs_value make_big_integer (emacs_env *@var{env}, const struct emacs_mpz *@var{value}) +This function, which is available since Emacs 27, takes an +arbitrary-sized integer argument and returns a corresponding +@code{emacs_value} object. @var{value} must not be @code{NULL}. +@code{@var{value}->value} must be an initialized @code{mpz_t} object. +@xref{Initializing Integers,,,gmp}. Emacs will return a corresponding +integral object. After you have finished using +@code{@var{value}->value}, you should free it using @code{mpz_clear} +or similar. +@end deftypefn + The @acronym{API} does not provide functions to manipulate Lisp data structures, for example, create lists with @code{cons} and @code{list} (@pxref{Building Lists}), extract list members with @code{car} and diff --git a/etc/NEWS b/etc/NEWS index fc9b828baa1..e861a372b17 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1913,6 +1913,10 @@ case. ** New module environment functions 'make_time' and 'extract_time' to convert between timespec structures and Emacs Lisp time values. +** New module environment functions 'make_big_integer' and +'extract_big_integer' to create and extract arbitrary-size integer +values. + * Changes in Emacs 27.1 on Non-Free Operating Systems diff --git a/src/emacs-module.c b/src/emacs-module.c index e46af30ce84..e203ce1d348 100644 --- a/src/emacs-module.c +++ b/src/emacs-module.c @@ -70,6 +70,7 @@ To add a new module function, proceed as follows: #include +#define EMACS_MODULE_GMP #include "emacs-module.h" #include @@ -79,7 +80,10 @@ To add a new module function, proceed as follows: #include #include +#include + #include "lisp.h" +#include "bignum.h" #include "dynlib.h" #include "coding.h" #include "keyboard.h" @@ -752,6 +756,27 @@ module_make_time (emacs_env *env, struct timespec time) return lisp_to_value (env, make_lisp_time (time)); } +static void +module_extract_big_integer (emacs_env *env, emacs_value value, + struct emacs_mpz *result) +{ + MODULE_FUNCTION_BEGIN (); + Lisp_Object o = value_to_lisp (value); + CHECK_INTEGER (o); + if (FIXNUMP (o)) + mpz_set_intmax (result->value, XFIXNUM (o)); + else + mpz_set (result->value, XBIGNUM (o)->value); +} + +static emacs_value +module_make_big_integer (emacs_env *env, const struct emacs_mpz *value) +{ + MODULE_FUNCTION_BEGIN (NULL); + mpz_set (mpz[0], value->value); + return lisp_to_value (env, make_integer_mpz ()); +} + /* Subroutines. */ @@ -1157,6 +1182,8 @@ initialize_environment (emacs_env *env, struct emacs_env_private *priv) env->process_input = module_process_input; env->extract_time = module_extract_time; env->make_time = module_make_time; + env->extract_big_integer = module_extract_big_integer; + env->make_big_integer = module_make_big_integer; Vmodule_environments = Fcons (make_mint_ptr (env), Vmodule_environments); return env; } diff --git a/src/emacs-module.h.in b/src/emacs-module.h.in index bfbe226dd90..e61aadfc3ac 100644 --- a/src/emacs-module.h.in +++ b/src/emacs-module.h.in @@ -28,6 +28,10 @@ along with GNU Emacs. If not, see . */ #include #endif +#ifdef EMACS_MODULE_GMP +#include +#endif + #if defined __cplusplus && __cplusplus >= 201103L # define EMACS_NOEXCEPT noexcept #else @@ -94,6 +98,12 @@ enum emacs_process_input_result emacs_process_input_quit = 1 }; +#ifdef EMACS_MODULE_GMP +struct emacs_mpz { mpz_t value; }; +#else +struct emacs_mpz; /* no definition */ +#endif + struct emacs_env_25 { @module_env_snippet_25@ diff --git a/src/lisp.h b/src/lisp.h index d803f160006..703fe76d64e 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -4151,6 +4151,7 @@ extern void *unexec_realloc (void *, size_t); extern void unexec_free (void *); #endif +#define EMACS_MODULE_GMP #include "emacs-module.h" /* Function prototype for the module Lisp functions. */ diff --git a/src/module-env-27.h b/src/module-env-27.h index e63843f8d63..00de3009007 100644 --- a/src/module-env-27.h +++ b/src/module-env-27.h @@ -8,3 +8,11 @@ emacs_value (*make_time) (emacs_env *env, struct timespec time) EMACS_ATTRIBUTE_NONNULL (1); + + void (*extract_big_integer) (emacs_env *env, emacs_value value, + struct emacs_mpz *result) + EMACS_ATTRIBUTE_NONNULL (1, 3); + + emacs_value (*make_big_integer) (emacs_env *env, + const struct emacs_mpz *value) + EMACS_ATTRIBUTE_NONNULL (1, 2); diff --git a/test/data/emacs-module/mod-test.c b/test/data/emacs-module/mod-test.c index dbdbfecfe6a..85a7f28e50d 100644 --- a/test/data/emacs-module/mod-test.c +++ b/test/data/emacs-module/mod-test.c @@ -27,8 +27,11 @@ along with GNU Emacs. If not, see . */ #include #include +#define EMACS_MODULE_GMP #include +#include + #include "timespec.h" int plugin_is_GPL_compatible; @@ -378,6 +381,21 @@ Fmod_test_add_nanosecond (emacs_env *env, ptrdiff_t nargs, emacs_value *args, return env->make_time (env, time); } +static emacs_value +Fmod_test_double (emacs_env *env, ptrdiff_t nargs, emacs_value *args, + void *data) +{ + assert (nargs == 1); + emacs_value arg = args[0]; + struct emacs_mpz value; + mpz_init (value.value); + env->extract_big_integer (env, arg, &value); + mpz_mul_ui (value.value, value.value, 2); + emacs_value result = env->make_big_integer (env, &value); + mpz_clear (value.value); + return result; +} + /* Lisp utilities for easier readability (simple wrappers). */ /* Provide FEATURE to Emacs. */ @@ -447,6 +465,7 @@ emacs_module_init (struct emacs_runtime *ert) NULL, NULL); DEFUN ("mod-test-sleep-until", Fmod_test_sleep_until, 2, 2, NULL, NULL); DEFUN ("mod-test-add-nanosecond", Fmod_test_add_nanosecond, 1, 1, NULL, NULL); + DEFUN ("mod-test-double", Fmod_test_double, 1, 1, NULL, NULL); #undef DEFUN diff --git a/test/src/emacs-module-tests.el b/test/src/emacs-module-tests.el index eea4c611655..78f238140da 100644 --- a/test/src/emacs-module-tests.el +++ b/test/src/emacs-module-tests.el @@ -338,4 +338,11 @@ Interactively, you can try hitting \\[keyboard-quit] to quit." (ert-info ((format "input: %s" input)) (should-error (mod-test-add-nanosecond input))))) +(ert-deftest mod-test-double () + (dolist (input (list 0 1 2 -1 42 12345678901234567890 + most-positive-fixnum (1+ most-positive-fixnum) + most-negative-fixnum (1- most-negative-fixnum))) + (ert-info ((format "input: %d" input)) + (should (= (mod-test-double input) (* 2 input)))))) + ;;; emacs-module-tests.el ends here -- 2.39.2