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