1651f58bfSDiana Picus //===-- runtime/internal-unit.cpp -----------------------------------------===//
295696d56Speter klausler //
395696d56Speter klausler // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
495696d56Speter klausler // See https://llvm.org/LICENSE.txt for license information.
595696d56Speter klausler // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
695696d56Speter klausler //
795696d56Speter klausler //===----------------------------------------------------------------------===//
895696d56Speter klausler 
995696d56Speter klausler #include "internal-unit.h"
1095696d56Speter klausler #include "io-error.h"
11830c0b90SPeter Klausler #include "flang/Runtime/descriptor.h"
1295696d56Speter klausler #include <algorithm>
1395696d56Speter klausler #include <type_traits>
1495696d56Speter klausler 
1595696d56Speter klausler namespace Fortran::runtime::io {
1695696d56Speter klausler 
173b635714Speter klausler template <Direction DIR>
InternalDescriptorUnit(Scalar scalar,std::size_t length)183b635714Speter klausler InternalDescriptorUnit<DIR>::InternalDescriptorUnit(
1995696d56Speter klausler     Scalar scalar, std::size_t length) {
2095696d56Speter klausler   recordLength = length;
2195696d56Speter klausler   endfileRecordNumber = 2;
2295696d56Speter klausler   void *pointer{reinterpret_cast<void *>(const_cast<char *>(scalar))};
2395696d56Speter klausler   descriptor().Establish(TypeCode{CFI_type_char}, length, pointer, 0, nullptr,
2495696d56Speter klausler       CFI_attribute_pointer);
2595696d56Speter klausler }
2695696d56Speter klausler 
273b635714Speter klausler template <Direction DIR>
InternalDescriptorUnit(const Descriptor & that,const Terminator & terminator)283b635714Speter klausler InternalDescriptorUnit<DIR>::InternalDescriptorUnit(
2995696d56Speter klausler     const Descriptor &that, const Terminator &terminator) {
3095696d56Speter klausler   RUNTIME_CHECK(terminator, that.type().IsCharacter());
3195696d56Speter klausler   Descriptor &d{descriptor()};
3295696d56Speter klausler   RUNTIME_CHECK(
3395696d56Speter klausler       terminator, that.SizeInBytes() <= d.SizeInBytes(maxRank, true, 0));
3495696d56Speter klausler   new (&d) Descriptor{that};
3595696d56Speter klausler   d.Check();
3695696d56Speter klausler   recordLength = d.ElementBytes();
3795696d56Speter klausler   endfileRecordNumber = d.Elements() + 1;
3895696d56Speter klausler }
3995696d56Speter klausler 
EndIoStatement()403b635714Speter klausler template <Direction DIR> void InternalDescriptorUnit<DIR>::EndIoStatement() {
41b77fd01aSPeter Klausler   if constexpr (DIR == Direction::Output) {
420f5c60f1SPeter Klausler     // Clear the remainder of the current record if anything was written
430f5c60f1SPeter Klausler     // to it, or if it is the only record.
44*ea5b205bSPeter Klausler     auto end{endfileRecordNumber.value_or(0)};
45*ea5b205bSPeter Klausler     if (currentRecordNumber < end &&
46*ea5b205bSPeter Klausler         (end == 2 || furthestPositionInRecord > 0)) {
47b77fd01aSPeter Klausler       BlankFillOutputRecord();
4895696d56Speter klausler     }
4995696d56Speter klausler   }
5095696d56Speter klausler }
5195696d56Speter klausler 
523b635714Speter klausler template <Direction DIR>
Emit(const char * data,std::size_t bytes,IoErrorHandler & handler)533b635714Speter klausler bool InternalDescriptorUnit<DIR>::Emit(
5495696d56Speter klausler     const char *data, std::size_t bytes, IoErrorHandler &handler) {
553b635714Speter klausler   if constexpr (DIR == Direction::Input) {
563b635714Speter klausler     handler.Crash("InternalDescriptorUnit<Direction::Input>::Emit() called");
573b635714Speter klausler     return false && data[bytes] != 0; // bogus compare silences GCC warning
583b635714Speter klausler   } else {
593b635714Speter klausler     if (bytes <= 0) {
603b635714Speter klausler       return true;
613b635714Speter klausler     }
623b635714Speter klausler     char *record{CurrentRecord()};
633b635714Speter klausler     if (!record) {
643b635714Speter klausler       handler.SignalError(IostatInternalWriteOverrun);
6595696d56Speter klausler       return false;
6695696d56Speter klausler     }
6795696d56Speter klausler     auto furthestAfter{std::max(furthestPositionInRecord,
6895696d56Speter klausler         positionInRecord + static_cast<std::int64_t>(bytes))};
6995696d56Speter klausler     bool ok{true};
7095696d56Speter klausler     if (furthestAfter > static_cast<std::int64_t>(recordLength.value_or(0))) {
713b635714Speter klausler       handler.SignalError(IostatRecordWriteOverrun);
7295696d56Speter klausler       furthestAfter = recordLength.value_or(0);
7395696d56Speter klausler       bytes = std::max(std::int64_t{0}, furthestAfter - positionInRecord);
7495696d56Speter klausler       ok = false;
753b635714Speter klausler     } else if (positionInRecord > furthestPositionInRecord) {
763b635714Speter klausler       std::fill_n(record + furthestPositionInRecord,
773b635714Speter klausler           positionInRecord - furthestPositionInRecord, ' ');
7895696d56Speter klausler     }
7995696d56Speter klausler     std::memcpy(record + positionInRecord, data, bytes);
8095696d56Speter klausler     positionInRecord += bytes;
8195696d56Speter klausler     furthestPositionInRecord = furthestAfter;
8295696d56Speter klausler     return ok;
8395696d56Speter klausler   }
843b635714Speter klausler }
8595696d56Speter klausler 
863b635714Speter klausler template <Direction DIR>
GetNextInputBytes(const char * & p,IoErrorHandler & handler)87da25f968SPeter Klausler std::size_t InternalDescriptorUnit<DIR>::GetNextInputBytes(
88da25f968SPeter Klausler     const char *&p, IoErrorHandler &handler) {
893b635714Speter klausler   if constexpr (DIR == Direction::Output) {
90da25f968SPeter Klausler     handler.Crash("InternalDescriptorUnit<Direction::Output>::"
91da25f968SPeter Klausler                   "GetNextInputBytes() called");
92da25f968SPeter Klausler     return 0;
93da25f968SPeter Klausler   } else {
943b635714Speter klausler     const char *record{CurrentRecord()};
953b635714Speter klausler     if (!record) {
963b635714Speter klausler       handler.SignalEnd();
97da25f968SPeter Klausler       return 0;
98da25f968SPeter Klausler     } else if (positionInRecord >= recordLength.value_or(positionInRecord)) {
99da25f968SPeter Klausler       return 0;
100da25f968SPeter Klausler     } else {
101da25f968SPeter Klausler       p = &record[positionInRecord];
102da25f968SPeter Klausler       return *recordLength - positionInRecord;
1033b635714Speter klausler     }
1043b635714Speter klausler   }
105da25f968SPeter Klausler }
106da25f968SPeter Klausler 
107da25f968SPeter Klausler template <Direction DIR>
AdvanceRecord(IoErrorHandler & handler)1083b635714Speter klausler bool InternalDescriptorUnit<DIR>::AdvanceRecord(IoErrorHandler &handler) {
10995696d56Speter klausler   if (currentRecordNumber >= endfileRecordNumber.value_or(0)) {
11095696d56Speter klausler     handler.SignalEnd();
11195696d56Speter klausler     return false;
11295696d56Speter klausler   }
113b77fd01aSPeter Klausler   if constexpr (DIR == Direction::Output) {
114b77fd01aSPeter Klausler     BlankFillOutputRecord();
11595696d56Speter klausler   }
11695696d56Speter klausler   ++currentRecordNumber;
11737f98f6fSpeter klausler   BeginRecord();
11895696d56Speter klausler   return true;
11995696d56Speter klausler }
12095696d56Speter klausler 
1213b635714Speter klausler template <Direction DIR>
BlankFillOutputRecord()122b77fd01aSPeter Klausler void InternalDescriptorUnit<DIR>::BlankFillOutputRecord() {
123b77fd01aSPeter Klausler   if constexpr (DIR == Direction::Output) {
124b77fd01aSPeter Klausler     if (furthestPositionInRecord <
125b77fd01aSPeter Klausler         recordLength.value_or(furthestPositionInRecord)) {
126b77fd01aSPeter Klausler       char *record{CurrentRecord()};
127b77fd01aSPeter Klausler       std::fill_n(record + furthestPositionInRecord,
128b77fd01aSPeter Klausler           *recordLength - furthestPositionInRecord, ' ');
129b77fd01aSPeter Klausler     }
130b77fd01aSPeter Klausler   }
131b77fd01aSPeter Klausler }
132b77fd01aSPeter Klausler 
133b77fd01aSPeter Klausler template <Direction DIR>
BackspaceRecord(IoErrorHandler & handler)1343b635714Speter klausler void InternalDescriptorUnit<DIR>::BackspaceRecord(IoErrorHandler &handler) {
1353b635714Speter klausler   RUNTIME_CHECK(handler, currentRecordNumber > 1);
1363b635714Speter klausler   --currentRecordNumber;
13737f98f6fSpeter klausler   BeginRecord();
13895696d56Speter klausler }
13995696d56Speter klausler 
1403b635714Speter klausler template class InternalDescriptorUnit<Direction::Output>;
1413b635714Speter klausler template class InternalDescriptorUnit<Direction::Input>;
1421f879005STim Keith } // namespace Fortran::runtime::io
143