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"
13 #include <type_traits>
15 namespace Fortran::runtime::io
{
17 template <Direction
DIR>
18 InternalDescriptorUnit
<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
);
28 template <Direction
DIR>
29 InternalDescriptorUnit
<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()};
36 terminator
, that
.SizeInBytes() <= d
.SizeInBytes(maxRank
, true, 0));
37 new (&d
) Descriptor
{that
};
39 internalIoCharKind
= thatType
->second
;
40 recordLength
= d
.ElementBytes();
41 endfileRecordNumber
= d
.Elements() + 1;
44 template <Direction
DIR> void InternalDescriptorUnit
<DIR>::EndIoStatement() {
45 if constexpr (DIR == Direction::Output
) {
46 // Clear the remainder of the current record if anything was written
47 // to it, or if it is the only record.
48 auto end
{endfileRecordNumber
.value_or(0)};
49 if (currentRecordNumber
< end
&&
50 (end
== 2 || furthestPositionInRecord
> 0)) {
51 BlankFillOutputRecord();
56 template <Direction
DIR>
57 bool InternalDescriptorUnit
<DIR>::Emit(
58 const char *data
, std::size_t bytes
, IoErrorHandler
&handler
) {
59 if constexpr (DIR == Direction::Input
) {
60 handler
.Crash("InternalDescriptorUnit<Direction::Input>::Emit() called");
61 return false && data
[bytes
] != 0; // bogus compare silences GCC warning
66 char *record
{CurrentRecord()};
68 handler
.SignalError(IostatInternalWriteOverrun
);
71 auto furthestAfter
{std::max(furthestPositionInRecord
,
72 positionInRecord
+ static_cast<std::int64_t>(bytes
))};
74 if (furthestAfter
> static_cast<std::int64_t>(recordLength
.value_or(0))) {
75 handler
.SignalError(IostatRecordWriteOverrun
);
76 furthestAfter
= recordLength
.value_or(0);
77 bytes
= std::max(std::int64_t{0}, furthestAfter
- positionInRecord
);
79 } else if (positionInRecord
> furthestPositionInRecord
) {
80 BlankFill(record
+ furthestPositionInRecord
,
81 positionInRecord
- furthestPositionInRecord
);
83 std::memcpy(record
+ positionInRecord
, data
, bytes
);
84 positionInRecord
+= bytes
;
85 furthestPositionInRecord
= furthestAfter
;
90 template <Direction
DIR>
91 std::size_t InternalDescriptorUnit
<DIR>::GetNextInputBytes(
92 const char *&p
, IoErrorHandler
&handler
) {
93 if constexpr (DIR == Direction::Output
) {
94 handler
.Crash("InternalDescriptorUnit<Direction::Output>::"
95 "GetNextInputBytes() called");
98 const char *record
{CurrentRecord()};
102 } else if (positionInRecord
>= recordLength
.value_or(positionInRecord
)) {
105 p
= &record
[positionInRecord
];
106 return *recordLength
- positionInRecord
;
111 template <Direction
DIR>
112 bool InternalDescriptorUnit
<DIR>::AdvanceRecord(IoErrorHandler
&handler
) {
113 if (currentRecordNumber
>= endfileRecordNumber
.value_or(0)) {
117 if constexpr (DIR == Direction::Output
) {
118 BlankFillOutputRecord();
120 ++currentRecordNumber
;
125 template <Direction
DIR>
126 void InternalDescriptorUnit
<DIR>::BlankFill(char *at
, std::size_t bytes
) {
127 switch (internalIoCharKind
) {
129 std::fill_n(reinterpret_cast<char16_t
*>(at
), bytes
/ 2,
130 static_cast<char16_t
>(' '));
133 std::fill_n(reinterpret_cast<char32_t
*>(at
), bytes
/ 4,
134 static_cast<char32_t
>(' '));
137 std::fill_n(at
, bytes
, ' ');
142 template <Direction
DIR>
143 void InternalDescriptorUnit
<DIR>::BlankFillOutputRecord() {
144 if constexpr (DIR == Direction::Output
) {
145 if (furthestPositionInRecord
<
146 recordLength
.value_or(furthestPositionInRecord
)) {
147 BlankFill(CurrentRecord() + furthestPositionInRecord
,
148 *recordLength
- furthestPositionInRecord
);
153 template <Direction
DIR>
154 void InternalDescriptorUnit
<DIR>::BackspaceRecord(IoErrorHandler
&handler
) {
155 RUNTIME_CHECK(handler
, currentRecordNumber
> 1);
156 --currentRecordNumber
;
160 template <Direction
DIR>
161 std::int64_t InternalDescriptorUnit
<DIR>::InquirePos() {
162 return (currentRecordNumber
- 1) * recordLength
.value_or(0) +
163 positionInRecord
+ 1;
166 template class InternalDescriptorUnit
<Direction::Output
>;
167 template class InternalDescriptorUnit
<Direction::Input
>;
168 } // namespace Fortran::runtime::io