1 //===-- runtime/unit.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 external I/O units 10 11 #ifndef FORTRAN_RUNTIME_IO_UNIT_H_ 12 #define FORTRAN_RUNTIME_IO_UNIT_H_ 13 14 #include "buffer.h" 15 #include "connection.h" 16 #include "environment.h" 17 #include "file.h" 18 #include "format.h" 19 #include "io-error.h" 20 #include "io-stmt.h" 21 #include "lock.h" 22 #include "terminator.h" 23 #include "flang/Common/constexpr-bitset.h" 24 #include "flang/Runtime/memory.h" 25 #include <cstdlib> 26 #include <cstring> 27 #include <optional> 28 #include <variant> 29 30 namespace Fortran::runtime::io { 31 32 class UnitMap; 33 class ChildIo; 34 35 class ExternalFileUnit : public ConnectionState, 36 public OpenFile, 37 public FileFrame<ExternalFileUnit> { 38 public: 39 explicit ExternalFileUnit(int unitNumber) : unitNumber_{unitNumber} { 40 isUTF8 = executionEnvironment.defaultUTF8; 41 asyncIdAvailable_.set(); 42 asyncIdAvailable_.reset(0); 43 } 44 ~ExternalFileUnit() {} 45 46 int unitNumber() const { return unitNumber_; } 47 bool swapEndianness() const { return swapEndianness_; } 48 bool createdForInternalChildIo() const { return createdForInternalChildIo_; } 49 50 static ExternalFileUnit *LookUp(int unit); 51 static ExternalFileUnit *LookUpOrCreate( 52 int unit, const Terminator &, bool &wasExtant); 53 static ExternalFileUnit *LookUpOrCreateAnonymous(int unit, Direction, 54 std::optional<bool> isUnformatted, const Terminator &); 55 static ExternalFileUnit *LookUp(const char *path, std::size_t pathLen); 56 static ExternalFileUnit &CreateNew(int unit, const Terminator &); 57 static ExternalFileUnit *LookUpForClose(int unit); 58 static ExternalFileUnit &NewUnit(const Terminator &, bool forChildIo); 59 static void CloseAll(IoErrorHandler &); 60 static void FlushAll(IoErrorHandler &); 61 62 void OpenUnit(std::optional<OpenStatus>, std::optional<Action>, Position, 63 OwningPtr<char> &&path, std::size_t pathLength, Convert, 64 IoErrorHandler &); 65 void OpenAnonymousUnit(std::optional<OpenStatus>, std::optional<Action>, 66 Position, Convert, IoErrorHandler &); 67 void CloseUnit(CloseStatus, IoErrorHandler &); 68 void DestroyClosed(); 69 70 Iostat SetDirection(Direction); 71 72 template <typename A, typename... X> 73 IoStatementState &BeginIoStatement(X &&...xs) { 74 lock_.Take(); // dropped in EndIoStatement() 75 A &state{u_.emplace<A>(std::forward<X>(xs)...)}; 76 if constexpr (!std::is_same_v<A, OpenStatementState>) { 77 state.mutableModes() = ConnectionState::modes; 78 } 79 directAccessRecWasSet_ = false; 80 io_.emplace(state); 81 return *io_; 82 } 83 84 bool Emit( 85 const char *, std::size_t, std::size_t elementBytes, IoErrorHandler &); 86 bool Receive(char *, std::size_t, std::size_t elementBytes, IoErrorHandler &); 87 std::size_t GetNextInputBytes(const char *&, IoErrorHandler &); 88 bool BeginReadingRecord(IoErrorHandler &); 89 void FinishReadingRecord(IoErrorHandler &); 90 bool AdvanceRecord(IoErrorHandler &); 91 void BackspaceRecord(IoErrorHandler &); 92 void FlushOutput(IoErrorHandler &); 93 void FlushIfTerminal(IoErrorHandler &); 94 void Endfile(IoErrorHandler &); 95 void Rewind(IoErrorHandler &); 96 void EndIoStatement(); 97 void SetPosition(std::int64_t, IoErrorHandler &); // zero-based 98 std::int64_t InquirePos() const { 99 // 12.6.2.11 defines POS=1 as the beginning of file 100 return frameOffsetInFile_ + recordOffsetInFrame_ + positionInRecord + 1; 101 } 102 103 ChildIo *GetChildIo() { return child_.get(); } 104 ChildIo &PushChildIo(IoStatementState &); 105 void PopChildIo(ChildIo &); 106 107 int GetAsynchronousId(IoErrorHandler &); 108 bool Wait(int); 109 110 private: 111 static UnitMap &GetUnitMap(); 112 const char *FrameNextInput(IoErrorHandler &, std::size_t); 113 void BeginSequentialVariableUnformattedInputRecord(IoErrorHandler &); 114 void BeginVariableFormattedInputRecord(IoErrorHandler &); 115 void BackspaceFixedRecord(IoErrorHandler &); 116 void BackspaceVariableUnformattedRecord(IoErrorHandler &); 117 void BackspaceVariableFormattedRecord(IoErrorHandler &); 118 bool SetVariableFormattedRecordLength(); 119 void DoImpliedEndfile(IoErrorHandler &); 120 void DoEndfile(IoErrorHandler &); 121 void CommitWrites(); 122 bool CheckDirectAccess(IoErrorHandler &); 123 void HitEndOnRead(IoErrorHandler &); 124 125 Lock lock_; 126 127 int unitNumber_{-1}; 128 Direction direction_{Direction::Output}; 129 bool impliedEndfile_{false}; // sequential/stream output has taken place 130 bool beganReadingRecord_{false}; 131 bool directAccessRecWasSet_{false}; // REC= appeared 132 // Subtle: The beginning of the frame can't be allowed to advance 133 // during a single list-directed READ due to the possibility of a 134 // multi-record CHARACTER value with a "r*" repeat count. So we 135 // manage the frame and the current record therein separately. 136 std::int64_t frameOffsetInFile_{0}; 137 std::size_t recordOffsetInFrame_{0}; // of currentRecordNumber 138 bool swapEndianness_{false}; 139 bool createdForInternalChildIo_{false}; 140 common::BitSet<64> asyncIdAvailable_; 141 142 // When a synchronous I/O statement is in progress on this unit, holds its 143 // state. 144 std::variant<std::monostate, OpenStatementState, CloseStatementState, 145 ExternalFormattedIoStatementState<Direction::Output>, 146 ExternalFormattedIoStatementState<Direction::Input>, 147 ExternalListIoStatementState<Direction::Output>, 148 ExternalListIoStatementState<Direction::Input>, 149 ExternalUnformattedIoStatementState<Direction::Output>, 150 ExternalUnformattedIoStatementState<Direction::Input>, InquireUnitState, 151 ExternalMiscIoStatementState, ErroneousIoStatementState> 152 u_; 153 154 // Points to the active alternative (if any) in u_ for use as a Cookie 155 std::optional<IoStatementState> io_; 156 157 // A stack of child I/O pseudo-units for user-defined derived type 158 // I/O that have this unit number. 159 OwningPtr<ChildIo> child_; 160 }; 161 162 // A pseudo-unit for child I/O statements in user-defined derived type 163 // I/O subroutines; it forwards operations to the parent I/O statement, 164 // which can also be a child I/O statement. 165 class ChildIo { 166 public: 167 ChildIo(IoStatementState &parent, OwningPtr<ChildIo> &&previous) 168 : parent_{parent}, previous_{std::move(previous)} {} 169 170 IoStatementState &parent() const { return parent_; } 171 172 void EndIoStatement(); 173 174 template <typename A, typename... X> 175 IoStatementState &BeginIoStatement(X &&...xs) { 176 A &state{u_.emplace<A>(std::forward<X>(xs)...)}; 177 io_.emplace(state); 178 return *io_; 179 } 180 181 OwningPtr<ChildIo> AcquirePrevious() { return std::move(previous_); } 182 183 Iostat CheckFormattingAndDirection(bool unformatted, Direction); 184 185 private: 186 IoStatementState &parent_; 187 OwningPtr<ChildIo> previous_; 188 std::variant<std::monostate, 189 ChildFormattedIoStatementState<Direction::Output>, 190 ChildFormattedIoStatementState<Direction::Input>, 191 ChildListIoStatementState<Direction::Output>, 192 ChildListIoStatementState<Direction::Input>, 193 ChildUnformattedIoStatementState<Direction::Output>, 194 ChildUnformattedIoStatementState<Direction::Input>, InquireUnitState, 195 ErroneousIoStatementState> 196 u_; 197 std::optional<IoStatementState> io_; 198 }; 199 200 } // namespace Fortran::runtime::io 201 #endif // FORTRAN_RUNTIME_IO_UNIT_H_ 202