diff options
-rw-r--r-- | ChangeLog | 47 | ||||
-rw-r--r-- | awk.h | 18 | ||||
-rw-r--r-- | extension/ChangeLog | 10 | ||||
-rw-r--r-- | extension/Makefile.am | 5 | ||||
-rw-r--r-- | extension/Makefile.in | 31 | ||||
-rw-r--r-- | extension/aclocal.m4 | 1 | ||||
-rw-r--r-- | extension/configh.in | 6 | ||||
-rwxr-xr-x | extension/configure | 86 | ||||
-rw-r--r-- | extension/configure.ac | 11 | ||||
-rw-r--r-- | extension/intdiv.c | 206 | ||||
-rw-r--r-- | gawkapi.c | 117 | ||||
-rw-r--r-- | gawkapi.h | 97 | ||||
-rw-r--r-- | mpfr.c | 18 | ||||
-rw-r--r-- | node.c | 14 |
14 files changed, 621 insertions, 46 deletions
@@ -1,3 +1,8 @@ +2017-04-12 Arnold D. Robbins <arnold@skeeve.com> + + * gawkapi.c (awk_value_to_node): Initialize ext_ret_val to NULL + to avoid compiler warnings. + 2017-04-10 Andrew J. Schorr <aschorr@telemetry-investments.com> * awk.h (enum opcodeval): For the avoidance of doubt, specify that @@ -465,6 +470,48 @@ not updating the node correctly by setting STRING and STRCUR flags and setting stfmt. +2017-01-06 Andrew J. Schorr <aschorr@telemetry-investments.com> + + Enhance API to support extended-precision arithmetic. + * awk.h (enum block_id): Add new values BLOCK_MPFR and BLOCK_MPZ. + (make_number_node): New inline function to reduce code duplication + for creating numeric nodes. + * gawkapi.h (gawk_api_major_version): Bump to 3. + (awk_number_t): New typedef to represent numbers with varying internal + representations. + (awk_value_t): For numbers, replace double with awk_number_t. + (num_value): Redefine. + (num_type, num_ptr): New defines for awk_number_t union members. + (gawk_api_t): Add constants for version checking: gmp_major_version, + gmp_minor_version, mpfr_major_version, and mpfr_minor_version. + Add functions api_get_mpfr and api_get_mpz to allocate memory for + extended-precision numbers to hand to gawk. + (get_mpfr_ptr, get_mpz_ptr): Helper macros to warp api_get_mpfr and + api_get_mpz. + (make_number): Modify to populate awk_number_t correctly. + (make_number_mpz, make_number_mpfr): New helper functions to create + extended-precision numeric nodes. + (check_mpfr_version): New macro to check GMP/MPFR version compatibility + in extensions that want to support extended-precision math. + * gawkapi.c (getmpfr, freempfr, getmpz, freempz): New macros to + allocate and free memory blocks for extended-precision math. + (awk_value_to_node): For AWK_NUMBER values, support 3 different kinds + of internal numbers: double, mpz_t, and mpfr_t. + (assign_number): New helper function to convert a numeric node to + an awk_value_t. + (node_to_awk_value): Use assign_number in a couple of places to + pass numbers properly. + (api_get_mpfr): Implement new api_get_mpfr hook. + (api_get_mpfz): Implement new api_get_mpz hook. + (api_impl): Add GMP & MPFR versions, api_get_mpfr, and api_get_mpz. + * node.c (r_make_number): Use new make_number_node inline function + to reduce code duplication. + (nextfree): Add block allocators for mpfr_t and mpz_t. + (more_blocks): Add an assert to protect against cases where the block + size is too small to hold our structure. + * mpfr.c (mpg_node): Use new make_number_node inline function + to reduce code duplication. + 2017-01-04 Arnold Robbins <arnold@skeeve.com> * config.guess, config.sub, compile, depcomp: Sync from latest @@ -1072,6 +1072,8 @@ struct block_header { enum block_id { BLOCK_NODE = 0, BLOCK_BUCKET, + BLOCK_MPFR, + BLOCK_MPZ, BLOCK_MAX /* count */ }; @@ -1975,6 +1977,22 @@ erealloc_real(void *ptr, size_t count, const char *where, const char *var, const return ret; } +/* make_number_node --- make node with the give flags */ + +static inline NODE * +make_number_node(unsigned int tp) +{ + NODE *r; + getnode(r); + r->type = Node_val; + r->valref = 1; + r->flags = (tp|MALLOC|NUMBER|NUMCUR); + r->stptr = NULL; + r->stlen = 0; + r->wstptr = NULL; + r->wstlen = 0; + return r; +} /* * str_terminate_f, str_terminate, str_restore: function and macros to diff --git a/extension/ChangeLog b/extension/ChangeLog index d10dc766..87d4358d 100644 --- a/extension/ChangeLog +++ b/extension/ChangeLog @@ -64,6 +64,16 @@ system headers assume that if this is defined, it must have a numeric value. +2017-01-06 Andrew J. Schorr <aschorr@telemetry-investments.com> + + * intdiv.c: New extension to demonstrate how to implement intdiv + using the new extended-precision math API. + * Makefile.am (pkgextension_LTLIBRARIES): Add intdiv.la. + (intdiv_la_SOURCES, intdiv_la_LDFLAGS, intdiv_la_LIBADD): Add support + for new intdiv library. + * configure.ac (AC_CHECK_FUNCS): Check for fmod needed by intdiv. + (GNUPG_CHECK_MPFR): Add check for MPFR support. + 2016-12-22 Arnold D. Robbins <arnold@skeeve.com> * testext.c (valrep2str): Update for new API types. diff --git a/extension/Makefile.am b/extension/Makefile.am index 6ea16f5d..54975b8e 100644 --- a/extension/Makefile.am +++ b/extension/Makefile.am @@ -39,6 +39,7 @@ pkgextension_LTLIBRARIES = \ fnmatch.la \ fork.la \ inplace.la \ + intdiv.la \ ordchr.la \ readdir.la \ readfile.la \ @@ -72,6 +73,10 @@ inplace_la_SOURCES = inplace.c inplace_la_LDFLAGS = $(MY_MODULE_FLAGS) inplace_la_LIBADD = $(MY_LIBS) +intdiv_la_SOURCES = intdiv.c +intdiv_la_LDFLAGS = $(MY_MODULE_FLAGS) +intdiv_la_LIBADD = $(MY_LIBS) + ordchr_la_SOURCES = ordchr.c ordchr_la_LDFLAGS = $(MY_MODULE_FLAGS) ordchr_la_LIBADD = $(MY_LIBS) diff --git a/extension/Makefile.in b/extension/Makefile.in index c0e2676b..c386eac6 100644 --- a/extension/Makefile.in +++ b/extension/Makefile.in @@ -114,10 +114,10 @@ host_triplet = @host@ subdir = . ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/../m4/arch.m4 \ - $(top_srcdir)/m4/dirfd.m4 $(top_srcdir)/m4/libtool.m4 \ - $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ - $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ - $(top_srcdir)/configure.ac + $(top_srcdir)/../m4/mpfr.m4 $(top_srcdir)/m4/dirfd.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(top_srcdir)/configure \ @@ -187,6 +187,12 @@ inplace_la_OBJECTS = $(am_inplace_la_OBJECTS) inplace_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(inplace_la_LDFLAGS) $(LDFLAGS) -o $@ +intdiv_la_DEPENDENCIES = $(am__DEPENDENCIES_1) +am_intdiv_la_OBJECTS = intdiv.lo +intdiv_la_OBJECTS = $(am_intdiv_la_OBJECTS) +intdiv_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(intdiv_la_LDFLAGS) $(LDFLAGS) -o $@ ordchr_la_DEPENDENCIES = $(am__DEPENDENCIES_1) am_ordchr_la_OBJECTS = ordchr.lo ordchr_la_OBJECTS = $(am_ordchr_la_OBJECTS) @@ -277,14 +283,14 @@ am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(filefuncs_la_SOURCES) $(fnmatch_la_SOURCES) \ - $(fork_la_SOURCES) $(inplace_la_SOURCES) $(ordchr_la_SOURCES) \ - $(readdir_la_SOURCES) $(readdir_test_la_SOURCES) \ + $(fork_la_SOURCES) $(inplace_la_SOURCES) $(intdiv_la_SOURCES) \ + $(ordchr_la_SOURCES) $(readdir_la_SOURCES) $(readdir_test_la_SOURCES) \ $(readfile_la_SOURCES) $(revoutput_la_SOURCES) \ $(revtwoway_la_SOURCES) $(rwarray_la_SOURCES) \ $(testext_la_SOURCES) $(time_la_SOURCES) DIST_SOURCES = $(filefuncs_la_SOURCES) $(fnmatch_la_SOURCES) \ - $(fork_la_SOURCES) $(inplace_la_SOURCES) $(ordchr_la_SOURCES) \ - $(readdir_la_SOURCES) $(readdir_test_la_SOURCES) \ + $(fork_la_SOURCES) $(inplace_la_SOURCES) $(intdiv_la_SOURCES) \ + $(ordchr_la_SOURCES) $(readdir_la_SOURCES) $(readdir_test_la_SOURCES) \ $(readfile_la_SOURCES) $(revoutput_la_SOURCES) \ $(revtwoway_la_SOURCES) $(rwarray_la_SOURCES) \ $(testext_la_SOURCES) $(time_la_SOURCES) @@ -422,6 +428,7 @@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ +LIBMPFR = @LIBMPFR@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ @@ -520,6 +527,7 @@ pkgextension_LTLIBRARIES = \ fnmatch.la \ fork.la \ inplace.la \ + intdiv.la \ ordchr.la \ readdir.la \ readfile.la \ @@ -549,6 +557,9 @@ fork_la_LIBADD = $(MY_LIBS) inplace_la_SOURCES = inplace.c inplace_la_LDFLAGS = $(MY_MODULE_FLAGS) inplace_la_LIBADD = $(MY_LIBS) +intdiv_la_SOURCES = intdiv.c +intdiv_la_LDFLAGS = $(MY_MODULE_FLAGS) +intdiv_la_LIBADD = $(MY_LIBS) ordchr_la_SOURCES = ordchr.c ordchr_la_LDFLAGS = $(MY_MODULE_FLAGS) ordchr_la_LIBADD = $(MY_LIBS) @@ -713,6 +724,9 @@ fork.la: $(fork_la_OBJECTS) $(fork_la_DEPENDENCIES) $(EXTRA_fork_la_DEPENDENCIES inplace.la: $(inplace_la_OBJECTS) $(inplace_la_DEPENDENCIES) $(EXTRA_inplace_la_DEPENDENCIES) $(AM_V_CCLD)$(inplace_la_LINK) -rpath $(pkgextensiondir) $(inplace_la_OBJECTS) $(inplace_la_LIBADD) $(LIBS) +intdiv.la: $(intdiv_la_OBJECTS) $(intdiv_la_DEPENDENCIES) $(EXTRA_intdiv_la_DEPENDENCIES) + $(AM_V_CCLD)$(intdiv_la_LINK) -rpath $(pkgextensiondir) $(intdiv_la_OBJECTS) $(intdiv_la_LIBADD) $(LIBS) + ordchr.la: $(ordchr_la_OBJECTS) $(ordchr_la_DEPENDENCIES) $(EXTRA_ordchr_la_DEPENDENCIES) $(AM_V_CCLD)$(ordchr_la_LINK) -rpath $(pkgextensiondir) $(ordchr_la_OBJECTS) $(ordchr_la_LIBADD) $(LIBS) @@ -751,6 +765,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fork.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gawkfts.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/inplace.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/intdiv.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ordchr.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/readdir.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/readdir_test.Plo@am__quote@ diff --git a/extension/aclocal.m4 b/extension/aclocal.m4 index 5665d48e..1e7a343c 100644 --- a/extension/aclocal.m4 +++ b/extension/aclocal.m4 @@ -1211,6 +1211,7 @@ AC_SUBST([am__untar]) ]) # _AM_PROG_TAR m4_include([../m4/arch.m4]) +m4_include([../m4/mpfr.m4]) m4_include([m4/dirfd.m4]) m4_include([m4/libtool.m4]) m4_include([m4/ltoptions.m4]) diff --git a/extension/configh.in b/extension/configh.in index d3a226a5..9d3d9919 100644 --- a/extension/configh.in +++ b/extension/configh.in @@ -27,6 +27,9 @@ /* Define to 1 if you have the `fdopendir' function. */ #undef HAVE_FDOPENDIR +/* Define to 1 if you have the `fmod' function. */ +#undef HAVE_FMOD + /* Define to 1 if you have the `fnmatch' function. */ #undef HAVE_FNMATCH @@ -51,6 +54,9 @@ /* Define to 1 if you have the <memory.h> header file. */ #undef HAVE_MEMORY_H +/* Define to 1 if you have fully functional mpfr and gmp libraries. */ +#undef HAVE_MPFR + /* Define to 1 if you have the `nanosleep' function. */ #undef HAVE_NANOSLEEP diff --git a/extension/configure b/extension/configure index 40601550..580cc150 100755 --- a/extension/configure +++ b/extension/configure @@ -635,6 +635,7 @@ ac_subst_vars='am__EXEEXT_FALSE am__EXEEXT_TRUE LTLIBOBJS LIBOBJS +LIBMPFR pkgextensiondir LT_SYS_LIBRARY_PATH OTOOL64 @@ -763,6 +764,7 @@ with_aix_soname with_gnu_ld with_sysroot enable_libtool_lock +with_mpfr ' ac_precious_vars='build_alias host_alias @@ -1416,6 +1418,7 @@ Optional Packages: --with-gnu-ld assume the C compiler uses GNU ld [default=no] --with-sysroot[=DIR] Search for dependent libraries within DIR (or the compiler's sysroot if not specified). + --with-mpfr=DIR look for the mpfr and gmp libraries in DIR Some influential environment variables: CC C compiler command @@ -12859,7 +12862,88 @@ $as_echo "#define TIME_WITH_SYS_TIME 1" >>confdefs.h fi -for ac_func in fdopendir fnmatch gettimeofday \ +case `uname -m` in +*'Power Macintosh'*) + : ;; +*) + + +# Check whether --with-mpfr was given. +if test "${with_mpfr+set}" = set; then : + withval=$with_mpfr; _do_mpfr=$withval +else + _do_mpfr=yes +fi + + + if test "$_do_mpfr" != "no" ; then + if test -d "$withval" ; then + CPPFLAGS="${CPPFLAGS} -I$withval/include" + LDFLAGS="${LDFLAGS} -L$withval/lib" + fi + + _mpfr_save_libs=$LIBS + _combo="-lmpfr -lgmp" + LIBS="$LIBS $_combo" + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether mpfr via \"$_combo\" is present and usable" >&5 +$as_echo_n "checking whether mpfr via \"$_combo\" is present and usable... " >&6; } + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + +#include <stdio.h> +#include <mpfr.h> +#include <gmp.h> + +int +main () +{ + +mpfr_t p; +mpz_t z; +mpfr_init(p); +mpz_init(z); +mpfr_printf("%Rf%Zd", p, z); +mpfr_clear(p); +mpz_clear(z); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + _found_mpfr=yes +else + _found_mpfr=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $_found_mpfr" >&5 +$as_echo "$_found_mpfr" >&6; } + + LIBS=$_mpfr_save_libs + + if test $_found_mpfr = yes ; then + +$as_echo "#define HAVE_MPFR 1" >>confdefs.h + + LIBMPFR=$_combo + + break + fi + + unset _mpfr_save_libs + unset _combo + unset _found_mpfr + fi + + ;; +esac + +for ac_func in fdopendir fnmatch gettimeofday fmod \ getdtablesize nanosleep select statvfs GetSystemTimeAsFileTime do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` diff --git a/extension/configure.ac b/extension/configure.ac index b5b27d03..58935b69 100644 --- a/extension/configure.ac +++ b/extension/configure.ac @@ -69,7 +69,16 @@ AC_HEADER_DIRENT AC_HEADER_MAJOR AC_HEADER_TIME -AC_CHECK_FUNCS(fdopendir fnmatch gettimeofday \ +dnl check for mpfr support +case `uname -m` in +*'Power Macintosh'*) + : ;; +*) + GNUPG_CHECK_MPFR + ;; +esac + +AC_CHECK_FUNCS(fdopendir fnmatch gettimeofday fmod \ getdtablesize nanosleep select statvfs GetSystemTimeAsFileTime) GAWK_FUNC_DIRFD diff --git a/extension/intdiv.c b/extension/intdiv.c new file mode 100644 index 00000000..2f446c47 --- /dev/null +++ b/extension/intdiv.c @@ -0,0 +1,206 @@ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <assert.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <math.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include "gawkapi.h" + +#ifdef HAVE_MPFR +#include <gmp.h> +#include <mpfr.h> +#endif + +#include "gettext.h" +#define _(msgid) gettext(msgid) +#define N_(msgid) msgid + +static const gawk_api_t *api; /* for convenience macros to work */ +static awk_ext_id_t *ext_id; +static const char *ext_version = "intdiv extension: version 1.0"; + +int plugin_is_GPL_compatible; + +static double +double_to_int(double d) +{ + if (d >= 0) + d = floor(d); + else + d = ceil(d); + return d; +} + +static void +array_set_number(awk_array_t array, const char *sub, size_t sublen, double num) +{ + awk_value_t index, tmp; + + set_array_element(array, make_const_string(sub, sublen, & index), make_number(num, & tmp)); +} + +#ifdef HAVE_MPFR + +static mpz_ptr +mpz_conv(const awk_value_t *arg, mpz_ptr tmp) +{ + switch (arg->num_type) { + case AWK_NUMBER_TYPE_MPZ: + return arg->num_ptr; + case AWK_NUMBER_TYPE_MPFR: + if (! mpfr_number_p(arg->num_ptr)) + return NULL; + mpz_init(tmp); + mpfr_get_z(tmp, arg->num_ptr, MPFR_RNDZ); + return tmp; + case AWK_NUMBER_TYPE_DOUBLE: /* can this happen? */ + mpz_init(tmp); + mpz_set_d(tmp, double_to_int(arg->num_value)); + return tmp; + default: /* should never happen */ + fatal(ext_id, _("intdiv: invalid numeric type `%d'"), arg->num_type); + return NULL; + } +} + +static void +array_set_mpz(awk_array_t array, const char *sub, size_t sublen, mpz_ptr num) +{ + awk_value_t index, tmp; + + set_array_element(array, make_const_string(sub, sublen, & index), make_number_mpz(num, & tmp)); +} + +#endif + +/* do_intdiv --- do integer division, return quotient and remainder in dest array */ + +/* + * We define the semantics as: + * numerator = int(numerator) + * denominator = int(denonmator) + * quotient = int(numerator / denomator) + * remainder = int(numerator % denomator) + */ + +static awk_value_t * +do_intdiv(int nargs, awk_value_t *result, struct awk_ext_func *unused) +{ + awk_value_t nv, dv, array_param; + awk_array_t array; + + if (! get_argument(0, AWK_NUMBER, & nv)) { + warning(ext_id, _("intdiv: first argument must be numeric")); + return make_number(-1, result); + } + if (! get_argument(1, AWK_NUMBER, & dv)) { + warning(ext_id, _("intdiv: second argument must be numeric")); + return make_number(-1, result); + } + if (! get_argument(2, AWK_ARRAY, & array_param)) { + warning(ext_id, _("intdiv: third argument must be an array")); + return make_number(-1, result); + } + array = array_param.array_cookie; + clear_array(array); + +#ifdef HAVE_MPFR + if (nv.num_type == AWK_NUMBER_TYPE_DOUBLE && dv.num_type == AWK_NUMBER_TYPE_DOUBLE) { +#endif + /* regular precision */ + double num, denom, quotient, remainder; + + num = double_to_int(nv.num_value); + denom = double_to_int(dv.num_value); + + if (denom == 0.0) + fatal(ext_id, _("intdiv: division by zero attempted")); + + quotient = double_to_int(num / denom); +#ifdef HAVE_FMOD + remainder = fmod(num, denom); +#else /* ! HAVE_FMOD */ + (void) modf(num / denom, & remainder); + remainder = num - remainder * denom; +#endif /* ! HAVE_FMOD */ + remainder = double_to_int(remainder); + + array_set_number(array, "quotient", 8, quotient); + array_set_number(array, "remainder", 9, remainder); +#ifdef HAVE_MPFR + } + else { + /* extended precision */ + mpz_ptr numer, denom; + mpz_t numer_tmp, denom_tmp; + mpz_ptr quotient, remainder; + + /* convert numerator and denominator to integer */ + if (!(numer = mpz_conv(&nv, numer_tmp))) { + warning(ext_id, _("intdiv: numerator is not finite")); + return make_number(-1, result); + } + if (!(denom = mpz_conv(&dv, denom_tmp))) { + warning(ext_id, _("intdiv: denominator is not finite")); + if (numer == numer_tmp) + mpz_clear(numer); + return make_number(-1, result); + } + if (mpz_sgn(denom) == 0) { + warning(ext_id, _("intdiv: division by zero attempted")); + if (numer == numer_tmp) + mpz_clear(numer); + if (denom == denom_tmp) + mpz_clear(denom); + return make_number(-1, result); + } + + /* ask gawk to allocate return values for us */ + quotient = get_mpz_ptr(); + remainder = get_mpz_ptr(); + + /* do the division */ + mpz_tdiv_qr(quotient, remainder, numer, denom); + + array_set_mpz(array, "quotient", 8, quotient); + array_set_mpz(array, "remainder", 9, remainder); + + /* release temporary variables */ + if (numer == numer_tmp) + mpz_clear(numer); + if (denom == denom_tmp) + mpz_clear(denom); + } +#endif + + return make_number(0, result); +} + +static awk_ext_func_t func_table[] = { + { "api_intdiv", do_intdiv, 3, 3, awk_false, NULL }, +}; + +/* init_intdiv --- initialization routine */ + +static awk_bool_t +init_intdiv(void) +{ +#ifdef HAVE_MPFR + check_mpfr_version(intdiv) +#endif + return awk_true; +} + +static awk_bool_t (*init_func)(void) = init_intdiv; + +/* define the dl_load function using the boilerplate macro */ + +dl_load_func(func_table, intdiv, "") @@ -25,6 +25,14 @@ #include "awk.h" +#ifdef HAVE_MPFR +#define getmpfr(n) getblock(n, BLOCK_MPFR, mpfr_ptr) +#define freempfr(n) freeblock(n, BLOCK_MPFR) + +#define getmpz(n) getblock(n, BLOCK_MPZ, mpz_ptr) +#define freempz(n) freeblock(n, BLOCK_MPZ) +#endif + /* Declare some globals used by api_get_file: */ extern IOBUF *curfile; extern INSTRUCTION *main_beginfile; @@ -145,7 +153,7 @@ api_set_argument(awk_ext_id_t id, NODE * awk_value_to_node(const awk_value_t *retval) { - NODE *ext_ret_val; + NODE *ext_ret_val = NULL; NODE *v; if (retval == NULL) @@ -159,7 +167,36 @@ awk_value_to_node(const awk_value_t *retval) ext_ret_val = dupnode(Nnull_string); break; case AWK_NUMBER: - ext_ret_val = make_number(retval->num_value); + switch (retval->num_type) { + case AWK_NUMBER_TYPE_DOUBLE: + ext_ret_val = make_number(retval->num_value); + break; + case AWK_NUMBER_TYPE_MPFR: +#ifdef HAVE_MPFR + if (! do_mpfr) + fatal(_("awk_value_to_node: not in MPFR mode")); + ext_ret_val = make_number_node(MPFN); + memcpy(&ext_ret_val->mpg_numbr, retval->num_ptr, sizeof(ext_ret_val->mpg_numbr)); + freempfr(retval->num_ptr); +#else + fatal(_("awk_value_to_node: MPFR not supported")); +#endif + break; + case AWK_NUMBER_TYPE_MPZ: +#ifdef HAVE_MPFR + if (! do_mpfr) + fatal(_("awk_value_to_node: not in MPFR mode")); + ext_ret_val = make_number_node(MPZN); + memcpy(&ext_ret_val->mpg_i, retval->num_ptr, sizeof(ext_ret_val->mpg_i)); + freempz(retval->num_ptr); +#else + fatal(_("awk_value_to_node: MPFR not supported")); +#endif + break; + default: + fatal(_("awk_value_to_node: invalid number type `%d'"), retval->num_type); + break; + } break; case AWK_STRING: ext_ret_val = make_str_node(retval->str_value.str, @@ -450,6 +487,34 @@ assign_string(NODE *node, awk_value_t *val, awk_valtype_t val_type) val->str_value.len = node->stlen; } +/* assign_number -- return a number node */ + +static inline void +assign_number(NODE *node, awk_value_t *val) +{ + val->val_type = AWK_NUMBER; + switch (node->flags & (MPFN|MPZN)) { + case 0: + val->num_value = node->numbr; + val->num_type = AWK_NUMBER_TYPE_DOUBLE; + val->num_ptr = NULL; + break; + case MPFN: + val->num_value = mpfr_get_d(node->mpg_numbr, ROUND_MODE); + val->num_type = AWK_NUMBER_TYPE_MPFR; + val->num_ptr = &node->mpg_numbr; + break; + case MPZN: + val->num_value = mpz_get_d(node->mpg_i); + val->num_type = AWK_NUMBER_TYPE_MPZ; + val->num_ptr = &node->mpg_i; + break; + default: + fatal(_("node_to_awk_value: detected invalid numeric flags combination `%s'; please file a bug report."), flags2str(node->flags)); + break; + } +} + /* assign_regex --- return a regex node */ static inline void @@ -502,9 +567,8 @@ node_to_awk_value(NODE *node, awk_value_t *val, awk_valtype_t wanted) if (node->flags & REGEX) val->val_type = AWK_REGEX; else { - val->val_type = AWK_NUMBER; (void) force_number(node); - val->num_value = get_number_d(node); + assign_number(node, val); ret = awk_true; } break; @@ -606,8 +670,7 @@ node_to_awk_value(NODE *node, awk_value_t *val, awk_valtype_t wanted) ret = awk_true; break; case NUMBER: - val->val_type = AWK_NUMBER; - val->num_value = get_number_d(node); + assign_number(node, val); ret = awk_true; break; case NUMBER|USER_INPUT: @@ -1220,6 +1283,36 @@ api_release_value(awk_ext_id_t id, awk_value_cookie_t value) return awk_true; } +/* api_get_mpfr --- allocate an mpfr_ptr */ + +static void * +api_get_mpfr(awk_ext_id_t id) +{ +#ifdef HAVE_MPFR + mpfr_ptr p; + getmpfr(p); + mpfr_init(p); + return p; +#else + fatal(_("api_get_mpfr: MPFR not supported")); +#endif +} + +/* api_get_mpz --- allocate an mpz_ptr */ + +static void * +api_get_mpz(awk_ext_id_t id) +{ +#ifdef HAVE_MPFR + mpz_ptr p; + getmpz(p); + mpz_init(p); + return p; +#else + fatal(_("api_get_mpfr: MPFR not supported")); +#endif +} + /* api_get_file --- return a handle to an existing or newly opened file */ static awk_bool_t @@ -1347,6 +1440,16 @@ gawk_api_t api_impl = { /* data */ GAWK_API_MAJOR_VERSION, /* major and minor versions */ GAWK_API_MINOR_VERSION, + +#ifdef HAVE_MPFR + __GNU_MP_VERSION, + __GNU_MP_VERSION_MINOR, + MPFR_VERSION_MAJOR, + MPFR_VERSION_MINOR, +#else + 0, 0, 0, 0, +#endif + { 0 }, /* do_flags */ /* registration functions */ @@ -1399,6 +1502,8 @@ gawk_api_t api_impl = { calloc, realloc, free, + api_get_mpfr, + api_get_mpz, /* Find/open a file */ api_get_file, @@ -296,7 +296,7 @@ typedef struct awk_two_way_processor { awk_const struct awk_two_way_processor *awk_const next; /* for use by gawk */ } awk_two_way_processor_t; -#define gawk_api_major_version 2 +#define gawk_api_major_version 3 #define gawk_api_minor_version 0 /* Current version of the API. */ @@ -322,6 +322,16 @@ typedef struct awk_string { size_t len; /* length thereof, in chars */ } awk_string_t; +typedef struct awk_number { + double d; /* always populated in data received from gawk */ + enum AWK_NUMBER_TYPE { + AWK_NUMBER_TYPE_DOUBLE, + AWK_NUMBER_TYPE_MPFR, + AWK_NUMBER_TYPE_MPZ + } type; + void *ptr; /* either NULL or mpfr_ptr or mpz_ptr */ +} awk_number_t; + /* Arrays are represented as an opaque type. */ typedef void *awk_array_t; @@ -357,7 +367,7 @@ typedef struct awk_value { awk_valtype_t val_type; union { awk_string_t s; - double d; + awk_number_t n; awk_array_t a; awk_scalar_t scl; awk_value_cookie_t vc; @@ -365,7 +375,9 @@ typedef struct awk_value { #define str_value u.s #define strnum_value str_value #define regex_value str_value -#define num_value u.d +#define num_value u.n.d +#define num_type u.n.type +#define num_ptr u.n.ptr #define array_cookie u.a #define scalar_cookie u.scl #define value_cookie u.vc @@ -451,6 +463,12 @@ typedef struct gawk_api { awk_const int major_version; awk_const int minor_version; + /* GMP/MPFR versions, if extended-precision is available */ + awk_const int gmp_major_version; + awk_const int gmp_minor_version; + awk_const int mpfr_major_version; + awk_const int mpfr_minor_version; + /* * These can change on the fly as things happen within gawk. * Currently only do_lint is prone to change, but we reserve @@ -747,6 +765,20 @@ typedef struct gawk_api { void *(*api_realloc)(void *ptr, size_t size); void (*api_free)(void *ptr); + /* + * A function that returns mpfr data should call this function + * to allocate and initialize an mpfr_ptr for use in an + * awk_value_t structure that will be handed to gawk. + */ + void *(*api_get_mpfr)(awk_ext_id_t id); + + /* + * A function that returns mpz data should call this function + * to allocate and initialize an mpz_ptr for use in an + * awk_value_t structure that will be handed to gawk. + */ + void *(*api_get_mpz)(awk_ext_id_t id); + /* * Look up a file. If the name is NULL or name_len is 0, it returns * data for the currently open input file corresponding to FILENAME @@ -779,7 +811,6 @@ typedef struct gawk_api { */ const awk_input_buf_t **ibufp, const awk_output_buf_t **obufp); - } gawk_api_t; #ifndef GAWK /* these are not for the gawk code itself! */ @@ -869,6 +900,9 @@ typedef struct gawk_api { #define get_file(name, namelen, filetype, fd, ibuf, obuf) \ (api->api_get_file(ext_id, name, namelen, filetype, fd, ibuf, obuf)) +#define get_mpfr_ptr() (api->api_get_mpfr(ext_id)) +#define get_mpz_ptr() (api->api_get_mpz(ext_id)) + #define register_ext_version(version) \ (api->api_register_ext_version(ext_id, version)) @@ -959,11 +993,39 @@ make_null_string(awk_value_t *result) static inline awk_value_t * make_number(double num, awk_value_t *result) { - memset(result, 0, sizeof(*result)); - result->val_type = AWK_NUMBER; result->num_value = num; + result->num_type = AWK_NUMBER_TYPE_DOUBLE; + return result; +} + +/* + * make_number_mpz --- make an mpz number value in result. + * The mpz_ptr must be from a call to get_mpz_ptr. Gawk will now + * take ownership of this memory. + */ +static inline awk_value_t * +make_number_mpz(void *mpz_ptr, awk_value_t *result) +{ + result->val_type = AWK_NUMBER; + result->num_type = AWK_NUMBER_TYPE_MPZ; + result->num_ptr = mpz_ptr; + return result; +} + +/* + * make_number_mpfr --- make an mpfr number value in result. + * The mpfr_ptr must be from a call to get_mpfr_ptr. Gawk will now + * take ownership of this memory. + */ + +static inline awk_value_t * +make_number_mpfr(void *mpfr_ptr, awk_value_t *result) +{ + result->val_type = AWK_NUMBER; + result->num_type = AWK_NUMBER_TYPE_MPFR; + result->num_ptr = mpfr_ptr; return result; } @@ -1056,6 +1118,29 @@ int dl_load(const gawk_api_t *const api_p, awk_ext_id_t id) \ return (errors == 0); \ } +/* + * If you are using extended-precision calculations in your library, please + * call this macro from your init_func. + */ +#define check_mpfr_version(extension) { \ + if (api->gmp_major_version != __GNU_MP_VERSION \ + || api->gmp_minor_version < __GNU_MP_VERSION_MINOR) { \ + fprintf(stderr, #extension ": GMP version mismatch with gawk!\n"); \ + fprintf(stderr, "\tmy version (%d, %d), gawk version (%d, %d)\n", \ + __GNU_MP_VERSION, __GNU_MP_VERSION_MINOR, \ + api->gmp_major_version, api->gmp_minor_version); \ + exit(1); \ + } \ + if (api->mpfr_major_version != MPFR_VERSION_MAJOR \ + || api->mpfr_minor_version < MPFR_VERSION_MINOR) { \ + fprintf(stderr, #extension ": MPFR version mismatch with gawk!\n"); \ + fprintf(stderr, "\tmy version (%d, %d), gawk version (%d, %d)\n", \ + MPFR_VERSION_MAJOR, MPFR_VERSION_MINOR, \ + api->mpfr_major_version, api->mpfr_minor_version); \ + exit(1); \ + } \ +} + #endif /* GAWK */ #ifdef __cplusplus @@ -105,26 +105,14 @@ cleanup_mpfr(void) NODE * mpg_node(unsigned int tp) { - NODE *r; - getnode(r); - r->type = Node_val; + NODE *r = make_number_node(tp); - if (tp == MPFN) { + if (tp == MPFN) /* Initialize, set precision to the default precision, and value to NaN */ mpfr_init(r->mpg_numbr); - r->flags = MPFN; - } else { + else /* Initialize and set value to 0 */ mpz_init(r->mpg_i); - r->flags = MPZN; - } - - r->valref = 1; - r->flags |= MALLOC|NUMBER|NUMCUR; - r->stptr = NULL; - r->stlen = 0; - r->wstptr = NULL; - r->wstlen = 0; return r; } @@ -333,16 +333,8 @@ r_dupnode(NODE *n) static NODE * r_make_number(double x) { - NODE *r; - getnode(r); - r->type = Node_val; + NODE *r = make_number_node(0); r->numbr = x; - r->flags = MALLOC|NUMBER|NUMCUR; - r->valref = 1; - r->stptr = NULL; - r->stlen = 0; - r->wstptr = NULL; - r->wstlen = 0; return r; } @@ -1006,6 +998,10 @@ void init_btowc_cache() struct block_header nextfree[BLOCK_MAX] = { { NULL, sizeof(NODE) }, { NULL, sizeof(BUCKET) }, +#ifdef HAVE_MPFR + { NULL, sizeof(mpfr_t) }, + { NULL, sizeof(mpz_t) }, +#endif }; |