1 //===-- runtime/internal-unit.cpp -----------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "internal-unit.h"
11 #include "flang/Runtime/descriptor.h"
12 #include "flang/Runtime/freestanding-tools.h"
14 #include <type_traits>
16 namespace Fortran::runtime::io
{
17 RT_OFFLOAD_API_GROUP_BEGIN
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
);
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()};
38 terminator
, that
.SizeInBytes() <= d
.SizeInBytes(maxRank
, true, 0));
39 new (&d
) Descriptor
{that
};
41 internalIoCharKind
= thatType
->second
;
42 recordLength
= d
.ElementBytes();
43 endfileRecordNumber
= d
.Elements() + 1;
46 template <Direction
DIR>
47 RT_API_ATTRS
bool InternalDescriptorUnit
<DIR>::Emit(
48 const char *data
, std::size_t bytes
, IoErrorHandler
&handler
) {
49 if constexpr (DIR == Direction::Input
) {
50 handler
.Crash("InternalDescriptorUnit<Direction::Input>::Emit() called");
51 return false && data
[bytes
] != 0; // bogus compare silences GCC warning
56 char *record
{CurrentRecord()};
58 handler
.SignalError(IostatInternalWriteOverrun
);
61 auto furthestAfter
{std::max(furthestPositionInRecord
,
62 positionInRecord
+ static_cast<std::int64_t>(bytes
))};
64 if (furthestAfter
> static_cast<std::int64_t>(recordLength
.value_or(0))) {
65 handler
.SignalError(IostatRecordWriteOverrun
);
66 furthestAfter
= recordLength
.value_or(0);
67 bytes
= std::max(std::int64_t{0}, furthestAfter
- positionInRecord
);
69 } else if (positionInRecord
> furthestPositionInRecord
) {
70 BlankFill(record
+ furthestPositionInRecord
,
71 positionInRecord
- furthestPositionInRecord
);
73 std::memcpy(record
+ positionInRecord
, data
, bytes
);
74 positionInRecord
+= bytes
;
75 furthestPositionInRecord
= furthestAfter
;
80 template <Direction
DIR>
81 RT_API_ATTRS
std::size_t InternalDescriptorUnit
<DIR>::GetNextInputBytes(
82 const char *&p
, IoErrorHandler
&handler
) {
84 if constexpr (DIR == Direction::Output
) {
85 handler
.Crash("InternalDescriptorUnit<Direction::Output>::"
86 "GetNextInputBytes() called");
89 const char *record
{CurrentRecord()};
93 } else if (positionInRecord
>= recordLength
.value_or(positionInRecord
)) {
96 p
= &record
[positionInRecord
];
97 return *recordLength
- positionInRecord
;
102 template <Direction
DIR>
103 RT_API_ATTRS
std::size_t InternalDescriptorUnit
<DIR>::ViewBytesInRecord(
104 const char *&p
, bool forward
) const {
106 auto recl
{recordLength
.value_or(positionInRecord
)};
107 const char *record
{CurrentRecord()};
109 if (positionInRecord
< recl
) {
111 p
= &record
[positionInRecord
];
113 return recl
- positionInRecord
;
116 if (record
&& positionInRecord
<= recl
) {
117 p
= &record
[positionInRecord
];
119 return positionInRecord
- leftTabLimit
.value_or(0);
124 template <Direction
DIR>
125 RT_API_ATTRS
bool InternalDescriptorUnit
<DIR>::AdvanceRecord(
126 IoErrorHandler
&handler
) {
127 if (currentRecordNumber
>= endfileRecordNumber
.value_or(0)) {
128 if constexpr (DIR == Direction::Input
) {
131 handler
.SignalError(IostatInternalWriteOverrun
);
135 if constexpr (DIR == Direction::Output
) {
136 BlankFillOutputRecord();
138 ++currentRecordNumber
;
143 template <Direction
DIR>
144 RT_API_ATTRS
void InternalDescriptorUnit
<DIR>::BlankFill(
145 char *at
, std::size_t bytes
) {
146 switch (internalIoCharKind
) {
148 Fortran::runtime::fill_n(reinterpret_cast<char16_t
*>(at
), bytes
/ 2,
149 static_cast<char16_t
>(' '));
152 Fortran::runtime::fill_n(reinterpret_cast<char32_t
*>(at
), bytes
/ 4,
153 static_cast<char32_t
>(' '));
156 Fortran::runtime::fill_n(at
, bytes
, ' ');
161 template <Direction
DIR>
162 RT_API_ATTRS
void InternalDescriptorUnit
<DIR>::BlankFillOutputRecord() {
163 if constexpr (DIR == Direction::Output
) {
164 if (furthestPositionInRecord
<
165 recordLength
.value_or(furthestPositionInRecord
)) {
166 BlankFill(CurrentRecord() + furthestPositionInRecord
,
167 *recordLength
- furthestPositionInRecord
);
172 template <Direction
DIR>
173 RT_API_ATTRS
void InternalDescriptorUnit
<DIR>::BackspaceRecord(
174 IoErrorHandler
&handler
) {
175 RUNTIME_CHECK(handler
, currentRecordNumber
> 1);
176 --currentRecordNumber
;
180 template <Direction
DIR>
181 RT_API_ATTRS
std::int64_t InternalDescriptorUnit
<DIR>::InquirePos() {
182 return (currentRecordNumber
- 1) * recordLength
.value_or(0) +
183 positionInRecord
+ 1;
186 template class InternalDescriptorUnit
<Direction::Output
>;
187 template class InternalDescriptorUnit
<Direction::Input
>;
189 RT_OFFLOAD_API_GROUP_END
190 } // namespace Fortran::runtime::io