[flang] Fix actions at end of output record

It turns out that unformatted fixed-size output records
do need to be padded out if short, in order to avoid a
spurious EOF crash on a short record at the end of the file.
While here in AdvanceRecord(), move the unformatted
variable-length record header/footer writing code to here
from EndIoStatement().

Differential revision: https://reviews.llvm.org/D88685
This commit is contained in:
peter klausler 2020-10-01 10:59:09 -07:00
parent 78a9e62aa6
commit a94d943f1a
3 changed files with 25 additions and 35 deletions

View File

@ -698,32 +698,6 @@ bool UnformattedIoStatementState<DIR>::Emit(
return ExternalIoStatementState<DIR>::Emit(data, bytes, elementBytes);
}
template <Direction DIR>
int UnformattedIoStatementState<DIR>::EndIoStatement() {
ExternalFileUnit &unit{this->unit()};
if constexpr (DIR == Direction::Output) {
if (unit.access == Access::Sequential && !unit.isFixedRecordLength) {
// Append the length of a sequential unformatted variable-length record
// as its footer, then overwrite the reserved first four bytes of the
// record with its length as its header. 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;
// TODO: Convert record length to little-endian on big-endian host?
if (!(this->Emit(u.c, sizeof u) &&
(this->HandleAbsolutePosition(0), this->Emit(u.c, sizeof u)))) {
return false;
}
}
}
return ExternalIoStatementState<DIR>::EndIoStatement();
}
template class InternalIoStatementState<Direction::Output>;
template class InternalIoStatementState<Direction::Input>;
template class InternalFormattedIoStatementState<Direction::Output>;

View File

@ -322,7 +322,6 @@ public:
using ExternalIoStatementState<DIR>::ExternalIoStatementState;
bool Receive(char *, std::size_t, std::size_t elementBytes = 0);
bool Emit(const char *, std::size_t, std::size_t elementBytes = 0);
int EndIoStatement();
};
class OpenStatementState : public ExternalIoStatementBase {

View File

@ -406,15 +406,32 @@ bool ExternalFileUnit::AdvanceRecord(IoErrorHandler &handler) {
FinishReadingRecord(handler);
BeginReadingRecord(handler);
} else { // Direction::Output
if (!isUnformatted) {
if (isFixedRecordLength && recordLength) {
if (furthestPositionInRecord < *recordLength) {
WriteFrame(frameOffsetInFile_, *recordLength, handler);
std::memset(Frame() + recordOffsetInFrame_ + furthestPositionInRecord,
' ', *recordLength - furthestPositionInRecord);
}
if (isFixedRecordLength && recordLength) {
// Pad remainder of fixed length record
if (furthestPositionInRecord < *recordLength) {
WriteFrame(
frameOffsetInFile_, recordOffsetInFrame_ + *recordLength, handler);
std::memset(Frame() + recordOffsetInFrame_ + furthestPositionInRecord,
isUnformatted ? 0 : ' ', *recordLength - furthestPositionInRecord);
}
} else {
positionInRecord = furthestPositionInRecord;
if (isUnformatted) {
// Append the length of a sequential unformatted variable-length record
// as its footer, then overwrite the reserved first four bytes of the
// record with its length as its header. These four bytes were skipped
// over in BeginUnformattedIO<Output>().
// TODO: Break very large records up into subrecords with negative
// headers &/or footers
std::uint32_t length;
length = furthestPositionInRecord - sizeof length;
ok &= Emit(reinterpret_cast<const char *>(&length), sizeof length,
sizeof length, handler);
positionInRecord = 0;
ok &= Emit(reinterpret_cast<const char *>(&length), sizeof length,
sizeof length, handler);
} else {
positionInRecord = furthestPositionInRecord;
// Terminate formatted variable length record
ok &= Emit("\n", 1, 1, handler); // TODO: Windows CR+LF
}
}