1 | //===-- runtime/connection.h ------------------------------------*- C++ -*-===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | // Fortran I/O connection state (abstracted over internal & external units) |
10 | |
11 | #ifndef FORTRAN_RUNTIME_IO_CONNECTION_H_ |
12 | #define FORTRAN_RUNTIME_IO_CONNECTION_H_ |
13 | |
14 | #include "format.h" |
15 | #include "flang/Common/optional.h" |
16 | #include <cinttypes> |
17 | |
18 | namespace Fortran::runtime::io { |
19 | |
20 | class IoStatementState; |
21 | |
22 | enum class Direction { Output, Input }; |
23 | enum class Access { Sequential, Direct, Stream }; |
24 | |
25 | // These characteristics of a connection are immutable after being |
26 | // established in an OPEN statement. |
27 | struct ConnectionAttributes { |
28 | Access access{Access::Sequential}; // ACCESS='SEQUENTIAL', 'DIRECT', 'STREAM' |
29 | Fortran::common::optional<bool> isUnformatted; // FORM='UNFORMATTED' if true |
30 | bool isUTF8{false}; // ENCODING='UTF-8' |
31 | unsigned char internalIoCharKind{0}; // 0->external, 1/2/4->internal |
32 | Fortran::common::optional<std::int64_t> openRecl; // RECL= on OPEN |
33 | |
34 | RT_API_ATTRS bool IsRecordFile() const { |
35 | // Formatted stream files are viewed as having records, at least on input |
36 | return access != Access::Stream || !isUnformatted.value_or(true); |
37 | } |
38 | |
39 | template <typename CHAR = char> constexpr RT_API_ATTRS bool useUTF8() const { |
40 | // For wide CHARACTER kinds, always use UTF-8 for formatted I/O. |
41 | // For single-byte CHARACTER, encode characters >= 0x80 with |
42 | // UTF-8 iff the mode is set. |
43 | return internalIoCharKind == 0 && (sizeof(CHAR) > 1 || isUTF8); |
44 | } |
45 | }; |
46 | |
47 | struct ConnectionState : public ConnectionAttributes { |
48 | RT_API_ATTRS bool |
49 | IsAtEOF() const; // true when read has hit EOF or endfile record |
50 | RT_API_ATTRS bool |
51 | IsAfterEndfile() const; // true after ENDFILE until repositioned |
52 | |
53 | // All positions and measurements are always in units of bytes, |
54 | // not characters. Multi-byte character encodings are possible in |
55 | // both internal I/O (when the character kind of the variable is 2 or 4) |
56 | // and external formatted I/O (when the encoding is UTF-8). |
57 | RT_API_ATTRS std::size_t RemainingSpaceInRecord() const; |
58 | RT_API_ATTRS bool NeedAdvance(std::size_t) const; |
59 | RT_API_ATTRS void HandleAbsolutePosition(std::int64_t); |
60 | RT_API_ATTRS void HandleRelativePosition(std::int64_t); |
61 | |
62 | RT_API_ATTRS void BeginRecord() { |
63 | positionInRecord = 0; |
64 | furthestPositionInRecord = 0; |
65 | unterminatedRecord = false; |
66 | } |
67 | |
68 | RT_API_ATTRS Fortran::common::optional<std::int64_t> |
69 | EffectiveRecordLength() const { |
70 | // When an input record is longer than an explicit RECL= from OPEN |
71 | // it is effectively truncated on input. |
72 | return openRecl && recordLength && *openRecl < *recordLength ? openRecl |
73 | : recordLength; |
74 | } |
75 | |
76 | Fortran::common::optional<std::int64_t> recordLength; |
77 | |
78 | std::int64_t currentRecordNumber{1}; // 1 is first |
79 | |
80 | // positionInRecord is the 0-based bytes offset in the current record |
81 | // to/from which the next data transfer will occur. It can be past |
82 | // furthestPositionInRecord if moved by an X or T or TR control edit |
83 | // descriptor. |
84 | std::int64_t positionInRecord{0}; |
85 | |
86 | // furthestPositionInRecord is the 0-based byte offset of the greatest |
87 | // position in the current record to/from which any data transfer has |
88 | // occurred, plus one. It can be viewed as a count of bytes processed. |
89 | std::int64_t furthestPositionInRecord{0}; // max(position+bytes) |
90 | |
91 | // Set at end of non-advancing I/O data transfer |
92 | Fortran::common::optional<std::int64_t> |
93 | leftTabLimit; // offset in current record |
94 | |
95 | // currentRecordNumber value captured after ENDFILE/REWIND/BACKSPACE statement |
96 | // or an end-of-file READ condition on a sequential access file |
97 | Fortran::common::optional<std::int64_t> endfileRecordNumber; |
98 | |
99 | // Mutable modes set at OPEN() that can be overridden in READ/WRITE & FORMAT |
100 | MutableModes modes; // BLANK=, DECIMAL=, SIGN=, ROUND=, PAD=, DELIM=, kP |
101 | |
102 | // Set when processing repeated items during list-directed & NAMELIST input |
103 | // in order to keep a span of records in frame on a non-positionable file, |
104 | // so that backspacing to the beginning of the repeated item doesn't require |
105 | // repositioning the external storage medium when that's impossible. |
106 | bool pinnedFrame{false}; |
107 | |
108 | // Set when the last record of a file is not properly terminated |
109 | // so that a non-advancing READ will not signal EOR. |
110 | bool unterminatedRecord{false}; |
111 | }; |
112 | |
113 | // Utility class for capturing and restoring a position in an input stream. |
114 | class SavedPosition { |
115 | public: |
116 | explicit RT_API_ATTRS SavedPosition(IoStatementState &); |
117 | RT_API_ATTRS ~SavedPosition(); |
118 | RT_API_ATTRS void Cancel() { cancelled_ = true; } |
119 | |
120 | private: |
121 | IoStatementState &io_; |
122 | ConnectionState saved_; |
123 | bool cancelled_{false}; |
124 | }; |
125 | |
126 | } // namespace Fortran::runtime::io |
127 | #endif // FORTRAN_RUNTIME_IO_CONNECTION_H_ |
128 | |