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 isFixedRecordLength = true; 21 recordLength = length; 22 endfileRecordNumber = 2; 23 void *pointer{reinterpret_cast<void *>(const_cast<char *>(scalar))}; 24 descriptor().Establish(TypeCode{CFI_type_char}, length, pointer, 0, nullptr, 25 CFI_attribute_pointer); 26 } 27 28 template <Direction DIR> 29 InternalDescriptorUnit<DIR>::InternalDescriptorUnit( 30 const Descriptor &that, const Terminator &terminator) { 31 RUNTIME_CHECK(terminator, that.type().IsCharacter()); 32 Descriptor &d{descriptor()}; 33 RUNTIME_CHECK( 34 terminator, that.SizeInBytes() <= d.SizeInBytes(maxRank, true, 0)); 35 new (&d) Descriptor{that}; 36 d.Check(); 37 isFixedRecordLength = true; 38 recordLength = d.ElementBytes(); 39 endfileRecordNumber = d.Elements() + 1; 40 } 41 42 template <Direction DIR> void InternalDescriptorUnit<DIR>::EndIoStatement() { 43 if constexpr (DIR == Direction::Output) { // blank fill 44 while (char *record{CurrentRecord()}) { 45 if (furthestPositionInRecord < 46 recordLength.value_or(furthestPositionInRecord)) { 47 std::fill_n(record + furthestPositionInRecord, 48 *recordLength - furthestPositionInRecord, ' '); 49 } 50 furthestPositionInRecord = 0; 51 ++currentRecordNumber; 52 } 53 } 54 } 55 56 template <Direction DIR> 57 bool InternalDescriptorUnit<DIR>::Emit( 58 const char *data, std::size_t bytes, IoErrorHandler &handler) { 59 if constexpr (DIR == Direction::Input) { 60 handler.Crash("InternalDescriptorUnit<Direction::Input>::Emit() called"); 61 return false && data[bytes] != 0; // bogus compare silences GCC warning 62 } else { 63 if (bytes <= 0) { 64 return true; 65 } 66 char *record{CurrentRecord()}; 67 if (!record) { 68 handler.SignalError(IostatInternalWriteOverrun); 69 return false; 70 } 71 auto furthestAfter{std::max(furthestPositionInRecord, 72 positionInRecord + static_cast<std::int64_t>(bytes))}; 73 bool ok{true}; 74 if (furthestAfter > static_cast<std::int64_t>(recordLength.value_or(0))) { 75 handler.SignalError(IostatRecordWriteOverrun); 76 furthestAfter = recordLength.value_or(0); 77 bytes = std::max(std::int64_t{0}, furthestAfter - positionInRecord); 78 ok = false; 79 } else if (positionInRecord > furthestPositionInRecord) { 80 std::fill_n(record + furthestPositionInRecord, 81 positionInRecord - furthestPositionInRecord, ' '); 82 } 83 std::memcpy(record + positionInRecord, data, bytes); 84 positionInRecord += bytes; 85 furthestPositionInRecord = furthestAfter; 86 return ok; 87 } 88 } 89 90 template <Direction DIR> 91 std::optional<char32_t> InternalDescriptorUnit<DIR>::GetCurrentChar( 92 IoErrorHandler &handler) { 93 if constexpr (DIR == Direction::Output) { 94 handler.Crash( 95 "InternalDescriptorUnit<Direction::Output>::GetCurrentChar() called"); 96 return std::nullopt; 97 } 98 const char *record{CurrentRecord()}; 99 if (!record) { 100 handler.SignalEnd(); 101 return std::nullopt; 102 } 103 if (positionInRecord >= recordLength.value_or(positionInRecord)) { 104 return std::nullopt; 105 } 106 if (isUTF8) { 107 // TODO: UTF-8 decoding 108 } 109 return record[positionInRecord]; 110 } 111 112 template <Direction DIR> 113 bool InternalDescriptorUnit<DIR>::AdvanceRecord(IoErrorHandler &handler) { 114 if (currentRecordNumber >= endfileRecordNumber.value_or(0)) { 115 handler.SignalEnd(); 116 return false; 117 } 118 if constexpr (DIR == Direction::Output) { // blank fill 119 if (furthestPositionInRecord < 120 recordLength.value_or(furthestPositionInRecord)) { 121 char *record{CurrentRecord()}; 122 RUNTIME_CHECK(handler, record != nullptr); 123 std::fill_n(record + furthestPositionInRecord, 124 *recordLength - furthestPositionInRecord, ' '); 125 } 126 } 127 ++currentRecordNumber; 128 BeginRecord(); 129 return true; 130 } 131 132 template <Direction DIR> 133 void InternalDescriptorUnit<DIR>::BackspaceRecord(IoErrorHandler &handler) { 134 RUNTIME_CHECK(handler, currentRecordNumber > 1); 135 --currentRecordNumber; 136 BeginRecord(); 137 } 138 139 template class InternalDescriptorUnit<Direction::Output>; 140 template class InternalDescriptorUnit<Direction::Input>; 141 } // namespace Fortran::runtime::io 142