2021-09-02 16:14:01 +08:00
|
|
|
//===-- runtime/unit.cpp --------------------------------------------------===//
|
2020-01-24 08:59:27 +08:00
|
|
|
//
|
|
|
|
// 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
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "unit.h"
|
2020-02-14 06:41:56 +08:00
|
|
|
#include "io-error.h"
|
2020-01-24 08:59:27 +08:00
|
|
|
#include "lock.h"
|
2020-02-14 06:41:56 +08:00
|
|
|
#include "unit-map.h"
|
2020-07-15 02:28:03 +08:00
|
|
|
#include <cstdio>
|
2021-05-24 17:11:52 +08:00
|
|
|
#include <limits>
|
2020-07-22 08:37:35 +08:00
|
|
|
#include <utility>
|
2020-01-24 08:59:27 +08:00
|
|
|
|
|
|
|
namespace Fortran::runtime::io {
|
|
|
|
|
2020-02-14 06:41:56 +08:00
|
|
|
// The per-unit data structures are created on demand so that Fortran I/O
|
|
|
|
// should work without a Fortran main program.
|
|
|
|
static Lock unitMapLock;
|
|
|
|
static UnitMap *unitMap{nullptr};
|
2021-11-19 01:34:43 +08:00
|
|
|
static ExternalFileUnit *defaultInput{nullptr}; // unit 5
|
|
|
|
static ExternalFileUnit *defaultOutput{nullptr}; // unit 6
|
|
|
|
static ExternalFileUnit *errorOutput{nullptr}; // unit 0 extension
|
2020-02-05 08:55:45 +08:00
|
|
|
|
|
|
|
void FlushOutputOnCrash(const Terminator &terminator) {
|
2021-11-19 01:34:43 +08:00
|
|
|
if (!defaultOutput && !errorOutput) {
|
2020-02-14 06:41:56 +08:00
|
|
|
return;
|
|
|
|
}
|
2021-11-19 01:34:43 +08:00
|
|
|
IoErrorHandler handler{terminator};
|
|
|
|
handler.HasIoStat(); // prevent nested crash if flush has error
|
2020-02-14 06:41:56 +08:00
|
|
|
CriticalSection critical{unitMapLock};
|
2020-02-05 08:55:45 +08:00
|
|
|
if (defaultOutput) {
|
2021-06-29 02:41:04 +08:00
|
|
|
defaultOutput->FlushOutput(handler);
|
2020-02-05 08:55:45 +08:00
|
|
|
}
|
2021-11-19 01:34:43 +08:00
|
|
|
if (errorOutput) {
|
|
|
|
errorOutput->FlushOutput(handler);
|
|
|
|
}
|
2020-02-05 08:55:45 +08:00
|
|
|
}
|
2020-01-24 08:59:27 +08:00
|
|
|
|
2020-02-05 08:55:45 +08:00
|
|
|
ExternalFileUnit *ExternalFileUnit::LookUp(int unit) {
|
2020-02-14 06:41:56 +08:00
|
|
|
return GetUnitMap().LookUp(unit);
|
2020-01-24 08:59:27 +08:00
|
|
|
}
|
|
|
|
|
2022-06-11 09:08:14 +08:00
|
|
|
ExternalFileUnit *ExternalFileUnit::LookUpOrCreate(
|
2020-07-15 02:28:03 +08:00
|
|
|
int unit, const Terminator &terminator, bool &wasExtant) {
|
2020-02-14 06:41:56 +08:00
|
|
|
return GetUnitMap().LookUpOrCreate(unit, terminator, wasExtant);
|
2020-01-24 08:59:27 +08:00
|
|
|
}
|
|
|
|
|
2022-06-11 09:08:14 +08:00
|
|
|
ExternalFileUnit *ExternalFileUnit::LookUpOrCreateAnonymous(int unit,
|
2021-05-06 02:33:00 +08:00
|
|
|
Direction dir, std::optional<bool> isUnformatted,
|
|
|
|
const Terminator &terminator) {
|
2020-07-15 02:28:03 +08:00
|
|
|
bool exists{false};
|
2022-06-11 09:08:14 +08:00
|
|
|
ExternalFileUnit *result{
|
2020-07-15 02:28:03 +08:00
|
|
|
GetUnitMap().LookUpOrCreate(unit, terminator, exists)};
|
2022-06-11 09:08:14 +08:00
|
|
|
if (result && !exists) {
|
2020-07-15 02:28:03 +08:00
|
|
|
IoErrorHandler handler{terminator};
|
2022-06-11 09:08:14 +08:00
|
|
|
result->OpenAnonymousUnit(
|
2020-08-04 02:35:29 +08:00
|
|
|
dir == Direction::Input ? OpenStatus::Unknown : OpenStatus::Replace,
|
|
|
|
Action::ReadWrite, Position::Rewind, Convert::Native, handler);
|
2022-06-11 09:08:14 +08:00
|
|
|
result->isUnformatted = isUnformatted;
|
2020-07-15 02:28:03 +08:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2022-06-04 04:44:13 +08:00
|
|
|
ExternalFileUnit *ExternalFileUnit::LookUp(
|
|
|
|
const char *path, std::size_t pathLen) {
|
|
|
|
return GetUnitMap().LookUp(path, pathLen);
|
2020-08-04 02:35:29 +08:00
|
|
|
}
|
|
|
|
|
2020-07-15 02:28:03 +08:00
|
|
|
ExternalFileUnit &ExternalFileUnit::CreateNew(
|
|
|
|
int unit, const Terminator &terminator) {
|
|
|
|
bool wasExtant{false};
|
2022-06-11 09:08:14 +08:00
|
|
|
ExternalFileUnit *result{
|
2020-07-15 02:28:03 +08:00
|
|
|
GetUnitMap().LookUpOrCreate(unit, terminator, wasExtant)};
|
2022-06-11 09:08:14 +08:00
|
|
|
RUNTIME_CHECK(terminator, result && !wasExtant);
|
|
|
|
return *result;
|
2020-07-15 02:28:03 +08:00
|
|
|
}
|
|
|
|
|
2020-02-14 06:41:56 +08:00
|
|
|
ExternalFileUnit *ExternalFileUnit::LookUpForClose(int unit) {
|
|
|
|
return GetUnitMap().LookUpForClose(unit);
|
|
|
|
}
|
|
|
|
|
2021-06-26 01:40:08 +08:00
|
|
|
ExternalFileUnit &ExternalFileUnit::NewUnit(
|
|
|
|
const Terminator &terminator, bool forChildIo) {
|
|
|
|
ExternalFileUnit &unit{GetUnitMap().NewUnit(terminator)};
|
|
|
|
unit.createdForInternalChildIo_ = forChildIo;
|
|
|
|
return unit;
|
2020-02-05 08:55:45 +08:00
|
|
|
}
|
|
|
|
|
2021-04-13 08:05:05 +08:00
|
|
|
void ExternalFileUnit::OpenUnit(std::optional<OpenStatus> status,
|
|
|
|
std::optional<Action> action, Position position, OwningPtr<char> &&newPath,
|
|
|
|
std::size_t newPathLength, Convert convert, IoErrorHandler &handler) {
|
2020-07-22 08:37:35 +08:00
|
|
|
if (executionEnvironment.conversion != Convert::Unknown) {
|
|
|
|
convert = executionEnvironment.conversion;
|
|
|
|
}
|
|
|
|
swapEndianness_ = convert == Convert::Swap ||
|
|
|
|
(convert == Convert::LittleEndian && !isHostLittleEndian) ||
|
|
|
|
(convert == Convert::BigEndian && isHostLittleEndian);
|
2021-11-25 08:05:37 +08:00
|
|
|
if (IsConnected()) {
|
2021-04-13 08:05:05 +08:00
|
|
|
bool isSamePath{newPath.get() && path() && pathLength() == newPathLength &&
|
|
|
|
std::memcmp(path(), newPath.get(), newPathLength) == 0};
|
|
|
|
if (status && *status != OpenStatus::Old && isSamePath) {
|
|
|
|
handler.SignalError("OPEN statement for connected unit may not have "
|
|
|
|
"explicit STATUS= other than 'OLD'");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!newPath.get() || isSamePath) {
|
|
|
|
// OPEN of existing unit, STATUS='OLD' or unspecified, not new FILE=
|
2020-02-05 08:55:45 +08:00
|
|
|
newPath.reset();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Otherwise, OPEN on open unit with new FILE= implies CLOSE
|
2020-07-04 03:38:22 +08:00
|
|
|
DoImpliedEndfile(handler);
|
2021-06-29 02:41:04 +08:00
|
|
|
FlushOutput(handler);
|
2020-02-05 08:55:45 +08:00
|
|
|
Close(CloseStatus::Keep, handler);
|
|
|
|
}
|
2022-06-04 04:44:13 +08:00
|
|
|
if (newPath.get() && newPathLength > 0) {
|
|
|
|
if (const auto *already{
|
|
|
|
GetUnitMap().LookUp(newPath.get(), newPathLength)}) {
|
|
|
|
handler.SignalError(IostatOpenAlreadyConnected,
|
|
|
|
"OPEN(UNIT=%d,FILE='%.*s'): file is already connected to unit %d",
|
|
|
|
unitNumber_, static_cast<int>(newPathLength), newPath.get(),
|
|
|
|
already->unitNumber_);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2020-02-05 08:55:45 +08:00
|
|
|
set_path(std::move(newPath), newPathLength);
|
2021-04-13 08:05:05 +08:00
|
|
|
Open(status.value_or(OpenStatus::Unknown), action, position, handler);
|
2020-07-04 03:38:22 +08:00
|
|
|
auto totalBytes{knownSize()};
|
|
|
|
if (access == Access::Direct) {
|
2021-12-03 08:36:09 +08:00
|
|
|
if (!openRecl) {
|
2020-07-04 03:38:22 +08:00
|
|
|
handler.SignalError(IostatOpenBadRecl,
|
|
|
|
"OPEN(UNIT=%d,ACCESS='DIRECT'): record length is not known",
|
|
|
|
unitNumber());
|
2021-12-03 08:36:09 +08:00
|
|
|
} else if (*openRecl <= 0) {
|
2020-07-04 03:38:22 +08:00
|
|
|
handler.SignalError(IostatOpenBadRecl,
|
|
|
|
"OPEN(UNIT=%d,ACCESS='DIRECT',RECL=%jd): record length is invalid",
|
2021-12-03 08:36:09 +08:00
|
|
|
unitNumber(), static_cast<std::intmax_t>(*openRecl));
|
|
|
|
} else if (totalBytes && (*totalBytes % *openRecl != 0)) {
|
2022-06-02 06:32:08 +08:00
|
|
|
handler.SignalError(IostatOpenBadRecl,
|
2020-07-04 03:38:22 +08:00
|
|
|
"OPEN(UNIT=%d,ACCESS='DIRECT',RECL=%jd): record length is not an "
|
|
|
|
"even divisor of the file size %jd",
|
2021-12-03 08:36:09 +08:00
|
|
|
unitNumber(), static_cast<std::intmax_t>(*openRecl),
|
2020-07-04 03:38:22 +08:00
|
|
|
static_cast<std::intmax_t>(*totalBytes));
|
|
|
|
}
|
2021-12-03 08:36:09 +08:00
|
|
|
recordLength = openRecl;
|
2020-07-04 03:38:22 +08:00
|
|
|
}
|
2020-10-02 00:50:48 +08:00
|
|
|
endfileRecordNumber.reset();
|
|
|
|
currentRecordNumber = 1;
|
2021-12-03 08:36:09 +08:00
|
|
|
if (totalBytes && access == Access::Direct && openRecl.value_or(0) > 0) {
|
|
|
|
endfileRecordNumber = 1 + (*totalBytes / *openRecl);
|
2020-10-02 00:50:48 +08:00
|
|
|
}
|
2022-06-02 06:32:08 +08:00
|
|
|
if (position == Position::Append) {
|
|
|
|
if (totalBytes) {
|
|
|
|
frameOffsetInFile_ = *totalBytes;
|
|
|
|
}
|
|
|
|
if (access != Access::Stream) {
|
|
|
|
if (!endfileRecordNumber) {
|
|
|
|
// Fake it so that we can backspace relative from the end
|
|
|
|
endfileRecordNumber = std::numeric_limits<std::int64_t>::max() - 2;
|
|
|
|
}
|
|
|
|
currentRecordNumber = *endfileRecordNumber;
|
2020-07-04 03:38:22 +08:00
|
|
|
}
|
|
|
|
}
|
2020-02-05 08:55:45 +08:00
|
|
|
}
|
|
|
|
|
2021-04-13 08:05:05 +08:00
|
|
|
void ExternalFileUnit::OpenAnonymousUnit(std::optional<OpenStatus> status,
|
2020-08-04 02:35:29 +08:00
|
|
|
std::optional<Action> action, Position position, Convert convert,
|
|
|
|
IoErrorHandler &handler) {
|
|
|
|
// I/O to an unconnected unit reads/creates a local file, e.g. fort.7
|
|
|
|
std::size_t pathMaxLen{32};
|
|
|
|
auto path{SizedNew<char>{handler}(pathMaxLen)};
|
|
|
|
std::snprintf(path.get(), pathMaxLen, "fort.%d", unitNumber_);
|
|
|
|
OpenUnit(status, action, position, std::move(path), std::strlen(path.get()),
|
|
|
|
convert, handler);
|
|
|
|
}
|
|
|
|
|
2020-02-05 08:55:45 +08:00
|
|
|
void ExternalFileUnit::CloseUnit(CloseStatus status, IoErrorHandler &handler) {
|
2020-07-04 03:38:22 +08:00
|
|
|
DoImpliedEndfile(handler);
|
2021-06-29 02:41:04 +08:00
|
|
|
FlushOutput(handler);
|
2020-02-14 06:41:56 +08:00
|
|
|
Close(status, handler);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ExternalFileUnit::DestroyClosed() {
|
2020-03-29 12:00:16 +08:00
|
|
|
GetUnitMap().DestroyClosed(*this); // destroys *this
|
2020-01-24 08:59:27 +08:00
|
|
|
}
|
|
|
|
|
2022-02-17 06:55:19 +08:00
|
|
|
Iostat ExternalFileUnit::SetDirection(Direction direction) {
|
2020-07-04 03:38:22 +08:00
|
|
|
if (direction == Direction::Input) {
|
|
|
|
if (mayRead()) {
|
|
|
|
direction_ = Direction::Input;
|
2022-02-17 06:55:19 +08:00
|
|
|
return IostatOk;
|
2020-07-04 03:38:22 +08:00
|
|
|
} else {
|
2022-02-17 06:55:19 +08:00
|
|
|
return IostatReadFromWriteOnly;
|
2020-07-04 03:38:22 +08:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (mayWrite()) {
|
|
|
|
direction_ = Direction::Output;
|
2022-02-17 06:55:19 +08:00
|
|
|
return IostatOk;
|
2020-07-04 03:38:22 +08:00
|
|
|
} else {
|
2022-02-17 06:55:19 +08:00
|
|
|
return IostatWriteToReadOnly;
|
2020-07-04 03:38:22 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-14 06:41:56 +08:00
|
|
|
UnitMap &ExternalFileUnit::GetUnitMap() {
|
|
|
|
if (unitMap) {
|
|
|
|
return *unitMap;
|
|
|
|
}
|
|
|
|
CriticalSection critical{unitMapLock};
|
|
|
|
if (unitMap) {
|
|
|
|
return *unitMap;
|
|
|
|
}
|
|
|
|
Terminator terminator{__FILE__, __LINE__};
|
2020-07-04 03:38:22 +08:00
|
|
|
IoErrorHandler handler{terminator};
|
2021-05-06 02:26:12 +08:00
|
|
|
UnitMap *newUnitMap{New<UnitMap>{terminator}().release()};
|
2021-11-19 01:34:43 +08:00
|
|
|
|
2021-05-06 02:26:12 +08:00
|
|
|
bool wasExtant{false};
|
2022-06-11 09:08:14 +08:00
|
|
|
ExternalFileUnit &out{*newUnitMap->LookUpOrCreate(6, terminator, wasExtant)};
|
2021-05-06 02:26:12 +08:00
|
|
|
RUNTIME_CHECK(terminator, !wasExtant);
|
2020-01-24 08:59:27 +08:00
|
|
|
out.Predefine(1);
|
2022-02-17 06:55:19 +08:00
|
|
|
handler.SignalError(out.SetDirection(Direction::Output));
|
2021-11-16 08:04:02 +08:00
|
|
|
out.isUnformatted = false;
|
2020-02-05 08:55:45 +08:00
|
|
|
defaultOutput = &out;
|
2021-11-19 01:34:43 +08:00
|
|
|
|
2022-06-11 09:08:14 +08:00
|
|
|
ExternalFileUnit &in{*newUnitMap->LookUpOrCreate(5, terminator, wasExtant)};
|
2021-05-06 02:26:12 +08:00
|
|
|
RUNTIME_CHECK(terminator, !wasExtant);
|
2020-01-24 08:59:27 +08:00
|
|
|
in.Predefine(0);
|
2022-02-17 06:55:19 +08:00
|
|
|
handler.SignalError(in.SetDirection(Direction::Input));
|
2021-11-16 08:04:02 +08:00
|
|
|
in.isUnformatted = false;
|
2020-07-04 03:38:22 +08:00
|
|
|
defaultInput = ∈
|
2021-11-19 01:34:43 +08:00
|
|
|
|
2022-06-11 09:08:14 +08:00
|
|
|
ExternalFileUnit &error{
|
|
|
|
*newUnitMap->LookUpOrCreate(0, terminator, wasExtant)};
|
2021-11-19 01:34:43 +08:00
|
|
|
RUNTIME_CHECK(terminator, !wasExtant);
|
|
|
|
error.Predefine(2);
|
2022-02-17 06:55:19 +08:00
|
|
|
handler.SignalError(error.SetDirection(Direction::Output));
|
2021-11-19 01:34:43 +08:00
|
|
|
error.isUnformatted = false;
|
|
|
|
errorOutput = &error;
|
|
|
|
|
2021-05-06 02:26:12 +08:00
|
|
|
unitMap = newUnitMap;
|
2020-02-14 06:41:56 +08:00
|
|
|
return *unitMap;
|
2020-01-24 08:59:27 +08:00
|
|
|
}
|
|
|
|
|
2020-02-05 08:55:45 +08:00
|
|
|
void ExternalFileUnit::CloseAll(IoErrorHandler &handler) {
|
2020-02-14 06:41:56 +08:00
|
|
|
CriticalSection critical{unitMapLock};
|
|
|
|
if (unitMap) {
|
|
|
|
unitMap->CloseAll(handler);
|
|
|
|
FreeMemoryAndNullify(unitMap);
|
2020-01-24 08:59:27 +08:00
|
|
|
}
|
2020-02-14 06:41:56 +08:00
|
|
|
defaultOutput = nullptr;
|
2021-11-19 01:34:43 +08:00
|
|
|
defaultInput = nullptr;
|
|
|
|
errorOutput = nullptr;
|
2020-01-24 08:59:27 +08:00
|
|
|
}
|
|
|
|
|
2020-07-04 03:38:22 +08:00
|
|
|
void ExternalFileUnit::FlushAll(IoErrorHandler &handler) {
|
|
|
|
CriticalSection critical{unitMapLock};
|
|
|
|
if (unitMap) {
|
|
|
|
unitMap->FlushAll(handler);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-22 08:37:35 +08:00
|
|
|
static void SwapEndianness(
|
|
|
|
char *data, std::size_t bytes, std::size_t elementBytes) {
|
|
|
|
if (elementBytes > 1) {
|
|
|
|
auto half{elementBytes >> 1};
|
|
|
|
for (std::size_t j{0}; j + elementBytes <= bytes; j += elementBytes) {
|
|
|
|
for (std::size_t k{0}; k < half; ++k) {
|
|
|
|
std::swap(data[j + k], data[j + elementBytes - 1 - k]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ExternalFileUnit::Emit(const char *data, std::size_t bytes,
|
|
|
|
std::size_t elementBytes, IoErrorHandler &handler) {
|
2020-02-05 08:55:45 +08:00
|
|
|
auto furthestAfter{std::max(furthestPositionInRecord,
|
|
|
|
positionInRecord + static_cast<std::int64_t>(bytes))};
|
2021-12-03 08:36:09 +08:00
|
|
|
if (openRecl) {
|
|
|
|
// Check for fixed-length record overrun, but allow for
|
2022-01-21 05:37:58 +08:00
|
|
|
// sequential record termination.
|
|
|
|
int extra{0};
|
|
|
|
int header{0};
|
|
|
|
if (access == Access::Sequential) {
|
|
|
|
if (isUnformatted.value_or(false)) {
|
|
|
|
// record header + footer
|
|
|
|
header = static_cast<int>(sizeof(std::uint32_t));
|
|
|
|
extra = 2 * header;
|
|
|
|
} else {
|
2022-01-29 07:34:28 +08:00
|
|
|
#ifdef _WIN32
|
|
|
|
if (!isWindowsTextFile()) {
|
|
|
|
++extra; // carriage return (CR)
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
++extra; // newline (LF)
|
2022-01-21 05:37:58 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (furthestAfter > extra + *openRecl) {
|
2021-08-24 04:56:22 +08:00
|
|
|
handler.SignalError(IostatRecordWriteOverrun,
|
|
|
|
"Attempt to write %zd bytes to position %jd in a fixed-size record "
|
|
|
|
"of %jd bytes",
|
2022-01-21 05:37:58 +08:00
|
|
|
bytes, static_cast<std::intmax_t>(positionInRecord - header),
|
2021-12-03 08:36:09 +08:00
|
|
|
static_cast<std::intmax_t>(*openRecl));
|
2021-08-24 04:56:22 +08:00
|
|
|
return false;
|
|
|
|
}
|
2022-02-24 03:45:21 +08:00
|
|
|
}
|
|
|
|
if (recordLength) {
|
2021-12-03 08:36:09 +08:00
|
|
|
// It is possible for recordLength to have a value now for a
|
|
|
|
// variable-length output record if the previous operation
|
|
|
|
// was a BACKSPACE or non advancing input statement.
|
|
|
|
recordLength.reset();
|
|
|
|
beganReadingRecord_ = false;
|
2020-02-14 06:41:56 +08:00
|
|
|
}
|
2022-01-08 02:29:23 +08:00
|
|
|
if (IsAfterEndfile()) {
|
|
|
|
handler.SignalError(IostatWriteAfterEndfile);
|
|
|
|
return false;
|
|
|
|
}
|
2022-02-18 09:22:09 +08:00
|
|
|
CheckDirectAccess(handler);
|
2020-02-14 06:41:56 +08:00
|
|
|
WriteFrame(frameOffsetInFile_, recordOffsetInFrame_ + furthestAfter, handler);
|
2020-06-19 03:19:49 +08:00
|
|
|
if (positionInRecord > furthestPositionInRecord) {
|
2020-07-04 03:38:22 +08:00
|
|
|
std::memset(Frame() + recordOffsetInFrame_ + furthestPositionInRecord, ' ',
|
2020-06-19 03:19:49 +08:00
|
|
|
positionInRecord - furthestPositionInRecord);
|
|
|
|
}
|
2020-07-22 08:37:35 +08:00
|
|
|
char *to{Frame() + recordOffsetInFrame_ + positionInRecord};
|
|
|
|
std::memcpy(to, data, bytes);
|
|
|
|
if (swapEndianness_) {
|
|
|
|
SwapEndianness(to, bytes, elementBytes);
|
|
|
|
}
|
2020-01-24 08:59:27 +08:00
|
|
|
positionInRecord += bytes;
|
|
|
|
furthestPositionInRecord = furthestAfter;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-07-22 08:37:35 +08:00
|
|
|
bool ExternalFileUnit::Receive(char *data, std::size_t bytes,
|
|
|
|
std::size_t elementBytes, IoErrorHandler &handler) {
|
2020-07-04 03:38:22 +08:00
|
|
|
RUNTIME_CHECK(handler, direction_ == Direction::Input);
|
|
|
|
auto furthestAfter{std::max(furthestPositionInRecord,
|
|
|
|
positionInRecord + static_cast<std::int64_t>(bytes))};
|
|
|
|
if (furthestAfter > recordLength.value_or(furthestAfter)) {
|
|
|
|
handler.SignalError(IostatRecordReadOverrun,
|
|
|
|
"Attempt to read %zd bytes at position %jd in a record of %jd bytes",
|
|
|
|
bytes, static_cast<std::intmax_t>(positionInRecord),
|
|
|
|
static_cast<std::intmax_t>(*recordLength));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
auto need{recordOffsetInFrame_ + furthestAfter};
|
|
|
|
auto got{ReadFrame(frameOffsetInFile_, need, handler)};
|
|
|
|
if (got >= need) {
|
|
|
|
std::memcpy(data, Frame() + recordOffsetInFrame_ + positionInRecord, bytes);
|
2020-07-22 08:37:35 +08:00
|
|
|
if (swapEndianness_) {
|
|
|
|
SwapEndianness(data, bytes, elementBytes);
|
|
|
|
}
|
2020-07-04 03:38:22 +08:00
|
|
|
positionInRecord += bytes;
|
|
|
|
furthestPositionInRecord = furthestAfter;
|
|
|
|
return true;
|
|
|
|
} else {
|
2022-05-19 04:40:33 +08:00
|
|
|
HitEndOnRead(handler);
|
2020-07-04 03:38:22 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-03 04:01:38 +08:00
|
|
|
std::size_t ExternalFileUnit::GetNextInputBytes(
|
|
|
|
const char *&p, IoErrorHandler &handler) {
|
|
|
|
RUNTIME_CHECK(handler, direction_ == Direction::Input);
|
2022-01-29 07:34:28 +08:00
|
|
|
std::size_t length{1};
|
|
|
|
if (auto recl{EffectiveRecordLength()}) {
|
|
|
|
if (positionInRecord < *recl) {
|
|
|
|
length = *recl - positionInRecord;
|
|
|
|
} else {
|
|
|
|
p = nullptr;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
p = FrameNextInput(handler, length);
|
|
|
|
return p ? length : 0;
|
2021-11-03 04:01:38 +08:00
|
|
|
}
|
|
|
|
|
2020-07-04 03:38:22 +08:00
|
|
|
const char *ExternalFileUnit::FrameNextInput(
|
|
|
|
IoErrorHandler &handler, std::size_t bytes) {
|
2021-05-06 02:33:00 +08:00
|
|
|
RUNTIME_CHECK(handler, isUnformatted.has_value() && !*isUnformatted);
|
2020-07-04 03:38:22 +08:00
|
|
|
if (static_cast<std::int64_t>(positionInRecord + bytes) <=
|
|
|
|
recordLength.value_or(positionInRecord + bytes)) {
|
|
|
|
auto at{recordOffsetInFrame_ + positionInRecord};
|
|
|
|
auto need{static_cast<std::size_t>(at + bytes)};
|
|
|
|
auto got{ReadFrame(frameOffsetInFile_, need, handler)};
|
2022-01-29 07:34:28 +08:00
|
|
|
SetVariableFormattedRecordLength();
|
2020-07-04 03:38:22 +08:00
|
|
|
if (got >= need) {
|
|
|
|
return Frame() + at;
|
|
|
|
}
|
2022-05-19 04:40:33 +08:00
|
|
|
HitEndOnRead(handler);
|
2020-02-14 06:41:56 +08:00
|
|
|
}
|
2020-07-04 03:38:22 +08:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2022-01-29 07:34:28 +08:00
|
|
|
bool ExternalFileUnit::SetVariableFormattedRecordLength() {
|
|
|
|
if (recordLength || access == Access::Direct) {
|
2020-07-04 03:38:22 +08:00
|
|
|
return true;
|
2021-07-21 02:39:21 +08:00
|
|
|
} else if (FrameLength() > recordOffsetInFrame_) {
|
2020-07-04 03:38:22 +08:00
|
|
|
const char *record{Frame() + recordOffsetInFrame_};
|
2021-07-21 02:39:21 +08:00
|
|
|
std::size_t bytes{FrameLength() - recordOffsetInFrame_};
|
|
|
|
if (const char *nl{
|
|
|
|
reinterpret_cast<const char *>(std::memchr(record, '\n', bytes))}) {
|
2020-07-04 03:38:22 +08:00
|
|
|
recordLength = nl - record;
|
|
|
|
if (*recordLength > 0 && record[*recordLength - 1] == '\r') {
|
|
|
|
--*recordLength;
|
|
|
|
}
|
2021-08-17 04:15:01 +08:00
|
|
|
return true;
|
2020-07-04 03:38:22 +08:00
|
|
|
}
|
2020-02-14 06:41:56 +08:00
|
|
|
}
|
2021-08-17 04:15:01 +08:00
|
|
|
return false;
|
2020-02-14 06:41:56 +08:00
|
|
|
}
|
|
|
|
|
2021-04-14 07:07:58 +08:00
|
|
|
bool ExternalFileUnit::BeginReadingRecord(IoErrorHandler &handler) {
|
2020-07-04 03:38:22 +08:00
|
|
|
RUNTIME_CHECK(handler, direction_ == Direction::Input);
|
2021-04-14 07:07:58 +08:00
|
|
|
if (!beganReadingRecord_) {
|
|
|
|
beganReadingRecord_ = true;
|
2021-12-03 08:36:09 +08:00
|
|
|
if (access == Access::Direct) {
|
2022-02-18 09:22:09 +08:00
|
|
|
CheckDirectAccess(handler);
|
2021-12-03 08:36:09 +08:00
|
|
|
auto need{static_cast<std::size_t>(recordOffsetInFrame_ + *openRecl)};
|
|
|
|
auto got{ReadFrame(frameOffsetInFile_, need, handler)};
|
|
|
|
if (got >= need) {
|
|
|
|
recordLength = openRecl;
|
|
|
|
} else {
|
|
|
|
recordLength.reset();
|
2022-05-19 04:40:33 +08:00
|
|
|
HitEndOnRead(handler);
|
2021-12-03 08:36:09 +08:00
|
|
|
}
|
2022-01-29 07:34:28 +08:00
|
|
|
} else {
|
2021-12-03 08:36:09 +08:00
|
|
|
recordLength.reset();
|
2022-01-08 02:29:23 +08:00
|
|
|
if (IsAtEOF()) {
|
2020-07-04 03:38:22 +08:00
|
|
|
handler.SignalEnd();
|
2021-05-06 02:33:00 +08:00
|
|
|
} else {
|
|
|
|
RUNTIME_CHECK(handler, isUnformatted.has_value());
|
2022-01-29 07:34:28 +08:00
|
|
|
if (*isUnformatted) {
|
|
|
|
if (access == Access::Sequential) {
|
|
|
|
BeginSequentialVariableUnformattedInputRecord(handler);
|
|
|
|
}
|
|
|
|
} else { // formatted sequential or stream
|
|
|
|
BeginVariableFormattedInputRecord(handler);
|
2021-05-06 02:33:00 +08:00
|
|
|
}
|
2020-07-04 03:38:22 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-04-14 07:07:58 +08:00
|
|
|
RUNTIME_CHECK(handler,
|
2022-01-29 07:34:28 +08:00
|
|
|
recordLength.has_value() || !IsRecordFile() || handler.InError());
|
2021-04-14 07:07:58 +08:00
|
|
|
return !handler.InError();
|
2020-07-04 03:38:22 +08:00
|
|
|
}
|
|
|
|
|
2020-10-01 03:53:00 +08:00
|
|
|
void ExternalFileUnit::FinishReadingRecord(IoErrorHandler &handler) {
|
|
|
|
RUNTIME_CHECK(handler, direction_ == Direction::Input && beganReadingRecord_);
|
|
|
|
beganReadingRecord_ = false;
|
2022-06-09 06:23:56 +08:00
|
|
|
if (handler.GetIoStat() == IostatEnd ||
|
|
|
|
(IsRecordFile() && !recordLength.has_value())) {
|
2022-01-29 07:34:28 +08:00
|
|
|
// Avoid bogus crashes in END/ERR circumstances; but
|
|
|
|
// still increment the current record number so that
|
|
|
|
// an attempted read of an endfile record, followed by
|
|
|
|
// a BACKSPACE, will still be at EOF.
|
|
|
|
++currentRecordNumber;
|
|
|
|
} else if (IsRecordFile()) {
|
2021-11-04 06:33:29 +08:00
|
|
|
recordOffsetInFrame_ += *recordLength;
|
2022-01-29 07:34:28 +08:00
|
|
|
if (access != Access::Direct) {
|
2021-05-06 02:33:00 +08:00
|
|
|
RUNTIME_CHECK(handler, isUnformatted.has_value());
|
2021-11-04 06:33:29 +08:00
|
|
|
recordLength.reset();
|
2021-05-06 02:33:00 +08:00
|
|
|
if (isUnformatted.value_or(false)) {
|
|
|
|
// Retain footer in frame for more efficient BACKSPACE
|
2021-11-04 06:33:29 +08:00
|
|
|
frameOffsetInFile_ += recordOffsetInFrame_;
|
2021-05-06 02:33:00 +08:00
|
|
|
recordOffsetInFrame_ = sizeof(std::uint32_t);
|
|
|
|
} else { // formatted
|
2021-11-04 06:33:29 +08:00
|
|
|
if (FrameLength() > recordOffsetInFrame_ &&
|
|
|
|
Frame()[recordOffsetInFrame_] == '\r') {
|
2021-05-06 02:33:00 +08:00
|
|
|
++recordOffsetInFrame_;
|
|
|
|
}
|
2022-01-29 07:34:28 +08:00
|
|
|
if (FrameLength() > recordOffsetInFrame_ &&
|
2021-11-04 06:33:29 +08:00
|
|
|
Frame()[recordOffsetInFrame_] == '\n') {
|
2021-07-21 02:39:21 +08:00
|
|
|
++recordOffsetInFrame_;
|
|
|
|
}
|
2021-11-04 06:33:29 +08:00
|
|
|
if (!pinnedFrame || mayPosition()) {
|
|
|
|
frameOffsetInFile_ += recordOffsetInFrame_;
|
2021-08-17 04:15:01 +08:00
|
|
|
recordOffsetInFrame_ = 0;
|
|
|
|
}
|
2020-10-01 03:53:00 +08:00
|
|
|
}
|
|
|
|
}
|
2022-01-29 07:34:28 +08:00
|
|
|
++currentRecordNumber;
|
|
|
|
} else { // unformatted stream
|
|
|
|
furthestPositionInRecord =
|
|
|
|
std::max(furthestPositionInRecord, positionInRecord);
|
|
|
|
frameOffsetInFile_ += recordOffsetInFrame_ + furthestPositionInRecord;
|
2020-10-01 03:53:00 +08:00
|
|
|
}
|
|
|
|
BeginRecord();
|
|
|
|
}
|
|
|
|
|
2020-02-05 08:55:45 +08:00
|
|
|
bool ExternalFileUnit::AdvanceRecord(IoErrorHandler &handler) {
|
2020-07-04 03:38:22 +08:00
|
|
|
if (direction_ == Direction::Input) {
|
2020-10-01 03:53:00 +08:00
|
|
|
FinishReadingRecord(handler);
|
2021-04-14 07:07:58 +08:00
|
|
|
return BeginReadingRecord(handler);
|
2020-07-04 03:38:22 +08:00
|
|
|
} else { // Direction::Output
|
2021-04-14 07:07:58 +08:00
|
|
|
bool ok{true};
|
2021-05-06 02:33:00 +08:00
|
|
|
RUNTIME_CHECK(handler, isUnformatted.has_value());
|
2022-01-29 07:34:28 +08:00
|
|
|
positionInRecord = furthestPositionInRecord;
|
|
|
|
if (access == Access::Direct) {
|
|
|
|
if (furthestPositionInRecord <
|
|
|
|
openRecl.value_or(furthestPositionInRecord)) {
|
2022-01-21 05:37:58 +08:00
|
|
|
// Pad remainder of fixed length record
|
|
|
|
WriteFrame(
|
|
|
|
frameOffsetInFile_, recordOffsetInFrame_ + *openRecl, handler);
|
|
|
|
std::memset(Frame() + recordOffsetInFrame_ + furthestPositionInRecord,
|
|
|
|
isUnformatted.value_or(false) ? 0 : ' ',
|
|
|
|
*openRecl - furthestPositionInRecord);
|
|
|
|
furthestPositionInRecord = *openRecl;
|
|
|
|
}
|
2022-01-29 07:34:28 +08:00
|
|
|
} else if (*isUnformatted) {
|
|
|
|
if (access == Access::Sequential) {
|
2020-10-02 01:59:09 +08:00
|
|
|
// 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;
|
2021-05-06 02:37:49 +08:00
|
|
|
ok = ok &&
|
|
|
|
Emit(reinterpret_cast<const char *>(&length), sizeof length,
|
|
|
|
sizeof length, handler);
|
2020-10-02 01:59:09 +08:00
|
|
|
positionInRecord = 0;
|
2021-05-06 02:37:49 +08:00
|
|
|
ok = ok &&
|
|
|
|
Emit(reinterpret_cast<const char *>(&length), sizeof length,
|
|
|
|
sizeof length, handler);
|
2020-07-04 03:38:22 +08:00
|
|
|
} else {
|
2022-01-29 07:34:28 +08:00
|
|
|
// Unformatted stream: nothing to do
|
2020-02-14 06:41:56 +08:00
|
|
|
}
|
2022-04-13 06:15:46 +08:00
|
|
|
} else if (handler.GetIoStat() != IostatOk &&
|
|
|
|
furthestPositionInRecord == 0) {
|
|
|
|
// Error in formatted variable length record, and no output yet; do
|
|
|
|
// nothing, like most other Fortran compilers do.
|
|
|
|
return true;
|
2022-01-29 07:34:28 +08:00
|
|
|
} else {
|
|
|
|
// Terminate formatted variable length record
|
|
|
|
const char *lineEnding{"\n"};
|
|
|
|
std::size_t lineEndingBytes{1};
|
|
|
|
#ifdef _WIN32
|
|
|
|
if (!isWindowsTextFile()) {
|
|
|
|
lineEnding = "\r\n";
|
|
|
|
lineEndingBytes = 2;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
ok = ok && Emit(lineEnding, lineEndingBytes, 1, handler);
|
2020-02-14 06:41:56 +08:00
|
|
|
}
|
2022-04-27 07:09:42 +08:00
|
|
|
leftTabLimit.reset();
|
2022-01-08 02:29:23 +08:00
|
|
|
if (IsAfterEndfile()) {
|
|
|
|
return false;
|
|
|
|
}
|
2021-06-29 02:41:04 +08:00
|
|
|
CommitWrites();
|
2020-10-01 03:53:00 +08:00
|
|
|
++currentRecordNumber;
|
2022-01-29 07:34:28 +08:00
|
|
|
if (access != Access::Direct) {
|
|
|
|
impliedEndfile_ = IsRecordFile();
|
|
|
|
if (IsAtEOF()) {
|
|
|
|
endfileRecordNumber.reset();
|
|
|
|
}
|
[flang] runtime: fix problems with I/O around EOF & delimited characters
When a WRITE overwrites an endfile record, we need to forget
that there was an endfile record. When doing a BACKSPACE
after an explicit ENDFILE statement, the position afterwards
must be upon the endfile record.
Attempts to join list-directed delimited character input across
record boundaries was due to a bad reading of the standard
and has been deleted, now that the requirements are better understood.
This problem would cause a read attempt past EOF if a delimited
character input value was at the end of a record.
It turns out that delimited list-directed (and NAMELIST) character
output is required to emit contiguous doubled instances of the
delimiter character when it appears in the output value. When
fixed-size records are being emitted, as is the case with internal
output, this is not possible when the problematic character falls
on the last position of a record. No two other Fortran compilers
do the same thing in this situation so there is no good precedent
to follow.
Because it seems least wrong, with this patch we now emit one copy
of the delimiter as the last character of the current record and
another as the first character of the next record. (The
second-least-wrong alternative might be to flag a runtime error,
but that seems harsh since it's not an explicit error in the standard,
and the output may not have to be usable later as input anyway.)
Consequently, the output is not suitable for use as list-directed or
NAMELIST input.
If a later standard were to clarify this case, this behavior will of
course change as needed to conform.
Differential Revision: https://reviews.llvm.org/D106695
2021-07-23 00:47:37 +08:00
|
|
|
}
|
2021-04-14 07:07:58 +08:00
|
|
|
return ok;
|
2020-01-24 08:59:27 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-14 06:41:56 +08:00
|
|
|
void ExternalFileUnit::BackspaceRecord(IoErrorHandler &handler) {
|
2022-01-29 07:34:28 +08:00
|
|
|
if (access == Access::Direct || !IsRecordFile()) {
|
2020-07-04 03:38:22 +08:00
|
|
|
handler.SignalError(IostatBackspaceNonSequential,
|
2022-01-29 07:34:28 +08:00
|
|
|
"BACKSPACE(UNIT=%d) on direct-access file or unformatted stream",
|
|
|
|
unitNumber());
|
2020-07-04 03:38:22 +08:00
|
|
|
} else {
|
2022-01-08 02:29:23 +08:00
|
|
|
if (IsAfterEndfile()) {
|
[flang] runtime: fix problems with I/O around EOF & delimited characters
When a WRITE overwrites an endfile record, we need to forget
that there was an endfile record. When doing a BACKSPACE
after an explicit ENDFILE statement, the position afterwards
must be upon the endfile record.
Attempts to join list-directed delimited character input across
record boundaries was due to a bad reading of the standard
and has been deleted, now that the requirements are better understood.
This problem would cause a read attempt past EOF if a delimited
character input value was at the end of a record.
It turns out that delimited list-directed (and NAMELIST) character
output is required to emit contiguous doubled instances of the
delimiter character when it appears in the output value. When
fixed-size records are being emitted, as is the case with internal
output, this is not possible when the problematic character falls
on the last position of a record. No two other Fortran compilers
do the same thing in this situation so there is no good precedent
to follow.
Because it seems least wrong, with this patch we now emit one copy
of the delimiter as the last character of the current record and
another as the first character of the next record. (The
second-least-wrong alternative might be to flag a runtime error,
but that seems harsh since it's not an explicit error in the standard,
and the output may not have to be usable later as input anyway.)
Consequently, the output is not suitable for use as list-directed or
NAMELIST input.
If a later standard were to clarify this case, this behavior will of
course change as needed to conform.
Differential Revision: https://reviews.llvm.org/D106695
2021-07-23 00:47:37 +08:00
|
|
|
// BACKSPACE after explicit ENDFILE
|
|
|
|
currentRecordNumber = *endfileRecordNumber;
|
2022-04-29 07:54:40 +08:00
|
|
|
} else if (leftTabLimit) {
|
|
|
|
// BACKSPACE after non-advancing I/O
|
|
|
|
leftTabLimit.reset();
|
2020-02-14 06:41:56 +08:00
|
|
|
} else {
|
2020-10-02 00:50:48 +08:00
|
|
|
DoImpliedEndfile(handler);
|
|
|
|
if (frameOffsetInFile_ + recordOffsetInFrame_ > 0) {
|
|
|
|
--currentRecordNumber;
|
2021-12-03 08:36:09 +08:00
|
|
|
if (openRecl && access == Access::Direct) {
|
2020-10-02 00:50:48 +08:00
|
|
|
BackspaceFixedRecord(handler);
|
|
|
|
} else {
|
2021-05-06 02:33:00 +08:00
|
|
|
RUNTIME_CHECK(handler, isUnformatted.has_value());
|
|
|
|
if (isUnformatted.value_or(false)) {
|
|
|
|
BackspaceVariableUnformattedRecord(handler);
|
|
|
|
} else {
|
|
|
|
BackspaceVariableFormattedRecord(handler);
|
|
|
|
}
|
2020-10-02 00:50:48 +08:00
|
|
|
}
|
|
|
|
}
|
2020-02-14 06:41:56 +08:00
|
|
|
}
|
2020-10-02 00:50:48 +08:00
|
|
|
BeginRecord();
|
2020-02-14 06:41:56 +08:00
|
|
|
}
|
2020-01-24 08:59:27 +08:00
|
|
|
}
|
|
|
|
|
2021-06-29 02:41:04 +08:00
|
|
|
void ExternalFileUnit::FlushOutput(IoErrorHandler &handler) {
|
|
|
|
if (!mayPosition()) {
|
|
|
|
auto frameAt{FrameAt()};
|
|
|
|
if (frameOffsetInFile_ >= frameAt &&
|
|
|
|
frameOffsetInFile_ <
|
|
|
|
static_cast<std::int64_t>(frameAt + FrameLength())) {
|
|
|
|
// A Flush() that's about to happen to a non-positionable file
|
|
|
|
// needs to advance frameOffsetInFile_ to prevent attempts at
|
|
|
|
// impossible seeks
|
|
|
|
CommitWrites();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Flush(handler);
|
|
|
|
}
|
|
|
|
|
2020-02-05 08:55:45 +08:00
|
|
|
void ExternalFileUnit::FlushIfTerminal(IoErrorHandler &handler) {
|
|
|
|
if (isTerminal()) {
|
2021-06-29 02:41:04 +08:00
|
|
|
FlushOutput(handler);
|
2020-02-05 08:55:45 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-04 03:38:22 +08:00
|
|
|
void ExternalFileUnit::Endfile(IoErrorHandler &handler) {
|
2022-01-29 07:34:28 +08:00
|
|
|
if (access == Access::Direct) {
|
|
|
|
handler.SignalError(IostatEndfileDirect,
|
|
|
|
"ENDFILE(UNIT=%d) on direct-access file", unitNumber());
|
2020-07-04 03:38:22 +08:00
|
|
|
} else if (!mayWrite()) {
|
|
|
|
handler.SignalError(IostatEndfileUnwritable,
|
|
|
|
"ENDFILE(UNIT=%d) on read-only file", unitNumber());
|
2022-01-08 02:29:23 +08:00
|
|
|
} else if (IsAfterEndfile()) {
|
2020-10-02 00:50:48 +08:00
|
|
|
// ENDFILE after ENDFILE
|
2020-07-04 03:38:22 +08:00
|
|
|
} else {
|
|
|
|
DoEndfile(handler);
|
2022-04-27 07:09:42 +08:00
|
|
|
if (IsRecordFile() && access != Access::Direct) {
|
2022-01-29 07:34:28 +08:00
|
|
|
// Explicit ENDFILE leaves position *after* the endfile record
|
|
|
|
RUNTIME_CHECK(handler, endfileRecordNumber.has_value());
|
|
|
|
currentRecordNumber = *endfileRecordNumber + 1;
|
|
|
|
}
|
2020-07-04 03:38:22 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ExternalFileUnit::Rewind(IoErrorHandler &handler) {
|
|
|
|
if (access == Access::Direct) {
|
|
|
|
handler.SignalError(IostatRewindNonSequential,
|
|
|
|
"REWIND(UNIT=%d) on non-sequential file", unitNumber());
|
|
|
|
} else {
|
2022-01-29 07:34:28 +08:00
|
|
|
SetPosition(0, handler);
|
2020-07-04 03:38:22 +08:00
|
|
|
currentRecordNumber = 1;
|
2022-06-10 06:30:38 +08:00
|
|
|
leftTabLimit.reset();
|
2020-07-04 03:38:22 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-29 07:34:28 +08:00
|
|
|
void ExternalFileUnit::SetPosition(std::int64_t pos, IoErrorHandler &handler) {
|
|
|
|
DoImpliedEndfile(handler);
|
|
|
|
frameOffsetInFile_ = pos;
|
|
|
|
recordOffsetInFrame_ = 0;
|
2022-02-18 09:22:09 +08:00
|
|
|
if (access == Access::Direct) {
|
|
|
|
directAccessRecWasSet_ = true;
|
|
|
|
}
|
2022-01-29 07:34:28 +08:00
|
|
|
BeginRecord();
|
|
|
|
}
|
|
|
|
|
2020-02-05 08:55:45 +08:00
|
|
|
void ExternalFileUnit::EndIoStatement() {
|
|
|
|
io_.reset();
|
2020-01-24 08:59:27 +08:00
|
|
|
u_.emplace<std::monostate>();
|
2020-02-14 06:41:56 +08:00
|
|
|
lock_.Drop();
|
|
|
|
}
|
|
|
|
|
2020-07-04 03:38:22 +08:00
|
|
|
void ExternalFileUnit::BeginSequentialVariableUnformattedInputRecord(
|
2020-02-14 06:41:56 +08:00
|
|
|
IoErrorHandler &handler) {
|
|
|
|
std::int32_t header{0}, footer{0};
|
2020-07-04 03:38:22 +08:00
|
|
|
std::size_t need{recordOffsetInFrame_ + sizeof header};
|
|
|
|
std::size_t got{ReadFrame(frameOffsetInFile_, need, handler)};
|
2020-02-14 06:41:56 +08:00
|
|
|
// Try to emit informative errors to help debug corrupted files.
|
|
|
|
const char *error{nullptr};
|
|
|
|
if (got < need) {
|
2020-07-04 03:38:22 +08:00
|
|
|
if (got == recordOffsetInFrame_) {
|
2022-05-19 04:40:33 +08:00
|
|
|
HitEndOnRead(handler);
|
2020-02-14 06:41:56 +08:00
|
|
|
} else {
|
2020-07-04 03:38:22 +08:00
|
|
|
error = "Unformatted variable-length sequential file input failed at "
|
|
|
|
"record #%jd (file offset %jd): truncated record header";
|
2020-02-14 06:41:56 +08:00
|
|
|
}
|
|
|
|
} else {
|
2020-07-04 03:38:22 +08:00
|
|
|
std::memcpy(&header, Frame() + recordOffsetInFrame_, sizeof header);
|
|
|
|
recordLength = sizeof header + header; // does not include footer
|
|
|
|
need = recordOffsetInFrame_ + *recordLength + sizeof footer;
|
|
|
|
got = ReadFrame(frameOffsetInFile_, need, handler);
|
2020-02-14 06:41:56 +08:00
|
|
|
if (got < need) {
|
2020-07-04 03:38:22 +08:00
|
|
|
error = "Unformatted variable-length sequential file input failed at "
|
|
|
|
"record #%jd (file offset %jd): hit EOF reading record with "
|
|
|
|
"length %jd bytes";
|
2020-02-14 06:41:56 +08:00
|
|
|
} else {
|
2020-07-04 03:38:22 +08:00
|
|
|
std::memcpy(&footer, Frame() + recordOffsetInFrame_ + *recordLength,
|
|
|
|
sizeof footer);
|
2020-02-14 06:41:56 +08:00
|
|
|
if (footer != header) {
|
2020-07-04 03:38:22 +08:00
|
|
|
error = "Unformatted variable-length sequential file input failed at "
|
|
|
|
"record #%jd (file offset %jd): record header has length %jd "
|
|
|
|
"that does not match record footer (%jd)";
|
2020-02-14 06:41:56 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (error) {
|
|
|
|
handler.SignalError(error, static_cast<std::intmax_t>(currentRecordNumber),
|
|
|
|
static_cast<std::intmax_t>(frameOffsetInFile_),
|
|
|
|
static_cast<std::intmax_t>(header), static_cast<std::intmax_t>(footer));
|
2020-07-04 03:38:22 +08:00
|
|
|
// TODO: error recovery
|
2020-02-14 06:41:56 +08:00
|
|
|
}
|
|
|
|
positionInRecord = sizeof header;
|
|
|
|
}
|
|
|
|
|
2022-01-29 07:34:28 +08:00
|
|
|
void ExternalFileUnit::BeginVariableFormattedInputRecord(
|
2020-02-14 06:41:56 +08:00
|
|
|
IoErrorHandler &handler) {
|
2021-11-19 01:34:43 +08:00
|
|
|
if (this == defaultInput) {
|
|
|
|
if (defaultOutput) {
|
|
|
|
defaultOutput->FlushOutput(handler);
|
|
|
|
}
|
|
|
|
if (errorOutput) {
|
|
|
|
errorOutput->FlushOutput(handler);
|
|
|
|
}
|
2020-02-14 06:41:56 +08:00
|
|
|
}
|
2020-07-04 03:38:22 +08:00
|
|
|
std::size_t length{0};
|
|
|
|
do {
|
2021-08-17 04:15:01 +08:00
|
|
|
std::size_t need{length + 1};
|
|
|
|
length =
|
|
|
|
ReadFrame(frameOffsetInFile_, recordOffsetInFrame_ + need, handler) -
|
|
|
|
recordOffsetInFrame_;
|
2020-07-04 03:38:22 +08:00
|
|
|
if (length < need) {
|
2021-08-17 04:15:01 +08:00
|
|
|
if (length > 0) {
|
|
|
|
// final record w/o \n
|
|
|
|
recordLength = length;
|
2022-04-27 07:09:42 +08:00
|
|
|
unterminatedRecord = true;
|
2021-08-17 04:15:01 +08:00
|
|
|
} else {
|
2022-05-19 04:40:33 +08:00
|
|
|
HitEndOnRead(handler);
|
2021-08-17 04:15:01 +08:00
|
|
|
}
|
2020-02-14 06:41:56 +08:00
|
|
|
break;
|
|
|
|
}
|
2022-01-29 07:34:28 +08:00
|
|
|
} while (!SetVariableFormattedRecordLength());
|
2020-07-04 03:38:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void ExternalFileUnit::BackspaceFixedRecord(IoErrorHandler &handler) {
|
2021-12-03 08:36:09 +08:00
|
|
|
RUNTIME_CHECK(handler, openRecl.has_value());
|
|
|
|
if (frameOffsetInFile_ < *openRecl) {
|
2020-07-04 03:38:22 +08:00
|
|
|
handler.SignalError(IostatBackspaceAtFirstRecord);
|
|
|
|
} else {
|
2021-12-03 08:36:09 +08:00
|
|
|
frameOffsetInFile_ -= *openRecl;
|
2020-02-14 06:41:56 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-04 03:38:22 +08:00
|
|
|
void ExternalFileUnit::BackspaceVariableUnformattedRecord(
|
2020-02-14 06:41:56 +08:00
|
|
|
IoErrorHandler &handler) {
|
|
|
|
std::int32_t header{0}, footer{0};
|
2020-07-04 03:38:22 +08:00
|
|
|
auto headerBytes{static_cast<std::int64_t>(sizeof header)};
|
|
|
|
frameOffsetInFile_ += recordOffsetInFrame_;
|
|
|
|
recordOffsetInFrame_ = 0;
|
|
|
|
if (frameOffsetInFile_ <= headerBytes) {
|
|
|
|
handler.SignalError(IostatBackspaceAtFirstRecord);
|
|
|
|
return;
|
|
|
|
}
|
2020-02-14 06:41:56 +08:00
|
|
|
// Error conditions here cause crashes, not file format errors, because the
|
|
|
|
// validity of the file structure before the current record will have been
|
2020-07-04 03:38:22 +08:00
|
|
|
// checked informatively in NextSequentialVariableUnformattedInputRecord().
|
2020-02-14 06:41:56 +08:00
|
|
|
std::size_t got{
|
2020-07-04 03:38:22 +08:00
|
|
|
ReadFrame(frameOffsetInFile_ - headerBytes, headerBytes, handler)};
|
2022-03-16 03:17:17 +08:00
|
|
|
if (static_cast<std::int64_t>(got) < headerBytes) {
|
|
|
|
handler.SignalError(IostatShortRead);
|
|
|
|
return;
|
|
|
|
}
|
2020-02-14 06:41:56 +08:00
|
|
|
std::memcpy(&footer, Frame(), sizeof footer);
|
|
|
|
recordLength = footer;
|
2022-03-16 03:17:17 +08:00
|
|
|
if (frameOffsetInFile_ < *recordLength + 2 * headerBytes) {
|
|
|
|
handler.SignalError(IostatBadUnformattedRecord);
|
|
|
|
return;
|
|
|
|
}
|
2020-07-04 03:38:22 +08:00
|
|
|
frameOffsetInFile_ -= *recordLength + 2 * headerBytes;
|
|
|
|
if (frameOffsetInFile_ >= headerBytes) {
|
|
|
|
frameOffsetInFile_ -= headerBytes;
|
|
|
|
recordOffsetInFrame_ = headerBytes;
|
|
|
|
}
|
|
|
|
auto need{static_cast<std::size_t>(
|
|
|
|
recordOffsetInFrame_ + sizeof header + *recordLength)};
|
|
|
|
got = ReadFrame(frameOffsetInFile_, need, handler);
|
2022-03-16 03:17:17 +08:00
|
|
|
if (got < need) {
|
|
|
|
handler.SignalError(IostatShortRead);
|
|
|
|
return;
|
|
|
|
}
|
2020-07-04 03:38:22 +08:00
|
|
|
std::memcpy(&header, Frame() + recordOffsetInFrame_, sizeof header);
|
2022-03-16 03:17:17 +08:00
|
|
|
if (header != *recordLength) {
|
|
|
|
handler.SignalError(IostatBadUnformattedRecord);
|
|
|
|
return;
|
|
|
|
}
|
2020-02-14 06:41:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// There's no portable memrchr(), unfortunately, and strrchr() would
|
|
|
|
// fail on a record with a NUL, so we have to do it the hard way.
|
|
|
|
static const char *FindLastNewline(const char *str, std::size_t length) {
|
|
|
|
for (const char *p{str + length}; p-- > str;) {
|
|
|
|
if (*p == '\n') {
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2020-07-04 03:38:22 +08:00
|
|
|
void ExternalFileUnit::BackspaceVariableFormattedRecord(
|
2020-02-14 06:41:56 +08:00
|
|
|
IoErrorHandler &handler) {
|
2020-07-04 03:38:22 +08:00
|
|
|
// File offset of previous record's newline
|
|
|
|
auto prevNL{
|
|
|
|
frameOffsetInFile_ + static_cast<std::int64_t>(recordOffsetInFrame_) - 1};
|
|
|
|
if (prevNL < 0) {
|
|
|
|
handler.SignalError(IostatBackspaceAtFirstRecord);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
while (true) {
|
|
|
|
if (frameOffsetInFile_ < prevNL) {
|
2020-02-14 06:41:56 +08:00
|
|
|
if (const char *p{
|
2020-07-04 03:38:22 +08:00
|
|
|
FindLastNewline(Frame(), prevNL - 1 - frameOffsetInFile_)}) {
|
2020-02-14 06:41:56 +08:00
|
|
|
recordOffsetInFrame_ = p - Frame() + 1;
|
2021-07-27 04:02:08 +08:00
|
|
|
recordLength = prevNL - (frameOffsetInFile_ + recordOffsetInFrame_);
|
2020-02-14 06:41:56 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2020-07-04 03:38:22 +08:00
|
|
|
if (frameOffsetInFile_ == 0) {
|
|
|
|
recordOffsetInFrame_ = 0;
|
2021-07-27 04:02:08 +08:00
|
|
|
recordLength = prevNL;
|
2020-07-04 03:38:22 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
frameOffsetInFile_ -= std::min<std::int64_t>(frameOffsetInFile_, 1024);
|
|
|
|
auto need{static_cast<std::size_t>(prevNL + 1 - frameOffsetInFile_)};
|
|
|
|
auto got{ReadFrame(frameOffsetInFile_, need, handler)};
|
2022-03-16 03:17:17 +08:00
|
|
|
if (got < need) {
|
|
|
|
handler.SignalError(IostatShortRead);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (Frame()[recordOffsetInFrame_ + *recordLength] != '\n') {
|
|
|
|
handler.SignalError(IostatMissingTerminator);
|
|
|
|
return;
|
2020-02-14 06:41:56 +08:00
|
|
|
}
|
|
|
|
if (*recordLength > 0 &&
|
|
|
|
Frame()[recordOffsetInFrame_ + *recordLength - 1] == '\r') {
|
|
|
|
--*recordLength;
|
|
|
|
}
|
2020-01-24 08:59:27 +08:00
|
|
|
}
|
2020-07-04 03:38:22 +08:00
|
|
|
|
|
|
|
void ExternalFileUnit::DoImpliedEndfile(IoErrorHandler &handler) {
|
|
|
|
if (impliedEndfile_) {
|
|
|
|
impliedEndfile_ = false;
|
2022-01-29 07:34:28 +08:00
|
|
|
if (access != Access::Direct && IsRecordFile() && mayPosition()) {
|
2020-07-04 03:38:22 +08:00
|
|
|
DoEndfile(handler);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ExternalFileUnit::DoEndfile(IoErrorHandler &handler) {
|
2022-04-27 07:09:42 +08:00
|
|
|
if (IsRecordFile() && access != Access::Direct) {
|
|
|
|
if (furthestPositionInRecord > 0) {
|
|
|
|
// Last write was non-advancing, so AdvanceRecord() was not called.
|
|
|
|
leftTabLimit.reset();
|
|
|
|
++currentRecordNumber;
|
|
|
|
}
|
2022-01-29 07:34:28 +08:00
|
|
|
endfileRecordNumber = currentRecordNumber;
|
|
|
|
}
|
|
|
|
FlushOutput(handler);
|
2022-04-27 07:09:42 +08:00
|
|
|
Truncate(frameOffsetInFile_ + recordOffsetInFrame_ + furthestPositionInRecord,
|
|
|
|
handler);
|
2020-07-04 03:38:22 +08:00
|
|
|
BeginRecord();
|
|
|
|
impliedEndfile_ = false;
|
|
|
|
}
|
2021-06-26 01:40:08 +08:00
|
|
|
|
2021-06-29 02:41:04 +08:00
|
|
|
void ExternalFileUnit::CommitWrites() {
|
|
|
|
frameOffsetInFile_ +=
|
|
|
|
recordOffsetInFrame_ + recordLength.value_or(furthestPositionInRecord);
|
|
|
|
recordOffsetInFrame_ = 0;
|
|
|
|
BeginRecord();
|
|
|
|
}
|
|
|
|
|
2022-02-18 09:22:09 +08:00
|
|
|
bool ExternalFileUnit::CheckDirectAccess(IoErrorHandler &handler) {
|
|
|
|
if (access == Access::Direct) {
|
|
|
|
RUNTIME_CHECK(handler, openRecl);
|
|
|
|
if (!directAccessRecWasSet_) {
|
2022-03-12 06:01:07 +08:00
|
|
|
handler.SignalError(
|
|
|
|
"No REC= was specified for a data transfer with ACCESS='DIRECT'");
|
2022-02-18 09:22:09 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-05-19 04:40:33 +08:00
|
|
|
void ExternalFileUnit::HitEndOnRead(IoErrorHandler &handler) {
|
|
|
|
handler.SignalEnd();
|
|
|
|
if (IsRecordFile() && access != Access::Direct) {
|
|
|
|
endfileRecordNumber = currentRecordNumber;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-26 01:40:08 +08:00
|
|
|
ChildIo &ExternalFileUnit::PushChildIo(IoStatementState &parent) {
|
|
|
|
OwningPtr<ChildIo> current{std::move(child_)};
|
|
|
|
Terminator &terminator{parent.GetIoErrorHandler()};
|
|
|
|
OwningPtr<ChildIo> next{New<ChildIo>{terminator}(parent, std::move(current))};
|
|
|
|
child_.reset(next.release());
|
|
|
|
return *child_;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ExternalFileUnit::PopChildIo(ChildIo &child) {
|
|
|
|
if (child_.get() != &child) {
|
|
|
|
child.parent().GetIoErrorHandler().Crash(
|
|
|
|
"ChildIo being popped is not top of stack");
|
|
|
|
}
|
|
|
|
child_.reset(child.AcquirePrevious().release()); // deletes top child
|
|
|
|
}
|
|
|
|
|
2022-06-07 02:44:19 +08:00
|
|
|
int ExternalFileUnit::GetAsynchronousId(IoErrorHandler &handler) {
|
|
|
|
if (!mayAsynchronous()) {
|
|
|
|
handler.SignalError(IostatBadAsynchronous);
|
|
|
|
return -1;
|
|
|
|
} else if (auto least{asyncIdAvailable_.LeastElement()}) {
|
|
|
|
asyncIdAvailable_.reset(*least);
|
|
|
|
return static_cast<int>(*least);
|
|
|
|
} else {
|
|
|
|
handler.SignalError(IostatTooManyAsyncOps);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ExternalFileUnit::Wait(int id) {
|
|
|
|
if (id < 0 || asyncIdAvailable_.test(id)) {
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
if (id == 0) {
|
|
|
|
asyncIdAvailable_.set();
|
|
|
|
} else {
|
|
|
|
asyncIdAvailable_.set(id);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-26 01:40:08 +08:00
|
|
|
void ChildIo::EndIoStatement() {
|
|
|
|
io_.reset();
|
|
|
|
u_.emplace<std::monostate>();
|
|
|
|
}
|
|
|
|
|
2022-02-17 06:55:19 +08:00
|
|
|
Iostat ChildIo::CheckFormattingAndDirection(
|
|
|
|
bool unformatted, Direction direction) {
|
2021-06-26 01:40:08 +08:00
|
|
|
bool parentIsInput{!parent_.get_if<IoDirectionState<Direction::Output>>()};
|
2021-09-21 01:52:39 +08:00
|
|
|
bool parentIsFormatted{parentIsInput
|
|
|
|
? parent_.get_if<FormattedIoStatementState<Direction::Input>>() !=
|
|
|
|
nullptr
|
|
|
|
: parent_.get_if<FormattedIoStatementState<Direction::Output>>() !=
|
|
|
|
nullptr};
|
|
|
|
bool parentIsUnformatted{!parentIsFormatted};
|
2021-06-26 01:40:08 +08:00
|
|
|
if (unformatted != parentIsUnformatted) {
|
2022-02-17 06:55:19 +08:00
|
|
|
return unformatted ? IostatUnformattedChildOnFormattedParent
|
|
|
|
: IostatFormattedChildOnUnformattedParent;
|
2021-06-26 01:40:08 +08:00
|
|
|
} else if (parentIsInput != (direction == Direction::Input)) {
|
2022-02-17 06:55:19 +08:00
|
|
|
return parentIsInput ? IostatChildOutputToInputParent
|
|
|
|
: IostatChildInputFromOutputParent;
|
2021-06-26 01:40:08 +08:00
|
|
|
} else {
|
2022-02-17 06:55:19 +08:00
|
|
|
return IostatOk;
|
2021-06-26 01:40:08 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-29 12:00:16 +08:00
|
|
|
} // namespace Fortran::runtime::io
|