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 if (furthestPositionInRecord > 0) { 43 BlankFillOutputRecord(); 44 } 45 } 46 } 47 48 template <Direction DIR> 49 bool InternalDescriptorUnit<DIR>::Emit( 50 const char *data, std::size_t bytes, IoErrorHandler &handler) { 51 if constexpr (DIR == Direction::Input) { 52 handler.Crash("InternalDescriptorUnit<Direction::Input>::Emit() called"); 53 return false && data[bytes] != 0; // bogus compare silences GCC warning 54 } else { 55 if (bytes <= 0) { 56 return true; 57 } 58 char *record{CurrentRecord()}; 59 if (!record) { 60 handler.SignalError(IostatInternalWriteOverrun); 61 return false; 62 } 63 auto furthestAfter{std::max(furthestPositionInRecord, 64 positionInRecord + static_cast<std::int64_t>(bytes))}; 65 bool ok{true}; 66 if (furthestAfter > static_cast<std::int64_t>(recordLength.value_or(0))) { 67 handler.SignalError(IostatRecordWriteOverrun); 68 furthestAfter = recordLength.value_or(0); 69 bytes = std::max(std::int64_t{0}, furthestAfter - positionInRecord); 70 ok = false; 71 } else if (positionInRecord > furthestPositionInRecord) { 72 std::fill_n(record + furthestPositionInRecord, 73 positionInRecord - furthestPositionInRecord, ' '); 74 } 75 std::memcpy(record + positionInRecord, data, bytes); 76 positionInRecord += bytes; 77 furthestPositionInRecord = furthestAfter; 78 return ok; 79 } 80 } 81 82 template <Direction DIR> 83 std::size_t InternalDescriptorUnit<DIR>::GetNextInputBytes( 84 const char *&p, IoErrorHandler &handler) { 85 if constexpr (DIR == Direction::Output) { 86 handler.Crash("InternalDescriptorUnit<Direction::Output>::" 87 "GetNextInputBytes() called"); 88 return 0; 89 } else { 90 const char *record{CurrentRecord()}; 91 if (!record) { 92 handler.SignalEnd(); 93 return 0; 94 } else if (positionInRecord >= recordLength.value_or(positionInRecord)) { 95 return 0; 96 } else { 97 p = &record[positionInRecord]; 98 return *recordLength - positionInRecord; 99 } 100 } 101 } 102 103 template <Direction DIR> 104 std::optional<char32_t> InternalDescriptorUnit<DIR>::GetCurrentChar( 105 IoErrorHandler &handler) { 106 const char *p{nullptr}; 107 std::size_t bytes{GetNextInputBytes(p, handler)}; 108 if (bytes == 0) { 109 return std::nullopt; 110 } else { 111 if (isUTF8) { 112 // TODO: UTF-8 decoding 113 } 114 return *p; 115 } 116 } 117 118 template <Direction DIR> 119 bool InternalDescriptorUnit<DIR>::AdvanceRecord(IoErrorHandler &handler) { 120 if (currentRecordNumber >= endfileRecordNumber.value_or(0)) { 121 handler.SignalEnd(); 122 return false; 123 } 124 if constexpr (DIR == Direction::Output) { 125 BlankFillOutputRecord(); 126 } 127 ++currentRecordNumber; 128 BeginRecord(); 129 return true; 130 } 131 132 template <Direction DIR> 133 void InternalDescriptorUnit<DIR>::BlankFillOutputRecord() { 134 if constexpr (DIR == Direction::Output) { 135 if (furthestPositionInRecord < 136 recordLength.value_or(furthestPositionInRecord)) { 137 char *record{CurrentRecord()}; 138 std::fill_n(record + furthestPositionInRecord, 139 *recordLength - furthestPositionInRecord, ' '); 140 } 141 } 142 } 143 144 template <Direction DIR> 145 void InternalDescriptorUnit<DIR>::BackspaceRecord(IoErrorHandler &handler) { 146 RUNTIME_CHECK(handler, currentRecordNumber > 1); 147 --currentRecordNumber; 148 BeginRecord(); 149 } 150 151 template class InternalDescriptorUnit<Direction::Output>; 152 template class InternalDescriptorUnit<Direction::Input>; 153 } // namespace Fortran::runtime::io 154