//===-- runtime/io-stmt.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 // //===----------------------------------------------------------------------===// // Representations of the state of an I/O statement in progress #ifndef FORTRAN_RUNTIME_IO_STMT_H_ #define FORTRAN_RUNTIME_IO_STMT_H_ #include "connection.h" #include "descriptor.h" #include "file.h" #include "format.h" #include "internal-unit.h" #include "io-api.h" #include "io-error.h" #include #include #include namespace Fortran::runtime::io { class ExternalFileUnit; class OpenStatementState; class InquireUnitState; class InquireNoUnitState; class InquireUnconnectedFileState; class InquireIOLengthState; class ExternalMiscIoStatementState; class CloseStatementState; class NoopCloseStatementState; template class InternalFormattedIoStatementState; template class InternalListIoStatementState; template class ExternalFormattedIoStatementState; template class ExternalListIoStatementState; template class UnformattedIoStatementState; struct InputStatementState {}; struct OutputStatementState {}; template using IoDirectionState = std::conditional_t; struct FormattedIoStatementState {}; // The Cookie type in the I/O API is a pointer (for C) to this class. class IoStatementState { public: template explicit IoStatementState(A &x) : u_{x} {} // These member functions each project themselves into the active alternative. // They're used by per-data-item routines in the I/O API (e.g., OutputReal64) // to interact with the state of the I/O statement in progress. // This design avoids virtual member functions and function pointers, // which may not have good support in some runtime environments. std::optional GetNextDataEdit(int = 1); bool Emit(const char *, std::size_t, std::size_t elementBytes = 0); std::optional GetCurrentChar(); // vacant after end of record bool AdvanceRecord(int = 1); void BackspaceRecord(); void HandleRelativePosition(std::int64_t); int EndIoStatement(); ConnectionState &GetConnectionState(); IoErrorHandler &GetIoErrorHandler() const; ExternalFileUnit *GetExternalFileUnit() const; // null if internal unit MutableModes &mutableModes(); void BeginReadingRecord(); void FinishReadingRecord(); bool Inquire(InquiryKeywordHash, char *, std::size_t); bool Inquire(InquiryKeywordHash, bool &); bool Inquire(InquiryKeywordHash, std::int64_t, bool &); // PENDING= bool Inquire(InquiryKeywordHash, std::int64_t &); // N.B.: this also works with base classes template A *get_if() const { return std::visit( [](auto &x) -> A * { if constexpr (std::is_convertible_v) { return &x.get(); } return nullptr; }, u_); } bool EmitRepeated(char, std::size_t); bool EmitField(const char *, std::size_t length, std::size_t width); std::optional SkipSpaces(std::optional &remaining); std::optional NextInField(std::optional &remaining); std::optional GetNextNonBlank(); // can advance record template void CheckFormattedStmtType(const char *name) { if (!get_if() || !get_if>()) { GetIoErrorHandler().Crash( "%s called for I/O statement that is not formatted %s", name, D == Direction::Output ? "output" : "input"); } } private: std::variant, std::reference_wrapper, std::reference_wrapper, std::reference_wrapper< InternalFormattedIoStatementState>, std::reference_wrapper< InternalFormattedIoStatementState>, std::reference_wrapper>, std::reference_wrapper>, std::reference_wrapper< ExternalFormattedIoStatementState>, std::reference_wrapper< ExternalFormattedIoStatementState>, std::reference_wrapper>, std::reference_wrapper>, std::reference_wrapper>, std::reference_wrapper>, std::reference_wrapper, std::reference_wrapper, std::reference_wrapper, std::reference_wrapper, std::reference_wrapper> u_; }; // Base class for all per-I/O statement state classes. // Inherits IoErrorHandler from its base. struct IoStatementBase : public DefaultFormatControlCallbacks { using DefaultFormatControlCallbacks::DefaultFormatControlCallbacks; int EndIoStatement(); std::optional GetNextDataEdit(IoStatementState &, int = 1); ExternalFileUnit *GetExternalFileUnit() const { return nullptr; } void BeginReadingRecord() {} void FinishReadingRecord() {} bool Inquire(InquiryKeywordHash, char *, std::size_t); bool Inquire(InquiryKeywordHash, bool &); bool Inquire(InquiryKeywordHash, std::int64_t, bool &); bool Inquire(InquiryKeywordHash, std::int64_t &); void BadInquiryKeywordHashCrash(InquiryKeywordHash); }; // Common state for list-directed internal & external I/O template class ListDirectedStatementState; template <> class ListDirectedStatementState : public FormattedIoStatementState { public: static std::size_t RemainingSpaceInRecord(const ConnectionState &); bool NeedAdvance(const ConnectionState &, std::size_t) const; bool EmitLeadingSpaceOrAdvance( IoStatementState &, std::size_t, bool isCharacter = false); std::optional GetNextDataEdit( IoStatementState &, int maxRepeat = 1); bool lastWasUndelimitedCharacter{false}; }; template <> class ListDirectedStatementState : public FormattedIoStatementState { public: // Skips value separators, handles repetition and null values. // Vacant when '/' appears; present with descriptor == ListDirectedNullValue // when a null value appears. std::optional GetNextDataEdit( IoStatementState &, int maxRepeat = 1); private: int remaining_{0}; // for "r*" repetition std::int64_t initialRecordNumber_; std::int64_t initialPositionInRecord_; bool isFirstItem_{true}; // leading separator implies null first item bool hitSlash_{false}; // once '/' is seen, nullify further items bool realPart_{false}; bool imaginaryPart_{false}; }; template class InternalIoStatementState : public IoStatementBase, public IoDirectionState { public: using CharType = CHAR; using Buffer = std::conditional_t; InternalIoStatementState(Buffer, std::size_t, const char *sourceFile = nullptr, int sourceLine = 0); InternalIoStatementState( const Descriptor &, const char *sourceFile = nullptr, int sourceLine = 0); int EndIoStatement(); bool Emit(const CharType *, std::size_t chars /* not necessarily bytes */, std::size_t elementBytes = 0); std::optional GetCurrentChar(); bool AdvanceRecord(int = 1); void BackspaceRecord(); ConnectionState &GetConnectionState() { return unit_; } MutableModes &mutableModes() { return unit_.modes; } void HandleRelativePosition(std::int64_t); void HandleAbsolutePosition(std::int64_t); protected: bool free_{true}; InternalDescriptorUnit unit_; }; template class InternalFormattedIoStatementState : public InternalIoStatementState, public FormattedIoStatementState { public: using CharType = CHAR; using typename InternalIoStatementState::Buffer; InternalFormattedIoStatementState(Buffer internal, std::size_t internalLength, const CharType *format, std::size_t formatLength, const char *sourceFile = nullptr, int sourceLine = 0); InternalFormattedIoStatementState(const Descriptor &, const CharType *format, std::size_t formatLength, const char *sourceFile = nullptr, int sourceLine = 0); IoStatementState &ioStatementState() { return ioStatementState_; } int EndIoStatement(); std::optional GetNextDataEdit( IoStatementState &, int maxRepeat = 1) { return format_.GetNextDataEdit(*this, maxRepeat); } private: IoStatementState ioStatementState_; // points to *this using InternalIoStatementState::unit_; // format_ *must* be last; it may be partial someday FormatControl format_; }; template class InternalListIoStatementState : public InternalIoStatementState, public ListDirectedStatementState { public: using CharType = CHAR; using typename InternalIoStatementState::Buffer; InternalListIoStatementState(Buffer internal, std::size_t internalLength, const char *sourceFile = nullptr, int sourceLine = 0); InternalListIoStatementState( const Descriptor &, const char *sourceFile = nullptr, int sourceLine = 0); IoStatementState &ioStatementState() { return ioStatementState_; } using ListDirectedStatementState::GetNextDataEdit; private: IoStatementState ioStatementState_; // points to *this using InternalIoStatementState::unit_; }; class ExternalIoStatementBase : public IoStatementBase { public: ExternalIoStatementBase( ExternalFileUnit &, const char *sourceFile = nullptr, int sourceLine = 0); ExternalFileUnit &unit() { return unit_; } MutableModes &mutableModes(); ConnectionState &GetConnectionState(); int EndIoStatement(); ExternalFileUnit *GetExternalFileUnit() { return &unit_; } private: ExternalFileUnit &unit_; }; template class ExternalIoStatementState : public ExternalIoStatementBase, public IoDirectionState { public: using ExternalIoStatementBase::ExternalIoStatementBase; int EndIoStatement(); bool Emit(const char *, std::size_t, std::size_t elementBytes = 0); bool Emit(const char16_t *, std::size_t chars /* not bytes */); bool Emit(const char32_t *, std::size_t chars /* not bytes */); std::optional GetCurrentChar(); bool AdvanceRecord(int = 1); void BackspaceRecord(); void HandleRelativePosition(std::int64_t); void HandleAbsolutePosition(std::int64_t); void BeginReadingRecord(); void FinishReadingRecord(); }; template class ExternalFormattedIoStatementState : public ExternalIoStatementState, public FormattedIoStatementState { public: using CharType = CHAR; ExternalFormattedIoStatementState(ExternalFileUnit &, const CharType *format, std::size_t formatLength, const char *sourceFile = nullptr, int sourceLine = 0); MutableModes &mutableModes() { return mutableModes_; } int EndIoStatement(); std::optional GetNextDataEdit( IoStatementState &, int maxRepeat = 1) { return format_.GetNextDataEdit(*this, maxRepeat); } private: // These are forked from ConnectionState's modes at the beginning // of each formatted I/O statement so they may be overridden by control // edit descriptors during the statement. MutableModes mutableModes_; FormatControl format_; }; template class ExternalListIoStatementState : public ExternalIoStatementState, public ListDirectedStatementState { public: using ExternalIoStatementState::ExternalIoStatementState; using ListDirectedStatementState::GetNextDataEdit; }; template class UnformattedIoStatementState : public ExternalIoStatementState { public: using ExternalIoStatementState::ExternalIoStatementState; bool Receive(char *, std::size_t, std::size_t elementBytes = 0); bool Emit(const char *, std::size_t, std::size_t elementBytes = 0); }; class OpenStatementState : public ExternalIoStatementBase { public: OpenStatementState(ExternalFileUnit &unit, bool wasExtant, const char *sourceFile = nullptr, int sourceLine = 0) : ExternalIoStatementBase{unit, sourceFile, sourceLine}, wasExtant_{ wasExtant} {} bool wasExtant() const { return wasExtant_; } void set_status(OpenStatus status) { status_ = status; } // STATUS= void set_path(const char *, std::size_t); // FILE= void set_position(Position position) { position_ = position; } // POSITION= void set_action(Action action) { action_ = action; } // ACTION= void set_convert(Convert convert) { convert_ = convert; } // CONVERT= void set_access(Access access) { access_ = access; } // ACCESS= void set_isUnformatted(bool yes = true) { isUnformatted_ = yes; } // FORM= int EndIoStatement(); private: bool wasExtant_; std::optional status_; Position position_{Position::AsIs}; std::optional action_; Convert convert_{Convert::Native}; OwningPtr path_; std::size_t pathLength_; std::optional isUnformatted_; std::optional access_; }; class CloseStatementState : public ExternalIoStatementBase { public: CloseStatementState(ExternalFileUnit &unit, const char *sourceFile = nullptr, int sourceLine = 0) : ExternalIoStatementBase{unit, sourceFile, sourceLine} {} void set_status(CloseStatus status) { status_ = status; } int EndIoStatement(); private: CloseStatus status_{CloseStatus::Keep}; }; // For CLOSE(bad unit) and INQUIRE(unconnected unit) class NoUnitIoStatementState : public IoStatementBase { public: IoStatementState &ioStatementState() { return ioStatementState_; } MutableModes &mutableModes() { return connection_.modes; } ConnectionState &GetConnectionState() { return connection_; } int EndIoStatement(); protected: template NoUnitIoStatementState(const char *sourceFile, int sourceLine, A &stmt) : IoStatementBase{sourceFile, sourceLine}, ioStatementState_{stmt} {} private: IoStatementState ioStatementState_; // points to *this ConnectionState connection_; }; class NoopCloseStatementState : public NoUnitIoStatementState { public: NoopCloseStatementState(const char *sourceFile, int sourceLine) : NoUnitIoStatementState{sourceFile, sourceLine, *this} {} void set_status(CloseStatus) {} // discards }; extern template class InternalIoStatementState; extern template class InternalIoStatementState; extern template class InternalFormattedIoStatementState; extern template class InternalFormattedIoStatementState; extern template class InternalListIoStatementState; extern template class InternalListIoStatementState; extern template class ExternalIoStatementState; extern template class ExternalIoStatementState; extern template class ExternalFormattedIoStatementState; extern template class ExternalFormattedIoStatementState; extern template class ExternalListIoStatementState; extern template class ExternalListIoStatementState; extern template class UnformattedIoStatementState; extern template class UnformattedIoStatementState; extern template class FormatControl< InternalFormattedIoStatementState>; extern template class FormatControl< InternalFormattedIoStatementState>; extern template class FormatControl< ExternalFormattedIoStatementState>; extern template class FormatControl< ExternalFormattedIoStatementState>; class InquireUnitState : public ExternalIoStatementBase { public: InquireUnitState(ExternalFileUnit &unit, const char *sourceFile = nullptr, int sourceLine = 0); bool Inquire(InquiryKeywordHash, char *, std::size_t); bool Inquire(InquiryKeywordHash, bool &); bool Inquire(InquiryKeywordHash, std::int64_t, bool &); bool Inquire(InquiryKeywordHash, std::int64_t &); }; class InquireNoUnitState : public NoUnitIoStatementState { public: InquireNoUnitState(const char *sourceFile = nullptr, int sourceLine = 0); bool Inquire(InquiryKeywordHash, char *, std::size_t); bool Inquire(InquiryKeywordHash, bool &); bool Inquire(InquiryKeywordHash, std::int64_t, bool &); bool Inquire(InquiryKeywordHash, std::int64_t &); }; class InquireUnconnectedFileState : public NoUnitIoStatementState { public: InquireUnconnectedFileState(OwningPtr &&path, const char *sourceFile = nullptr, int sourceLine = 0); bool Inquire(InquiryKeywordHash, char *, std::size_t); bool Inquire(InquiryKeywordHash, bool &); bool Inquire(InquiryKeywordHash, std::int64_t, bool &); bool Inquire(InquiryKeywordHash, std::int64_t &); private: OwningPtr path_; // trimmed and NUL terminated }; class InquireIOLengthState : public NoUnitIoStatementState, public OutputStatementState { public: InquireIOLengthState(const char *sourceFile = nullptr, int sourceLine = 0); std::size_t bytes() const { return bytes_; } bool Emit(const char *, std::size_t, std::size_t elementBytes = 0); private: std::size_t bytes_{0}; }; class ExternalMiscIoStatementState : public ExternalIoStatementBase { public: enum Which { Flush, Backspace, Endfile, Rewind }; ExternalMiscIoStatementState(ExternalFileUnit &unit, Which which, const char *sourceFile = nullptr, int sourceLine = 0) : ExternalIoStatementBase{unit, sourceFile, sourceLine}, which_{which} {} int EndIoStatement(); private: Which which_; }; } // namespace Fortran::runtime::io #endif // FORTRAN_RUNTIME_IO_STMT_H_