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 <cinttypes> |
16 | #include <optional> |
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 | std::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 | std::optional<std::int64_t> openRecl; // RECL= on OPEN |
33 | |
34 | bool IsRecordFile() const { |
35 | // Formatted stream files are viewed as having records, at least on input |
36 | return access != Access::Stream || !isUnformatted.value_or(u: true); |
37 | } |
38 | |
39 | template <typename CHAR = char> constexpr 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 | bool IsAtEOF() const; // true when read has hit EOF or endfile record |
49 | bool IsAfterEndfile() const; // true after ENDFILE until repositioned |
50 | |
51 | // All positions and measurements are always in units of bytes, |
52 | // not characters. Multi-byte character encodings are possible in |
53 | // both internal I/O (when the character kind of the variable is 2 or 4) |
54 | // and external formatted I/O (when the encoding is UTF-8). |
55 | std::size_t RemainingSpaceInRecord() const; |
56 | bool NeedAdvance(std::size_t) const; |
57 | void HandleAbsolutePosition(std::int64_t); |
58 | void HandleRelativePosition(std::int64_t); |
59 | |
60 | void BeginRecord() { |
61 | positionInRecord = 0; |
62 | furthestPositionInRecord = 0; |
63 | unterminatedRecord = false; |
64 | } |
65 | |
66 | std::optional<std::int64_t> EffectiveRecordLength() const { |
67 | // When an input record is longer than an explicit RECL= from OPEN |
68 | // it is effectively truncated on input. |
69 | return openRecl && recordLength && *openRecl < *recordLength ? openRecl |
70 | : recordLength; |
71 | } |
72 | |
73 | std::optional<std::int64_t> recordLength; |
74 | |
75 | std::int64_t currentRecordNumber{1}; // 1 is first |
76 | |
77 | // positionInRecord is the 0-based bytes offset in the current record |
78 | // to/from which the next data transfer will occur. It can be past |
79 | // furthestPositionInRecord if moved by an X or T or TR control edit |
80 | // descriptor. |
81 | std::int64_t positionInRecord{0}; |
82 | |
83 | // furthestPositionInRecord is the 0-based byte offset of the greatest |
84 | // position in the current record to/from which any data transfer has |
85 | // occurred, plus one. It can be viewed as a count of bytes processed. |
86 | std::int64_t furthestPositionInRecord{0}; // max(position+bytes) |
87 | |
88 | // Set at end of non-advancing I/O data transfer |
89 | std::optional<std::int64_t> leftTabLimit; // offset in current record |
90 | |
91 | // currentRecordNumber value captured after ENDFILE/REWIND/BACKSPACE statement |
92 | // or an end-of-file READ condition on a sequential access file |
93 | std::optional<std::int64_t> endfileRecordNumber; |
94 | |
95 | // Mutable modes set at OPEN() that can be overridden in READ/WRITE & FORMAT |
96 | MutableModes modes; // BLANK=, DECIMAL=, SIGN=, ROUND=, PAD=, DELIM=, kP |
97 | |
98 | // Set when processing repeated items during list-directed & NAMELIST input |
99 | // in order to keep a span of records in frame on a non-positionable file, |
100 | // so that backspacing to the beginning of the repeated item doesn't require |
101 | // repositioning the external storage medium when that's impossible. |
102 | bool pinnedFrame{false}; |
103 | |
104 | // Set when the last record of a file is not properly terminated |
105 | // so that a non-advancing READ will not signal EOR. |
106 | bool unterminatedRecord{false}; |
107 | }; |
108 | |
109 | // Utility class for capturing and restoring a position in an input stream. |
110 | class SavedPosition { |
111 | public: |
112 | explicit SavedPosition(IoStatementState &); |
113 | ~SavedPosition(); |
114 | void Cancel() { cancelled_ = true; } |
115 | |
116 | private: |
117 | IoStatementState &io_; |
118 | ConnectionState saved_; |
119 | bool cancelled_{false}; |
120 | }; |
121 | |
122 | } // namespace Fortran::runtime::io |
123 | #endif // FORTRAN_RUNTIME_IO_CONNECTION_H_ |
124 | |