| //===-- 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 "file.h" |
| #include "format.h" |
| #include "internal-unit.h" |
| #include "io-error.h" |
| #include "flang/Common/optional.h" |
| #include "flang/Common/reference-wrapper.h" |
| #include "flang/Common/visit.h" |
| #include "flang/Runtime/descriptor.h" |
| #include "flang/Runtime/io-api.h" |
| #include <flang/Common/variant.h> |
| #include <functional> |
| #include <type_traits> |
| |
| namespace Fortran::runtime::io { |
| |
| class ExternalFileUnit; |
| class ChildIo; |
| |
| class OpenStatementState; |
| class InquireUnitState; |
| class InquireNoUnitState; |
| class InquireUnconnectedFileState; |
| class InquireIOLengthState; |
| class ExternalMiscIoStatementState; |
| class CloseStatementState; |
| class NoopStatementState; // CLOSE or FLUSH on unknown unit |
| class ErroneousIoStatementState; |
| |
| template <Direction, typename CHAR = char> |
| class InternalFormattedIoStatementState; |
| template <Direction> class InternalListIoStatementState; |
| template <Direction, typename CHAR = char> |
| class ExternalFormattedIoStatementState; |
| template <Direction> class ExternalListIoStatementState; |
| template <Direction> class ExternalUnformattedIoStatementState; |
| template <Direction, typename CHAR = char> class ChildFormattedIoStatementState; |
| template <Direction> class ChildListIoStatementState; |
| template <Direction> class ChildUnformattedIoStatementState; |
| |
| struct InputStatementState {}; |
| struct OutputStatementState {}; |
| template <Direction D> |
| using IoDirectionState = std::conditional_t<D == Direction::Input, |
| InputStatementState, OutputStatementState>; |
| |
| // Common state for all kinds of formatted I/O |
| template <Direction D> class FormattedIoStatementState {}; |
| template <> class FormattedIoStatementState<Direction::Input> { |
| public: |
| RT_API_ATTRS std::size_t GetEditDescriptorChars() const; |
| RT_API_ATTRS void GotChar(int); |
| |
| private: |
| // Account of characters read for edit descriptors (i.e., formatted I/O |
| // with a FORMAT, not list-directed or NAMELIST), not including padding. |
| std::size_t chars_{0}; // for READ(SIZE=) |
| }; |
| |
| // The Cookie type in the I/O API is a pointer (for C) to this class. |
| class IoStatementState { |
| public: |
| template <typename A> explicit RT_API_ATTRS 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. |
| |
| // CompleteOperation() is the last opportunity to raise an I/O error. |
| // It is called by EndIoStatement(), but it can be invoked earlier to |
| // catch errors for (e.g.) GetIoMsg() and GetNewUnit(). If called |
| // more than once, it is a no-op. |
| RT_API_ATTRS void CompleteOperation(); |
| // Completes an I/O statement and reclaims storage. |
| RT_API_ATTRS int EndIoStatement(); |
| |
| RT_API_ATTRS bool Emit( |
| const char *, std::size_t bytes, std::size_t elementBytes = 0); |
| RT_API_ATTRS bool Receive(char *, std::size_t, std::size_t elementBytes = 0); |
| RT_API_ATTRS std::size_t GetNextInputBytes(const char *&); |
| RT_API_ATTRS bool AdvanceRecord(int = 1); |
| RT_API_ATTRS void BackspaceRecord(); |
| RT_API_ATTRS void HandleRelativePosition(std::int64_t byteOffset); |
| RT_API_ATTRS void HandleAbsolutePosition( |
| std::int64_t byteOffset); // for r* in list I/O |
| RT_API_ATTRS Fortran::common::optional<DataEdit> GetNextDataEdit( |
| int maxRepeat = 1); |
| RT_API_ATTRS ExternalFileUnit * |
| GetExternalFileUnit() const; // null if internal unit |
| RT_API_ATTRS bool BeginReadingRecord(); |
| RT_API_ATTRS void FinishReadingRecord(); |
| RT_API_ATTRS bool Inquire(InquiryKeywordHash, char *, std::size_t); |
| RT_API_ATTRS bool Inquire(InquiryKeywordHash, bool &); |
| RT_API_ATTRS bool Inquire( |
| InquiryKeywordHash, std::int64_t, bool &); // PENDING= |
| RT_API_ATTRS bool Inquire(InquiryKeywordHash, std::int64_t &); |
| RT_API_ATTRS std::int64_t InquirePos(); |
| RT_API_ATTRS void GotChar(signed int = 1); // for READ(SIZE=); can be <0 |
| |
| RT_API_ATTRS MutableModes &mutableModes(); |
| RT_API_ATTRS ConnectionState &GetConnectionState(); |
| RT_API_ATTRS IoErrorHandler &GetIoErrorHandler() const; |
| |
| // N.B.: this also works with base classes |
| template <typename A> RT_API_ATTRS A *get_if() const { |
| return common::visit( |
| [](auto &x) -> A * { |
| if constexpr (std::is_convertible_v<decltype(x.get()), A &>) { |
| return &x.get(); |
| } |
| return nullptr; |
| }, |
| u_); |
| } |
| |
| // Vacant after the end of the current record |
| RT_API_ATTRS Fortran::common::optional<char32_t> GetCurrentChar( |
| std::size_t &byteCount); |
| |
| // The "remaining" arguments to CueUpInput(), SkipSpaces(), & NextInField() |
| // are always in units of bytes, not characters; the distinction matters |
| // for internal input from CHARACTER(KIND=2 and 4). |
| |
| // For fixed-width fields, return the number of remaining bytes. |
| // Skip over leading blanks. |
| RT_API_ATTRS Fortran::common::optional<int> CueUpInput(const DataEdit &edit) { |
| Fortran::common::optional<int> remaining; |
| if (edit.IsListDirected()) { |
| std::size_t byteCount{0}; |
| GetNextNonBlank(byteCount); |
| } else { |
| if (edit.width.value_or(0) > 0) { |
| remaining = *edit.width; |
| if (int bytesPerChar{GetConnectionState().internalIoCharKind}; |
| bytesPerChar > 1) { |
| *remaining *= bytesPerChar; |
| } |
| } |
| SkipSpaces(remaining); |
| } |
| return remaining; |
| } |
| |
| RT_API_ATTRS Fortran::common::optional<char32_t> SkipSpaces( |
| Fortran::common::optional<int> &remaining) { |
| while (!remaining || *remaining > 0) { |
| std::size_t byteCount{0}; |
| if (auto ch{GetCurrentChar(byteCount)}) { |
| if (*ch != ' ' && *ch != '\t') { |
| return ch; |
| } |
| if (remaining) { |
| if (static_cast<std::size_t>(*remaining) < byteCount) { |
| break; |
| } |
| GotChar(byteCount); |
| *remaining -= byteCount; |
| } |
| HandleRelativePosition(byteCount); |
| } else { |
| break; |
| } |
| } |
| return Fortran::common::nullopt; |
| } |
| |
| // Acquires the next input character, respecting any applicable field width |
| // or separator character. |
| RT_API_ATTRS Fortran::common::optional<char32_t> NextInField( |
| Fortran::common::optional<int> &remaining, const DataEdit &); |
| |
| // Detect and signal any end-of-record condition after input. |
| // Returns true if at EOR and remaining input should be padded with blanks. |
| RT_API_ATTRS bool CheckForEndOfRecord(std::size_t afterReading); |
| |
| // Skips spaces, advances records, and ignores NAMELIST comments |
| RT_API_ATTRS Fortran::common::optional<char32_t> GetNextNonBlank( |
| std::size_t &byteCount) { |
| auto ch{GetCurrentChar(byteCount)}; |
| bool inNamelist{mutableModes().inNamelist}; |
| while (!ch || *ch == ' ' || *ch == '\t' || (inNamelist && *ch == '!')) { |
| if (ch && (*ch == ' ' || *ch == '\t')) { |
| HandleRelativePosition(byteCount); |
| } else if (!AdvanceRecord()) { |
| return Fortran::common::nullopt; |
| } |
| ch = GetCurrentChar(byteCount); |
| } |
| return ch; |
| } |
| |
| template <Direction D> |
| RT_API_ATTRS bool CheckFormattedStmtType(const char *name) { |
| if (get_if<FormattedIoStatementState<D>>()) { |
| return true; |
| } else { |
| auto &handler{GetIoErrorHandler()}; |
| if (!handler.InError()) { |
| handler.Crash("%s called for I/O statement that is not formatted %s", |
| name, D == Direction::Output ? "output" : "input"); |
| } |
| return false; |
| } |
| } |
| |
| private: |
| std::variant<Fortran::common::reference_wrapper<OpenStatementState>, |
| Fortran::common::reference_wrapper<CloseStatementState>, |
| Fortran::common::reference_wrapper<NoopStatementState>, |
| Fortran::common::reference_wrapper< |
| InternalFormattedIoStatementState<Direction::Output>>, |
| Fortran::common::reference_wrapper< |
| InternalFormattedIoStatementState<Direction::Input>>, |
| Fortran::common::reference_wrapper< |
| InternalListIoStatementState<Direction::Output>>, |
| Fortran::common::reference_wrapper< |
| InternalListIoStatementState<Direction::Input>>, |
| Fortran::common::reference_wrapper< |
| ExternalFormattedIoStatementState<Direction::Output>>, |
| Fortran::common::reference_wrapper< |
| ExternalFormattedIoStatementState<Direction::Input>>, |
| Fortran::common::reference_wrapper< |
| ExternalListIoStatementState<Direction::Output>>, |
| Fortran::common::reference_wrapper< |
| ExternalListIoStatementState<Direction::Input>>, |
| Fortran::common::reference_wrapper< |
| ExternalUnformattedIoStatementState<Direction::Output>>, |
| Fortran::common::reference_wrapper< |
| ExternalUnformattedIoStatementState<Direction::Input>>, |
| Fortran::common::reference_wrapper< |
| ChildFormattedIoStatementState<Direction::Output>>, |
| Fortran::common::reference_wrapper< |
| ChildFormattedIoStatementState<Direction::Input>>, |
| Fortran::common::reference_wrapper< |
| ChildListIoStatementState<Direction::Output>>, |
| Fortran::common::reference_wrapper< |
| ChildListIoStatementState<Direction::Input>>, |
| Fortran::common::reference_wrapper< |
| ChildUnformattedIoStatementState<Direction::Output>>, |
| Fortran::common::reference_wrapper< |
| ChildUnformattedIoStatementState<Direction::Input>>, |
| Fortran::common::reference_wrapper<InquireUnitState>, |
| Fortran::common::reference_wrapper<InquireNoUnitState>, |
| Fortran::common::reference_wrapper<InquireUnconnectedFileState>, |
| Fortran::common::reference_wrapper<InquireIOLengthState>, |
| Fortran::common::reference_wrapper<ExternalMiscIoStatementState>, |
| Fortran::common::reference_wrapper<ErroneousIoStatementState>> |
| u_; |
| }; |
| |
| // Base class for all per-I/O statement state classes. |
| class IoStatementBase : public IoErrorHandler { |
| public: |
| using IoErrorHandler::IoErrorHandler; |
| |
| RT_API_ATTRS bool completedOperation() const { return completedOperation_; } |
| |
| RT_API_ATTRS void CompleteOperation() { completedOperation_ = true; } |
| RT_API_ATTRS int EndIoStatement() { return GetIoStat(); } |
| |
| // These are default no-op backstops that can be overridden by descendants. |
| RT_API_ATTRS bool Emit( |
| const char *, std::size_t bytes, std::size_t elementBytes = 0); |
| RT_API_ATTRS bool Receive( |
| char *, std::size_t bytes, std::size_t elementBytes = 0); |
| RT_API_ATTRS std::size_t GetNextInputBytes(const char *&); |
| RT_API_ATTRS bool AdvanceRecord(int); |
| RT_API_ATTRS void BackspaceRecord(); |
| RT_API_ATTRS void HandleRelativePosition(std::int64_t); |
| RT_API_ATTRS void HandleAbsolutePosition(std::int64_t); |
| RT_API_ATTRS Fortran::common::optional<DataEdit> GetNextDataEdit( |
| IoStatementState &, int maxRepeat = 1); |
| RT_API_ATTRS ExternalFileUnit *GetExternalFileUnit() const; |
| RT_API_ATTRS bool BeginReadingRecord(); |
| RT_API_ATTRS void FinishReadingRecord(); |
| RT_API_ATTRS bool Inquire(InquiryKeywordHash, char *, std::size_t); |
| RT_API_ATTRS bool Inquire(InquiryKeywordHash, bool &); |
| RT_API_ATTRS bool Inquire(InquiryKeywordHash, std::int64_t, bool &); |
| RT_API_ATTRS bool Inquire(InquiryKeywordHash, std::int64_t &); |
| RT_API_ATTRS std::int64_t InquirePos(); |
| |
| RT_API_ATTRS void BadInquiryKeywordHashCrash(InquiryKeywordHash); |
| |
| RT_API_ATTRS void ReportUnsupportedChildIo() const { |
| Crash("not yet implemented: child IO"); |
| } |
| |
| protected: |
| bool completedOperation_{false}; |
| }; |
| |
| // Common state for list-directed & NAMELIST I/O, both internal & external |
| template <Direction> class ListDirectedStatementState; |
| template <> |
| class ListDirectedStatementState<Direction::Output> |
| : public FormattedIoStatementState<Direction::Output> { |
| public: |
| RT_API_ATTRS bool EmitLeadingSpaceOrAdvance( |
| IoStatementState &, std::size_t = 1, bool isCharacter = false); |
| RT_API_ATTRS Fortran::common::optional<DataEdit> GetNextDataEdit( |
| IoStatementState &, int maxRepeat = 1); |
| RT_API_ATTRS bool lastWasUndelimitedCharacter() const { |
| return lastWasUndelimitedCharacter_; |
| } |
| RT_API_ATTRS void set_lastWasUndelimitedCharacter(bool yes = true) { |
| lastWasUndelimitedCharacter_ = yes; |
| } |
| |
| private: |
| bool lastWasUndelimitedCharacter_{false}; |
| }; |
| template <> |
| class ListDirectedStatementState<Direction::Input> |
| : public FormattedIoStatementState<Direction::Input> { |
| public: |
| RT_API_ATTRS bool inNamelistSequence() const { return inNamelistSequence_; } |
| RT_API_ATTRS int EndIoStatement(); |
| |
| // Skips value separators, handles repetition and null values. |
| // Vacant when '/' appears; present with descriptor == ListDirectedNullValue |
| // when a null value appears. |
| RT_API_ATTRS Fortran::common::optional<DataEdit> GetNextDataEdit( |
| IoStatementState &, int maxRepeat = 1); |
| |
| // Each NAMELIST input item is treated like a distinct list-directed |
| // input statement. This member function resets some state so that |
| // repetition and null values work correctly for each successive |
| // NAMELIST input item. |
| RT_API_ATTRS void ResetForNextNamelistItem(bool inNamelistSequence) { |
| remaining_ = 0; |
| if (repeatPosition_) { |
| repeatPosition_->Cancel(); |
| } |
| eatComma_ = false; |
| realPart_ = imaginaryPart_ = false; |
| inNamelistSequence_ = inNamelistSequence; |
| } |
| |
| private: |
| int remaining_{0}; // for "r*" repetition |
| Fortran::common::optional<SavedPosition> repeatPosition_; |
| bool eatComma_{false}; // consume comma after previously read item |
| bool hitSlash_{false}; // once '/' is seen, nullify further items |
| bool realPart_{false}; |
| bool imaginaryPart_{false}; |
| bool inNamelistSequence_{false}; |
| }; |
| |
| template <Direction DIR> |
| class InternalIoStatementState : public IoStatementBase, |
| public IoDirectionState<DIR> { |
| public: |
| using Buffer = |
| std::conditional_t<DIR == Direction::Input, const char *, char *>; |
| RT_API_ATTRS InternalIoStatementState(Buffer, std::size_t, |
| const char *sourceFile = nullptr, int sourceLine = 0); |
| RT_API_ATTRS InternalIoStatementState( |
| const Descriptor &, const char *sourceFile = nullptr, int sourceLine = 0); |
| RT_API_ATTRS int EndIoStatement(); |
| |
| RT_API_ATTRS bool Emit( |
| const char *data, std::size_t bytes, std::size_t elementBytes = 0); |
| RT_API_ATTRS std::size_t GetNextInputBytes(const char *&); |
| RT_API_ATTRS bool AdvanceRecord(int = 1); |
| RT_API_ATTRS void BackspaceRecord(); |
| RT_API_ATTRS ConnectionState &GetConnectionState() { return unit_; } |
| RT_API_ATTRS MutableModes &mutableModes() { return unit_.modes; } |
| RT_API_ATTRS void HandleRelativePosition(std::int64_t); |
| RT_API_ATTRS void HandleAbsolutePosition(std::int64_t); |
| RT_API_ATTRS std::int64_t InquirePos(); |
| |
| protected: |
| bool free_{true}; |
| InternalDescriptorUnit<DIR> unit_; |
| }; |
| |
| template <Direction DIR, typename CHAR> |
| class InternalFormattedIoStatementState |
| : public InternalIoStatementState<DIR>, |
| public FormattedIoStatementState<DIR> { |
| public: |
| using CharType = CHAR; |
| using typename InternalIoStatementState<DIR>::Buffer; |
| RT_API_ATTRS InternalFormattedIoStatementState(Buffer internal, |
| std::size_t internalLength, const CharType *format, |
| std::size_t formatLength, const Descriptor *formatDescriptor = nullptr, |
| const char *sourceFile = nullptr, int sourceLine = 0); |
| RT_API_ATTRS InternalFormattedIoStatementState(const Descriptor &, |
| const CharType *format, std::size_t formatLength, |
| const Descriptor *formatDescriptor = nullptr, |
| const char *sourceFile = nullptr, int sourceLine = 0); |
| RT_API_ATTRS IoStatementState &ioStatementState() { |
| return ioStatementState_; |
| } |
| RT_API_ATTRS void CompleteOperation(); |
| RT_API_ATTRS int EndIoStatement(); |
| RT_API_ATTRS Fortran::common::optional<DataEdit> GetNextDataEdit( |
| IoStatementState &, int maxRepeat = 1) { |
| return format_.GetNextDataEdit(*this, maxRepeat); |
| } |
| |
| private: |
| IoStatementState ioStatementState_; // points to *this |
| using InternalIoStatementState<DIR>::unit_; |
| // format_ *must* be last; it may be partial someday |
| FormatControl<InternalFormattedIoStatementState> format_; |
| }; |
| |
| template <Direction DIR> |
| class InternalListIoStatementState : public InternalIoStatementState<DIR>, |
| public ListDirectedStatementState<DIR> { |
| public: |
| using typename InternalIoStatementState<DIR>::Buffer; |
| RT_API_ATTRS InternalListIoStatementState(Buffer internal, |
| std::size_t internalLength, const char *sourceFile = nullptr, |
| int sourceLine = 0); |
| RT_API_ATTRS InternalListIoStatementState( |
| const Descriptor &, const char *sourceFile = nullptr, int sourceLine = 0); |
| RT_API_ATTRS IoStatementState &ioStatementState() { |
| return ioStatementState_; |
| } |
| using ListDirectedStatementState<DIR>::GetNextDataEdit; |
| RT_API_ATTRS void CompleteOperation(); |
| RT_API_ATTRS int EndIoStatement(); |
| |
| private: |
| IoStatementState ioStatementState_; // points to *this |
| using InternalIoStatementState<DIR>::unit_; |
| }; |
| |
| class ExternalIoStatementBase : public IoStatementBase { |
| public: |
| RT_API_ATTRS ExternalIoStatementBase( |
| ExternalFileUnit &, const char *sourceFile = nullptr, int sourceLine = 0); |
| RT_API_ATTRS ExternalFileUnit &unit() { return unit_; } |
| RT_API_ATTRS MutableModes &mutableModes(); |
| RT_API_ATTRS ConnectionState &GetConnectionState(); |
| RT_API_ATTRS int asynchronousID() const { return asynchronousID_; } |
| RT_API_ATTRS int EndIoStatement(); |
| RT_API_ATTRS ExternalFileUnit *GetExternalFileUnit() const { return &unit_; } |
| RT_API_ATTRS void SetAsynchronous(); |
| RT_API_ATTRS std::int64_t InquirePos(); |
| |
| private: |
| ExternalFileUnit &unit_; |
| int asynchronousID_{-1}; |
| }; |
| |
| template <Direction DIR> |
| class ExternalIoStatementState : public ExternalIoStatementBase, |
| public IoDirectionState<DIR> { |
| public: |
| RT_API_ATTRS ExternalIoStatementState( |
| ExternalFileUnit &, const char *sourceFile = nullptr, int sourceLine = 0); |
| RT_API_ATTRS MutableModes &mutableModes() { return mutableModes_; } |
| RT_API_ATTRS void CompleteOperation(); |
| RT_API_ATTRS int EndIoStatement(); |
| RT_API_ATTRS bool Emit( |
| const char *, std::size_t bytes, std::size_t elementBytes = 0); |
| RT_API_ATTRS std::size_t GetNextInputBytes(const char *&); |
| RT_API_ATTRS bool AdvanceRecord(int = 1); |
| RT_API_ATTRS void BackspaceRecord(); |
| RT_API_ATTRS void HandleRelativePosition(std::int64_t); |
| RT_API_ATTRS void HandleAbsolutePosition(std::int64_t); |
| RT_API_ATTRS bool BeginReadingRecord(); |
| RT_API_ATTRS void FinishReadingRecord(); |
| |
| 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_; |
| }; |
| |
| template <Direction DIR, typename CHAR> |
| class ExternalFormattedIoStatementState |
| : public ExternalIoStatementState<DIR>, |
| public FormattedIoStatementState<DIR> { |
| public: |
| using CharType = CHAR; |
| RT_API_ATTRS ExternalFormattedIoStatementState(ExternalFileUnit &, |
| const CharType *format, std::size_t formatLength, |
| const Descriptor *formatDescriptor = nullptr, |
| const char *sourceFile = nullptr, int sourceLine = 0); |
| RT_API_ATTRS void CompleteOperation(); |
| RT_API_ATTRS int EndIoStatement(); |
| RT_API_ATTRS Fortran::common::optional<DataEdit> GetNextDataEdit( |
| IoStatementState &, int maxRepeat = 1) { |
| return format_.GetNextDataEdit(*this, maxRepeat); |
| } |
| |
| private: |
| FormatControl<ExternalFormattedIoStatementState> format_; |
| }; |
| |
| template <Direction DIR> |
| class ExternalListIoStatementState : public ExternalIoStatementState<DIR>, |
| public ListDirectedStatementState<DIR> { |
| public: |
| using ExternalIoStatementState<DIR>::ExternalIoStatementState; |
| using ListDirectedStatementState<DIR>::GetNextDataEdit; |
| RT_API_ATTRS int EndIoStatement(); |
| }; |
| |
| template <Direction DIR> |
| class ExternalUnformattedIoStatementState |
| : public ExternalIoStatementState<DIR> { |
| public: |
| using ExternalIoStatementState<DIR>::ExternalIoStatementState; |
| RT_API_ATTRS bool Receive(char *, std::size_t, std::size_t elementBytes = 0); |
| }; |
| |
| template <Direction DIR> |
| class ChildIoStatementState : public IoStatementBase, |
| public IoDirectionState<DIR> { |
| public: |
| RT_API_ATTRS ChildIoStatementState( |
| ChildIo &, const char *sourceFile = nullptr, int sourceLine = 0); |
| RT_API_ATTRS ChildIo &child() { return child_; } |
| RT_API_ATTRS MutableModes &mutableModes(); |
| RT_API_ATTRS ConnectionState &GetConnectionState(); |
| RT_API_ATTRS ExternalFileUnit *GetExternalFileUnit() const; |
| RT_API_ATTRS int EndIoStatement(); |
| RT_API_ATTRS bool Emit( |
| const char *, std::size_t bytes, std::size_t elementBytes = 0); |
| RT_API_ATTRS std::size_t GetNextInputBytes(const char *&); |
| RT_API_ATTRS void HandleRelativePosition(std::int64_t); |
| RT_API_ATTRS void HandleAbsolutePosition(std::int64_t); |
| |
| private: |
| ChildIo &child_; |
| }; |
| |
| template <Direction DIR, typename CHAR> |
| class ChildFormattedIoStatementState : public ChildIoStatementState<DIR>, |
| public FormattedIoStatementState<DIR> { |
| public: |
| using CharType = CHAR; |
| RT_API_ATTRS ChildFormattedIoStatementState(ChildIo &, const CharType *format, |
| std::size_t formatLength, const Descriptor *formatDescriptor = nullptr, |
| const char *sourceFile = nullptr, int sourceLine = 0); |
| RT_API_ATTRS MutableModes &mutableModes() { return mutableModes_; } |
| RT_API_ATTRS void CompleteOperation(); |
| RT_API_ATTRS int EndIoStatement(); |
| RT_API_ATTRS bool AdvanceRecord(int = 1); |
| RT_API_ATTRS Fortran::common::optional<DataEdit> GetNextDataEdit( |
| IoStatementState &, int maxRepeat = 1) { |
| return format_.GetNextDataEdit(*this, maxRepeat); |
| } |
| |
| private: |
| MutableModes mutableModes_; |
| FormatControl<ChildFormattedIoStatementState> format_; |
| }; |
| |
| template <Direction DIR> |
| class ChildListIoStatementState : public ChildIoStatementState<DIR>, |
| public ListDirectedStatementState<DIR> { |
| public: |
| using ChildIoStatementState<DIR>::ChildIoStatementState; |
| using ListDirectedStatementState<DIR>::GetNextDataEdit; |
| RT_API_ATTRS int EndIoStatement(); |
| }; |
| |
| template <Direction DIR> |
| class ChildUnformattedIoStatementState : public ChildIoStatementState<DIR> { |
| public: |
| using ChildIoStatementState<DIR>::ChildIoStatementState; |
| RT_API_ATTRS bool Receive(char *, std::size_t, std::size_t elementBytes = 0); |
| }; |
| |
| // OPEN |
| class OpenStatementState : public ExternalIoStatementBase { |
| public: |
| RT_API_ATTRS OpenStatementState(ExternalFileUnit &unit, bool wasExtant, |
| bool isNewUnit, const char *sourceFile = nullptr, int sourceLine = 0) |
| : ExternalIoStatementBase{unit, sourceFile, sourceLine}, |
| wasExtant_{wasExtant}, isNewUnit_{isNewUnit} {} |
| RT_API_ATTRS bool wasExtant() const { return wasExtant_; } |
| RT_API_ATTRS void set_status(OpenStatus status) { |
| status_ = status; |
| } // STATUS= |
| RT_API_ATTRS void set_path(const char *, std::size_t); // FILE= |
| RT_API_ATTRS void set_position(Position position) { |
| position_ = position; |
| } // POSITION= |
| RT_API_ATTRS void set_action(Action action) { action_ = action; } // ACTION= |
| RT_API_ATTRS void set_convert(Convert convert) { |
| convert_ = convert; |
| } // CONVERT= |
| RT_API_ATTRS void set_access(Access access) { access_ = access; } // ACCESS= |
| RT_API_ATTRS void set_isUnformatted(bool yes = true) { |
| isUnformatted_ = yes; |
| } // FORM= |
| |
| RT_API_ATTRS void CompleteOperation(); |
| RT_API_ATTRS int EndIoStatement(); |
| |
| private: |
| bool wasExtant_; |
| bool isNewUnit_; |
| Fortran::common::optional<OpenStatus> status_; |
| Fortran::common::optional<Position> position_; |
| Fortran::common::optional<Action> action_; |
| Convert convert_{Convert::Unknown}; |
| OwningPtr<char> path_; |
| std::size_t pathLength_; |
| Fortran::common::optional<bool> isUnformatted_; |
| Fortran::common::optional<Access> access_; |
| }; |
| |
| class CloseStatementState : public ExternalIoStatementBase { |
| public: |
| RT_API_ATTRS CloseStatementState(ExternalFileUnit &unit, |
| const char *sourceFile = nullptr, int sourceLine = 0) |
| : ExternalIoStatementBase{unit, sourceFile, sourceLine} {} |
| RT_API_ATTRS void set_status(CloseStatus status) { status_ = status; } |
| RT_API_ATTRS int EndIoStatement(); |
| |
| private: |
| CloseStatus status_{CloseStatus::Keep}; |
| }; |
| |
| // For CLOSE(bad unit), WAIT(bad unit, ID=nonzero), INQUIRE(unconnected unit), |
| // and recoverable BACKSPACE(bad unit) |
| class NoUnitIoStatementState : public IoStatementBase { |
| public: |
| RT_API_ATTRS IoStatementState &ioStatementState() { |
| return ioStatementState_; |
| } |
| RT_API_ATTRS MutableModes &mutableModes() { return connection_.modes; } |
| RT_API_ATTRS ConnectionState &GetConnectionState() { return connection_; } |
| RT_API_ATTRS int badUnitNumber() const { return badUnitNumber_; } |
| RT_API_ATTRS void CompleteOperation(); |
| RT_API_ATTRS int EndIoStatement(); |
| |
| protected: |
| template <typename A> |
| RT_API_ATTRS NoUnitIoStatementState(A &stmt, const char *sourceFile = nullptr, |
| int sourceLine = 0, int badUnitNumber = -1) |
| : IoStatementBase{sourceFile, sourceLine}, ioStatementState_{stmt}, |
| badUnitNumber_{badUnitNumber} {} |
| |
| private: |
| IoStatementState ioStatementState_; // points to *this |
| ConnectionState connection_; |
| int badUnitNumber_; |
| }; |
| |
| class NoopStatementState : public NoUnitIoStatementState { |
| public: |
| RT_API_ATTRS NoopStatementState( |
| const char *sourceFile = nullptr, int sourceLine = 0, int unitNumber = -1) |
| : NoUnitIoStatementState{*this, sourceFile, sourceLine, unitNumber} {} |
| RT_API_ATTRS void set_status(CloseStatus) {} // discards |
| }; |
| |
| extern template class InternalIoStatementState<Direction::Output>; |
| extern template class InternalIoStatementState<Direction::Input>; |
| extern template class InternalFormattedIoStatementState<Direction::Output>; |
| extern template class InternalFormattedIoStatementState<Direction::Input>; |
| extern template class InternalListIoStatementState<Direction::Output>; |
| extern template class InternalListIoStatementState<Direction::Input>; |
| extern template class ExternalIoStatementState<Direction::Output>; |
| extern template class ExternalIoStatementState<Direction::Input>; |
| extern template class ExternalFormattedIoStatementState<Direction::Output>; |
| extern template class ExternalFormattedIoStatementState<Direction::Input>; |
| extern template class ExternalListIoStatementState<Direction::Output>; |
| extern template class ExternalListIoStatementState<Direction::Input>; |
| extern template class ExternalUnformattedIoStatementState<Direction::Output>; |
| extern template class ExternalUnformattedIoStatementState<Direction::Input>; |
| extern template class ChildIoStatementState<Direction::Output>; |
| extern template class ChildIoStatementState<Direction::Input>; |
| extern template class ChildFormattedIoStatementState<Direction::Output>; |
| extern template class ChildFormattedIoStatementState<Direction::Input>; |
| extern template class ChildListIoStatementState<Direction::Output>; |
| extern template class ChildListIoStatementState<Direction::Input>; |
| extern template class ChildUnformattedIoStatementState<Direction::Output>; |
| extern template class ChildUnformattedIoStatementState<Direction::Input>; |
| |
| extern template class FormatControl< |
| InternalFormattedIoStatementState<Direction::Output>>; |
| extern template class FormatControl< |
| InternalFormattedIoStatementState<Direction::Input>>; |
| extern template class FormatControl< |
| ExternalFormattedIoStatementState<Direction::Output>>; |
| extern template class FormatControl< |
| ExternalFormattedIoStatementState<Direction::Input>>; |
| extern template class FormatControl< |
| ChildFormattedIoStatementState<Direction::Output>>; |
| extern template class FormatControl< |
| ChildFormattedIoStatementState<Direction::Input>>; |
| |
| class InquireUnitState : public ExternalIoStatementBase { |
| public: |
| RT_API_ATTRS InquireUnitState(ExternalFileUnit &unit, |
| const char *sourceFile = nullptr, int sourceLine = 0); |
| RT_API_ATTRS bool Inquire(InquiryKeywordHash, char *, std::size_t); |
| RT_API_ATTRS bool Inquire(InquiryKeywordHash, bool &); |
| RT_API_ATTRS bool Inquire(InquiryKeywordHash, std::int64_t, bool &); |
| RT_API_ATTRS bool Inquire(InquiryKeywordHash, std::int64_t &); |
| }; |
| |
| class InquireNoUnitState : public NoUnitIoStatementState { |
| public: |
| RT_API_ATTRS InquireNoUnitState(const char *sourceFile = nullptr, |
| int sourceLine = 0, int badUnitNumber = -1); |
| RT_API_ATTRS bool Inquire(InquiryKeywordHash, char *, std::size_t); |
| RT_API_ATTRS bool Inquire(InquiryKeywordHash, bool &); |
| RT_API_ATTRS bool Inquire(InquiryKeywordHash, std::int64_t, bool &); |
| RT_API_ATTRS bool Inquire(InquiryKeywordHash, std::int64_t &); |
| }; |
| |
| class InquireUnconnectedFileState : public NoUnitIoStatementState { |
| public: |
| RT_API_ATTRS InquireUnconnectedFileState(OwningPtr<char> &&path, |
| const char *sourceFile = nullptr, int sourceLine = 0); |
| RT_API_ATTRS bool Inquire(InquiryKeywordHash, char *, std::size_t); |
| RT_API_ATTRS bool Inquire(InquiryKeywordHash, bool &); |
| RT_API_ATTRS bool Inquire(InquiryKeywordHash, std::int64_t, bool &); |
| RT_API_ATTRS bool Inquire(InquiryKeywordHash, std::int64_t &); |
| |
| private: |
| OwningPtr<char> path_; // trimmed and NUL terminated |
| }; |
| |
| class InquireIOLengthState : public NoUnitIoStatementState, |
| public OutputStatementState { |
| public: |
| RT_API_ATTRS InquireIOLengthState( |
| const char *sourceFile = nullptr, int sourceLine = 0); |
| RT_API_ATTRS std::size_t bytes() const { return bytes_; } |
| RT_API_ATTRS bool Emit( |
| const char *, std::size_t bytes, std::size_t elementBytes = 0); |
| |
| private: |
| std::size_t bytes_{0}; |
| }; |
| |
| class ExternalMiscIoStatementState : public ExternalIoStatementBase { |
| public: |
| enum Which { Flush, Backspace, Endfile, Rewind, Wait }; |
| RT_API_ATTRS ExternalMiscIoStatementState(ExternalFileUnit &unit, Which which, |
| const char *sourceFile = nullptr, int sourceLine = 0) |
| : ExternalIoStatementBase{unit, sourceFile, sourceLine}, which_{which} {} |
| RT_API_ATTRS void CompleteOperation(); |
| RT_API_ATTRS int EndIoStatement(); |
| |
| private: |
| Which which_; |
| }; |
| |
| class ErroneousIoStatementState : public IoStatementBase { |
| public: |
| explicit RT_API_ATTRS ErroneousIoStatementState(Iostat iostat, |
| ExternalFileUnit *unit = nullptr, const char *sourceFile = nullptr, |
| int sourceLine = 0) |
| : IoStatementBase{sourceFile, sourceLine}, unit_{unit} { |
| SetPendingError(iostat); |
| } |
| RT_API_ATTRS int EndIoStatement(); |
| RT_API_ATTRS ConnectionState &GetConnectionState() { return connection_; } |
| RT_API_ATTRS MutableModes &mutableModes() { return connection_.modes; } |
| |
| private: |
| ConnectionState connection_; |
| ExternalFileUnit *unit_{nullptr}; |
| }; |
| |
| } // namespace Fortran::runtime::io |
| #endif // FORTRAN_RUNTIME_IO_STMT_H_ |