xref: /llvm-project/flang/runtime/internal-unit.cpp (revision c91ba04328e1ded6f284469a7828d181324d4e30)
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 "flang/Runtime/freestanding-tools.h"
13 #include <algorithm>
14 #include <type_traits>
15 
16 namespace Fortran::runtime::io {
17 RT_OFFLOAD_API_GROUP_BEGIN
18 
19 template <Direction DIR>
20 RT_API_ATTRS InternalDescriptorUnit<DIR>::InternalDescriptorUnit(
21     Scalar scalar, std::size_t length, int kind) {
22   internalIoCharKind = kind;
23   recordLength = length;
24   endfileRecordNumber = 2;
25   void *pointer{reinterpret_cast<void *>(const_cast<char *>(scalar))};
26   descriptor().Establish(TypeCode{TypeCategory::Character, kind}, length * kind,
27       pointer, 0, nullptr, CFI_attribute_pointer);
28 }
29 
30 template <Direction DIR>
31 RT_API_ATTRS InternalDescriptorUnit<DIR>::InternalDescriptorUnit(
32     const Descriptor &that, const Terminator &terminator) {
33   auto thatType{that.type().GetCategoryAndKind()};
34   RUNTIME_CHECK(terminator, thatType.has_value());
35   RUNTIME_CHECK(terminator, thatType->first == TypeCategory::Character);
36   Descriptor &d{descriptor()};
37   RUNTIME_CHECK(
38       terminator, that.SizeInBytes() <= d.SizeInBytes(maxRank, true, 0));
39   RUNTIME_CHECK(terminator,
40       that.SizeInBytes() <= MaxDescriptorSizeInBytes(maxRank, true, 0));
41   new (&d) Descriptor{that};
42   d.Check();
43   internalIoCharKind = thatType->second;
44   recordLength = d.ElementBytes();
45   endfileRecordNumber = d.Elements() + 1;
46 }
47 
48 template <Direction DIR>
49 RT_API_ATTRS 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       BlankFill(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 RT_API_ATTRS std::size_t InternalDescriptorUnit<DIR>::GetNextInputBytes(
84     const char *&p, IoErrorHandler &handler) {
85   p = nullptr;
86   if constexpr (DIR == Direction::Output) {
87     handler.Crash("InternalDescriptorUnit<Direction::Output>::"
88                   "GetNextInputBytes() called");
89     return 0;
90   } else {
91     const char *record{CurrentRecord()};
92     if (!record) {
93       handler.SignalEnd();
94       return 0;
95     } else if (positionInRecord >= recordLength.value_or(positionInRecord)) {
96       return 0;
97     } else {
98       p = &record[positionInRecord];
99       return *recordLength - positionInRecord;
100     }
101   }
102 }
103 
104 template <Direction DIR>
105 RT_API_ATTRS std::size_t InternalDescriptorUnit<DIR>::ViewBytesInRecord(
106     const char *&p, bool forward) const {
107   p = nullptr;
108   auto recl{recordLength.value_or(positionInRecord)};
109   const char *record{CurrentRecord()};
110   if (forward) {
111     if (positionInRecord < recl) {
112       if (record) {
113         p = &record[positionInRecord];
114       }
115       return recl - positionInRecord;
116     }
117   } else {
118     if (record && positionInRecord <= recl) {
119       p = &record[positionInRecord];
120     }
121     return positionInRecord - leftTabLimit.value_or(0);
122   }
123   return 0;
124 }
125 
126 template <Direction DIR>
127 RT_API_ATTRS bool InternalDescriptorUnit<DIR>::AdvanceRecord(
128     IoErrorHandler &handler) {
129   if (currentRecordNumber >= endfileRecordNumber.value_or(0)) {
130     if constexpr (DIR == Direction::Input) {
131       handler.SignalEnd();
132     } else {
133       handler.SignalError(IostatInternalWriteOverrun);
134     }
135     return false;
136   }
137   if constexpr (DIR == Direction::Output) {
138     BlankFillOutputRecord();
139   }
140   ++currentRecordNumber;
141   BeginRecord();
142   return true;
143 }
144 
145 template <Direction DIR>
146 RT_API_ATTRS void InternalDescriptorUnit<DIR>::BlankFill(
147     char *at, std::size_t bytes) {
148   switch (internalIoCharKind) {
149   case 2:
150     Fortran::runtime::fill_n(reinterpret_cast<char16_t *>(at), bytes / 2,
151         static_cast<char16_t>(' '));
152     break;
153   case 4:
154     Fortran::runtime::fill_n(reinterpret_cast<char32_t *>(at), bytes / 4,
155         static_cast<char32_t>(' '));
156     break;
157   default:
158     Fortran::runtime::fill_n(at, bytes, ' ');
159     break;
160   }
161 }
162 
163 template <Direction DIR>
164 RT_API_ATTRS void InternalDescriptorUnit<DIR>::BlankFillOutputRecord() {
165   if constexpr (DIR == Direction::Output) {
166     if (furthestPositionInRecord <
167         recordLength.value_or(furthestPositionInRecord)) {
168       BlankFill(CurrentRecord() + furthestPositionInRecord,
169           *recordLength - furthestPositionInRecord);
170     }
171   }
172 }
173 
174 template <Direction DIR>
175 RT_API_ATTRS void InternalDescriptorUnit<DIR>::BackspaceRecord(
176     IoErrorHandler &handler) {
177   RUNTIME_CHECK(handler, currentRecordNumber > 1);
178   --currentRecordNumber;
179   BeginRecord();
180 }
181 
182 template <Direction DIR>
183 RT_API_ATTRS std::int64_t InternalDescriptorUnit<DIR>::InquirePos() {
184   return (currentRecordNumber - 1) * recordLength.value_or(0) +
185       positionInRecord + 1;
186 }
187 
188 template class InternalDescriptorUnit<Direction::Output>;
189 template class InternalDescriptorUnit<Direction::Input>;
190 
191 RT_OFFLOAD_API_GROUP_END
192 } // namespace Fortran::runtime::io
193