1 //===-- runtime/internal-unit.cpp -----------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "internal-unit.h" 10 #include "io-error.h" 11 #include "flang/Runtime/descriptor.h" 12 #include <algorithm> 13 #include <type_traits> 14 15 namespace Fortran::runtime::io { 16 17 template <Direction DIR> 18 InternalDescriptorUnit<DIR>::InternalDescriptorUnit( 19 Scalar scalar, std::size_t length) { 20 recordLength = length; 21 endfileRecordNumber = 2; 22 void *pointer{reinterpret_cast<void *>(const_cast<char *>(scalar))}; 23 descriptor().Establish(TypeCode{CFI_type_char}, length, pointer, 0, nullptr, 24 CFI_attribute_pointer); 25 } 26 27 template <Direction DIR> 28 InternalDescriptorUnit<DIR>::InternalDescriptorUnit( 29 const Descriptor &that, const Terminator &terminator) { 30 RUNTIME_CHECK(terminator, that.type().IsCharacter()); 31 Descriptor &d{descriptor()}; 32 RUNTIME_CHECK( 33 terminator, that.SizeInBytes() <= d.SizeInBytes(maxRank, true, 0)); 34 new (&d) Descriptor{that}; 35 d.Check(); 36 recordLength = d.ElementBytes(); 37 endfileRecordNumber = d.Elements() + 1; 38 } 39 40 template <Direction DIR> void InternalDescriptorUnit<DIR>::EndIoStatement() { 41 if constexpr (DIR == Direction::Output) { 42 // Clear the remainder of the current record if anything was written 43 // to it, or if it is the only record. 44 auto end{endfileRecordNumber.value_or(0)}; 45 if (currentRecordNumber < end && 46 (end == 2 || furthestPositionInRecord > 0)) { 47 BlankFillOutputRecord(); 48 } 49 } 50 } 51 52 template <Direction DIR> 53 bool InternalDescriptorUnit<DIR>::Emit( 54 const char *data, std::size_t bytes, IoErrorHandler &handler) { 55 if constexpr (DIR == Direction::Input) { 56 handler.Crash("InternalDescriptorUnit<Direction::Input>::Emit() called"); 57 return false && data[bytes] != 0; // bogus compare silences GCC warning 58 } else { 59 if (bytes <= 0) { 60 return true; 61 } 62 char *record{CurrentRecord()}; 63 if (!record) { 64 handler.SignalError(IostatInternalWriteOverrun); 65 return false; 66 } 67 auto furthestAfter{std::max(furthestPositionInRecord, 68 positionInRecord + static_cast<std::int64_t>(bytes))}; 69 bool ok{true}; 70 if (furthestAfter > static_cast<std::int64_t>(recordLength.value_or(0))) { 71 handler.SignalError(IostatRecordWriteOverrun); 72 furthestAfter = recordLength.value_or(0); 73 bytes = std::max(std::int64_t{0}, furthestAfter - positionInRecord); 74 ok = false; 75 } else if (positionInRecord > furthestPositionInRecord) { 76 std::fill_n(record + furthestPositionInRecord, 77 positionInRecord - furthestPositionInRecord, ' '); 78 } 79 std::memcpy(record + positionInRecord, data, bytes); 80 positionInRecord += bytes; 81 furthestPositionInRecord = furthestAfter; 82 return ok; 83 } 84 } 85 86 template <Direction DIR> 87 std::size_t InternalDescriptorUnit<DIR>::GetNextInputBytes( 88 const char *&p, IoErrorHandler &handler) { 89 if constexpr (DIR == Direction::Output) { 90 handler.Crash("InternalDescriptorUnit<Direction::Output>::" 91 "GetNextInputBytes() called"); 92 return 0; 93 } else { 94 const char *record{CurrentRecord()}; 95 if (!record) { 96 handler.SignalEnd(); 97 return 0; 98 } else if (positionInRecord >= recordLength.value_or(positionInRecord)) { 99 return 0; 100 } else { 101 p = &record[positionInRecord]; 102 return *recordLength - positionInRecord; 103 } 104 } 105 } 106 107 template <Direction DIR> 108 bool InternalDescriptorUnit<DIR>::AdvanceRecord(IoErrorHandler &handler) { 109 if (currentRecordNumber >= endfileRecordNumber.value_or(0)) { 110 handler.SignalEnd(); 111 return false; 112 } 113 if constexpr (DIR == Direction::Output) { 114 BlankFillOutputRecord(); 115 } 116 ++currentRecordNumber; 117 BeginRecord(); 118 return true; 119 } 120 121 template <Direction DIR> 122 void InternalDescriptorUnit<DIR>::BlankFillOutputRecord() { 123 if constexpr (DIR == Direction::Output) { 124 if (furthestPositionInRecord < 125 recordLength.value_or(furthestPositionInRecord)) { 126 char *record{CurrentRecord()}; 127 std::fill_n(record + furthestPositionInRecord, 128 *recordLength - furthestPositionInRecord, ' '); 129 } 130 } 131 } 132 133 template <Direction DIR> 134 void InternalDescriptorUnit<DIR>::BackspaceRecord(IoErrorHandler &handler) { 135 RUNTIME_CHECK(handler, currentRecordNumber > 1); 136 --currentRecordNumber; 137 BeginRecord(); 138 } 139 140 template class InternalDescriptorUnit<Direction::Output>; 141 template class InternalDescriptorUnit<Direction::Input>; 142 } // namespace Fortran::runtime::io 143