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