diff options
Diffstat (limited to 'extension/rwarray.c')
-rw-r--r-- | extension/rwarray.c | 314 |
1 files changed, 251 insertions, 63 deletions
diff --git a/extension/rwarray.c b/extension/rwarray.c index 9a8c1e4a..8d6d28ef 100644 --- a/extension/rwarray.c +++ b/extension/rwarray.c @@ -78,10 +78,16 @@ 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); +typedef union { + mpz_t mpz_val; + mpfr_t mpfr_val; +} value_storage; + +typedef awk_array_t (*array_handle_t)(awk_value_t *); 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); +static awk_bool_t read_elem(FILE *fp, awk_element_t *element, array_handle_t, value_storage *); +static awk_bool_t read_value(FILE *fp, awk_value_t *value, array_handle_t, awk_value_t *idx, value_storage *vs); +static awk_bool_t read_number(FILE *fp, awk_value_t *value, uint32_t code, value_storage *); /* * Format of array info: @@ -117,12 +123,12 @@ static awk_bool_t read_number(FILE *fp, awk_value_t *value, uint32_t code); #define VT_BOOL 8 #define VT_UNDEFINED 20 -/* do_writea --- write an array */ +/* write_backend --- write an array */ static awk_value_t * -do_writea(int nargs, awk_value_t *result, struct awk_ext_func *unused) +write_backend(awk_value_t *result, awk_array_t array, const char *name) { - awk_value_t filename, array; + awk_value_t filename; FILE *fp = NULL; uint32_t major = MAJOR; uint32_t minor = MINOR; @@ -130,18 +136,9 @@ do_writea(int nargs, awk_value_t *result, struct awk_ext_func *unused) assert(result != NULL); make_number(0.0, result); - if (nargs < 2) - goto out; - - /* filename is first arg, array to dump is second */ + /* filename is first arg */ if (! get_argument(0, AWK_STRING, & filename)) { - warning(ext_id, _("do_writea: first argument is not a string")); - errno = EINVAL; - goto done1; - } - - if (! get_argument(1, AWK_ARRAY, & array)) { - warning(ext_id, _("do_writea: second argument is not an array")); + warning(ext_id, _("%s: first argument is not a string"), name); errno = EINVAL; goto done1; } @@ -162,21 +159,55 @@ do_writea(int nargs, awk_value_t *result, struct awk_ext_func *unused) if (fwrite(& minor, 1, sizeof(minor), fp) != sizeof(minor)) goto done1; - if (write_array(fp, array.array_cookie)) { + if (write_array(fp, array)) { make_number(1.0, result); - goto done0; + fclose(fp); + return result; } done1: update_ERRNO_int(errno); - unlink(filename.str_value.str); - -done0: - fclose(fp); -out: + if (fp != NULL) { + fclose(fp); + unlink(filename.str_value.str); + } return result; } +/* do_writea --- write an array */ + +static awk_value_t * +do_writea(int nargs, awk_value_t *result, struct awk_ext_func *unused) +{ + awk_value_t array; + + if (! get_argument(1, AWK_ARRAY, & array)) { + warning(ext_id, _("writea: second argument is not an array")); + errno = EINVAL; + update_ERRNO_int(errno); + make_number(0.0, result); + return result; + } + return write_backend(result, array.array_cookie, "writea"); +} + +/* do_writeall --- write out SYMTAB */ + +static awk_value_t * +do_writeall(int nargs, awk_value_t *result, struct awk_ext_func *unused) +{ + awk_value_t array; + + if (! sym_lookup("SYMTAB", AWK_ARRAY, & array)) { + warning(ext_id, _("writeall: unable to find SYMTAB array")); + errno = EINVAL; + update_ERRNO_int(errno); + make_number(0.0, result); + return result; + } + return write_backend(result, array.array_cookie, "writeall"); +} + /* write_array --- write out an array or a sub-array */ @@ -330,7 +361,22 @@ write_number(FILE *fp, awk_value_t *val) if (fwrite(& code, 1, sizeof(code), fp) != sizeof(code)) return awk_false; +#ifdef USE_MPFR_FPIF + /* This would be preferable, but it is not available + * on older platforms with mpfr 3.x. It's also marked + * experimental in mpfr 4.1, so perhaps not ready for + * production use yet. */ if (mpfr_fpif_export(fp, val->num_ptr) != 0) +#else +#define MPFR_STR_BASE 62 /* maximize base to minimize string len */ +#define MPFR_STR_ROUND MPFR_RNDN + /* XXX does the choice of MPFR_RNDN matter, given + * that the precision is 0, so we should be rendering + * in full precision? */ + /* We need to write a terminating space, since + * mpfr_inp_str reads until it hits a space or EOF */ + if ((mpfr_out_str(fp, MPFR_STR_BASE, 0, val->num_ptr, MPFR_STR_ROUND) == 0) || (putc(' ', fp) == EOF)) +#endif return awk_false; } else { code = htonl(VT_GMP); @@ -348,12 +394,139 @@ write_number(FILE *fp, awk_value_t *val) return awk_true; } -/* do_reada --- read an array */ +/* free_value --- release memory for ignored global variables */ + +static void +free_value(awk_value_t *v) +{ + switch (v->val_type) { + case AWK_ARRAY: + clear_array(v->array_cookie); + break; + case AWK_STRING: + case AWK_REGEX: + case AWK_STRNUM: + case AWK_UNDEFINED: + gawk_free(v->str_value.str); + break; + case AWK_BOOL: + /* no memory allocated */ + break; + case AWK_NUMBER: + switch (v->num_type) { + case AWK_NUMBER_TYPE_DOUBLE: + /* no memory allocated */ + break; + case AWK_NUMBER_TYPE_MPZ: + mpz_clear(v->num_ptr); + break; + case AWK_NUMBER_TYPE_MPFR: + mpfr_clear(v->num_ptr); + break; + default: + warning(ext_id, _("cannot free number with unknown type %d"), v->num_type); + break; + } + break; + default: + warning(ext_id, _("cannot free value with unhandled type %d"), v->val_type); + break; + } +} + +/* do_poke --- create a global variable */ + +static awk_bool_t +do_poke(awk_element_t *e) +{ + awk_value_t t; + + if (e->index.val_type != AWK_STRING) + return awk_false; + /* So this is a bit tricky. If the program refers to the variable, + * then it will already exist in an undefined state after parsing. + * If the program never refers to it, then the lookup fails. + * We still need to create it in case the program accesses it via + * indirection through the SYMTAB table. */ + if (sym_lookup(e->index.str_value.str, AWK_UNDEFINED, &t) && (t.val_type != AWK_UNDEFINED)) + return awk_false; + if (! sym_update(e->index.str_value.str, & e->value)) { + warning(ext_id, _("readall: unable to set %s"), e->index.str_value.str); + return awk_false; + } + return awk_true; +} + +/* regular_array_handle --- array creation hook for normal reada */ + +static awk_array_t +regular_array_handle(awk_value_t *unused) +{ + return create_array(); +} + +/* global_array_handle --- array creation hook for readall */ + +static awk_array_t +global_array_handle(awk_value_t *n) +{ + awk_value_t t; + size_t count; + + /* The array may exist already because it was instantiated during + * program parsing, so we use the existing array if it is empty. */ + return ((n->val_type == AWK_STRING) && sym_lookup(n->str_value.str, AWK_UNDEFINED, &t) && (t.val_type == AWK_ARRAY) && get_element_count(t.array_cookie, & count) && ! count) ? t.array_cookie : create_array(); +} + +/* read_global --- read top-level variables dumped from SYMTAB */ + +static awk_bool_t +read_global(FILE *fp, awk_array_t unused) +{ + uint32_t i; + uint32_t count; + awk_element_t new_elem; + value_storage vs; + + if (fread(& count, 1, sizeof(count), fp) != sizeof(count)) + return awk_false; + + count = ntohl(count); + + for (i = 0; i < count; i++) { + if (read_elem(fp, & new_elem, global_array_handle, &vs)) { + if (! do_poke(& new_elem)) + free_value(& new_elem.value); + if (new_elem.index.str_value.len) + /* free string allocated by make_const_string */ + gawk_free(new_elem.index.str_value.str); + } else + return awk_false; + } + + return awk_true; +} + +/* read_one --- read one array */ + +static awk_bool_t +read_one(FILE *fp, awk_array_t array) +{ + if (! clear_array(array)) { + errno = ENOMEM; + warning(ext_id, _("reada: clear_array failed")); + return awk_false; + } + + return read_array(fp, array); +} + +/* read_backend --- common code for reada and readall */ static awk_value_t * -do_reada(int nargs, awk_value_t *result, struct awk_ext_func *unused) +read_backend(awk_value_t *result, awk_array_t array, const char *name, awk_bool_t (*func)(FILE *, awk_array_t)) { - awk_value_t filename, array; + awk_value_t filename; FILE *fp = NULL; uint32_t major; uint32_t minor; @@ -362,18 +535,9 @@ do_reada(int nargs, awk_value_t *result, struct awk_ext_func *unused) assert(result != NULL); make_number(0.0, result); - if (nargs < 2) - goto out; - - /* directory is first arg, array to read is second */ + /* filename is first arg */ if (! get_argument(0, AWK_STRING, & filename)) { - warning(ext_id, _("do_reada: first argument is not a string")); - errno = EINVAL; - goto done1; - } - - if (! get_argument(1, AWK_ARRAY, & array)) { - warning(ext_id, _("do_reada: second argument is not an array")); + warning(ext_id, _("%s: first argument is not a string"), name); errno = EINVAL; goto done1; } @@ -415,13 +579,7 @@ do_reada(int nargs, awk_value_t *result, struct awk_ext_func *unused) goto done1; } - if (! clear_array(array.array_cookie)) { - errno = ENOMEM; - warning(ext_id, _("do_reada: clear_array failed")); - goto done1; - } - - if (read_array(fp, array.array_cookie)) { + if ((*func)(fp, array)) { make_number(1.0, result); goto done0; } @@ -431,10 +589,34 @@ done1: done0: if (fp != NULL) fclose(fp); -out: return result; } +/* do_reada --- read an array */ + +static awk_value_t * +do_reada(int nargs, awk_value_t *result, struct awk_ext_func *unused) +{ + awk_value_t array; + + if (! get_argument(1, AWK_ARRAY, & array)) { + warning(ext_id, _("reada: second argument is not an array")); + errno = EINVAL; + update_ERRNO_int(errno); + make_number(0.0, result); + return result; + } + return read_backend(result, array.array_cookie, "read", read_one); +} + +/* do_readall --- read top-level variables */ + +static awk_value_t * +do_readall(int nargs, awk_value_t *result, struct awk_ext_func *unused) +{ + return read_backend(result, NULL, "readall", read_global); +} + /* read_array --- read in an array or sub-array */ @@ -444,6 +626,7 @@ read_array(FILE *fp, awk_array_t array) uint32_t i; uint32_t count; awk_element_t new_elem; + value_storage vs; if (fread(& count, 1, sizeof(count), fp) != sizeof(count)) return awk_false; @@ -451,7 +634,7 @@ read_array(FILE *fp, awk_array_t array) count = ntohl(count); for (i = 0; i < count; i++) { - if (read_elem(fp, & new_elem)) { + if (read_elem(fp, & new_elem, regular_array_handle, &vs)) { /* add to array */ if (! set_array_element_by_elem(array, & new_elem)) { warning(ext_id, _("read_array: set_array_element failed")); @@ -470,7 +653,7 @@ read_array(FILE *fp, awk_array_t array) /* read_elem --- read in a single element */ static awk_bool_t -read_elem(FILE *fp, awk_element_t *element) +read_elem(FILE *fp, awk_element_t *element, array_handle_t array_handle, value_storage *vs) { uint32_t index_len; static char *buffer; @@ -508,7 +691,7 @@ read_elem(FILE *fp, awk_element_t *element) make_null_string(& element->index); } - if (! read_value(fp, & element->value)) + if (! read_value(fp, & element->value, array_handle, & element->index, vs)) return awk_false; return awk_true; @@ -517,7 +700,7 @@ read_elem(FILE *fp, awk_element_t *element) /* read_value --- read a number or a string */ static awk_bool_t -read_value(FILE *fp, awk_value_t *value) +read_value(FILE *fp, awk_value_t *value, array_handle_t array_handle, awk_value_t *idx, value_storage *vs) { uint32_t code, len; @@ -527,7 +710,7 @@ read_value(FILE *fp, awk_value_t *value) code = ntohl(code); if (code == VT_ARRAY) { - awk_array_t array = create_array(); + awk_array_t array = (*array_handle)(idx); if (! read_array(fp, array)) return awk_false; @@ -538,7 +721,7 @@ read_value(FILE *fp, awk_value_t *value) } else if (code == VT_NUMBER || code == VT_GMP || code == VT_MPFR) { - return read_number(fp, value, code); + return read_number(fp, value, code, vs); } else { if (fread(& len, 1, sizeof(len), fp) != sizeof(len)) { return awk_false; @@ -591,7 +774,7 @@ read_value(FILE *fp, awk_value_t *value) /* read_number --- read a double, GMP, or MPFR number */ static awk_bool_t -read_number(FILE *fp, awk_value_t *value, uint32_t code) +read_number(FILE *fp, awk_value_t *value, uint32_t code, value_storage *vs) { uint32_t len; @@ -613,21 +796,24 @@ read_number(FILE *fp, awk_value_t *value, uint32_t code) } else { #ifdef HAVE_MPFR if (code == VT_GMP) { - mpz_t mp_ptr; - - mpz_init(mp_ptr); - if (mpz_inp_raw(mp_ptr, fp) == 0) + mpz_init(vs->mpz_val); + if (mpz_inp_raw(vs->mpz_val, fp) == 0) return awk_false; - value = make_number_mpz(mp_ptr, value); + value = make_number_mpz(vs->mpz_val, value); } else { - mpfr_t mpfr_val; - mpfr_init(mpfr_val); - - if (mpfr_fpif_import(mpfr_val, fp) != 0) + mpfr_init(vs->mpfr_val); +#ifdef USE_MPFR_FPIF + /* preferable if widely available and stable */ + if (mpfr_fpif_import(vs->mpfr_val, fp) != 0) +#else + /* N.B. need to consume the terminating space we wrote + * after mpfr_out_str */ + if ((mpfr_inp_str(vs->mpfr_val, fp, MPFR_STR_BASE, MPFR_STR_ROUND) == 0) || (getc(fp) != ' ')) +#endif return awk_false; - value = make_number_mpfr(& mpfr_val, value); + value = make_number_mpfr(vs->mpfr_val, value); } #else fatal(ext_id(_("rwarray extension: GMP/MPFR value in file but compiled without GMP/MPFR support.")); @@ -640,6 +826,8 @@ read_number(FILE *fp, awk_value_t *value, uint32_t code) static awk_ext_func_t func_table[] = { { "writea", do_writea, 2, 2, awk_false, NULL }, { "reada", do_reada, 2, 2, awk_false, NULL }, + { "writeall", do_writeall, 1, 1, awk_false, NULL }, + { "readall", do_readall, 1, 1, awk_false, NULL }, }; |