diff options
Diffstat (limited to 'extension/rwarray.c')
-rw-r--r-- | extension/rwarray.c | 207 |
1 files changed, 156 insertions, 51 deletions
diff --git a/extension/rwarray.c b/extension/rwarray.c index 45f9c734..9a8d15e9 100644 --- a/extension/rwarray.c +++ b/extension/rwarray.c @@ -5,10 +5,11 @@ * May 2009 * Redone June 2012 * Improved September 2017 + * GMP/MPFR support added November 2021 */ /* - * Copyright (C) 2009-2014, 2017, 2018, 2020 the Free Software Foundation, Inc. + * Copyright (C) 2009-2014, 2017, 2018, 2020, 2021 the Free Software Foundation, Inc. * * This file is part of GAWK, the GNU implementation of the * AWK Programming Language. @@ -49,6 +50,11 @@ #include <sys/types.h> #include <sys/stat.h> +#ifdef HAVE_MPFR +#include <gmp.h> +#include <mpfr.h> +#endif + #include "gawkapi.h" #include "gettext.h" @@ -56,12 +62,12 @@ #define N_(msgid) msgid #define MAGIC "awkrulz\n" -#define MAJOR 3 -#define MINOR 1 +#define MAJOR 4 +#define MINOR 0 static const gawk_api_t *api; /* for convenience macros to work */ static awk_ext_id_t ext_id; -static const char *ext_version = "rwarray extension: version 1.2"; +static const char *ext_version = "rwarray extension: version 2.0"; static awk_bool_t (*init_func)(void) = NULL; int plugin_is_GPL_compatible; @@ -69,10 +75,12 @@ int plugin_is_GPL_compatible; static awk_bool_t write_array(FILE *fp, awk_array_t array); static awk_bool_t write_elem(FILE *fp, awk_element_t *element); static awk_bool_t write_value(FILE *fp, awk_value_t *val); +static awk_bool_t write_number(FILE *fp, awk_value_t *val); static awk_bool_t read_array(FILE *fp, awk_array_t array); static awk_bool_t read_elem(FILE *fp, awk_element_t *element); static awk_bool_t read_value(FILE *fp, awk_value_t *value); +static awk_bool_t read_number(FILE *fp, awk_value_t *value, uint32_t code); /* * Format of array info: @@ -86,7 +94,7 @@ static awk_bool_t read_value(FILE *fp, awk_value_t *value); * For each element: * Length of index val: 4 bytes - network order * Index val as characters (N bytes) - * Value type 4 bytes (0 = string, 1 = number, 2 = array, 3 = regex, 4 = strnum, 5 = undefined) + * Value type 4 bytes (see list below) * IF string: * Length of value 4 bytes * Value as characters (N bytes) @@ -98,6 +106,15 @@ static awk_bool_t read_value(FILE *fp, awk_value_t *value); * END IF */ +#define VT_STRING 1 +#define VT_NUMBER 2 +#define VT_GMP 3 +#define VT_MPFR 4 +#define VT_ARRAY 5 +#define VT_REGEX 6 +#define VT_STRNUM 7 +#define VT_UNDEFINED 20 + /* do_writea --- write an array */ static awk_value_t * @@ -222,51 +239,95 @@ write_value(FILE *fp, awk_value_t *val) uint32_t code, len; if (val->val_type == AWK_ARRAY) { - code = htonl(2); + code = htonl(VT_ARRAY); if (fwrite(& code, 1, sizeof(code), fp) != sizeof(code)) return awk_false; return write_array(fp, val->array_cookie); } - if (val->val_type == AWK_NUMBER) { - code = htonl(1); - if (fwrite(& code, 1, sizeof(code), fp) != sizeof(code)) - return awk_false; + if (val->val_type == AWK_NUMBER) + return write_number(fp, val); + + switch (val->val_type) { + case AWK_STRING: + code = htonl(VT_STRING); + break; + case AWK_STRNUM: + code = htonl(VT_STRNUM); + break; + case AWK_REGEX: + code = htonl(VT_REGEX); + break; + case AWK_UNDEFINED: + code = htonl(VT_UNDEFINED); + break; + default: + /* XXX can this happen? */ + code = htonl(VT_UNDEFINED); + warning(ext_id, _("array value has unknown type %d"), val->val_type); + break; + } + if (fwrite(& code, 1, sizeof(code), fp) != sizeof(code)) + return awk_false; - if (fwrite(& val->num_value, 1, sizeof(val->num_value), fp) != sizeof(val->num_value)) - return awk_false; - } else { - switch (val->val_type) { - case AWK_STRING: - code = htonl(0); - break; - case AWK_STRNUM: - code = htonl(4); - break; - case AWK_REGEX: - code = htonl(3); - break; - case AWK_UNDEFINED: - code = htonl(5); - break; - default: - /* XXX can this happen? */ - code = htonl(0); - warning(ext_id, _("array value has unknown type %d"), val->val_type); - break; - } + len = htonl(val->str_value.len); + if (fwrite(& len, 1, sizeof(len), fp) != sizeof(len)) + return awk_false; + + if (fwrite(val->str_value.str, 1, val->str_value.len, fp) + != (ssize_t) val->str_value.len) + return awk_false; + + return awk_true; +} + +/* write_number --- write a double, GMP or MPFR number */ + +static awk_bool_t +write_number(FILE *fp, awk_value_t *val) +{ + uint32_t len, code; + char buffer[BUFSIZ]; + + if (val->num_type == AWK_NUMBER_TYPE_DOUBLE) { + uint32_t network_order_len; + + code = htonl(VT_NUMBER); if (fwrite(& code, 1, sizeof(code), fp) != sizeof(code)) return awk_false; - len = htonl(val->str_value.len); - if (fwrite(& len, 1, sizeof(len), fp) != sizeof(len)) + // for portability, save double precision number as a string + sprintf(buffer, "%.17g", val->num_value); + len = strlen(buffer) + 1; // get trailing '\0' too... + network_order_len = htonl(len); + + if (fwrite(& network_order_len, 1, sizeof(len), fp) != sizeof(len)) return awk_false; - if (fwrite(val->str_value.str, 1, val->str_value.len, fp) - != (ssize_t) val->str_value.len) + if (fwrite(buffer, 1, len, fp) != len) return awk_false; - } + } else { +#ifdef HAVE_MPFR + if (val->num_type == AWK_NUMBER_TYPE_MPFR) { + code = htonl(VT_MPFR); + if (fwrite(& code, 1, sizeof(code), fp) != sizeof(code)) + return awk_false; + if (mpfr_fpif_export(fp, val->num_ptr) != 0) + return awk_false; + } else { + code = htonl(VT_GMP); + if (fwrite(& code, 1, sizeof(code), fp) != sizeof(code)) + return awk_false; + + if (mpz_out_raw(fp, val->num_ptr) == 0) + return awk_false; + } +#else + fatal(ext_id, _("rwarray extension: received GMP/MPFR value but compiled without GMP/MPFR support.")); +#endif + } + // all the OK cases fall through to here return awk_true; } @@ -448,7 +509,7 @@ read_value(FILE *fp, awk_value_t *value) code = ntohl(code); - if (code == 2) { + if (code == VT_ARRAY) { awk_array_t array = create_array(); if (! read_array(fp, array)) @@ -457,31 +518,26 @@ read_value(FILE *fp, awk_value_t *value) /* hook into value */ value->val_type = AWK_ARRAY; value->array_cookie = array; - } else if (code == 1) { - double d; - - if (fread(& d, 1, sizeof(d), fp) != sizeof(d)) - return awk_false; - - /* hook into value */ - value->val_type = AWK_NUMBER; - value->num_value = d; + } else if (code == VT_NUMBER + || code == VT_GMP + || code == VT_MPFR) { + return read_number(fp, value, code); } else { if (fread(& len, 1, sizeof(len), fp) != sizeof(len)) { return awk_false; } len = ntohl(len); switch (code) { - case 0: + case VT_STRING: value->val_type = AWK_STRING; break; - case 3: + case VT_REGEX: value->val_type = AWK_REGEX; break; - case 4: + case VT_STRNUM: value->val_type = AWK_STRNUM; break; - case 5: + case VT_UNDEFINED: value->val_type = AWK_UNDEFINED; break; default: @@ -503,6 +559,55 @@ read_value(FILE *fp, awk_value_t *value) return awk_true; } +/* read_number --- read a double, GMP, or MPFR number */ + +static awk_bool_t +read_number(FILE *fp, awk_value_t *value, uint32_t code) +{ + uint32_t len; + + if (code == VT_NUMBER) { + char buffer[BUFSIZ]; + double d; + + if (fread(& len, 1, sizeof(len), fp) != sizeof(len)) + return awk_false; + + len = ntohl(len); + if (fread(buffer, 1, len, fp) != len) + return awk_false; + + (void) sscanf(buffer, "%lg", & d); + + /* hook into value */ + value = make_number(d, value); + } else { +#ifdef HAVE_MPFR + if (code == VT_GMP) { + mpz_t mp_ptr; + + mpz_init(mp_ptr); + if (mpz_inp_raw(mp_ptr, fp) == 0) + return awk_false; + + value = make_number_mpz(mp_ptr, value); + } else { + mpfr_t mpfr_val; + mpfr_init(mpfr_val); + + if (mpfr_fpif_import(mpfr_val, fp) != 0) + return awk_false; + + value = make_number_mpfr(& mpfr_val, value); + } +#else + fatal(ext_id(_("rwarray extension: GMP/MPFR value in file but compiled without GMP/MPFR support.")); +#endif + } + + return awk_true; +} + static awk_ext_func_t func_table[] = { { "writea", do_writea, 2, 2, awk_false, NULL }, { "reada", do_reada, 2, 2, awk_false, NULL }, |