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: ExternalFileUnit(int unitNumber)39 explicit ExternalFileUnit(int unitNumber) : unitNumber_{unitNumber} { 40 isUTF8 = executionEnvironment.defaultUTF8; 41 asyncIdAvailable_.set(); 42 asyncIdAvailable_.reset(0); 43 } ~ExternalFileUnit()44 ~ExternalFileUnit() {} 45 unitNumber()46 int unitNumber() const { return unitNumber_; } swapEndianness()47 bool swapEndianness() const { return swapEndianness_; } createdForInternalChildIo()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> BeginIoStatement(X &&...xs)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 bool SetStreamPos(std::int64_t, IoErrorHandler &); // one-based, for POS= 98 bool SetDirectRec(std::int64_t, IoErrorHandler &); // one-based, for REC= InquirePos()99 std::int64_t InquirePos() const { 100 // 12.6.2.11 defines POS=1 as the beginning of file 101 return frameOffsetInFile_ + recordOffsetInFrame_ + positionInRecord + 1; 102 } 103 GetChildIo()104 ChildIo *GetChildIo() { return child_.get(); } 105 ChildIo &PushChildIo(IoStatementState &); 106 void PopChildIo(ChildIo &); 107 108 int GetAsynchronousId(IoErrorHandler &); 109 bool Wait(int); 110 111 private: 112 static UnitMap &GetUnitMap(); 113 const char *FrameNextInput(IoErrorHandler &, std::size_t); 114 void SetPosition(std::int64_t, IoErrorHandler &); // zero-based 115 void BeginSequentialVariableUnformattedInputRecord(IoErrorHandler &); 116 void BeginVariableFormattedInputRecord(IoErrorHandler &); 117 void BackspaceFixedRecord(IoErrorHandler &); 118 void BackspaceVariableUnformattedRecord(IoErrorHandler &); 119 void BackspaceVariableFormattedRecord(IoErrorHandler &); 120 bool SetVariableFormattedRecordLength(); 121 void DoImpliedEndfile(IoErrorHandler &); 122 void DoEndfile(IoErrorHandler &); 123 void CommitWrites(); 124 bool CheckDirectAccess(IoErrorHandler &); 125 void HitEndOnRead(IoErrorHandler &); 126 127 Lock lock_; 128 129 int unitNumber_{-1}; 130 Direction direction_{Direction::Output}; 131 bool impliedEndfile_{false}; // sequential/stream output has taken place 132 bool beganReadingRecord_{false}; 133 bool directAccessRecWasSet_{false}; // REC= appeared 134 // Subtle: The beginning of the frame can't be allowed to advance 135 // during a single list-directed READ due to the possibility of a 136 // multi-record CHARACTER value with a "r*" repeat count. So we 137 // manage the frame and the current record therein separately. 138 std::int64_t frameOffsetInFile_{0}; 139 std::size_t recordOffsetInFrame_{0}; // of currentRecordNumber 140 bool swapEndianness_{false}; 141 bool createdForInternalChildIo_{false}; 142 common::BitSet<64> asyncIdAvailable_; 143 144 // When a synchronous I/O statement is in progress on this unit, holds its 145 // state. 146 std::variant<std::monostate, OpenStatementState, CloseStatementState, 147 ExternalFormattedIoStatementState<Direction::Output>, 148 ExternalFormattedIoStatementState<Direction::Input>, 149 ExternalListIoStatementState<Direction::Output>, 150 ExternalListIoStatementState<Direction::Input>, 151 ExternalUnformattedIoStatementState<Direction::Output>, 152 ExternalUnformattedIoStatementState<Direction::Input>, InquireUnitState, 153 ExternalMiscIoStatementState, ErroneousIoStatementState> 154 u_; 155 156 // Points to the active alternative (if any) in u_ for use as a Cookie 157 std::optional<IoStatementState> io_; 158 159 // A stack of child I/O pseudo-units for user-defined derived type 160 // I/O that have this unit number. 161 OwningPtr<ChildIo> child_; 162 }; 163 164 // A pseudo-unit for child I/O statements in user-defined derived type 165 // I/O subroutines; it forwards operations to the parent I/O statement, 166 // which can also be a child I/O statement. 167 class ChildIo { 168 public: ChildIo(IoStatementState & parent,OwningPtr<ChildIo> && previous)169 ChildIo(IoStatementState &parent, OwningPtr<ChildIo> &&previous) 170 : parent_{parent}, previous_{std::move(previous)} {} 171 parent()172 IoStatementState &parent() const { return parent_; } 173 174 void EndIoStatement(); 175 176 template <typename A, typename... X> BeginIoStatement(X &&...xs)177 IoStatementState &BeginIoStatement(X &&...xs) { 178 A &state{u_.emplace<A>(std::forward<X>(xs)...)}; 179 io_.emplace(state); 180 return *io_; 181 } 182 AcquirePrevious()183 OwningPtr<ChildIo> AcquirePrevious() { return std::move(previous_); } 184 185 Iostat CheckFormattingAndDirection(bool unformatted, Direction); 186 187 private: 188 IoStatementState &parent_; 189 OwningPtr<ChildIo> previous_; 190 std::variant<std::monostate, 191 ChildFormattedIoStatementState<Direction::Output>, 192 ChildFormattedIoStatementState<Direction::Input>, 193 ChildListIoStatementState<Direction::Output>, 194 ChildListIoStatementState<Direction::Input>, 195 ChildUnformattedIoStatementState<Direction::Output>, 196 ChildUnformattedIoStatementState<Direction::Input>, InquireUnitState, 197 ErroneousIoStatementState> 198 u_; 199 std::optional<IoStatementState> io_; 200 }; 201 202 } // namespace Fortran::runtime::io 203 #endif // FORTRAN_RUNTIME_IO_UNIT_H_ 204