1 | //===-- runtime/io-stmt.h ---------------------------------------*- C++ -*-===// |
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 | // Representations of the state of an I/O statement in progress |
10 | |
11 | #ifndef FORTRAN_RUNTIME_IO_STMT_H_ |
12 | #define FORTRAN_RUNTIME_IO_STMT_H_ |
13 | |
14 | #include "connection.h" |
15 | #include "file.h" |
16 | #include "format.h" |
17 | #include "internal-unit.h" |
18 | #include "io-error.h" |
19 | #include "flang/Common/optional.h" |
20 | #include "flang/Common/reference-wrapper.h" |
21 | #include "flang/Common/visit.h" |
22 | #include "flang/Runtime/descriptor.h" |
23 | #include "flang/Runtime/io-api.h" |
24 | #include <flang/Common/variant.h> |
25 | #include <functional> |
26 | #include <type_traits> |
27 | |
28 | namespace Fortran::runtime::io { |
29 | |
30 | class ExternalFileUnit; |
31 | class ChildIo; |
32 | |
33 | class OpenStatementState; |
34 | class InquireUnitState; |
35 | class InquireNoUnitState; |
36 | class InquireUnconnectedFileState; |
37 | class InquireIOLengthState; |
38 | class ExternalMiscIoStatementState; |
39 | class CloseStatementState; |
40 | class NoopStatementState; // CLOSE or FLUSH on unknown unit |
41 | class ErroneousIoStatementState; |
42 | |
43 | template <Direction, typename CHAR = char> |
44 | class InternalFormattedIoStatementState; |
45 | template <Direction> class InternalListIoStatementState; |
46 | template <Direction, typename CHAR = char> |
47 | class ExternalFormattedIoStatementState; |
48 | template <Direction> class ExternalListIoStatementState; |
49 | template <Direction> class ExternalUnformattedIoStatementState; |
50 | template <Direction, typename CHAR = char> class ChildFormattedIoStatementState; |
51 | template <Direction> class ChildListIoStatementState; |
52 | template <Direction> class ChildUnformattedIoStatementState; |
53 | |
54 | struct InputStatementState {}; |
55 | struct OutputStatementState {}; |
56 | template <Direction D> |
57 | using IoDirectionState = std::conditional_t<D == Direction::Input, |
58 | InputStatementState, OutputStatementState>; |
59 | |
60 | // Common state for all kinds of formatted I/O |
61 | template <Direction D> class FormattedIoStatementState {}; |
62 | template <> class FormattedIoStatementState<Direction::Input> { |
63 | public: |
64 | RT_API_ATTRS std::size_t GetEditDescriptorChars() const; |
65 | RT_API_ATTRS void GotChar(int); |
66 | |
67 | private: |
68 | // Account of characters read for edit descriptors (i.e., formatted I/O |
69 | // with a FORMAT, not list-directed or NAMELIST), not including padding. |
70 | std::size_t chars_{0}; // for READ(SIZE=) |
71 | }; |
72 | |
73 | // The Cookie type in the I/O API is a pointer (for C) to this class. |
74 | class IoStatementState { |
75 | public: |
76 | template <typename A> explicit RT_API_ATTRS IoStatementState(A &x) : u_{x} {} |
77 | |
78 | // These member functions each project themselves into the active alternative. |
79 | // They're used by per-data-item routines in the I/O API (e.g., OutputReal64) |
80 | // to interact with the state of the I/O statement in progress. |
81 | // This design avoids virtual member functions and function pointers, |
82 | // which may not have good support in some runtime environments. |
83 | |
84 | // CompleteOperation() is the last opportunity to raise an I/O error. |
85 | // It is called by EndIoStatement(), but it can be invoked earlier to |
86 | // catch errors for (e.g.) GetIoMsg() and GetNewUnit(). If called |
87 | // more than once, it is a no-op. |
88 | RT_API_ATTRS void CompleteOperation(); |
89 | // Completes an I/O statement and reclaims storage. |
90 | RT_API_ATTRS int EndIoStatement(); |
91 | |
92 | RT_API_ATTRS bool Emit( |
93 | const char *, std::size_t bytes, std::size_t elementBytes = 0); |
94 | RT_API_ATTRS bool Receive(char *, std::size_t, std::size_t elementBytes = 0); |
95 | RT_API_ATTRS std::size_t GetNextInputBytes(const char *&); |
96 | RT_API_ATTRS bool AdvanceRecord(int = 1); |
97 | RT_API_ATTRS void BackspaceRecord(); |
98 | RT_API_ATTRS void HandleRelativePosition(std::int64_t byteOffset); |
99 | RT_API_ATTRS void HandleAbsolutePosition( |
100 | std::int64_t byteOffset); // for r* in list I/O |
101 | RT_API_ATTRS Fortran::common::optional<DataEdit> GetNextDataEdit( |
102 | int maxRepeat = 1); |
103 | RT_API_ATTRS ExternalFileUnit * |
104 | GetExternalFileUnit() const; // null if internal unit |
105 | RT_API_ATTRS bool BeginReadingRecord(); |
106 | RT_API_ATTRS void FinishReadingRecord(); |
107 | RT_API_ATTRS bool Inquire(InquiryKeywordHash, char *, std::size_t); |
108 | RT_API_ATTRS bool Inquire(InquiryKeywordHash, bool &); |
109 | RT_API_ATTRS bool Inquire( |
110 | InquiryKeywordHash, std::int64_t, bool &); // PENDING= |
111 | RT_API_ATTRS bool Inquire(InquiryKeywordHash, std::int64_t &); |
112 | RT_API_ATTRS std::int64_t InquirePos(); |
113 | RT_API_ATTRS void GotChar(signed int = 1); // for READ(SIZE=); can be <0 |
114 | |
115 | RT_API_ATTRS MutableModes &mutableModes(); |
116 | RT_API_ATTRS ConnectionState &GetConnectionState(); |
117 | RT_API_ATTRS IoErrorHandler &GetIoErrorHandler() const; |
118 | |
119 | // N.B.: this also works with base classes |
120 | template <typename A> RT_API_ATTRS A *get_if() const { |
121 | return common::visit( |
122 | [](auto &x) -> A * { |
123 | if constexpr (std::is_convertible_v<decltype(x.get()), A &>) { |
124 | return &x.get(); |
125 | } |
126 | return nullptr; |
127 | }, |
128 | u_); |
129 | } |
130 | |
131 | // Vacant after the end of the current record |
132 | RT_API_ATTRS Fortran::common::optional<char32_t> GetCurrentChar( |
133 | std::size_t &byteCount); |
134 | |
135 | // The "remaining" arguments to CueUpInput(), SkipSpaces(), & NextInField() |
136 | // are always in units of bytes, not characters; the distinction matters |
137 | // for internal input from CHARACTER(KIND=2 and 4). |
138 | |
139 | // For fixed-width fields, return the number of remaining bytes. |
140 | // Skip over leading blanks. |
141 | RT_API_ATTRS Fortran::common::optional<int> CueUpInput(const DataEdit &edit) { |
142 | Fortran::common::optional<int> remaining; |
143 | if (edit.IsListDirected()) { |
144 | std::size_t byteCount{0}; |
145 | GetNextNonBlank(byteCount); |
146 | } else { |
147 | if (edit.width.value_or(0) > 0) { |
148 | remaining = *edit.width; |
149 | if (int bytesPerChar{GetConnectionState().internalIoCharKind}; |
150 | bytesPerChar > 1) { |
151 | *remaining *= bytesPerChar; |
152 | } |
153 | } |
154 | SkipSpaces(remaining); |
155 | } |
156 | return remaining; |
157 | } |
158 | |
159 | RT_API_ATTRS Fortran::common::optional<char32_t> SkipSpaces( |
160 | Fortran::common::optional<int> &remaining) { |
161 | while (!remaining || *remaining > 0) { |
162 | std::size_t byteCount{0}; |
163 | if (auto ch{GetCurrentChar(byteCount)}) { |
164 | if (*ch != ' ' && *ch != '\t') { |
165 | return ch; |
166 | } |
167 | if (remaining) { |
168 | if (static_cast<std::size_t>(*remaining) < byteCount) { |
169 | break; |
170 | } |
171 | GotChar(byteCount); |
172 | *remaining -= byteCount; |
173 | } |
174 | HandleRelativePosition(byteCount); |
175 | } else { |
176 | break; |
177 | } |
178 | } |
179 | return Fortran::common::nullopt; |
180 | } |
181 | |
182 | // Acquires the next input character, respecting any applicable field width |
183 | // or separator character. |
184 | RT_API_ATTRS Fortran::common::optional<char32_t> NextInField( |
185 | Fortran::common::optional<int> &remaining, const DataEdit &); |
186 | |
187 | // Detect and signal any end-of-record condition after input. |
188 | // Returns true if at EOR and remaining input should be padded with blanks. |
189 | RT_API_ATTRS bool CheckForEndOfRecord(std::size_t afterReading); |
190 | |
191 | // Skips spaces, advances records, and ignores NAMELIST comments |
192 | RT_API_ATTRS Fortran::common::optional<char32_t> GetNextNonBlank( |
193 | std::size_t &byteCount) { |
194 | auto ch{GetCurrentChar(byteCount)}; |
195 | bool inNamelist{mutableModes().inNamelist}; |
196 | while (!ch || *ch == ' ' || *ch == '\t' || (inNamelist && *ch == '!')) { |
197 | if (ch && (*ch == ' ' || *ch == '\t')) { |
198 | HandleRelativePosition(byteCount); |
199 | } else if (!AdvanceRecord()) { |
200 | return Fortran::common::nullopt; |
201 | } |
202 | ch = GetCurrentChar(byteCount); |
203 | } |
204 | return ch; |
205 | } |
206 | |
207 | template <Direction D> |
208 | RT_API_ATTRS bool CheckFormattedStmtType(const char *name) { |
209 | if (get_if<FormattedIoStatementState<D>>()) { |
210 | return true; |
211 | } else { |
212 | auto &handler{GetIoErrorHandler()}; |
213 | if (!handler.InError()) { |
214 | handler.Crash("%s called for I/O statement that is not formatted %s" , |
215 | name, D == Direction::Output ? "output" : "input" ); |
216 | } |
217 | return false; |
218 | } |
219 | } |
220 | |
221 | private: |
222 | std::variant<Fortran::common::reference_wrapper<OpenStatementState>, |
223 | Fortran::common::reference_wrapper<CloseStatementState>, |
224 | Fortran::common::reference_wrapper<NoopStatementState>, |
225 | Fortran::common::reference_wrapper< |
226 | InternalFormattedIoStatementState<Direction::Output>>, |
227 | Fortran::common::reference_wrapper< |
228 | InternalFormattedIoStatementState<Direction::Input>>, |
229 | Fortran::common::reference_wrapper< |
230 | InternalListIoStatementState<Direction::Output>>, |
231 | Fortran::common::reference_wrapper< |
232 | InternalListIoStatementState<Direction::Input>>, |
233 | Fortran::common::reference_wrapper< |
234 | ExternalFormattedIoStatementState<Direction::Output>>, |
235 | Fortran::common::reference_wrapper< |
236 | ExternalFormattedIoStatementState<Direction::Input>>, |
237 | Fortran::common::reference_wrapper< |
238 | ExternalListIoStatementState<Direction::Output>>, |
239 | Fortran::common::reference_wrapper< |
240 | ExternalListIoStatementState<Direction::Input>>, |
241 | Fortran::common::reference_wrapper< |
242 | ExternalUnformattedIoStatementState<Direction::Output>>, |
243 | Fortran::common::reference_wrapper< |
244 | ExternalUnformattedIoStatementState<Direction::Input>>, |
245 | Fortran::common::reference_wrapper< |
246 | ChildFormattedIoStatementState<Direction::Output>>, |
247 | Fortran::common::reference_wrapper< |
248 | ChildFormattedIoStatementState<Direction::Input>>, |
249 | Fortran::common::reference_wrapper< |
250 | ChildListIoStatementState<Direction::Output>>, |
251 | Fortran::common::reference_wrapper< |
252 | ChildListIoStatementState<Direction::Input>>, |
253 | Fortran::common::reference_wrapper< |
254 | ChildUnformattedIoStatementState<Direction::Output>>, |
255 | Fortran::common::reference_wrapper< |
256 | ChildUnformattedIoStatementState<Direction::Input>>, |
257 | Fortran::common::reference_wrapper<InquireUnitState>, |
258 | Fortran::common::reference_wrapper<InquireNoUnitState>, |
259 | Fortran::common::reference_wrapper<InquireUnconnectedFileState>, |
260 | Fortran::common::reference_wrapper<InquireIOLengthState>, |
261 | Fortran::common::reference_wrapper<ExternalMiscIoStatementState>, |
262 | Fortran::common::reference_wrapper<ErroneousIoStatementState>> |
263 | u_; |
264 | }; |
265 | |
266 | // Base class for all per-I/O statement state classes. |
267 | class IoStatementBase : public IoErrorHandler { |
268 | public: |
269 | using IoErrorHandler::IoErrorHandler; |
270 | |
271 | RT_API_ATTRS bool completedOperation() const { return completedOperation_; } |
272 | |
273 | RT_API_ATTRS void CompleteOperation() { completedOperation_ = true; } |
274 | RT_API_ATTRS int EndIoStatement() { return GetIoStat(); } |
275 | |
276 | // These are default no-op backstops that can be overridden by descendants. |
277 | RT_API_ATTRS bool Emit( |
278 | const char *, std::size_t bytes, std::size_t elementBytes = 0); |
279 | RT_API_ATTRS bool Receive( |
280 | char *, std::size_t bytes, std::size_t elementBytes = 0); |
281 | RT_API_ATTRS std::size_t GetNextInputBytes(const char *&); |
282 | RT_API_ATTRS bool AdvanceRecord(int); |
283 | RT_API_ATTRS void BackspaceRecord(); |
284 | RT_API_ATTRS void HandleRelativePosition(std::int64_t); |
285 | RT_API_ATTRS void HandleAbsolutePosition(std::int64_t); |
286 | RT_API_ATTRS Fortran::common::optional<DataEdit> GetNextDataEdit( |
287 | IoStatementState &, int maxRepeat = 1); |
288 | RT_API_ATTRS ExternalFileUnit *GetExternalFileUnit() const; |
289 | RT_API_ATTRS bool BeginReadingRecord(); |
290 | RT_API_ATTRS void FinishReadingRecord(); |
291 | RT_API_ATTRS bool Inquire(InquiryKeywordHash, char *, std::size_t); |
292 | RT_API_ATTRS bool Inquire(InquiryKeywordHash, bool &); |
293 | RT_API_ATTRS bool Inquire(InquiryKeywordHash, std::int64_t, bool &); |
294 | RT_API_ATTRS bool Inquire(InquiryKeywordHash, std::int64_t &); |
295 | RT_API_ATTRS std::int64_t InquirePos(); |
296 | |
297 | RT_API_ATTRS void BadInquiryKeywordHashCrash(InquiryKeywordHash); |
298 | |
299 | RT_API_ATTRS void ReportUnsupportedChildIo() const { |
300 | Crash("not yet implemented: child IO" ); |
301 | } |
302 | |
303 | protected: |
304 | bool completedOperation_{false}; |
305 | }; |
306 | |
307 | // Common state for list-directed & NAMELIST I/O, both internal & external |
308 | template <Direction> class ListDirectedStatementState; |
309 | template <> |
310 | class ListDirectedStatementState<Direction::Output> |
311 | : public FormattedIoStatementState<Direction::Output> { |
312 | public: |
313 | RT_API_ATTRS bool EmitLeadingSpaceOrAdvance( |
314 | IoStatementState &, std::size_t = 1, bool isCharacter = false); |
315 | RT_API_ATTRS Fortran::common::optional<DataEdit> GetNextDataEdit( |
316 | IoStatementState &, int maxRepeat = 1); |
317 | RT_API_ATTRS bool lastWasUndelimitedCharacter() const { |
318 | return lastWasUndelimitedCharacter_; |
319 | } |
320 | RT_API_ATTRS void set_lastWasUndelimitedCharacter(bool yes = true) { |
321 | lastWasUndelimitedCharacter_ = yes; |
322 | } |
323 | |
324 | private: |
325 | bool lastWasUndelimitedCharacter_{false}; |
326 | }; |
327 | template <> |
328 | class ListDirectedStatementState<Direction::Input> |
329 | : public FormattedIoStatementState<Direction::Input> { |
330 | public: |
331 | RT_API_ATTRS bool inNamelistSequence() const { return inNamelistSequence_; } |
332 | RT_API_ATTRS int EndIoStatement(); |
333 | |
334 | // Skips value separators, handles repetition and null values. |
335 | // Vacant when '/' appears; present with descriptor == ListDirectedNullValue |
336 | // when a null value appears. |
337 | RT_API_ATTRS Fortran::common::optional<DataEdit> GetNextDataEdit( |
338 | IoStatementState &, int maxRepeat = 1); |
339 | |
340 | // Each NAMELIST input item is treated like a distinct list-directed |
341 | // input statement. This member function resets some state so that |
342 | // repetition and null values work correctly for each successive |
343 | // NAMELIST input item. |
344 | RT_API_ATTRS void ResetForNextNamelistItem(bool inNamelistSequence) { |
345 | remaining_ = 0; |
346 | if (repeatPosition_) { |
347 | repeatPosition_->Cancel(); |
348 | } |
349 | eatComma_ = false; |
350 | realPart_ = imaginaryPart_ = false; |
351 | inNamelistSequence_ = inNamelistSequence; |
352 | } |
353 | |
354 | private: |
355 | int remaining_{0}; // for "r*" repetition |
356 | Fortran::common::optional<SavedPosition> repeatPosition_; |
357 | bool eatComma_{false}; // consume comma after previously read item |
358 | bool hitSlash_{false}; // once '/' is seen, nullify further items |
359 | bool realPart_{false}; |
360 | bool imaginaryPart_{false}; |
361 | bool inNamelistSequence_{false}; |
362 | }; |
363 | |
364 | template <Direction DIR> |
365 | class InternalIoStatementState : public IoStatementBase, |
366 | public IoDirectionState<DIR> { |
367 | public: |
368 | using Buffer = |
369 | std::conditional_t<DIR == Direction::Input, const char *, char *>; |
370 | RT_API_ATTRS InternalIoStatementState(Buffer, std::size_t, |
371 | const char *sourceFile = nullptr, int sourceLine = 0); |
372 | RT_API_ATTRS InternalIoStatementState( |
373 | const Descriptor &, const char *sourceFile = nullptr, int sourceLine = 0); |
374 | RT_API_ATTRS int EndIoStatement(); |
375 | |
376 | RT_API_ATTRS bool Emit( |
377 | const char *data, std::size_t bytes, std::size_t elementBytes = 0); |
378 | RT_API_ATTRS std::size_t GetNextInputBytes(const char *&); |
379 | RT_API_ATTRS bool AdvanceRecord(int = 1); |
380 | RT_API_ATTRS void BackspaceRecord(); |
381 | RT_API_ATTRS ConnectionState &GetConnectionState() { return unit_; } |
382 | RT_API_ATTRS MutableModes &mutableModes() { return unit_.modes; } |
383 | RT_API_ATTRS void HandleRelativePosition(std::int64_t); |
384 | RT_API_ATTRS void HandleAbsolutePosition(std::int64_t); |
385 | RT_API_ATTRS std::int64_t InquirePos(); |
386 | |
387 | protected: |
388 | bool free_{true}; |
389 | InternalDescriptorUnit<DIR> unit_; |
390 | }; |
391 | |
392 | template <Direction DIR, typename CHAR> |
393 | class InternalFormattedIoStatementState |
394 | : public InternalIoStatementState<DIR>, |
395 | public FormattedIoStatementState<DIR> { |
396 | public: |
397 | using CharType = CHAR; |
398 | using typename InternalIoStatementState<DIR>::Buffer; |
399 | RT_API_ATTRS InternalFormattedIoStatementState(Buffer internal, |
400 | std::size_t internalLength, const CharType *format, |
401 | std::size_t formatLength, const Descriptor *formatDescriptor = nullptr, |
402 | const char *sourceFile = nullptr, int sourceLine = 0); |
403 | RT_API_ATTRS InternalFormattedIoStatementState(const Descriptor &, |
404 | const CharType *format, std::size_t formatLength, |
405 | const Descriptor *formatDescriptor = nullptr, |
406 | const char *sourceFile = nullptr, int sourceLine = 0); |
407 | RT_API_ATTRS IoStatementState &ioStatementState() { |
408 | return ioStatementState_; |
409 | } |
410 | RT_API_ATTRS void CompleteOperation(); |
411 | RT_API_ATTRS int EndIoStatement(); |
412 | RT_API_ATTRS Fortran::common::optional<DataEdit> GetNextDataEdit( |
413 | IoStatementState &, int maxRepeat = 1) { |
414 | return format_.GetNextDataEdit(*this, maxRepeat); |
415 | } |
416 | |
417 | private: |
418 | IoStatementState ioStatementState_; // points to *this |
419 | using InternalIoStatementState<DIR>::unit_; |
420 | // format_ *must* be last; it may be partial someday |
421 | FormatControl<InternalFormattedIoStatementState> format_; |
422 | }; |
423 | |
424 | template <Direction DIR> |
425 | class InternalListIoStatementState : public InternalIoStatementState<DIR>, |
426 | public ListDirectedStatementState<DIR> { |
427 | public: |
428 | using typename InternalIoStatementState<DIR>::Buffer; |
429 | RT_API_ATTRS InternalListIoStatementState(Buffer internal, |
430 | std::size_t internalLength, const char *sourceFile = nullptr, |
431 | int sourceLine = 0); |
432 | RT_API_ATTRS InternalListIoStatementState( |
433 | const Descriptor &, const char *sourceFile = nullptr, int sourceLine = 0); |
434 | RT_API_ATTRS IoStatementState &ioStatementState() { |
435 | return ioStatementState_; |
436 | } |
437 | using ListDirectedStatementState<DIR>::GetNextDataEdit; |
438 | RT_API_ATTRS void CompleteOperation(); |
439 | RT_API_ATTRS int EndIoStatement(); |
440 | |
441 | private: |
442 | IoStatementState ioStatementState_; // points to *this |
443 | using InternalIoStatementState<DIR>::unit_; |
444 | }; |
445 | |
446 | class ExternalIoStatementBase : public IoStatementBase { |
447 | public: |
448 | RT_API_ATTRS ExternalIoStatementBase( |
449 | ExternalFileUnit &, const char *sourceFile = nullptr, int sourceLine = 0); |
450 | RT_API_ATTRS ExternalFileUnit &unit() { return unit_; } |
451 | RT_API_ATTRS MutableModes &mutableModes(); |
452 | RT_API_ATTRS ConnectionState &GetConnectionState(); |
453 | RT_API_ATTRS int asynchronousID() const { return asynchronousID_; } |
454 | RT_API_ATTRS int EndIoStatement(); |
455 | RT_API_ATTRS ExternalFileUnit *GetExternalFileUnit() const { return &unit_; } |
456 | RT_API_ATTRS void SetAsynchronous(); |
457 | RT_API_ATTRS std::int64_t InquirePos(); |
458 | |
459 | private: |
460 | ExternalFileUnit &unit_; |
461 | int asynchronousID_{-1}; |
462 | }; |
463 | |
464 | template <Direction DIR> |
465 | class ExternalIoStatementState : public ExternalIoStatementBase, |
466 | public IoDirectionState<DIR> { |
467 | public: |
468 | RT_API_ATTRS ExternalIoStatementState( |
469 | ExternalFileUnit &, const char *sourceFile = nullptr, int sourceLine = 0); |
470 | RT_API_ATTRS MutableModes &mutableModes() { return mutableModes_; } |
471 | RT_API_ATTRS void CompleteOperation(); |
472 | RT_API_ATTRS int EndIoStatement(); |
473 | RT_API_ATTRS bool Emit( |
474 | const char *, std::size_t bytes, std::size_t elementBytes = 0); |
475 | RT_API_ATTRS std::size_t GetNextInputBytes(const char *&); |
476 | RT_API_ATTRS bool AdvanceRecord(int = 1); |
477 | RT_API_ATTRS void BackspaceRecord(); |
478 | RT_API_ATTRS void HandleRelativePosition(std::int64_t); |
479 | RT_API_ATTRS void HandleAbsolutePosition(std::int64_t); |
480 | RT_API_ATTRS bool BeginReadingRecord(); |
481 | RT_API_ATTRS void FinishReadingRecord(); |
482 | |
483 | private: |
484 | // These are forked from ConnectionState's modes at the beginning |
485 | // of each formatted I/O statement so they may be overridden by control |
486 | // edit descriptors during the statement. |
487 | MutableModes mutableModes_; |
488 | }; |
489 | |
490 | template <Direction DIR, typename CHAR> |
491 | class ExternalFormattedIoStatementState |
492 | : public ExternalIoStatementState<DIR>, |
493 | public FormattedIoStatementState<DIR> { |
494 | public: |
495 | using CharType = CHAR; |
496 | RT_API_ATTRS ExternalFormattedIoStatementState(ExternalFileUnit &, |
497 | const CharType *format, std::size_t formatLength, |
498 | const Descriptor *formatDescriptor = nullptr, |
499 | const char *sourceFile = nullptr, int sourceLine = 0); |
500 | RT_API_ATTRS void CompleteOperation(); |
501 | RT_API_ATTRS int EndIoStatement(); |
502 | RT_API_ATTRS Fortran::common::optional<DataEdit> GetNextDataEdit( |
503 | IoStatementState &, int maxRepeat = 1) { |
504 | return format_.GetNextDataEdit(*this, maxRepeat); |
505 | } |
506 | |
507 | private: |
508 | FormatControl<ExternalFormattedIoStatementState> format_; |
509 | }; |
510 | |
511 | template <Direction DIR> |
512 | class ExternalListIoStatementState : public ExternalIoStatementState<DIR>, |
513 | public ListDirectedStatementState<DIR> { |
514 | public: |
515 | using ExternalIoStatementState<DIR>::ExternalIoStatementState; |
516 | using ListDirectedStatementState<DIR>::GetNextDataEdit; |
517 | RT_API_ATTRS int EndIoStatement(); |
518 | }; |
519 | |
520 | template <Direction DIR> |
521 | class ExternalUnformattedIoStatementState |
522 | : public ExternalIoStatementState<DIR> { |
523 | public: |
524 | using ExternalIoStatementState<DIR>::ExternalIoStatementState; |
525 | RT_API_ATTRS bool Receive(char *, std::size_t, std::size_t elementBytes = 0); |
526 | }; |
527 | |
528 | template <Direction DIR> |
529 | class ChildIoStatementState : public IoStatementBase, |
530 | public IoDirectionState<DIR> { |
531 | public: |
532 | RT_API_ATTRS ChildIoStatementState( |
533 | ChildIo &, const char *sourceFile = nullptr, int sourceLine = 0); |
534 | RT_API_ATTRS ChildIo &child() { return child_; } |
535 | RT_API_ATTRS MutableModes &mutableModes(); |
536 | RT_API_ATTRS ConnectionState &GetConnectionState(); |
537 | RT_API_ATTRS ExternalFileUnit *GetExternalFileUnit() const; |
538 | RT_API_ATTRS int EndIoStatement(); |
539 | RT_API_ATTRS bool Emit( |
540 | const char *, std::size_t bytes, std::size_t elementBytes = 0); |
541 | RT_API_ATTRS std::size_t GetNextInputBytes(const char *&); |
542 | RT_API_ATTRS void HandleRelativePosition(std::int64_t); |
543 | RT_API_ATTRS void HandleAbsolutePosition(std::int64_t); |
544 | |
545 | private: |
546 | ChildIo &child_; |
547 | }; |
548 | |
549 | template <Direction DIR, typename CHAR> |
550 | class ChildFormattedIoStatementState : public ChildIoStatementState<DIR>, |
551 | public FormattedIoStatementState<DIR> { |
552 | public: |
553 | using CharType = CHAR; |
554 | RT_API_ATTRS ChildFormattedIoStatementState(ChildIo &, const CharType *format, |
555 | std::size_t formatLength, const Descriptor *formatDescriptor = nullptr, |
556 | const char *sourceFile = nullptr, int sourceLine = 0); |
557 | RT_API_ATTRS MutableModes &mutableModes() { return mutableModes_; } |
558 | RT_API_ATTRS void CompleteOperation(); |
559 | RT_API_ATTRS int EndIoStatement(); |
560 | RT_API_ATTRS bool AdvanceRecord(int = 1); |
561 | RT_API_ATTRS Fortran::common::optional<DataEdit> GetNextDataEdit( |
562 | IoStatementState &, int maxRepeat = 1) { |
563 | return format_.GetNextDataEdit(*this, maxRepeat); |
564 | } |
565 | |
566 | private: |
567 | MutableModes mutableModes_; |
568 | FormatControl<ChildFormattedIoStatementState> format_; |
569 | }; |
570 | |
571 | template <Direction DIR> |
572 | class ChildListIoStatementState : public ChildIoStatementState<DIR>, |
573 | public ListDirectedStatementState<DIR> { |
574 | public: |
575 | using ChildIoStatementState<DIR>::ChildIoStatementState; |
576 | using ListDirectedStatementState<DIR>::GetNextDataEdit; |
577 | RT_API_ATTRS int EndIoStatement(); |
578 | }; |
579 | |
580 | template <Direction DIR> |
581 | class ChildUnformattedIoStatementState : public ChildIoStatementState<DIR> { |
582 | public: |
583 | using ChildIoStatementState<DIR>::ChildIoStatementState; |
584 | RT_API_ATTRS bool Receive(char *, std::size_t, std::size_t elementBytes = 0); |
585 | }; |
586 | |
587 | // OPEN |
588 | class OpenStatementState : public ExternalIoStatementBase { |
589 | public: |
590 | RT_API_ATTRS OpenStatementState(ExternalFileUnit &unit, bool wasExtant, |
591 | bool isNewUnit, const char *sourceFile = nullptr, int sourceLine = 0) |
592 | : ExternalIoStatementBase{unit, sourceFile, sourceLine}, |
593 | wasExtant_{wasExtant}, isNewUnit_{isNewUnit} {} |
594 | RT_API_ATTRS bool wasExtant() const { return wasExtant_; } |
595 | RT_API_ATTRS void set_status(OpenStatus status) { |
596 | status_ = status; |
597 | } // STATUS= |
598 | RT_API_ATTRS void set_path(const char *, std::size_t); // FILE= |
599 | RT_API_ATTRS void set_position(Position position) { |
600 | position_ = position; |
601 | } // POSITION= |
602 | RT_API_ATTRS void set_action(Action action) { action_ = action; } // ACTION= |
603 | RT_API_ATTRS void set_convert(Convert convert) { |
604 | convert_ = convert; |
605 | } // CONVERT= |
606 | RT_API_ATTRS void set_access(Access access) { access_ = access; } // ACCESS= |
607 | RT_API_ATTRS void set_isUnformatted(bool yes = true) { |
608 | isUnformatted_ = yes; |
609 | } // FORM= |
610 | |
611 | RT_API_ATTRS void CompleteOperation(); |
612 | RT_API_ATTRS int EndIoStatement(); |
613 | |
614 | private: |
615 | bool wasExtant_; |
616 | bool isNewUnit_; |
617 | Fortran::common::optional<OpenStatus> status_; |
618 | Fortran::common::optional<Position> position_; |
619 | Fortran::common::optional<Action> action_; |
620 | Convert convert_{Convert::Unknown}; |
621 | OwningPtr<char> path_; |
622 | std::size_t pathLength_; |
623 | Fortran::common::optional<bool> isUnformatted_; |
624 | Fortran::common::optional<Access> access_; |
625 | }; |
626 | |
627 | class CloseStatementState : public ExternalIoStatementBase { |
628 | public: |
629 | RT_API_ATTRS CloseStatementState(ExternalFileUnit &unit, |
630 | const char *sourceFile = nullptr, int sourceLine = 0) |
631 | : ExternalIoStatementBase{unit, sourceFile, sourceLine} {} |
632 | RT_API_ATTRS void set_status(CloseStatus status) { status_ = status; } |
633 | RT_API_ATTRS int EndIoStatement(); |
634 | |
635 | private: |
636 | CloseStatus status_{CloseStatus::Keep}; |
637 | }; |
638 | |
639 | // For CLOSE(bad unit), WAIT(bad unit, ID=nonzero), INQUIRE(unconnected unit), |
640 | // and recoverable BACKSPACE(bad unit) |
641 | class NoUnitIoStatementState : public IoStatementBase { |
642 | public: |
643 | RT_API_ATTRS IoStatementState &ioStatementState() { |
644 | return ioStatementState_; |
645 | } |
646 | RT_API_ATTRS MutableModes &mutableModes() { return connection_.modes; } |
647 | RT_API_ATTRS ConnectionState &GetConnectionState() { return connection_; } |
648 | RT_API_ATTRS int badUnitNumber() const { return badUnitNumber_; } |
649 | RT_API_ATTRS void CompleteOperation(); |
650 | RT_API_ATTRS int EndIoStatement(); |
651 | |
652 | protected: |
653 | template <typename A> |
654 | RT_API_ATTRS NoUnitIoStatementState(A &stmt, const char *sourceFile = nullptr, |
655 | int sourceLine = 0, int badUnitNumber = -1) |
656 | : IoStatementBase{sourceFile, sourceLine}, ioStatementState_{stmt}, |
657 | badUnitNumber_{badUnitNumber} {} |
658 | |
659 | private: |
660 | IoStatementState ioStatementState_; // points to *this |
661 | ConnectionState connection_; |
662 | int badUnitNumber_; |
663 | }; |
664 | |
665 | class NoopStatementState : public NoUnitIoStatementState { |
666 | public: |
667 | RT_API_ATTRS NoopStatementState( |
668 | const char *sourceFile = nullptr, int sourceLine = 0, int unitNumber = -1) |
669 | : NoUnitIoStatementState{*this, sourceFile, sourceLine, unitNumber} {} |
670 | RT_API_ATTRS void set_status(CloseStatus) {} // discards |
671 | }; |
672 | |
673 | extern template class InternalIoStatementState<Direction::Output>; |
674 | extern template class InternalIoStatementState<Direction::Input>; |
675 | extern template class InternalFormattedIoStatementState<Direction::Output>; |
676 | extern template class InternalFormattedIoStatementState<Direction::Input>; |
677 | extern template class InternalListIoStatementState<Direction::Output>; |
678 | extern template class InternalListIoStatementState<Direction::Input>; |
679 | extern template class ExternalIoStatementState<Direction::Output>; |
680 | extern template class ExternalIoStatementState<Direction::Input>; |
681 | extern template class ExternalFormattedIoStatementState<Direction::Output>; |
682 | extern template class ExternalFormattedIoStatementState<Direction::Input>; |
683 | extern template class ExternalListIoStatementState<Direction::Output>; |
684 | extern template class ExternalListIoStatementState<Direction::Input>; |
685 | extern template class ExternalUnformattedIoStatementState<Direction::Output>; |
686 | extern template class ExternalUnformattedIoStatementState<Direction::Input>; |
687 | extern template class ChildIoStatementState<Direction::Output>; |
688 | extern template class ChildIoStatementState<Direction::Input>; |
689 | extern template class ChildFormattedIoStatementState<Direction::Output>; |
690 | extern template class ChildFormattedIoStatementState<Direction::Input>; |
691 | extern template class ChildListIoStatementState<Direction::Output>; |
692 | extern template class ChildListIoStatementState<Direction::Input>; |
693 | extern template class ChildUnformattedIoStatementState<Direction::Output>; |
694 | extern template class ChildUnformattedIoStatementState<Direction::Input>; |
695 | |
696 | extern template class FormatControl< |
697 | InternalFormattedIoStatementState<Direction::Output>>; |
698 | extern template class FormatControl< |
699 | InternalFormattedIoStatementState<Direction::Input>>; |
700 | extern template class FormatControl< |
701 | ExternalFormattedIoStatementState<Direction::Output>>; |
702 | extern template class FormatControl< |
703 | ExternalFormattedIoStatementState<Direction::Input>>; |
704 | extern template class FormatControl< |
705 | ChildFormattedIoStatementState<Direction::Output>>; |
706 | extern template class FormatControl< |
707 | ChildFormattedIoStatementState<Direction::Input>>; |
708 | |
709 | class InquireUnitState : public ExternalIoStatementBase { |
710 | public: |
711 | RT_API_ATTRS InquireUnitState(ExternalFileUnit &unit, |
712 | const char *sourceFile = nullptr, int sourceLine = 0); |
713 | RT_API_ATTRS bool Inquire(InquiryKeywordHash, char *, std::size_t); |
714 | RT_API_ATTRS bool Inquire(InquiryKeywordHash, bool &); |
715 | RT_API_ATTRS bool Inquire(InquiryKeywordHash, std::int64_t, bool &); |
716 | RT_API_ATTRS bool Inquire(InquiryKeywordHash, std::int64_t &); |
717 | }; |
718 | |
719 | class InquireNoUnitState : public NoUnitIoStatementState { |
720 | public: |
721 | RT_API_ATTRS InquireNoUnitState(const char *sourceFile = nullptr, |
722 | int sourceLine = 0, int badUnitNumber = -1); |
723 | RT_API_ATTRS bool Inquire(InquiryKeywordHash, char *, std::size_t); |
724 | RT_API_ATTRS bool Inquire(InquiryKeywordHash, bool &); |
725 | RT_API_ATTRS bool Inquire(InquiryKeywordHash, std::int64_t, bool &); |
726 | RT_API_ATTRS bool Inquire(InquiryKeywordHash, std::int64_t &); |
727 | }; |
728 | |
729 | class InquireUnconnectedFileState : public NoUnitIoStatementState { |
730 | public: |
731 | RT_API_ATTRS InquireUnconnectedFileState(OwningPtr<char> &&path, |
732 | const char *sourceFile = nullptr, int sourceLine = 0); |
733 | RT_API_ATTRS bool Inquire(InquiryKeywordHash, char *, std::size_t); |
734 | RT_API_ATTRS bool Inquire(InquiryKeywordHash, bool &); |
735 | RT_API_ATTRS bool Inquire(InquiryKeywordHash, std::int64_t, bool &); |
736 | RT_API_ATTRS bool Inquire(InquiryKeywordHash, std::int64_t &); |
737 | |
738 | private: |
739 | OwningPtr<char> path_; // trimmed and NUL terminated |
740 | }; |
741 | |
742 | class InquireIOLengthState : public NoUnitIoStatementState, |
743 | public OutputStatementState { |
744 | public: |
745 | RT_API_ATTRS InquireIOLengthState( |
746 | const char *sourceFile = nullptr, int sourceLine = 0); |
747 | RT_API_ATTRS std::size_t bytes() const { return bytes_; } |
748 | RT_API_ATTRS bool Emit( |
749 | const char *, std::size_t bytes, std::size_t elementBytes = 0); |
750 | |
751 | private: |
752 | std::size_t bytes_{0}; |
753 | }; |
754 | |
755 | class ExternalMiscIoStatementState : public ExternalIoStatementBase { |
756 | public: |
757 | enum Which { Flush, Backspace, Endfile, Rewind, Wait }; |
758 | RT_API_ATTRS ExternalMiscIoStatementState(ExternalFileUnit &unit, Which which, |
759 | const char *sourceFile = nullptr, int sourceLine = 0) |
760 | : ExternalIoStatementBase{unit, sourceFile, sourceLine}, which_{which} {} |
761 | RT_API_ATTRS void CompleteOperation(); |
762 | RT_API_ATTRS int EndIoStatement(); |
763 | |
764 | private: |
765 | Which which_; |
766 | }; |
767 | |
768 | class ErroneousIoStatementState : public IoStatementBase { |
769 | public: |
770 | explicit RT_API_ATTRS ErroneousIoStatementState(Iostat iostat, |
771 | ExternalFileUnit *unit = nullptr, const char *sourceFile = nullptr, |
772 | int sourceLine = 0) |
773 | : IoStatementBase{sourceFile, sourceLine}, unit_{unit} { |
774 | SetPendingError(iostat); |
775 | } |
776 | RT_API_ATTRS int EndIoStatement(); |
777 | RT_API_ATTRS ConnectionState &GetConnectionState() { return connection_; } |
778 | RT_API_ATTRS MutableModes &mutableModes() { return connection_.modes; } |
779 | |
780 | private: |
781 | ConnectionState connection_; |
782 | ExternalFileUnit *unit_{nullptr}; |
783 | }; |
784 | |
785 | } // namespace Fortran::runtime::io |
786 | #endif // FORTRAN_RUNTIME_IO_STMT_H_ |
787 | |