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
15namespace Fortran::runtime::io {
16
17template <Direction DIR>
18InternalDescriptorUnit<DIR>::InternalDescriptorUnit(
19 Scalar scalar, std::size_t length, int kind) {
20 internalIoCharKind = kind;
21 recordLength = length;
22 endfileRecordNumber = 2;
23 void *pointer{reinterpret_cast<void *>(const_cast<char *>(scalar))};
24 descriptor().Establish(TypeCode{TypeCategory::Character, kind}, length * kind,
25 pointer, 0, nullptr, CFI_attribute_pointer);
26}
27
28template <Direction DIR>
29InternalDescriptorUnit<DIR>::InternalDescriptorUnit(
30 const Descriptor &that, const Terminator &terminator) {
31 auto thatType{that.type().GetCategoryAndKind()};
32 RUNTIME_CHECK(terminator, thatType.has_value());
33 RUNTIME_CHECK(terminator, thatType->first == TypeCategory::Character);
34 Descriptor &d{descriptor()};
35 RUNTIME_CHECK(
36 terminator, that.SizeInBytes() <= d.SizeInBytes(maxRank, true, 0));
37 new (&d) Descriptor{that};
38 d.Check();
39 internalIoCharKind = thatType->second;
40 recordLength = d.ElementBytes();
41 endfileRecordNumber = d.Elements() + 1;
42}
43
44template <Direction DIR> void InternalDescriptorUnit<DIR>::EndIoStatement() {
45 if constexpr (DIR == Direction::Output) {
46 // Clear the remainder of the current record.
47 auto end{endfileRecordNumber.value_or(0)};
48 if (currentRecordNumber < end) {
49 BlankFillOutputRecord();
50 }
51 }
52}
53
54template <Direction DIR>
55bool 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 BlankFill(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
88template <Direction DIR>
89std::size_t InternalDescriptorUnit<DIR>::GetNextInputBytes(
90 const char *&p, IoErrorHandler &handler) {
91 if constexpr (DIR == Direction::Output) {
92 handler.Crash("InternalDescriptorUnit<Direction::Output>::"
93 "GetNextInputBytes() called");
94 return 0;
95 } else {
96 const char *record{CurrentRecord()};
97 if (!record) {
98 handler.SignalEnd();
99 return 0;
100 } else if (positionInRecord >= recordLength.value_or(positionInRecord)) {
101 return 0;
102 } else {
103 p = &record[positionInRecord];
104 return *recordLength - positionInRecord;
105 }
106 }
107}
108
109template <Direction DIR>
110bool InternalDescriptorUnit<DIR>::AdvanceRecord(IoErrorHandler &handler) {
111 if (currentRecordNumber >= endfileRecordNumber.value_or(0)) {
112 handler.SignalEnd();
113 return false;
114 }
115 if constexpr (DIR == Direction::Output) {
116 BlankFillOutputRecord();
117 }
118 ++currentRecordNumber;
119 BeginRecord();
120 return true;
121}
122
123template <Direction DIR>
124void InternalDescriptorUnit<DIR>::BlankFill(char *at, std::size_t bytes) {
125 switch (internalIoCharKind) {
126 case 2:
127 std::fill_n(first: reinterpret_cast<char16_t *>(at), n: bytes / 2,
128 value: static_cast<char16_t>(' '));
129 break;
130 case 4:
131 std::fill_n(first: reinterpret_cast<char32_t *>(at), n: bytes / 4,
132 value: static_cast<char32_t>(' '));
133 break;
134 default:
135 std::fill_n(first: at, n: bytes, value: ' ');
136 break;
137 }
138}
139
140template <Direction DIR>
141void InternalDescriptorUnit<DIR>::BlankFillOutputRecord() {
142 if constexpr (DIR == Direction::Output) {
143 if (furthestPositionInRecord <
144 recordLength.value_or(furthestPositionInRecord)) {
145 BlankFill(at: CurrentRecord() + furthestPositionInRecord,
146 bytes: *recordLength - furthestPositionInRecord);
147 }
148 }
149}
150
151template <Direction DIR>
152void InternalDescriptorUnit<DIR>::BackspaceRecord(IoErrorHandler &handler) {
153 RUNTIME_CHECK(handler, currentRecordNumber > 1);
154 --currentRecordNumber;
155 BeginRecord();
156}
157
158template <Direction DIR>
159std::int64_t InternalDescriptorUnit<DIR>::InquirePos() {
160 return (currentRecordNumber - 1) * recordLength.value_or(0) +
161 positionInRecord + 1;
162}
163
164template class InternalDescriptorUnit<Direction::Output>;
165template class InternalDescriptorUnit<Direction::Input>;
166} // namespace Fortran::runtime::io
167

source code of flang/runtime/internal-unit.cpp