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