diff options
Diffstat (limited to 'flang/runtime/io-stmt.cpp')
-rw-r--r-- | flang/runtime/io-stmt.cpp | 402 |
1 files changed, 282 insertions, 120 deletions
diff --git a/flang/runtime/io-stmt.cpp b/flang/runtime/io-stmt.cpp index e54a67a328e2..adc9bae6c150 100644 --- a/flang/runtime/io-stmt.cpp +++ b/flang/runtime/io-stmt.cpp @@ -7,118 +7,60 @@ //===----------------------------------------------------------------------===// #include "io-stmt.h" +#include "connection.h" +#include "format.h" #include "memory.h" +#include "tools.h" #include "unit.h" #include <algorithm> #include <cstring> +#include <limits> namespace Fortran::runtime::io { -IoStatementState::IoStatementState(const char *sourceFile, int sourceLine) - : IoErrorHandler{sourceFile, sourceLine} {} +int IoStatementBase::EndIoStatement() { return GetIoStat(); } -int IoStatementState::EndIoStatement() { return GetIoStat(); } - -// Defaults -void IoStatementState::GetNext(DataEdit &, int) { - Crash("GetNext() called for I/O statement that is not a formatted data " - "transfer statement"); -} -bool IoStatementState::Emit(const char *, std::size_t) { - Crash("Emit() called for I/O statement that is not an output statement"); - return false; -} -bool IoStatementState::Emit(const char16_t *, std::size_t) { - Crash("Emit() called for I/O statement that is not an output statement"); - return false; -} -bool IoStatementState::Emit(const char32_t *, std::size_t) { - Crash("Emit() called for I/O statement that is not an output statement"); - return false; -} -bool IoStatementState::HandleSlash(int) { - Crash("HandleSlash() called for I/O statement that is not a formatted data " - "transfer statement"); - return false; -} -bool IoStatementState::HandleRelativePosition(std::int64_t) { - Crash("HandleRelativePosition() called for I/O statement that is not a " - "formatted data transfer statement"); - return false; -} -bool IoStatementState::HandleAbsolutePosition(std::int64_t) { - Crash("HandleAbsolutePosition() called for I/O statement that is not a " - "formatted data transfer statement"); - return false; +DataEdit IoStatementBase::GetNextDataEdit(int) { + Crash("IoStatementBase::GetNextDataEdit() called for non-formatted I/O " + "statement"); } template<bool isInput, typename CHAR> -FixedRecordIoStatementState<isInput, CHAR>::FixedRecordIoStatementState( - Buffer buffer, std::size_t length, const char *sourceFile, int sourceLine) - : IoStatementState{sourceFile, sourceLine}, buffer_{buffer}, length_{length} { -} +InternalIoStatementState<isInput, CHAR>::InternalIoStatementState( + Buffer scalar, std::size_t length, const char *sourceFile, int sourceLine) + : IoStatementBase{sourceFile, sourceLine}, unit_{scalar, length} {} + +template<bool isInput, typename CHAR> +InternalIoStatementState<isInput, CHAR>::InternalIoStatementState( + const Descriptor &d, const char *sourceFile, int sourceLine) + : IoStatementBase{sourceFile, sourceLine}, unit_{d, *this} {} template<bool isInput, typename CHAR> -bool FixedRecordIoStatementState<isInput, CHAR>::Emit( - const CHAR *data, std::size_t chars) { +bool InternalIoStatementState<isInput, CHAR>::Emit( + const CharType *data, std::size_t chars) { if constexpr (isInput) { - IoStatementState::Emit(data, chars); // default Crash() + Crash("InternalIoStatementState<true>::Emit() called for input statement"); return false; - } else if (at_ + chars > length_) { - SignalEor(); - if (at_ < length_) { - std::memcpy(buffer_ + at_, data, (length_ - at_) * sizeof(CHAR)); - at_ = furthest_ = length_; - } - return false; - } else { - std::memcpy(buffer_ + at_, data, chars * sizeof(CHAR)); - at_ += chars; - furthest_ = std::max(furthest_, at_); - return true; } + return unit_.Emit(data, chars, *this); } template<bool isInput, typename CHAR> -bool FixedRecordIoStatementState<isInput, CHAR>::HandleAbsolutePosition( - std::int64_t n) { - if (n < 0) { - n = 0; - } - n += leftTabLimit_; - bool ok{true}; - if (static_cast<std::size_t>(n) > length_) { - SignalEor(); - n = length_; - ok = false; - } - if constexpr (!isInput) { - if (static_cast<std::size_t>(n) > furthest_) { - std::fill_n(buffer_ + furthest_, n - furthest_, static_cast<CHAR>(' ')); +bool InternalIoStatementState<isInput, CHAR>::AdvanceRecord(int n) { + while (n-- > 0) { + if (!unit_.AdvanceRecord(*this)) { + return false; } } - at_ = n; - furthest_ = std::max(furthest_, at_); - return ok; -} - -template<bool isInput, typename CHAR> -bool FixedRecordIoStatementState<isInput, CHAR>::HandleRelativePosition( - std::int64_t n) { - return HandleAbsolutePosition(n + at_ - leftTabLimit_); + return true; } template<bool isInput, typename CHAR> -int FixedRecordIoStatementState<isInput, CHAR>::EndIoStatement() { +int InternalIoStatementState<isInput, CHAR>::EndIoStatement() { if constexpr (!isInput) { - HandleAbsolutePosition(length_ - leftTabLimit_); // fill + unit_.EndIoStatement(); // fill } - return GetIoStat(); -} - -template<bool isInput, typename CHAR> -int InternalIoStatementState<isInput, CHAR>::EndIoStatement() { - auto result{FixedRecordIoStatementState<isInput, CHAR>::EndIoStatement()}; + auto result{IoStatementBase::EndIoStatement()}; if (free_) { FreeMemory(this); } @@ -126,75 +68,295 @@ int InternalIoStatementState<isInput, CHAR>::EndIoStatement() { } template<bool isInput, typename CHAR> -InternalIoStatementState<isInput, CHAR>::InternalIoStatementState( - Buffer buffer, std::size_t length, const char *sourceFile, int sourceLine) - : FixedRecordIoStatementState<isInput, CHAR>( - buffer, length, sourceFile, sourceLine) {} - -template<bool isInput, typename CHAR> InternalFormattedIoStatementState<isInput, CHAR>::InternalFormattedIoStatementState(Buffer buffer, std::size_t length, const CHAR *format, std::size_t formatLength, const char *sourceFile, int sourceLine) : InternalIoStatementState<isInput, CHAR>{buffer, length, sourceFile, sourceLine}, - format_{*this, format, formatLength} {} + ioStatementState_{*this}, format_{*this, format, formatLength} {} + +template<bool isInput, typename CHAR> +InternalFormattedIoStatementState<isInput, + CHAR>::InternalFormattedIoStatementState(const Descriptor &d, + const CHAR *format, std::size_t formatLength, const char *sourceFile, + int sourceLine) + : InternalIoStatementState<isInput, CHAR>{d, sourceFile, sourceLine}, + ioStatementState_{*this}, format_{*this, format, formatLength} {} template<bool isInput, typename CHAR> int InternalFormattedIoStatementState<isInput, CHAR>::EndIoStatement() { - format_.FinishOutput(*this); + if constexpr (!isInput) { + format_.FinishOutput(*this); + } return InternalIoStatementState<isInput, CHAR>::EndIoStatement(); } template<bool isInput, typename CHAR> -ExternalFormattedIoStatementState<isInput, - CHAR>::ExternalFormattedIoStatementState(ExternalFile &file, - const CHAR *format, std::size_t formatLength, const char *sourceFile, - int sourceLine) - : IoStatementState{sourceFile, sourceLine}, file_{file}, format_{*this, - format, - formatLength} {} +bool InternalFormattedIoStatementState<isInput, CHAR>::HandleAbsolutePosition( + std::int64_t n) { + return unit_.HandleAbsolutePosition(n, *this); +} template<bool isInput, typename CHAR> -bool ExternalFormattedIoStatementState<isInput, CHAR>::Emit( - const CHAR *data, std::size_t chars) { - // TODO: UTF-8 encoding of 2- and 4-byte characters - return file_.Emit(data, chars * sizeof(CHAR), *this); +bool InternalFormattedIoStatementState<isInput, CHAR>::HandleRelativePosition( + std::int64_t n) { + return unit_.HandleRelativePosition(n, *this); } template<bool isInput, typename CHAR> -bool ExternalFormattedIoStatementState<isInput, CHAR>::HandleSlash(int n) { +InternalListIoStatementState<isInput, CHAR>::InternalListIoStatementState( + Buffer buffer, std::size_t length, const char *sourceFile, int sourceLine) + : InternalIoStatementState<isInput, CharType>{buffer, length, sourceFile, + sourceLine}, + ioStatementState_{*this} {} + +template<bool isInput, typename CHAR> +InternalListIoStatementState<isInput, CHAR>::InternalListIoStatementState( + const Descriptor &d, const char *sourceFile, int sourceLine) + : InternalIoStatementState<isInput, CharType>{d, sourceFile, sourceLine}, + ioStatementState_{*this} {} + +ExternalIoStatementBase::ExternalIoStatementBase( + ExternalFileUnit &unit, const char *sourceFile, int sourceLine) + : IoStatementBase{sourceFile, sourceLine}, unit_{unit} {} + +MutableModes &ExternalIoStatementBase::mutableModes() { return unit_.modes; } + +ConnectionState &ExternalIoStatementBase::GetConnectionState() { return unit_; } + +int ExternalIoStatementBase::EndIoStatement() { + if (unit_.nonAdvancing) { + unit_.leftTabLimit = unit_.furthestPositionInRecord; + unit_.nonAdvancing = false; + } else { + unit_.leftTabLimit.reset(); + } + auto result{IoStatementBase::EndIoStatement()}; + unit_.EndIoStatement(); // annihilates *this in unit_.u_ + return result; +} + +void OpenStatementState::set_path( + const char *path, std::size_t length, int kind) { + if (kind != 1) { // TODO + Crash("OPEN: FILE= with unimplemented: CHARACTER(KIND=%d)", kind); + } + std::size_t bytes{length * kind}; // TODO: UTF-8 encoding of Unicode path + path_ = SaveDefaultCharacter(path, bytes, *this); + pathLength_ = length; +} + +int OpenStatementState::EndIoStatement() { + if (wasExtant_ && status_ != OpenStatus::Old) { + Crash("OPEN statement for connected unit must have STATUS='OLD'"); + } + unit().OpenUnit(status_, position_, std::move(path_), pathLength_, *this); + return IoStatementBase::EndIoStatement(); +} + +int CloseStatementState::EndIoStatement() { + unit().CloseUnit(status_, *this); + return IoStatementBase::EndIoStatement(); +} + +int NoopCloseStatementState::EndIoStatement() { + auto result{IoStatementBase::EndIoStatement()}; + FreeMemory(this); + return result; +} + +template<bool isInput> int ExternalIoStatementState<isInput>::EndIoStatement() { + if constexpr (!isInput) { + if (!unit().nonAdvancing) { + unit().AdvanceRecord(*this); + } + unit().FlushIfTerminal(*this); + } + return ExternalIoStatementBase::EndIoStatement(); +} + +template<bool isInput> +bool ExternalIoStatementState<isInput>::Emit( + const char *data, std::size_t chars) { + if (isInput) { + Crash("ExternalIoStatementState::Emit called for input statement"); + } + return unit().Emit(data, chars * sizeof(*data), *this); +} + +template<bool isInput> +bool ExternalIoStatementState<isInput>::Emit( + const char16_t *data, std::size_t chars) { + if (isInput) { + Crash("ExternalIoStatementState::Emit called for input statement"); + } + // TODO: UTF-8 encoding + return unit().Emit( + reinterpret_cast<const char *>(data), chars * sizeof(*data), *this); +} + +template<bool isInput> +bool ExternalIoStatementState<isInput>::Emit( + const char32_t *data, std::size_t chars) { + if (isInput) { + Crash("ExternalIoStatementState::Emit called for input statement"); + } + // TODO: UTF-8 encoding + return unit().Emit( + reinterpret_cast<const char *>(data), chars * sizeof(*data), *this); +} + +template<bool isInput> +bool ExternalIoStatementState<isInput>::AdvanceRecord(int n) { while (n-- > 0) { - if (!file_.NextOutputRecord(*this)) { + if (!unit().AdvanceRecord(*this)) { return false; } } return true; } -template<bool isInput, typename CHAR> -bool ExternalFormattedIoStatementState<isInput, CHAR>::HandleAbsolutePosition( - std::int64_t n) { - return file_.HandleAbsolutePosition(n, *this); +template<bool isInput> +bool ExternalIoStatementState<isInput>::HandleAbsolutePosition(std::int64_t n) { + return unit().HandleAbsolutePosition(n, *this); } -template<bool isInput, typename CHAR> -bool ExternalFormattedIoStatementState<isInput, CHAR>::HandleRelativePosition( - std::int64_t n) { - return file_.HandleRelativePosition(n, *this); +template<bool isInput> +bool ExternalIoStatementState<isInput>::HandleRelativePosition(std::int64_t n) { + return unit().HandleRelativePosition(n, *this); } template<bool isInput, typename CHAR> +ExternalFormattedIoStatementState<isInput, + CHAR>::ExternalFormattedIoStatementState(ExternalFileUnit &unit, + const CHAR *format, std::size_t formatLength, const char *sourceFile, + int sourceLine) + : ExternalIoStatementState<isInput>{unit, sourceFile, sourceLine}, + mutableModes_{unit.modes}, format_{*this, format, formatLength} {} + +template<bool isInput, typename CHAR> int ExternalFormattedIoStatementState<isInput, CHAR>::EndIoStatement() { format_.FinishOutput(*this); - if constexpr (!isInput) { - file_.NextOutputRecord(*this); // TODO: non-advancing I/O + return ExternalIoStatementState<isInput>::EndIoStatement(); +} + +DataEdit IoStatementState::GetNextDataEdit(int n) { + return std::visit([&](auto &x) { return x.get().GetNextDataEdit(n); }, u_); +} + +bool IoStatementState::Emit(const char *data, std::size_t n) { + return std::visit([=](auto &x) { return x.get().Emit(data, n); }, u_); +} + +bool IoStatementState::AdvanceRecord(int n) { + return std::visit([=](auto &x) { return x.get().AdvanceRecord(n); }, u_); +} + +int IoStatementState::EndIoStatement() { + return std::visit([](auto &x) { return x.get().EndIoStatement(); }, u_); +} + +ConnectionState &IoStatementState::GetConnectionState() { + return std::visit( + [](auto &x) -> ConnectionState & { return x.get().GetConnectionState(); }, + u_); +} + +MutableModes &IoStatementState::mutableModes() { + return std::visit( + [](auto &x) -> MutableModes & { return x.get().mutableModes(); }, u_); +} + +IoErrorHandler &IoStatementState::GetIoErrorHandler() const { + return std::visit( + [](auto &x) -> IoErrorHandler & { + return static_cast<IoErrorHandler &>(x.get()); + }, + u_); +} + +bool IoStatementState::EmitRepeated(char ch, std::size_t n) { + return std::visit( + [=](auto &x) { + for (std::size_t j{0}; j < n; ++j) { + if (!x.get().Emit(&ch, 1)) { + return false; + } + } + return true; + }, + u_); +} + +bool IoStatementState::EmitField( + const char *p, std::size_t length, std::size_t width) { + if (width <= 0) { + width = static_cast<int>(length); } - int result{GetIoStat()}; - file_.EndIoStatement(); // annihilates *this in file_.u_ - return result; + if (length > static_cast<std::size_t>(width)) { + return EmitRepeated('*', width); + } else { + return EmitRepeated(' ', static_cast<int>(width - length)) && + Emit(p, length); + } +} + +bool ListDirectedStatementState<false>::NeedAdvance( + const ConnectionState &connection, std::size_t width) const { + return connection.positionInRecord > 0 && + width > connection.RemainingSpaceInRecord(); +} + +bool ListDirectedStatementState<false>::EmitLeadingSpaceOrAdvance( + IoStatementState &io, std::size_t length, bool isCharacter) { + if (length == 0) { + return true; + } + const ConnectionState &connection{io.GetConnectionState()}; + int space{connection.positionInRecord == 0 || + !(isCharacter && lastWasUndelimitedCharacter)}; + lastWasUndelimitedCharacter = false; + if (NeedAdvance(connection, space + length)) { + return io.AdvanceRecord(); + } + if (space) { + return io.Emit(" ", 1); + } + return true; +} + +template<bool isInput> +int UnformattedIoStatementState<isInput>::EndIoStatement() { + auto &ext{static_cast<ExternalIoStatementState<isInput> &>(*this)}; + ExternalFileUnit &unit{ext.unit()}; + if (unit.access == Access::Sequential && !unit.recordLength.has_value()) { + // Overwrite the first four bytes of the record with its length, + // and also append the length. These four bytes were skipped over + // in BeginUnformattedOutput(). + // TODO: Break very large records up into subrecords with negative + // headers &/or footers + union { + std::uint32_t u; + char c[sizeof u]; + } u; + u.u = unit.furthestPositionInRecord - sizeof u.c; + // TODO: Convert record length to little-endian on big-endian host? + if (!(ext.Emit(u.c, sizeof u.c) && ext.HandleAbsolutePosition(0) && + ext.Emit(u.c, sizeof u.c) && ext.AdvanceRecord())) { + return false; + } + } + return ext.EndIoStatement(); } +template class InternalIoStatementState<false>; +template class InternalIoStatementState<true>; template class InternalFormattedIoStatementState<false>; +template class InternalFormattedIoStatementState<true>; +template class InternalListIoStatementState<false>; +template class ExternalIoStatementState<false>; template class ExternalFormattedIoStatementState<false>; +template class ExternalListIoStatementState<false>; +template class UnformattedIoStatementState<false>; } |