bool IONAME(GetNewUnit)(Cookie, int &, int kind = 4);
// READ(SIZE=), after all input items
-bool IONAME(GetSize)(Cookie, std::int64_t, int kind = 8);
+std::size_t IONAME(GetSize)(Cookie);
// INQUIRE(IOLENGTH=), after all output items
-bool IONAME(GetIoLength)(Cookie, std::int64_t, int kind = 8);
+std::size_t IONAME(GetIoLength)(Cookie);
// GetIoMsg() does not modify its argument unless an error or
// end-of-record/file condition is present.
// Regular derived type unformatted I/O, not user-defined
auto *externalUnf{io.get_if<ExternalUnformattedIoStatementState<DIR>>()};
auto *childUnf{io.get_if<ChildUnformattedIoStatementState<DIR>>()};
- RUNTIME_CHECK(handler, externalUnf != nullptr || childUnf != nullptr);
+ auto *inq{
+ DIR == Direction::Output ? io.get_if<InquireIOLengthState>() : nullptr};
+ RUNTIME_CHECK(handler, externalUnf || childUnf || inq);
std::size_t elementBytes{descriptor.ElementBytes()};
std::size_t numElements{descriptor.Elements()};
SubscriptValue subscripts[maxRank];
std::size_t elementBytes) -> bool {
if constexpr (DIR == Direction::Output) {
return externalUnf ? externalUnf->Emit(&x, totalBytes, elementBytes)
- : childUnf->Emit(&x, totalBytes, elementBytes);
+ : childUnf ? childUnf->Emit(&x, totalBytes, elementBytes)
+ : inq->Emit(&x, totalBytes, elementBytes);
} else {
return externalUnf ? externalUnf->Receive(&x, totalBytes, elementBytes)
: childUnf->Receive(&x, totalBytes, elementBytes);
return false;
}
}
- if (!io.get_if<FormattedIoStatementState>()) {
+ if (!io.get_if<FormattedIoStatementState<DIR>>()) {
return UnformattedDescriptorIO<DIR>(io, descriptor);
}
IoErrorHandler &handler{io.GetIoErrorHandler()};
if (next) {
negative = *next == '-';
if (negative || *next == '+') {
+ io.GotChar();
io.SkipSpaces(remaining);
next = io.NextInField(remaining);
}
next = io.NextInField(remaining)) {
if (skip > 0) {
--skip;
+ io.GotChar(-1);
} else {
*x++ = *next;
--length;
if (auto *unf{io.get_if<
ExternalUnformattedIoStatementState<Direction::Output>>()}) {
return unf->Emit(x, length, elementBytes);
+ } else if (auto *inq{io.get_if<InquireIOLengthState>()}) {
+ return inq->Emit(x, length, elementBytes);
}
io.GetIoErrorHandler().Crash("OutputUnformattedBlock() called for an I/O "
"statement that is not unformatted output");
return descr::DescriptorIO<Direction::Input>(*cookie, descriptor);
}
+std::size_t IONAME(GetSize)(Cookie cookie) {
+ IoStatementState &io{*cookie};
+ if (const auto *formatted{
+ io.get_if<FormattedIoStatementState<Direction::Input>>()}) {
+ return formatted->GetEditDescriptorChars();
+ }
+ io.GetIoErrorHandler().Crash(
+ "GetIoSize() called for an I/O statement that is not a formatted READ()");
+ return 0;
+}
+
+std::size_t IONAME(GetIoLength)(Cookie cookie) {
+ IoStatementState &io{*cookie};
+ if (const auto *inq{io.get_if<InquireIOLengthState>()}) {
+ return inq->bytes();
+ }
+ io.GetIoErrorHandler().Crash("GetIoLength() called for an I/O statement that "
+ "is not INQUIRE(IOLENGTH=)");
+ return 0;
+}
+
void IONAME(GetIoMsg)(Cookie cookie, char *msg, std::size_t length) {
IoErrorHandler &handler{cookie->GetIoErrorHandler()};
if (handler.InError()) { // leave "msg" alone when no error
}
HandleRelativePosition(1);
if (remaining) {
+ GotChar();
--*remaining;
}
} else {
if (auto next{GetCurrentChar()}) {
--*remaining;
HandleRelativePosition(1);
+ GotChar();
return next;
}
const ConnectionState &connection{GetConnectionState()};
return std::visit([&](auto &x) { return x.get().Inquire(inquiry, n); }, u_);
}
+void IoStatementState::GotChar(int n) {
+ if (auto *formattedIn{
+ get_if<FormattedIoStatementState<Direction::Input>>()}) {
+ formattedIn->GotChar(n);
+ } else {
+ GetIoErrorHandler().Crash("IoStatementState::GotChar() called for "
+ "statement that is not formatted input");
+ }
+}
+
+std::size_t
+FormattedIoStatementState<Direction::Input>::GetEditDescriptorChars() const {
+ return chars_;
+}
+
+void FormattedIoStatementState<Direction::Input>::GotChar(int n) {
+ chars_ += n;
+}
+
bool ListDirectedStatementState<Direction::Output>::EmitLeadingSpaceOrAdvance(
IoStatementState &io, std::size_t length, bool isCharacter) {
if (length == 0) {
const char *sourceFile, int sourceLine)
: NoUnitIoStatementState{sourceFile, sourceLine, *this} {}
+bool InquireIOLengthState::Emit(
+ const char *, std::size_t n, std::size_t elementBytes) {
+ bytes_ += n * elementBytes;
+ return true;
+}
+
+bool InquireIOLengthState::Emit(const char *p, std::size_t n) {
+ bytes_ += sizeof *p * n;
+ return true;
+}
+
+bool InquireIOLengthState::Emit(const char16_t *p, std::size_t n) {
+ bytes_ += sizeof *p * n;
+ return true;
+}
+
+bool InquireIOLengthState::Emit(const char32_t *p, std::size_t n) {
+ bytes_ += sizeof *p * n;
+ return true;
+}
+
} // namespace Fortran::runtime::io
template <Direction D>
using IoDirectionState = std::conditional_t<D == Direction::Input,
InputStatementState, OutputStatementState>;
-struct FormattedIoStatementState {};
+
+// Common state for all kinds of formatted I/O
+template <Direction D> class FormattedIoStatementState {};
+template <> class FormattedIoStatementState<Direction::Input> {
+public:
+ std::size_t GetEditDescriptorChars() const;
+ 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 {
bool Inquire(InquiryKeywordHash, bool &);
bool Inquire(InquiryKeywordHash, std::int64_t, bool &); // PENDING=
bool Inquire(InquiryKeywordHash, std::int64_t &);
+ void GotChar(signed int = 1); // for READ(SIZE=); can be <0
MutableModes &mutableModes();
ConnectionState &GetConnectionState();
std::optional<char32_t> GetNextNonBlank();
template <Direction D> void CheckFormattedStmtType(const char *name) {
- if (!get_if<FormattedIoStatementState>() ||
- !get_if<IoDirectionState<D>>()) {
+ if (!get_if<FormattedIoStatementState<D>>()) {
GetIoErrorHandler().Crash(
"%s called for I/O statement that is not formatted %s", name,
D == Direction::Output ? "output" : "input");
template <Direction> class ListDirectedStatementState;
template <>
class ListDirectedStatementState<Direction::Output>
- : public FormattedIoStatementState {
+ : public FormattedIoStatementState<Direction::Output> {
public:
bool EmitLeadingSpaceOrAdvance(
IoStatementState &, std::size_t = 1, bool isCharacter = false);
};
template <>
class ListDirectedStatementState<Direction::Input>
- : public FormattedIoStatementState {
+ : public FormattedIoStatementState<Direction::Input> {
public:
// Skips value separators, handles repetition and null values.
// Vacant when '/' appears; present with descriptor == ListDirectedNullValue
template <Direction DIR, typename CHAR>
class InternalFormattedIoStatementState
: public InternalIoStatementState<DIR, CHAR>,
- public FormattedIoStatementState {
+ public FormattedIoStatementState<DIR> {
public:
using CharType = CHAR;
using typename InternalIoStatementState<DIR, CharType>::Buffer;
};
template <Direction DIR, typename CHAR>
-class ExternalFormattedIoStatementState : public ExternalIoStatementState<DIR>,
- public FormattedIoStatementState {
+class ExternalFormattedIoStatementState
+ : public ExternalIoStatementState<DIR>,
+ public FormattedIoStatementState<DIR> {
public:
using CharType = CHAR;
ExternalFormattedIoStatementState(ExternalFileUnit &, const CharType *format,
template <Direction DIR, typename CHAR>
class ChildFormattedIoStatementState : public ChildIoStatementState<DIR>,
- public FormattedIoStatementState {
+ public FormattedIoStatementState<DIR> {
public:
using CharType = CHAR;
ChildFormattedIoStatementState(ChildIo &, const CharType *format,
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);
+ bool Emit(const char *, std::size_t);
+ bool Emit(const char16_t *, std::size_t chars);
+ bool Emit(const char32_t *, std::size_t chars);
private:
std::size_t bytes_{0};
bool ChildIo::CheckFormattingAndDirection(Terminator &terminator,
const char *what, bool unformatted, Direction direction) {
- bool parentIsUnformatted{!parent_.get_if<FormattedIoStatementState>()};
bool parentIsInput{!parent_.get_if<IoDirectionState<Direction::Output>>()};
+ bool parentIsFormatted{parentIsInput
+ ? parent_.get_if<FormattedIoStatementState<Direction::Input>>() !=
+ nullptr
+ : parent_.get_if<FormattedIoStatementState<Direction::Output>>() !=
+ nullptr};
+ bool parentIsUnformatted{!parentIsFormatted};
if (unformatted != parentIsUnformatted) {
terminator.Crash("Child %s attempted on %s parent I/O unit", what,
parentIsUnformatted ? "unformatted" : "formatted");
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
<< "EndIoStatement() for OpenNewUnit";
+ // INQUIRE(IOLENGTH=) j
+ io = IONAME(BeginInquireIoLength)(__FILE__, __LINE__);
+ ASSERT_TRUE(IONAME(OutputUnformattedBlock)(
+ io, reinterpret_cast<const char *>(&buffer), 1, recl))
+ << "OutputUnformattedBlock() for InquireIoLength";
+ ASSERT_EQ(IONAME(GetIoLength)(io), recl) << "GetIoLength";
+ ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
+ << "EndIoStatement() for InquireIoLength";
+
static constexpr int records{10};
for (int j{1}; j <= records; ++j) {
// WRITE(UNIT=unit,REC=j) j
buffer = j;
ASSERT_TRUE(IONAME(OutputUnformattedBlock)(
- io, reinterpret_cast<const char *>(&buffer), recl, recl))
+ io, reinterpret_cast<const char *>(&buffer), 1, recl))
<< "OutputUnformattedBlock()";
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__);
ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')';
ASSERT_TRUE(IONAME(InputUnformattedBlock)(
- io, reinterpret_cast<char *>(&buffer), recl, recl))
+ io, reinterpret_cast<char *>(&buffer), 1, recl))
<< "InputUnformattedBlock()";
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
<< "EndIoStatement() for OpenNewUnit";
+ // INQUIRE(IOLENGTH=) j, ...
+ io = IONAME(BeginInquireIoLength)(__FILE__, __LINE__);
+ for (int j{1}; j <= 3; ++j) {
+ ASSERT_TRUE(IONAME(OutputUnformattedBlock)(
+ io, reinterpret_cast<const char *>(&buffer), 1, recl))
+ << "OutputUnformattedBlock() for InquireIoLength";
+ }
+ ASSERT_EQ(IONAME(GetIoLength)(io), 3 * recl) << "GetIoLength";
+ ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
+ << "EndIoStatement() for InquireIoLength";
+
static const int records{10};
for (int j{1}; j <= records; ++j) {
// DO J=1,RECORDS; WRITE(UNIT=unit) j; END DO
<< "EndIoStatement() for Backspace (before read)";
std::snprintf(fmt, sizeof fmt, "(%dI4)", j);
- // READ(UNIT=unit,FMT=fmt) n; check
+ // READ(UNIT=unit,FMT=fmt,SIZE=chars) n; check
io = IONAME(BeginExternalFormattedInput)(
fmt, std::strlen(fmt), unit, __FILE__, __LINE__);
ASSERT_TRUE(IONAME(InputInteger)(io, check[k])) << "InputInteger()";
}
+ std::size_t chars{IONAME(GetSize)(io)};
+ ASSERT_EQ(chars, j * 4u)
+ << "GetSize()=" << chars << ", expected " << (j * 4u) << '\n';
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
<< "EndIoStatement() for InputInteger";
for (int k{0}; k < j; ++k) {