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