//===-- runtime/internal-unit.cpp -------------------------------*- 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 // //===----------------------------------------------------------------------===// #include "internal-unit.h" #include "descriptor.h" #include "io-error.h" #include #include namespace Fortran::runtime::io { template InternalDescriptorUnit::InternalDescriptorUnit( Scalar scalar, std::size_t length) { isFixedRecordLength = true; recordLength = length; endfileRecordNumber = 2; void *pointer{reinterpret_cast(const_cast(scalar))}; descriptor().Establish(TypeCode{CFI_type_char}, length, pointer, 0, nullptr, CFI_attribute_pointer); } template InternalDescriptorUnit::InternalDescriptorUnit( const Descriptor &that, const Terminator &terminator) { RUNTIME_CHECK(terminator, that.type().IsCharacter()); Descriptor &d{descriptor()}; RUNTIME_CHECK( terminator, that.SizeInBytes() <= d.SizeInBytes(maxRank, true, 0)); new (&d) Descriptor{that}; d.Check(); isFixedRecordLength = true; recordLength = d.ElementBytes(); endfileRecordNumber = d.Elements() + 1; } template void InternalDescriptorUnit::EndIoStatement() { if constexpr (DIR == Direction::Output) { // blank fill while (char *record{CurrentRecord()}) { if (furthestPositionInRecord < recordLength.value_or(furthestPositionInRecord)) { std::fill_n(record + furthestPositionInRecord, *recordLength - furthestPositionInRecord, ' '); } furthestPositionInRecord = 0; ++currentRecordNumber; } } } template bool InternalDescriptorUnit::Emit( const char *data, std::size_t bytes, IoErrorHandler &handler) { if constexpr (DIR == Direction::Input) { handler.Crash("InternalDescriptorUnit::Emit() called"); return false && data[bytes] != 0; // bogus compare silences GCC warning } else { if (bytes <= 0) { return true; } char *record{CurrentRecord()}; if (!record) { handler.SignalError(IostatInternalWriteOverrun); return false; } auto furthestAfter{std::max(furthestPositionInRecord, positionInRecord + static_cast(bytes))}; bool ok{true}; if (furthestAfter > static_cast(recordLength.value_or(0))) { handler.SignalError(IostatRecordWriteOverrun); furthestAfter = recordLength.value_or(0); bytes = std::max(std::int64_t{0}, furthestAfter - positionInRecord); ok = false; } else if (positionInRecord > furthestPositionInRecord) { std::fill_n(record + furthestPositionInRecord, positionInRecord - furthestPositionInRecord, ' '); } std::memcpy(record + positionInRecord, data, bytes); positionInRecord += bytes; furthestPositionInRecord = furthestAfter; return ok; } } template std::optional InternalDescriptorUnit::GetCurrentChar( IoErrorHandler &handler) { if constexpr (DIR == Direction::Output) { handler.Crash( "InternalDescriptorUnit::GetCurrentChar() called"); return std::nullopt; } const char *record{CurrentRecord()}; if (!record) { handler.SignalEnd(); return std::nullopt; } if (positionInRecord >= recordLength.value_or(positionInRecord)) { return std::nullopt; } if (isUTF8) { // TODO: UTF-8 decoding } return record[positionInRecord]; } template bool InternalDescriptorUnit::AdvanceRecord(IoErrorHandler &handler) { if (currentRecordNumber >= endfileRecordNumber.value_or(0)) { handler.SignalEnd(); return false; } if constexpr (DIR == Direction::Output) { // blank fill if (furthestPositionInRecord < recordLength.value_or(furthestPositionInRecord)) { char *record{CurrentRecord()}; RUNTIME_CHECK(handler, record != nullptr); std::fill_n(record + furthestPositionInRecord, *recordLength - furthestPositionInRecord, ' '); } } ++currentRecordNumber; BeginRecord(); return true; } template void InternalDescriptorUnit::BackspaceRecord(IoErrorHandler &handler) { RUNTIME_CHECK(handler, currentRecordNumber > 1); --currentRecordNumber; BeginRecord(); } template class InternalDescriptorUnit; template class InternalDescriptorUnit; } // namespace Fortran::runtime::io