| //===-- runtime/internal-unit.cpp -----------------------------------------===// |
| // |
| // 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 "io-error.h" |
| #include "flang/Runtime/descriptor.h" |
| #include "flang/Runtime/freestanding-tools.h" |
| #include <algorithm> |
| #include <type_traits> |
| |
| namespace Fortran::runtime::io { |
| RT_OFFLOAD_API_GROUP_BEGIN |
| |
| template <Direction DIR> |
| RT_API_ATTRS InternalDescriptorUnit<DIR>::InternalDescriptorUnit( |
| Scalar scalar, std::size_t length, int kind) { |
| internalIoCharKind = kind; |
| recordLength = length; |
| endfileRecordNumber = 2; |
| void *pointer{reinterpret_cast<void *>(const_cast<char *>(scalar))}; |
| descriptor().Establish(TypeCode{TypeCategory::Character, kind}, length * kind, |
| pointer, 0, nullptr, CFI_attribute_pointer); |
| } |
| |
| template <Direction DIR> |
| RT_API_ATTRS InternalDescriptorUnit<DIR>::InternalDescriptorUnit( |
| const Descriptor &that, const Terminator &terminator) { |
| auto thatType{that.type().GetCategoryAndKind()}; |
| RUNTIME_CHECK(terminator, thatType.has_value()); |
| RUNTIME_CHECK(terminator, thatType->first == TypeCategory::Character); |
| Descriptor &d{descriptor()}; |
| RUNTIME_CHECK( |
| terminator, that.SizeInBytes() <= d.SizeInBytes(maxRank, true, 0)); |
| new (&d) Descriptor{that}; |
| d.Check(); |
| internalIoCharKind = thatType->second; |
| recordLength = d.ElementBytes(); |
| endfileRecordNumber = d.Elements() + 1; |
| } |
| |
| template <Direction DIR> |
| RT_API_ATTRS bool InternalDescriptorUnit<DIR>::Emit( |
| const char *data, std::size_t bytes, IoErrorHandler &handler) { |
| if constexpr (DIR == Direction::Input) { |
| handler.Crash("InternalDescriptorUnit<Direction::Input>::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<std::int64_t>(bytes))}; |
| bool ok{true}; |
| if (furthestAfter > static_cast<std::int64_t>(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) { |
| BlankFill(record + furthestPositionInRecord, |
| positionInRecord - furthestPositionInRecord); |
| } |
| std::memcpy(record + positionInRecord, data, bytes); |
| positionInRecord += bytes; |
| furthestPositionInRecord = furthestAfter; |
| return ok; |
| } |
| } |
| |
| template <Direction DIR> |
| RT_API_ATTRS std::size_t InternalDescriptorUnit<DIR>::GetNextInputBytes( |
| const char *&p, IoErrorHandler &handler) { |
| p = nullptr; |
| if constexpr (DIR == Direction::Output) { |
| handler.Crash("InternalDescriptorUnit<Direction::Output>::" |
| "GetNextInputBytes() called"); |
| return 0; |
| } else { |
| const char *record{CurrentRecord()}; |
| if (!record) { |
| handler.SignalEnd(); |
| return 0; |
| } else if (positionInRecord >= recordLength.value_or(positionInRecord)) { |
| return 0; |
| } else { |
| p = &record[positionInRecord]; |
| return *recordLength - positionInRecord; |
| } |
| } |
| } |
| |
| template <Direction DIR> |
| RT_API_ATTRS std::size_t InternalDescriptorUnit<DIR>::ViewBytesInRecord( |
| const char *&p, bool forward) const { |
| p = nullptr; |
| auto recl{recordLength.value_or(positionInRecord)}; |
| const char *record{CurrentRecord()}; |
| if (forward) { |
| if (positionInRecord < recl) { |
| if (record) { |
| p = &record[positionInRecord]; |
| } |
| return recl - positionInRecord; |
| } |
| } else { |
| if (record && positionInRecord <= recl) { |
| p = &record[positionInRecord]; |
| } |
| return positionInRecord - leftTabLimit.value_or(0); |
| } |
| return 0; |
| } |
| |
| template <Direction DIR> |
| RT_API_ATTRS bool InternalDescriptorUnit<DIR>::AdvanceRecord( |
| IoErrorHandler &handler) { |
| if (currentRecordNumber >= endfileRecordNumber.value_or(0)) { |
| if constexpr (DIR == Direction::Input) { |
| handler.SignalEnd(); |
| } else { |
| handler.SignalError(IostatInternalWriteOverrun); |
| } |
| return false; |
| } |
| if constexpr (DIR == Direction::Output) { |
| BlankFillOutputRecord(); |
| } |
| ++currentRecordNumber; |
| BeginRecord(); |
| return true; |
| } |
| |
| template <Direction DIR> |
| RT_API_ATTRS void InternalDescriptorUnit<DIR>::BlankFill( |
| char *at, std::size_t bytes) { |
| switch (internalIoCharKind) { |
| case 2: |
| Fortran::runtime::fill_n(reinterpret_cast<char16_t *>(at), bytes / 2, |
| static_cast<char16_t>(' ')); |
| break; |
| case 4: |
| Fortran::runtime::fill_n(reinterpret_cast<char32_t *>(at), bytes / 4, |
| static_cast<char32_t>(' ')); |
| break; |
| default: |
| Fortran::runtime::fill_n(at, bytes, ' '); |
| break; |
| } |
| } |
| |
| template <Direction DIR> |
| RT_API_ATTRS void InternalDescriptorUnit<DIR>::BlankFillOutputRecord() { |
| if constexpr (DIR == Direction::Output) { |
| if (furthestPositionInRecord < |
| recordLength.value_or(furthestPositionInRecord)) { |
| BlankFill(CurrentRecord() + furthestPositionInRecord, |
| *recordLength - furthestPositionInRecord); |
| } |
| } |
| } |
| |
| template <Direction DIR> |
| RT_API_ATTRS void InternalDescriptorUnit<DIR>::BackspaceRecord( |
| IoErrorHandler &handler) { |
| RUNTIME_CHECK(handler, currentRecordNumber > 1); |
| --currentRecordNumber; |
| BeginRecord(); |
| } |
| |
| template <Direction DIR> |
| RT_API_ATTRS std::int64_t InternalDescriptorUnit<DIR>::InquirePos() { |
| return (currentRecordNumber - 1) * recordLength.value_or(0) + |
| positionInRecord + 1; |
| } |
| |
| template class InternalDescriptorUnit<Direction::Output>; |
| template class InternalDescriptorUnit<Direction::Input>; |
| |
| RT_OFFLOAD_API_GROUP_END |
| } // namespace Fortran::runtime::io |