| //===-- runtime/unit.h ------------------------------------------*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| // Fortran external I/O units |
| |
| #ifndef FORTRAN_RUNTIME_IO_UNIT_H_ |
| #define FORTRAN_RUNTIME_IO_UNIT_H_ |
| |
| #include "buffer.h" |
| #include "connection.h" |
| #include "file.h" |
| #include "format.h" |
| #include "io-error.h" |
| #include "io-stmt.h" |
| #include "lock.h" |
| #include "terminator.h" |
| #include "flang/Runtime/memory.h" |
| #include <cstdlib> |
| #include <cstring> |
| #include <optional> |
| #include <variant> |
| |
| namespace Fortran::runtime::io { |
| |
| class UnitMap; |
| class ChildIo; |
| |
| class ExternalFileUnit : public ConnectionState, |
| public OpenFile, |
| public FileFrame<ExternalFileUnit> { |
| public: |
| explicit ExternalFileUnit(int unitNumber) : unitNumber_{unitNumber} {} |
| int unitNumber() const { return unitNumber_; } |
| bool swapEndianness() const { return swapEndianness_; } |
| bool createdForInternalChildIo() const { return createdForInternalChildIo_; } |
| |
| static ExternalFileUnit *LookUp(int unit); |
| static ExternalFileUnit &LookUpOrCrash(int unit, const Terminator &); |
| static ExternalFileUnit &LookUpOrCreate( |
| int unit, const Terminator &, bool &wasExtant); |
| static ExternalFileUnit &LookUpOrCreateAnonymous(int unit, Direction, |
| std::optional<bool> isUnformatted, const Terminator &); |
| static ExternalFileUnit *LookUp(const char *path); |
| static ExternalFileUnit &CreateNew(int unit, const Terminator &); |
| static ExternalFileUnit *LookUpForClose(int unit); |
| static ExternalFileUnit &NewUnit(const Terminator &, bool forChildIo = false); |
| static void CloseAll(IoErrorHandler &); |
| static void FlushAll(IoErrorHandler &); |
| |
| void OpenUnit(std::optional<OpenStatus>, std::optional<Action>, Position, |
| OwningPtr<char> &&path, std::size_t pathLength, Convert, |
| IoErrorHandler &); |
| void OpenAnonymousUnit(std::optional<OpenStatus>, std::optional<Action>, |
| Position, Convert, IoErrorHandler &); |
| void CloseUnit(CloseStatus, IoErrorHandler &); |
| void DestroyClosed(); |
| |
| bool SetDirection(Direction, IoErrorHandler &); |
| |
| template <typename A, typename... X> |
| IoStatementState &BeginIoStatement(X &&...xs) { |
| lock_.Take(); // dropped in EndIoStatement() |
| A &state{u_.emplace<A>(std::forward<X>(xs)...)}; |
| if constexpr (!std::is_same_v<A, OpenStatementState>) { |
| state.mutableModes() = ConnectionState::modes; |
| } |
| io_.emplace(state); |
| return *io_; |
| } |
| |
| bool Emit( |
| const char *, std::size_t, std::size_t elementBytes, IoErrorHandler &); |
| bool Receive(char *, std::size_t, std::size_t elementBytes, IoErrorHandler &); |
| std::size_t GetNextInputBytes(const char *&, IoErrorHandler &); |
| std::optional<char32_t> GetCurrentChar(IoErrorHandler &); |
| void SetLeftTabLimit(); |
| bool BeginReadingRecord(IoErrorHandler &); |
| void FinishReadingRecord(IoErrorHandler &); |
| bool AdvanceRecord(IoErrorHandler &); |
| void BackspaceRecord(IoErrorHandler &); |
| void FlushOutput(IoErrorHandler &); |
| void FlushIfTerminal(IoErrorHandler &); |
| void Endfile(IoErrorHandler &); |
| void Rewind(IoErrorHandler &); |
| void EndIoStatement(); |
| void SetPosition(std::int64_t pos) { |
| frameOffsetInFile_ = pos; |
| recordOffsetInFrame_ = 0; |
| BeginRecord(); |
| } |
| |
| ChildIo *GetChildIo() { return child_.get(); } |
| ChildIo &PushChildIo(IoStatementState &); |
| void PopChildIo(ChildIo &); |
| |
| private: |
| static UnitMap &GetUnitMap(); |
| const char *FrameNextInput(IoErrorHandler &, std::size_t); |
| void BeginSequentialVariableUnformattedInputRecord(IoErrorHandler &); |
| void BeginSequentialVariableFormattedInputRecord(IoErrorHandler &); |
| void BackspaceFixedRecord(IoErrorHandler &); |
| void BackspaceVariableUnformattedRecord(IoErrorHandler &); |
| void BackspaceVariableFormattedRecord(IoErrorHandler &); |
| bool SetSequentialVariableFormattedRecordLength(); |
| void DoImpliedEndfile(IoErrorHandler &); |
| void DoEndfile(IoErrorHandler &); |
| void CommitWrites(); |
| |
| int unitNumber_{-1}; |
| Direction direction_{Direction::Output}; |
| bool impliedEndfile_{false}; // seq. output has taken place |
| bool beganReadingRecord_{false}; |
| |
| Lock lock_; |
| |
| // When an I/O statement is in progress on this unit, holds its state. |
| std::variant<std::monostate, OpenStatementState, CloseStatementState, |
| ExternalFormattedIoStatementState<Direction::Output>, |
| ExternalFormattedIoStatementState<Direction::Input>, |
| ExternalListIoStatementState<Direction::Output>, |
| ExternalListIoStatementState<Direction::Input>, |
| ExternalUnformattedIoStatementState<Direction::Output>, |
| ExternalUnformattedIoStatementState<Direction::Input>, InquireUnitState, |
| ExternalMiscIoStatementState> |
| u_; |
| |
| // Points to the active alternative (if any) in u_ for use as a Cookie |
| std::optional<IoStatementState> io_; |
| |
| // Subtle: The beginning of the frame can't be allowed to advance |
| // during a single list-directed READ due to the possibility of a |
| // multi-record CHARACTER value with a "r*" repeat count. So we |
| // manage the frame and the current record therein separately. |
| std::int64_t frameOffsetInFile_{0}; |
| std::size_t recordOffsetInFrame_{0}; // of currentRecordNumber |
| |
| bool swapEndianness_{false}; |
| |
| bool createdForInternalChildIo_{false}; |
| |
| // A stack of child I/O pseudo-units for user-defined derived type |
| // I/O that have this unit number. |
| OwningPtr<ChildIo> child_; |
| }; |
| |
| // A pseudo-unit for child I/O statements in user-defined derived type |
| // I/O subroutines; it forwards operations to the parent I/O statement, |
| // which can also be a child I/O statement. |
| class ChildIo { |
| public: |
| ChildIo(IoStatementState &parent, OwningPtr<ChildIo> &&previous) |
| : parent_{parent}, previous_{std::move(previous)} {} |
| |
| IoStatementState &parent() const { return parent_; } |
| |
| void EndIoStatement(); |
| |
| template <typename A, typename... X> |
| IoStatementState &BeginIoStatement(X &&...xs) { |
| A &state{u_.emplace<A>(std::forward<X>(xs)...)}; |
| io_.emplace(state); |
| return *io_; |
| } |
| |
| OwningPtr<ChildIo> AcquirePrevious() { return std::move(previous_); } |
| |
| bool CheckFormattingAndDirection( |
| Terminator &, const char *what, bool unformatted, Direction); |
| |
| private: |
| IoStatementState &parent_; |
| OwningPtr<ChildIo> previous_; |
| std::variant<std::monostate, |
| ChildFormattedIoStatementState<Direction::Output>, |
| ChildFormattedIoStatementState<Direction::Input>, |
| ChildListIoStatementState<Direction::Output>, |
| ChildListIoStatementState<Direction::Input>, |
| ChildUnformattedIoStatementState<Direction::Output>, |
| ChildUnformattedIoStatementState<Direction::Input>> |
| u_; |
| std::optional<IoStatementState> io_; |
| }; |
| |
| } // namespace Fortran::runtime::io |
| #endif // FORTRAN_RUNTIME_IO_UNIT_H_ |