diff options
author | Thomas Koenig <Thomas.Koenig@online.de> | 2005-12-10 20:01:56 +0000 |
---|---|---|
committer | Thomas Koenig <tkoenig@gcc.gnu.org> | 2005-12-10 20:01:56 +0000 |
commit | 181c9f4a9ba6b2d64c7c0b56b777ad366e05a9c1 (patch) | |
tree | a3d754eebe0bc2166ffe8c241b2d9dfdd1098340 | |
parent | 775fe6e36ddaef38cca67c39bf34b93fcb836dc3 (diff) | |
download | gcc-181c9f4a9ba6b2d64c7c0b56b777ad366e05a9c1.tar.gz |
re PR fortran/23815 (Add -byteswapio flag)
2005-12-10 Thomas Koenig <Thomas.Koenig@online.de>
PR fortran/23815
* io.c (top level): Add convert to io_tag.
(resolve_tag): convert is GFC_STD_GNU.
(match_open_element): Add convert.
(gfc_free_open): Likewise.
(gfc_resolve_open): Likewise.
(gfc_free_inquire): Likewise.
(match_inquire_element): Likewise.
* dump-parse-tree.c (gfc_show_code_node): Add
convet for open and inquire.
gfortran.h: Add convert to gfc_open and gfc_inquire.
* trans-io.c (gfc_trans_open): Add convert.
(gfc_trans_inquire): Likewise.
* ioparm.def: Add convert to open and inquire.
* gfortran.texi: Document CONVERT.
2005-12-10 Thomas Koenig <Thomas.Koenig@online.de>
PR fortran/23815
* io/file_pos.c (unformatted_backspace): If flags.convert
does not equal CONVERT_NATIVE, reverse the record marker.
* io/open.c: Add convert_opt[].
(st_open): If no convert option is given, set CONVERT_NATIVE.
If CONVERT_BIG or CONVERT_LITTLE are given, set flags.convert to
CONVERT_NATIVE or CONVERT_SWAP (depending on wether we have
a big- or little-endian system).
* io/transfer.c (unformatted_read): Remove unused attribute
from arguments.
If we need to reverse
bytes, break up large transfers into a loop. Split complex
numbers into its two parts.
(unformatted_write): Likewise.
(us_read): If flags.convert does not equal CONVERT_NATIVE,
reverse the record marker.
(next_record_w): Likewise.
(reverse_memcpy): New function.
* io/inquire.c (inquire_via_unit): Implement convert.
* io/io.h (top level): Add enum unit_convert.
Add convert to st_parameter_open and st_parameter_inquire.
Define IOPARM_OPEN_HAS_CONVERT and IOPARM_INQUIRE_HAS_CONVERT.
Increase padding for st_parameter_dt.
Declare reverse_memcpy().
2005-12-10 Thomas Koenig <Thomas.Koenig@online.de>
PR fortran/23815
* gfortran.dg/unf_io_convert_1.f90: New test.
* gfortran.dg/unf_io_convert_2.f90: New test.
* gfortran.dg/unf_io_convert_3.f90: New test.
From-SVN: r108358
-rw-r--r-- | gcc/fortran/ChangeLog | 18 | ||||
-rw-r--r-- | gcc/fortran/dump-parse-tree.c | 10 | ||||
-rw-r--r-- | gcc/fortran/gfortran.h | 4 | ||||
-rw-r--r-- | gcc/fortran/gfortran.texi | 37 | ||||
-rw-r--r-- | gcc/fortran/io.c | 15 | ||||
-rw-r--r-- | gcc/fortran/ioparm.def | 2 | ||||
-rw-r--r-- | gcc/fortran/trans-io.c | 8 | ||||
-rw-r--r-- | gcc/testsuite/ChangeLog | 7 | ||||
-rw-r--r-- | gcc/testsuite/gfortran.dg/unf_io_convert_1.f90 | 95 | ||||
-rw-r--r-- | gcc/testsuite/gfortran.dg/unf_io_convert_2.f90 | 39 | ||||
-rw-r--r-- | gcc/testsuite/gfortran.dg/unf_io_convert_3.f90 | 19 | ||||
-rw-r--r-- | libgfortran/ChangeLog | 27 | ||||
-rw-r--r-- | libgfortran/io/file_pos.c | 7 | ||||
-rw-r--r-- | libgfortran/io/inquire.c | 23 | ||||
-rw-r--r-- | libgfortran/io/io.h | 14 | ||||
-rw-r--r-- | libgfortran/io/open.c | 38 | ||||
-rw-r--r-- | libgfortran/io/transfer.c | 118 |
17 files changed, 465 insertions, 16 deletions
diff --git a/gcc/fortran/ChangeLog b/gcc/fortran/ChangeLog index 58cf367f1c8..e9a8f308dae 100644 --- a/gcc/fortran/ChangeLog +++ b/gcc/fortran/ChangeLog @@ -1,3 +1,21 @@ +2005-12-10 Thomas Koenig <Thomas.Koenig@online.de> + + PR fortran/23815 + * io.c (top level): Add convert to io_tag. + (resolve_tag): convert is GFC_STD_GNU. + (match_open_element): Add convert. + (gfc_free_open): Likewise. + (gfc_resolve_open): Likewise. + (gfc_free_inquire): Likewise. + (match_inquire_element): Likewise. + * dump-parse-tree.c (gfc_show_code_node): Add + convet for open and inquire. + gfortran.h: Add convert to gfc_open and gfc_inquire. + * trans-io.c (gfc_trans_open): Add convert. + (gfc_trans_inquire): Likewise. + * ioparm.def: Add convert to open and inquire. + * gfortran.texi: Document CONVERT. + 2005-12-09 Roger Sayle <roger@eyesopen.com> PR fortran/22527 diff --git a/gcc/fortran/dump-parse-tree.c b/gcc/fortran/dump-parse-tree.c index 499e1fa22e5..ef5c88a94b4 100644 --- a/gcc/fortran/dump-parse-tree.c +++ b/gcc/fortran/dump-parse-tree.c @@ -1148,6 +1148,11 @@ gfc_show_code_node (int level, gfc_code * c) gfc_status (" PAD="); gfc_show_expr (open->pad); } + if (open->convert) + { + gfc_status (" CONVERT="); + gfc_show_expr (open->convert); + } if (open->err != NULL) gfc_status (" ERR=%d", open->err->value); @@ -1349,6 +1354,11 @@ gfc_show_code_node (int level, gfc_code * c) gfc_status (" PAD="); gfc_show_expr (i->pad); } + if (i->convert) + { + gfc_status (" CONVERT="); + gfc_show_expr (i->convert); + } if (i->err != NULL) gfc_status (" ERR=%d", i->err->value); diff --git a/gcc/fortran/gfortran.h b/gcc/fortran/gfortran.h index 83de2675489..f22f6a48ab6 100644 --- a/gcc/fortran/gfortran.h +++ b/gcc/fortran/gfortran.h @@ -1309,7 +1309,7 @@ gfc_alloc; typedef struct { gfc_expr *unit, *file, *status, *access, *form, *recl, - *blank, *position, *action, *delim, *pad, *iostat, *iomsg; + *blank, *position, *action, *delim, *pad, *iostat, *iomsg, *convert; gfc_st_label *err; } gfc_open; @@ -1336,7 +1336,7 @@ typedef struct gfc_expr *unit, *file, *iostat, *exist, *opened, *number, *named, *name, *access, *sequential, *direct, *form, *formatted, *unformatted, *recl, *nextrec, *blank, *position, *action, *read, - *write, *readwrite, *delim, *pad, *iolength, *iomsg; + *write, *readwrite, *delim, *pad, *iolength, *iomsg, *convert; gfc_st_label *err; diff --git a/gcc/fortran/gfortran.texi b/gcc/fortran/gfortran.texi index 92a6d5f4593..ea3ac245aa5 100644 --- a/gcc/fortran/gfortran.texi +++ b/gcc/fortran/gfortran.texi @@ -587,6 +587,7 @@ of extensions, and @option{-std=legacy} allows both without warning. * Implicitly interconvert LOGICAL and INTEGER:: * Hollerith constants support:: * Cray pointers:: +* CONVERT specifier:: @end menu @node Old-style kind specifications @@ -930,6 +931,42 @@ pointees are passed as arguments, they are treated as ordinary variables in the invoked function. Subsequent changes to the pointer will not change the base address of the array that was passed. +@node CONVERT specifier +@section CONVERT specifier +@cindex CONVERT specifier + +gfortran allows the conversion of unformatted data between little- +and big-endian representation to facilitate moving of data +between different systems. The conversion is indicated with +the @code{CONVERT} specifier on the @code{OPEN} statement. + +Valid values for @code{CONVERT} are: +@itemize @w{} +@item @code{CONVERT='NATIVE'} Use the native format. This is the default. +@item @code{CONVERT='SWAP'} Swap between little- and big-endian. +@item @code{CONVERT='LITTLE_ENDIAN'} Use the little-endian format + for unformatted files. +@item @code{CONVERT='BIG_ENDIAN'} Use the big-endian format for + unformatted files. +@end itemize + +Using the option could look like this: +@smallexample + open(file='big.dat',form='unformatted',access='sequential', & + convert='big_endian') +@end smallexample + +The value of the conversion can be queried by using +@code{INQUIRE(CONVERT=ch)}. The values returned are +@code{'BIG_ENDIAN'} and @code{'LITTLE_ENDIAN'}. + +@code{CONVERT} works between big- and little-endian for +@code{INTEGER} values of all supported kinds and for @code{REAL} +on IEEE sytems of kinds 4 and 8. Conversion between different +``extended double'' types on different architectures such as +m68k and x86_64, which gfortran +supports as @code{REAL(KIND=10)} will probably not work. + @c --------------------------------------------------------------------- @include intrinsic.texi @c --------------------------------------------------------------------- diff --git a/gcc/fortran/io.c b/gcc/fortran/io.c index 6adc1efb613..090f905ea30 100644 --- a/gcc/fortran/io.c +++ b/gcc/fortran/io.c @@ -78,6 +78,7 @@ static const io_tag tag_s_delim = {"DELIM", " delim = %v", BT_CHARACTER}, tag_s_pad = {"PAD", " pad = %v", BT_CHARACTER}, tag_iolength = {"IOLENGTH", " iolength = %v", BT_INTEGER}, + tag_convert = {"CONVERT", " convert = %e", BT_CHARACTER}, tag_err = {"ERR", " err = %l", BT_UNKNOWN}, tag_end = {"END", " end = %l", BT_UNKNOWN}, tag_eor = {"EOR", " eor = %l", BT_UNKNOWN}; @@ -1051,6 +1052,12 @@ resolve_tag (const io_tag * tag, gfc_expr * e) &e->where) == FAILURE) return FAILURE; } + if (tag == &tag_convert) + { + if (gfc_notify_std (GFC_STD_GNU, "Extension: CONVERT tag at %L", + &e->where) == FAILURE) + return FAILURE; + } } return SUCCESS; @@ -1106,6 +1113,9 @@ match_open_element (gfc_open * open) m = match_ltag (&tag_err, &open->err); if (m != MATCH_NO) return m; + m = match_etag (&tag_convert, &open->convert); + if (m != MATCH_NO) + return m; return MATCH_NO; } @@ -1133,6 +1143,7 @@ gfc_free_open (gfc_open * open) gfc_free_expr (open->action); gfc_free_expr (open->delim); gfc_free_expr (open->pad); + gfc_free_expr (open->convert); gfc_free (open); } @@ -1158,6 +1169,7 @@ gfc_resolve_open (gfc_open * open) RESOLVE_TAG (&tag_e_action, open->action); RESOLVE_TAG (&tag_e_delim, open->delim); RESOLVE_TAG (&tag_e_pad, open->pad); + RESOLVE_TAG (&tag_convert, open->convert); if (gfc_reference_st_label (open->err, ST_LABEL_TARGET) == FAILURE) return FAILURE; @@ -2438,6 +2450,7 @@ gfc_free_inquire (gfc_inquire * inquire) gfc_free_expr (inquire->delim); gfc_free_expr (inquire->pad); gfc_free_expr (inquire->iolength); + gfc_free_expr (inquire->convert); gfc_free (inquire); } @@ -2479,6 +2492,7 @@ match_inquire_element (gfc_inquire * inquire) RETM m = match_vtag (&tag_s_delim, &inquire->delim); RETM m = match_vtag (&tag_s_pad, &inquire->pad); RETM m = match_vtag (&tag_iolength, &inquire->iolength); + RETM m = match_vtag (&tag_convert, &inquire->convert); RETM return MATCH_NO; } @@ -2632,6 +2646,7 @@ gfc_resolve_inquire (gfc_inquire * inquire) RESOLVE_TAG (&tag_s_delim, inquire->delim); RESOLVE_TAG (&tag_s_pad, inquire->pad); RESOLVE_TAG (&tag_iolength, inquire->iolength); + RESOLVE_TAG (&tag_convert, inquire->convert); if (gfc_reference_st_label (inquire->err, ST_LABEL_TARGET) == FAILURE) return FAILURE; diff --git a/gcc/fortran/ioparm.def b/gcc/fortran/ioparm.def index 9ca0cf659b1..0fe9a7b45bf 100644 --- a/gcc/fortran/ioparm.def +++ b/gcc/fortran/ioparm.def @@ -25,6 +25,7 @@ IOPARM (open, position, 1 << 13, char1) IOPARM (open, action, 1 << 14, char2) IOPARM (open, delim, 1 << 15, char1) IOPARM (open, pad, 1 << 16, char2) +IOPARM (open, convert, 1 << 17, char1) IOPARM (close, common, 0, common) IOPARM (close, status, 1 << 7, char1) IOPARM (filepos, common, 0, common) @@ -51,6 +52,7 @@ IOPARM (inquire, unformatted, 1 << 25, char1) IOPARM (inquire, read, 1 << 26, char2) IOPARM (inquire, write, 1 << 27, char1) IOPARM (inquire, readwrite, 1 << 28, char2) +IOPARM (inquire, convert, 1 << 29, char1) #ifndef IOPARM_dt_list_format #define IOPARM_dt_list_format (1 << 7) #define IOPARM_dt_namelist_read_mode (1 << 8) diff --git a/gcc/fortran/trans-io.c b/gcc/fortran/trans-io.c index 98c1d1fcf8b..87a11c3d861 100644 --- a/gcc/fortran/trans-io.c +++ b/gcc/fortran/trans-io.c @@ -791,6 +791,10 @@ gfc_trans_open (gfc_code * code) if (p->err) mask |= IOPARM_common_err; + if (p->convert) + mask |= set_string (&block, &post_block, var, IOPARM_open_convert, + p->convert); + set_parameter_const (&block, var, IOPARM_common_flags, mask); tmp = gfc_build_addr_expr (NULL_TREE, var); @@ -1073,6 +1077,10 @@ gfc_trans_inquire (gfc_code * code) if (p->err) mask |= IOPARM_common_err; + if (p->convert) + mask |= set_string (&block, &post_block, var, IOPARM_inquire_convert, + p->convert); + set_parameter_const (&block, var, IOPARM_common_flags, mask); tmp = gfc_build_addr_expr (NULL_TREE, var); diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 88cbc84e754..8c170f04102 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,10 @@ +2005-12-10 Thomas Koenig <Thomas.Koenig@online.de> + + PR fortran/23815 + * gfortran.dg/unf_io_convert_1.f90: New test. + * gfortran.dg/unf_io_convert_2.f90: New test. + * gfortran.dg/unf_io_convert_3.f90: New test. + 2005-12-10 Kaveh R. Ghazi <ghazi@caip.rutgers.edu> PR testsuite/20772 diff --git a/gcc/testsuite/gfortran.dg/unf_io_convert_1.f90 b/gcc/testsuite/gfortran.dg/unf_io_convert_1.f90 new file mode 100644 index 00000000000..0ed3fc5dd9c --- /dev/null +++ b/gcc/testsuite/gfortran.dg/unf_io_convert_1.f90 @@ -0,0 +1,95 @@ +! { dg-do run } +! { dg-options "-pedantic" } +! This test verifies the most basic sequential unformatted I/O +! with convert="swap". +! Adapted from seq_io.f. +! write 3 records of various sizes +! then read them back +program main + implicit none + integer size + parameter(size=100) + logical debug + data debug /.FALSE./ +! set debug to true for help in debugging failures. + integer m(2) + integer n + real*4 r(size) + integer i + character*4 str + + m(1) = Z'11223344' + m(2) = Z'55667788' + n = Z'77AABBCC' + str = 'asdf' + do i = 1,size + r(i) = i + end do + open(9,form="unformatted",access="sequential",convert="swap") ! { dg-warning "Extension: CONVERT" } + write(9) m ! an array of 2 + write(9) n ! an integer + write(9) r ! an array of reals + write(9)str ! String +! zero all the results so we can compare after they are read back + do i = 1,size + r(i) = 0 + end do + m(1) = 0 + m(2) = 0 + n = 0 + str = ' ' + + rewind(9) + read(9) m + read(9) n + read(9) r + read(9) str + ! + ! check results + if (m(1).ne.Z'11223344') then + if (debug) then + print '(A,Z8)','m(1) incorrect. m(1) = ',m(1) + else + call abort + endif + endif + + if (m(2).ne.Z'55667788') then + if (debug) then + print '(A,Z8)','m(2) incorrect. m(2) = ',m(2) + else + call abort + endif + endif + + if (n.ne.Z'77AABBCC') then + if (debug) then + print '(A,Z8)','n incorrect. n = ',n + else + call abort + endif + endif + + do i = 1,size + if (int(r(i)).ne.i) then + if (debug) then + print*,'element ',i,' was ',r(i),' should be ',i + else + call abort + endif + endif + end do + if (str .ne. 'asdf') then + if (debug) then + print *,'str incorrect, str = ', str + else + call abort + endif + ! use hexdump to look at the file "fort.9" + if (debug) then + close(9) + else + close(9,status='DELETE') + endif + end if +end program main diff --git a/gcc/testsuite/gfortran.dg/unf_io_convert_2.f90 b/gcc/testsuite/gfortran.dg/unf_io_convert_2.f90 new file mode 100644 index 00000000000..42bad3ee30f --- /dev/null +++ b/gcc/testsuite/gfortran.dg/unf_io_convert_2.f90 @@ -0,0 +1,39 @@ +! { dg-do run } +program main + complex(kind=4) :: c + real(kind=4) :: a(2) + integer(kind=4) :: i(2) + integer(kind=1) :: b(8) + integer(kind=8) :: j + + c = (3.14, 2.71) + open (10, form="unformatted",convert="swap") ! { dg-warning "Extension: CONVERT" } + write (10) c + rewind (10) + read (10) a + if (a(1) /= 3.14 .or. a(2) /= 2.71) call abort + close(10,status="delete") + + open (10, form="unformatted",convert="big_endian") ! { dg-warning "Extension: CONVERT" } + i = (/ Z'11223344', Z'55667700' /) + write (10) i + rewind (10) + read (10) b + if (any(b /= (/ Z'11', Z'22', Z'33', Z'44', Z'55', Z'66', Z'77', Z'00' /))) & + call abort + backspace 10 + read (10) j + if (j /= Z'1122334455667700') call abort + close (10, status="delete") + + open (10, form="unformatted", convert="little_endian") ! { dg-warning "Extension: CONVERT" } + write (10) i + rewind (10) + read (10) b + if (any(b /= (/ Z'44', Z'33', Z'22', Z'11', Z'00', Z'77', Z'66', Z'55' /))) & + call abort + backspace 10 + read (10) j + if (j /= Z'5566770011223344') call abort + +end program main diff --git a/gcc/testsuite/gfortran.dg/unf_io_convert_3.f90 b/gcc/testsuite/gfortran.dg/unf_io_convert_3.f90 new file mode 100644 index 00000000000..40ace2a0e06 --- /dev/null +++ b/gcc/testsuite/gfortran.dg/unf_io_convert_3.f90 @@ -0,0 +1,19 @@ +! { dg-do run} +! { dg-require-effective-target fortran_large_real } +program main + integer,parameter :: k = selected_real_kind (precision (0.0_8) + 1) + real(kind=k) a,b,c + a = 1.1_k + open(10,convert="swap",form="unformatted") ! { dg-warning "Extension: CONVERT" } + write(10) a + backspace 10 + read (10) b + close(10,status="delete") + if (a /= b) call abort + write (11) a + backspace 11 + open (11,form="unformatted") + read (11) c + if (a .ne. c) call abort + close (11, status="delete") +end program main diff --git a/libgfortran/ChangeLog b/libgfortran/ChangeLog index a47e0028e97..88da4a60e6e 100644 --- a/libgfortran/ChangeLog +++ b/libgfortran/ChangeLog @@ -1,3 +1,30 @@ +2005-12-10 Thomas Koenig <Thomas.Koenig@online.de> + + PR fortran/23815 + * io/file_pos.c (unformatted_backspace): If flags.convert + does not equal CONVERT_NATIVE, reverse the record marker. + * io/open.c: Add convert_opt[]. + (st_open): If no convert option is given, set CONVERT_NATIVE. + If CONVERT_BIG or CONVERT_LITTLE are given, set flags.convert to + CONVERT_NATIVE or CONVERT_SWAP (depending on wether we have + a big- or little-endian system). + * io/transfer.c (unformatted_read): Remove unused attribute + from arguments. + If we need to reverse + bytes, break up large transfers into a loop. Split complex + numbers into its two parts. + (unformatted_write): Likewise. + (us_read): If flags.convert does not equal CONVERT_NATIVE, + reverse the record marker. + (next_record_w): Likewise. + (reverse_memcpy): New function. + * io/inquire.c (inquire_via_unit): Implement convert. + * io/io.h (top level): Add enum unit_convert. + Add convert to st_parameter_open and st_parameter_inquire. + Define IOPARM_OPEN_HAS_CONVERT and IOPARM_INQUIRE_HAS_CONVERT. + Increase padding for st_parameter_dt. + Declare reverse_memcpy(). + 2005-12-09 Jakub Jelinek <jakub@redhat.com> PR libfortran/24991 diff --git a/libgfortran/io/file_pos.c b/libgfortran/io/file_pos.c index 0049718f633..3d7dd9ab8b6 100644 --- a/libgfortran/io/file_pos.c +++ b/libgfortran/io/file_pos.c @@ -114,7 +114,12 @@ unformatted_backspace (st_parameter_filepos *fpp, gfc_unit *u) if (p == NULL) goto io_error; - memcpy (&m, p, sizeof (gfc_offset)); + /* Only CONVERT_NATIVE and CONVERT_SWAP are valid here. */ + if (u->flags.convert == CONVERT_NATIVE) + memcpy (&m, p, sizeof (gfc_offset)); + else + reverse_memcpy (&m, p, sizeof (gfc_offset)); + new = file_position (u->s) - m - 2*length; if (sseek (u->s, new) == FAILURE) goto io_error; diff --git a/libgfortran/io/inquire.c b/libgfortran/io/inquire.c index bccd5a185bb..9044bf83e21 100644 --- a/libgfortran/io/inquire.c +++ b/libgfortran/io/inquire.c @@ -283,6 +283,29 @@ inquire_via_unit (st_parameter_inquire *iqp, gfc_unit * u) cf_strcpy (iqp->pad, iqp->pad_len, p); } + + if ((cf & IOPARM_INQUIRE_HAS_CONVERT) != 0) + { + if (u == NULL) + p = undefined; + else + switch (u->flags.convert) + { + /* l8_to_l4_offset is 0 for little-endian, 1 for big-endian. */ + case CONVERT_NATIVE: + p = l8_to_l4_offset ? "BIG_ENDIAN" : "LITTLE_ENDIAN"; + break; + + case CONVERT_SWAP: + p = l8_to_l4_offset ? "LITTLE_ENDIAN" : "BIG_ENDIAN"; + break; + + default: + internal_error (&iqp->common, "inquire_via_unit(): Bad convert"); + } + + cf_strcpy (iqp->convert, iqp->convert_len, p); + } } diff --git a/libgfortran/io/io.h b/libgfortran/io/io.h index e7b0ac18d1e..e36417100cd 100644 --- a/libgfortran/io/io.h +++ b/libgfortran/io/io.h @@ -206,6 +206,10 @@ typedef enum {READING, WRITING} unit_mode; +typedef enum +{ CONVERT_NATIVE, CONVERT_SWAP, CONVERT_BIG, CONVERT_LITTLE } +unit_convert; + #define CHARACTER1(name) \ char * name; \ gfc_charlen_type name ## _len @@ -247,6 +251,7 @@ st_parameter_common; #define IOPARM_OPEN_HAS_ACTION (1 << 14) #define IOPARM_OPEN_HAS_DELIM (1 << 15) #define IOPARM_OPEN_HAS_PAD (1 << 16) +#define IOPARM_OPEN_HAS_CONVERT (1 << 17) typedef struct { @@ -261,6 +266,7 @@ typedef struct CHARACTER2 (action); CHARACTER1 (delim); CHARACTER2 (pad); + CHARACTER1 (convert); } st_parameter_open; @@ -301,6 +307,7 @@ st_parameter_filepos; #define IOPARM_INQUIRE_HAS_READ (1 << 26) #define IOPARM_INQUIRE_HAS_WRITE (1 << 27) #define IOPARM_INQUIRE_HAS_READWRITE (1 << 28) +#define IOPARM_INQUIRE_HAS_CONVERT (1 << 29) typedef struct { @@ -323,6 +330,7 @@ typedef struct CHARACTER2 (read); CHARACTER1 (write); CHARACTER2 (readwrite); + CHARACTER1 (convert); } st_parameter_inquire; @@ -419,7 +427,7 @@ typedef struct st_parameter_dt kind. */ char value[32]; } p; - char pad[16 * sizeof (char *) + 32 * sizeof (int)]; + char pad[16 * sizeof (char *) + 34 * sizeof (int)]; } u; } st_parameter_dt; @@ -438,6 +446,7 @@ typedef struct unit_position position; unit_status status; unit_pad pad; + unit_convert convert; } unit_flags; @@ -738,6 +747,9 @@ internal_proto(init_loop_spec); extern void next_record (st_parameter_dt *, int); internal_proto(next_record); +extern void reverse_memcpy (void *, const void *, size_t); +internal_proto (reverse_memcpy); + /* read.c */ extern void set_integer (void *, GFC_INTEGER_LARGEST, int); diff --git a/libgfortran/io/open.c b/libgfortran/io/open.c index 7e42cc6a2c8..3dc2b11955c 100644 --- a/libgfortran/io/open.c +++ b/libgfortran/io/open.c @@ -98,6 +98,14 @@ static const st_option pad_opt[] = { NULL, 0} }; +static const st_option convert_opt[] = +{ + { "native", CONVERT_NATIVE}, + { "swap", CONVERT_SWAP}, + { "big_endian", CONVERT_BIG}, + { "little_endian", CONVERT_LITTLE}, + { NULL, 0} +}; /* Given a unit, test to see if the file is positioned at the terminal point, and if so, change state from NO_ENDFILE flag to AT_ENDFILE. @@ -531,6 +539,36 @@ st_open (st_parameter_open *opp) find_option (&opp->common, opp->status, opp->status_len, status_opt, "Bad STATUS parameter in OPEN statement"); + if (cf & IOPARM_OPEN_HAS_CONVERT) + { + unit_convert conv; + conv = find_option (&opp->common, opp->convert, opp->convert_len, + convert_opt, "Bad CONVERT parameter in OPEN statement"); + /* We use l8_to_l4_offset, which is 0 on little-endian machines + and 1 on big-endian machines. */ + switch (conv) + { + case CONVERT_NATIVE: + case CONVERT_SWAP: + break; + + case CONVERT_BIG: + conv = l8_to_l4_offset ? CONVERT_NATIVE : CONVERT_SWAP; + break; + + case CONVERT_LITTLE: + conv = l8_to_l4_offset ? CONVERT_SWAP : CONVERT_NATIVE; + break; + + default: + internal_error (&opp->common, "Illegal value for CONVERT"); + break; + } + flags.convert = conv; + } + else + flags.convert = CONVERT_NATIVE; + if (opp->common.unit < 0) generate_error (&opp->common, ERROR_BAD_OPTION, "Bad unit number in OPEN statement"); diff --git a/libgfortran/io/transfer.c b/libgfortran/io/transfer.c index b2d26ac7be8..f3ca8dfb039 100644 --- a/libgfortran/io/transfer.c +++ b/libgfortran/io/transfer.c @@ -399,26 +399,89 @@ write_block_direct (st_parameter_dt *dtp, void *buf, size_t *nbytes) /* Master function for unformatted reads. */ static void -unformatted_read (st_parameter_dt *dtp, bt type __attribute__((unused)), - void *dest, int kind __attribute__((unused)), +unformatted_read (st_parameter_dt *dtp, bt type, + void *dest, int kind, size_t size, size_t nelems) { - size *= nelems; - - read_block_direct (dtp, dest, &size); + /* Currently, character implies size=1. */ + if (dtp->u.p.current_unit->flags.convert == CONVERT_NATIVE + || size == 1 || type == BT_CHARACTER) + { + size *= nelems; + read_block_direct (dtp, dest, &size); + } + else + { + char buffer[16]; + char *p; + size_t i, sz; + + /* Break up complex into its constituent reals. */ + if (type == BT_COMPLEX) + { + nelems *= 2; + size /= 2; + } + p = dest; + + /* By now, all complex variables have been split into their + constituent reals. For types with padding, we only need to + read kind bytes. We don't care about the contents + of the padding. */ + + sz = kind; + for (i=0; i<nelems; i++) + { + read_block_direct (dtp, buffer, &sz); + reverse_memcpy (p, buffer, sz); + p += size; + } + } } /* Master function for unformatted writes. */ static void -unformatted_write (st_parameter_dt *dtp, bt type __attribute__((unused)), - void *source, int kind __attribute__((unused)), +unformatted_write (st_parameter_dt *dtp, bt type, + void *source, int kind, size_t size, size_t nelems) { - size *= nelems; + if (dtp->u.p.current_unit->flags.convert == CONVERT_NATIVE || + size == 1 || type == BT_CHARACTER) + { + size *= nelems; + + write_block_direct (dtp, source, &size); + } + else + { + char buffer[16]; + char *p; + size_t i, sz; + + /* Break up complex into its constituent reals. */ + if (type == BT_COMPLEX) + { + nelems *= 2; + size /= 2; + } + + p = source; - write_block_direct (dtp, source, &size); + /* By now, all complex variables have been split into their + constituent reals. For types with padding, we only need to + read kind bytes. We don't care about the contents + of the padding. */ + + sz = kind; + for (i=0; i<nelems; i++) + { + reverse_memcpy(buffer, p, size); + p+= size; + write_block_direct (dtp, buffer, &sz); + } + } } @@ -1154,7 +1217,12 @@ us_read (st_parameter_dt *dtp) return; } - memcpy (&i, p, sizeof (gfc_offset)); + /* Only CONVERT_NATIVE and CONVERT_SWAP are valid here. */ + if (dtp->u.p.current_unit->flags.convert == CONVERT_NATIVE) + memcpy (&i, p, sizeof (gfc_offset)); + else + reverse_memcpy (&i, p, sizeof (gfc_offset)); + dtp->u.p.current_unit->bytes_left = i; } @@ -1722,7 +1790,12 @@ next_record_w (st_parameter_dt *dtp) if (p == NULL) goto io_error; - memcpy (p, &m, sizeof (gfc_offset)); + /* Only CONVERT_NATIVE and CONVERT_SWAP are valid here. */ + if (dtp->u.p.current_unit->flags.convert == CONVERT_NATIVE) + memcpy (p, &m, sizeof (gfc_offset)); + else + reverse_memcpy (p, &m, sizeof (gfc_offset)); + if (sfree (dtp->u.p.current_unit->s) == FAILURE) goto io_error; @@ -1733,7 +1806,12 @@ next_record_w (st_parameter_dt *dtp) if (p == NULL) generate_error (&dtp->common, ERROR_OS, NULL); - memcpy (p, &m, sizeof (gfc_offset)); + /* Only CONVERT_NATIVE and CONVERT_SWAP are valid here. */ + if (dtp->u.p.current_unit->flags.convert == CONVERT_NATIVE) + memcpy (p, &m, sizeof (gfc_offset)); + else + reverse_memcpy (p, &m, sizeof (gfc_offset)); + if (sfree (dtp->u.p.current_unit->s) == FAILURE) goto io_error; @@ -2161,3 +2239,19 @@ st_set_nml_var_dim (st_parameter_dt *dtp, GFC_INTEGER_4 n_dim, nml->dim[n].lbound = (ssize_t)lbound; nml->dim[n].ubound = (ssize_t)ubound; } + +/* Reverse memcpy - used for byte swapping. */ + +void reverse_memcpy (void *dest, const void *src, size_t n) +{ + char *d, *s; + size_t i; + + d = (char *) dest; + s = (char *) src + n - 1; + + /* Write with ascending order - this is likely faster + on modern architectures because of write combining. */ + for (i=0; i<n; i++) + *(d++) = *(s--); +} |