https://gcc.gnu.org/g:4ee8acd349ebc55526421b7fa73b0b7a30ee4ebe
commit 4ee8acd349ebc55526421b7fa73b0b7a30ee4ebe Author: Thomas Koenig <tkoe...@gcc.gnu.org> Date: Fri Aug 2 18:33:40 2024 +0200 Add decimal formatted I/O for unsigneds. Diff: --- gcc/testsuite/gfortran.dg/unsigned_4.f90 | 15 ++++ libgfortran/io/io.h | 7 ++ libgfortran/io/read.c | 135 ++++++++++++++++++++++++++----- libgfortran/io/transfer.c | 42 +++++++++- libgfortran/io/write.c | 5 ++ 5 files changed, 178 insertions(+), 26 deletions(-) diff --git a/gcc/testsuite/gfortran.dg/unsigned_4.f90 b/gcc/testsuite/gfortran.dg/unsigned_4.f90 new file mode 100644 index 000000000000..495523d919d3 --- /dev/null +++ b/gcc/testsuite/gfortran.dg/unsigned_4.f90 @@ -0,0 +1,15 @@ +! { dg-do run } +! { dg-options "-funsigned" } +! Test some basic formatted I/O. + +program main + unsigned :: u + open (10,status="scratch") + write (10,'(I4)') 1u + write (10,'(I4)') -1 + rewind 10 + read (10,'(I4)') u + if (u /= 1u) stop 1 + read (10,'(I4)') u + if (u /= 4294967295u) stop 2 +end program main diff --git a/libgfortran/io/io.h b/libgfortran/io/io.h index 32e2b825ed5b..2677551b277d 100644 --- a/libgfortran/io/io.h +++ b/libgfortran/io/io.h @@ -897,6 +897,10 @@ internal_proto(read_radix); extern void read_decimal (st_parameter_dt *, const fnode *, char *, int); internal_proto(read_decimal); +extern void read_decimal_unsigned (st_parameter_dt *, const fnode *, char *, + int); +internal_proto(read_decimal_unsigned); + extern void read_user_defined (st_parameter_dt *, void *); internal_proto(read_user_defined); @@ -947,6 +951,9 @@ internal_proto(write_f); extern void write_i (st_parameter_dt *, const fnode *, const char *, int); internal_proto(write_i); +extern void write_iu (st_parameter_dt *, const fnode *, const char *, int); +internal_proto(write_iu); + extern void write_l (st_parameter_dt *, const fnode *, char *, int); internal_proto(write_l); diff --git a/libgfortran/io/read.c b/libgfortran/io/read.c index 2fb39392fc99..60b497a810d9 100644 --- a/libgfortran/io/read.c +++ b/libgfortran/io/read.c @@ -470,7 +470,7 @@ read_utf8 (st_parameter_dt *dtp, size_t *nbytes) if ((c & ~masks[nb-1]) == patns[nb-1]) goto found; goto invalid; - + found: c = (c & masks[nb-1]); nread = nb - 1; @@ -501,7 +501,7 @@ read_utf8 (st_parameter_dt *dtp, size_t *nbytes) goto invalid; return c; - + invalid: generate_error (&dtp->common, LIBERROR_READ_VALUE, "Invalid UTF-8 encoding"); return (gfc_char4_t) '?'; @@ -544,7 +544,7 @@ read_default_char1 (st_parameter_dt *dtp, char *p, size_t len, size_t width) size_t m; s = read_block_form (dtp, &width); - + if (s == NULL) return; if (width > len) @@ -688,7 +688,7 @@ read_a_char4 (st_parameter_dt *dtp, const fnode *f, char *p, size_t length) read_utf8_char4 (dtp, p, length, w); else read_default_char4 (dtp, p, length, w); - + dtp->u.p.sf_read_comma = dtp->u.p.current_unit->decimal_status == DECIMAL_COMMA ? 0 : 1; } @@ -729,7 +729,7 @@ next_char (st_parameter_dt *dtp, char **p, size_t *w) if (c != ' ') return c; if (dtp->u.p.blank_status != BLANK_UNSPECIFIED) - return ' '; /* return a blank to signal a null */ + return ' '; /* return a blank to signal a null */ /* At this point, the rest of the field has to be trailing blanks */ @@ -808,19 +808,19 @@ read_decimal (st_parameter_dt *dtp, const fnode *f, char *dest, int length) c = next_char (dtp, &p, &w); if (c == '\0') break; - + if (c == ' ') { if (dtp->u.p.blank_status == BLANK_NULL) { /* Skip spaces. */ for ( ; w > 0; p++, w--) - if (*p != ' ') break; + if (*p != ' ') break; continue; } if (dtp->u.p.blank_status == BLANK_ZERO) c = '0'; } - + if (c < '0' || c > '9') goto bad; @@ -856,6 +856,98 @@ read_decimal (st_parameter_dt *dtp, const fnode *f, char *dest, int length) } +/* read_decimal_unsigned () - almost the same as above, but we do not check for + overflow, but just calculate everything mod 2^n. */ + +void +read_decimal_unsigned (st_parameter_dt *dtp, const fnode *f, char *dest, + int length) +{ + GFC_UINTEGER_LARGEST value, v; + size_t w; + int negative; + char c, *p; + + w = f->u.w; + + /* This is a legacy extension, and the frontend will only allow such cases + * through when -fdec-format-defaults is passed. + */ + if (w == (size_t) DEFAULT_WIDTH) + w = default_width_for_integer (length); + + p = read_block_form (dtp, &w); + + if (p == NULL) + return; + + p = eat_leading_spaces (&w, p); + if (w == 0) + { + set_unsigned (dest, (GFC_UINTEGER_LARGEST) 0, length); + return; + } + + negative = 0; + + switch (*p) + { + case '-': + negative = 1; + /* Fall through */ + + case '+': + p++; + if (--w == 0) + goto bad; + /* Fall through */ + + default: + break; + } + + /* At this point we have a digit-string */ + value = 0; + + for (;;) + { + c = next_char (dtp, &p, &w); + if (c == '\0') + break; + + if (c == ' ') + { + if (dtp->u.p.blank_status == BLANK_NULL) + { + /* Skip spaces. */ + for ( ; w > 0; p++, w--) + if (*p != ' ') break; + continue; + } + if (dtp->u.p.blank_status == BLANK_ZERO) c = '0'; + } + + if (c < '0' || c > '9') + goto bad; + + c -= '0'; + value = 10 * value; + value += c; + } + + if (negative) + value = -value; + + set_unsigned (dest, value, length); + return; + + bad: + generate_error (&dtp->common, LIBERROR_READ_VALUE, + "Bad value during integer read"); + next_record (dtp, 1); + return; +} + /* read_radix()-- This function reads values for non-decimal radixes. The difference here is that we treat the values here as unsigned @@ -1070,7 +1162,7 @@ read_f (st_parameter_dt *dtp, const fnode *f, char *dest, int length) if (w == 0) goto zero; - /* Check for Infinity or NaN. */ + /* Check for Infinity or NaN. */ if (unlikely ((w >= 3 && (*p == 'i' || *p == 'I' || *p == 'n' || *p == 'N')))) { int seen_paren = 0; @@ -1112,9 +1204,9 @@ read_f (st_parameter_dt *dtp, const fnode *f, char *dest, int length) ++p; ++out; } - + *out = '\0'; - + if (seen_paren != 0 && seen_paren != 2) goto bad_float; @@ -1211,7 +1303,7 @@ found_digit: ++p; --w; } - + /* No exponent has been seen, so we use the current scale factor. */ exponent = - dtp->u.p.scale_factor; goto done; @@ -1249,7 +1341,7 @@ exponent: ++p; --w; } - + /* Only allow trailing blanks. */ while (w > 0) { @@ -1258,7 +1350,7 @@ exponent: ++p; --w; } - } + } else /* BZ or BN status is enabled. */ { while (w > 0) @@ -1298,7 +1390,7 @@ done: significand. */ else if (!seen_int_digit && !seen_dec_digit) { - notify_std (&dtp->common, GFC_STD_LEGACY, + notify_std (&dtp->common, GFC_STD_LEGACY, "REAL input of style 'E+NN'"); *(out++) = '0'; } @@ -1391,20 +1483,20 @@ read_x (st_parameter_dt *dtp, size_t n) if ((dtp->u.p.current_unit->pad_status == PAD_NO || is_internal_unit (dtp)) && dtp->u.p.current_unit->bytes_left < (gfc_offset) n) n = dtp->u.p.current_unit->bytes_left; - + if (n == 0) return; - + if (dtp->u.p.current_unit->flags.encoding == ENCODING_UTF8) { gfc_char4_t c; size_t nbytes, j; - + /* Proceed with decoding one character at a time. */ for (j = 0; j < n; j++) { c = read_utf8 (dtp, &nbytes); - + /* Check for a short read and if so, break out. */ if (nbytes == 0 || c == (gfc_char4_t)0) break; @@ -1441,7 +1533,7 @@ read_x (st_parameter_dt *dtp, size_t n) the rest of the I/O statement. Set the corresponding flag. */ if (dtp->u.p.advance_status == ADVANCE_NO || dtp->u.p.seen_dollar) dtp->u.p.eor_condition = 1; - + /* If we encounter a CR, it might be a CRLF. */ if (q == '\r') /* Probably a CRLF */ { @@ -1455,7 +1547,7 @@ read_x (st_parameter_dt *dtp, size_t n) goto done; } n++; - } + } done: if (((dtp->common.flags & IOPARM_DT_HAS_SIZE) != 0) || @@ -1464,4 +1556,3 @@ read_x (st_parameter_dt *dtp, size_t n) dtp->u.p.current_unit->bytes_left -= n; dtp->u.p.current_unit->strm_pos += (gfc_offset) n; } - diff --git a/libgfortran/io/transfer.c b/libgfortran/io/transfer.c index 741dbd9cc981..64f394dddc75 100644 --- a/libgfortran/io/transfer.c +++ b/libgfortran/io/transfer.c @@ -1418,6 +1418,9 @@ type_name (bt type) case BT_INTEGER: p = "INTEGER"; break; + case BT_UNSIGNED: + p = "UNSIGNED"; + break; case BT_LOGICAL: p = "LOGICAL"; break; @@ -1493,6 +1496,31 @@ require_type (st_parameter_dt *dtp, bt expected, bt actual, const fnode *f) return 1; } +/* Check that the actual matches one of two expected types; issue an error + if that is not the case. */ + + +static int +require_one_of_two_types (st_parameter_dt *dtp, bt expected1, bt expected2, + bt actual, const fnode *f) +{ + char buffer[BUFLEN]; + + if (actual == expected1) + return 0; + + if (actual == expected2) + return 0; + + snprintf (buffer, BUFLEN, + "Expected %s or %s for item %d in formatted transfer, got %s", + type_name (expected1), type_name (expected2), + dtp->u.p.item_count - 1, type_name (actual)); + + format_error (dtp, f, buffer); + return 1; + +} /* Check that the dtio procedure required for formatted IO is present. */ @@ -1635,9 +1663,12 @@ formatted_transfer_scalar_read (st_parameter_dt *dtp, bt type, void *p, int kind case FMT_I: if (n == 0) goto need_read_data; - if (require_type (dtp, BT_INTEGER, type, f)) + if (require_one_of_two_types (dtp, BT_INTEGER, BT_UNSIGNED, type, f)) return; - read_decimal (dtp, f, p, kind); + if (type == BT_INTEGER) + read_decimal (dtp, f, p, kind); + else + read_decimal_unsigned (dtp, f, p, kind); break; case FMT_B: @@ -2131,9 +2162,12 @@ formatted_transfer_scalar_write (st_parameter_dt *dtp, bt type, void *p, int kin case FMT_I: if (n == 0) goto need_data; - if (require_type (dtp, BT_INTEGER, type, f)) + if (require_one_of_two_types (dtp, BT_INTEGER, BT_UNSIGNED, type, f)) return; - write_i (dtp, f, p, kind); + if (type == BT_INTEGER) + write_i (dtp, f, p, kind); + else + write_iu (dtp, f, p, kind); break; case FMT_B: diff --git a/libgfortran/io/write.c b/libgfortran/io/write.c index 0f9600f5f1fe..2f414c6b57d2 100644 --- a/libgfortran/io/write.c +++ b/libgfortran/io/write.c @@ -1367,6 +1367,11 @@ write_i (st_parameter_dt *dtp, const fnode *f, const char *p, int len) write_decimal (dtp, f, p, len); } +void +write_iu (st_parameter_dt *dtp, const fnode *f, const char *p, int len) +{ + write_decimal_unsigned (dtp, f, p, len); +} void write_b (st_parameter_dt *dtp, const fnode *f, const char *source, int len)