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 "descriptor.h"
16 #include "file.h"
17 #include "format.h"
18 #include "internal-unit.h"
19 #include "io-error.h"
20 #include <functional>
21 #include <type_traits>
22 #include <variant>
23 
24 namespace Fortran::runtime::io {
25 
26 class ExternalFileUnit;
27 
28 class OpenStatementState;
29 class CloseStatementState;
30 class NoopCloseStatementState;
31 
32 template <Direction, typename CHAR = char>
33 class InternalFormattedIoStatementState;
34 template <Direction, typename CHAR = char> class InternalListIoStatementState;
35 template <Direction, typename CHAR = char>
36 class ExternalFormattedIoStatementState;
37 template <Direction> class ExternalListIoStatementState;
38 template <Direction> class UnformattedIoStatementState;
39 class ExternalMiscIoStatementState;
40 
41 // The Cookie type in the I/O API is a pointer (for C) to this class.
42 class IoStatementState {
43 public:
IoStatementState(A & x)44   template <typename A> explicit IoStatementState(A &x) : u_{x} {}
45 
46   // These member functions each project themselves into the active alternative.
47   // They're used by per-data-item routines in the I/O API (e.g., OutputReal64)
48   // to interact with the state of the I/O statement in progress.
49   // This design avoids virtual member functions and function pointers,
50   // which may not have good support in some runtime environments.
51   std::optional<DataEdit> GetNextDataEdit(int = 1);
52   bool Emit(const char *, std::size_t);
53   std::optional<char32_t> GetCurrentChar(); // vacant after end of record
54   bool AdvanceRecord(int = 1);
55   void BackspaceRecord();
56   void HandleRelativePosition(std::int64_t);
57   int EndIoStatement();
58   ConnectionState &GetConnectionState();
59   IoErrorHandler &GetIoErrorHandler() const;
60   ExternalFileUnit *GetExternalFileUnit() const; // null if internal unit
61   MutableModes &mutableModes();
62 
63   // N.B.: this also works with base classes
get_if()64   template <typename A> A *get_if() const {
65     return std::visit(
66         [](auto &x) -> A * {
67           if constexpr (std::is_convertible_v<decltype(x.get()), A &>) {
68             return &x.get();
69           }
70           return nullptr;
71         },
72         u_);
73   }
74 
75   bool EmitRepeated(char, std::size_t);
76   bool EmitField(const char *, std::size_t length, std::size_t width);
77 
78   std::optional<char32_t> SkipSpaces(std::optional<int> &remaining);
79   std::optional<char32_t> NextInField(std::optional<int> &remaining);
80   std::optional<char32_t> GetNextNonBlank(); // can advance record
81 
82 private:
83   std::variant<std::reference_wrapper<OpenStatementState>,
84       std::reference_wrapper<CloseStatementState>,
85       std::reference_wrapper<NoopCloseStatementState>,
86       std::reference_wrapper<
87           InternalFormattedIoStatementState<Direction::Output>>,
88       std::reference_wrapper<
89           InternalFormattedIoStatementState<Direction::Input>>,
90       std::reference_wrapper<InternalListIoStatementState<Direction::Output>>,
91       std::reference_wrapper<InternalListIoStatementState<Direction::Input>>,
92       std::reference_wrapper<
93           ExternalFormattedIoStatementState<Direction::Output>>,
94       std::reference_wrapper<
95           ExternalFormattedIoStatementState<Direction::Input>>,
96       std::reference_wrapper<ExternalListIoStatementState<Direction::Output>>,
97       std::reference_wrapper<ExternalListIoStatementState<Direction::Input>>,
98       std::reference_wrapper<UnformattedIoStatementState<Direction::Output>>,
99       std::reference_wrapper<UnformattedIoStatementState<Direction::Input>>,
100       std::reference_wrapper<ExternalMiscIoStatementState>>
101       u_;
102 };
103 
104 // Base class for all per-I/O statement state classes.
105 // Inherits IoErrorHandler from its base.
106 struct IoStatementBase : public DefaultFormatControlCallbacks {
107   using DefaultFormatControlCallbacks::DefaultFormatControlCallbacks;
108   int EndIoStatement();
109   std::optional<DataEdit> GetNextDataEdit(IoStatementState &, int = 1);
GetExternalFileUnitIoStatementBase110   ExternalFileUnit *GetExternalFileUnit() const { return nullptr; }
111 };
112 
113 struct InputStatementState {};
114 struct OutputStatementState {};
115 template <Direction D>
116 using IoDirectionState = std::conditional_t<D == Direction::Input,
117     InputStatementState, OutputStatementState>;
118 
119 struct FormattedStatementState {};
120 
121 // Common state for list-directed internal & external I/O
122 template <Direction> struct ListDirectedStatementState {};
123 template <> struct ListDirectedStatementState<Direction::Output> {
124   static std::size_t RemainingSpaceInRecord(const ConnectionState &);
125   bool NeedAdvance(const ConnectionState &, std::size_t) const;
126   bool EmitLeadingSpaceOrAdvance(
127       IoStatementState &, std::size_t, bool isCharacter = false);
128   std::optional<DataEdit> GetNextDataEdit(
129       IoStatementState &, int maxRepeat = 1);
130   bool lastWasUndelimitedCharacter{false};
131 };
132 template <> class ListDirectedStatementState<Direction::Input> {
133 public:
134   // Skips value separators, handles repetition and null values.
135   // Vacant when '/' appears; present with descriptor == ListDirectedNullValue
136   // when a null value appears.
137   std::optional<DataEdit> GetNextDataEdit(
138       IoStatementState &, int maxRepeat = 1);
139 
140 private:
141   int remaining_{0}; // for "r*" repetition
142   std::int64_t initialRecordNumber_;
143   std::int64_t initialPositionInRecord_;
144   bool isFirstItem_{true}; // leading separator implies null first item
145   bool hitSlash_{false}; // once '/' is seen, nullify further items
146   bool realPart_{false};
147   bool imaginaryPart_{false};
148 };
149 
150 template <Direction DIR, typename CHAR = char>
151 class InternalIoStatementState : public IoStatementBase,
152                                  public IoDirectionState<DIR> {
153 public:
154   using CharType = CHAR;
155   using Buffer =
156       std::conditional_t<DIR == Direction::Input, const CharType *, CharType *>;
157   InternalIoStatementState(Buffer, std::size_t,
158       const char *sourceFile = nullptr, int sourceLine = 0);
159   InternalIoStatementState(
160       const Descriptor &, const char *sourceFile = nullptr, int sourceLine = 0);
161   int EndIoStatement();
162   bool Emit(const CharType *, std::size_t chars /* not bytes */);
163   std::optional<char32_t> GetCurrentChar();
164   bool AdvanceRecord(int = 1);
165   void BackspaceRecord();
166   ConnectionState &GetConnectionState() { return unit_; }
167   MutableModes &mutableModes() { return unit_.modes; }
168   void HandleRelativePosition(std::int64_t);
169   void HandleAbsolutePosition(std::int64_t);
170 
171 protected:
172   bool free_{true};
173   InternalDescriptorUnit<DIR> unit_;
174 };
175 
176 template <Direction DIR, typename CHAR>
177 class InternalFormattedIoStatementState
178     : public InternalIoStatementState<DIR, CHAR>,
179       public FormattedStatementState {
180 public:
181   using CharType = CHAR;
182   using typename InternalIoStatementState<DIR, CharType>::Buffer;
183   InternalFormattedIoStatementState(Buffer internal, std::size_t internalLength,
184       const CharType *format, std::size_t formatLength,
185       const char *sourceFile = nullptr, int sourceLine = 0);
186   InternalFormattedIoStatementState(const Descriptor &, const CharType *format,
187       std::size_t formatLength, const char *sourceFile = nullptr,
188       int sourceLine = 0);
189   IoStatementState &ioStatementState() { return ioStatementState_; }
190   int EndIoStatement();
191   std::optional<DataEdit> GetNextDataEdit(
192       IoStatementState &, int maxRepeat = 1) {
193     return format_.GetNextDataEdit(*this, maxRepeat);
194   }
195 
196 private:
197   IoStatementState ioStatementState_; // points to *this
198   using InternalIoStatementState<DIR, CharType>::unit_;
199   // format_ *must* be last; it may be partial someday
200   FormatControl<InternalFormattedIoStatementState> format_;
201 };
202 
203 template <Direction DIR, typename CHAR>
204 class InternalListIoStatementState : public InternalIoStatementState<DIR, CHAR>,
205                                      public ListDirectedStatementState<DIR> {
206 public:
207   using CharType = CHAR;
208   using typename InternalIoStatementState<DIR, CharType>::Buffer;
209   InternalListIoStatementState(Buffer internal, std::size_t internalLength,
210       const char *sourceFile = nullptr, int sourceLine = 0);
211   InternalListIoStatementState(
212       const Descriptor &, const char *sourceFile = nullptr, int sourceLine = 0);
213   IoStatementState &ioStatementState() { return ioStatementState_; }
214   using ListDirectedStatementState<DIR>::GetNextDataEdit;
215 
216 private:
217   IoStatementState ioStatementState_; // points to *this
218   using InternalIoStatementState<DIR, CharType>::unit_;
219 };
220 
221 class ExternalIoStatementBase : public IoStatementBase {
222 public:
223   ExternalIoStatementBase(
224       ExternalFileUnit &, const char *sourceFile = nullptr, int sourceLine = 0);
225   ExternalFileUnit &unit() { return unit_; }
226   MutableModes &mutableModes();
227   ConnectionState &GetConnectionState();
228   int EndIoStatement();
229   ExternalFileUnit *GetExternalFileUnit() { return &unit_; }
230 
231 private:
232   ExternalFileUnit &unit_;
233 };
234 
235 template <Direction DIR>
236 class ExternalIoStatementState : public ExternalIoStatementBase,
237                                  public IoDirectionState<DIR> {
238 public:
239   using ExternalIoStatementBase::ExternalIoStatementBase;
240   int EndIoStatement();
241   bool Emit(const char *, std::size_t);
242   bool Emit(const char16_t *, std::size_t chars /* not bytes */);
243   bool Emit(const char32_t *, std::size_t chars /* not bytes */);
244   std::optional<char32_t> GetCurrentChar();
245   bool AdvanceRecord(int = 1);
246   void BackspaceRecord();
247   void HandleRelativePosition(std::int64_t);
248   void HandleAbsolutePosition(std::int64_t);
249 };
250 
251 template <Direction DIR, typename CHAR>
252 class ExternalFormattedIoStatementState : public ExternalIoStatementState<DIR>,
253                                           public FormattedStatementState {
254 public:
255   using CharType = CHAR;
256   ExternalFormattedIoStatementState(ExternalFileUnit &, const CharType *format,
257       std::size_t formatLength, const char *sourceFile = nullptr,
258       int sourceLine = 0);
259   MutableModes &mutableModes() { return mutableModes_; }
260   int EndIoStatement();
261   std::optional<DataEdit> GetNextDataEdit(
262       IoStatementState &, int maxRepeat = 1) {
263     return format_.GetNextDataEdit(*this, maxRepeat);
264   }
265 
266 private:
267   // These are forked from ConnectionState's modes at the beginning
268   // of each formatted I/O statement so they may be overridden by control
269   // edit descriptors during the statement.
270   MutableModes mutableModes_;
271   FormatControl<ExternalFormattedIoStatementState> format_;
272 };
273 
274 template <Direction DIR>
275 class ExternalListIoStatementState : public ExternalIoStatementState<DIR>,
276                                      public ListDirectedStatementState<DIR> {
277 public:
278   using ExternalIoStatementState<DIR>::ExternalIoStatementState;
279   using ListDirectedStatementState<DIR>::GetNextDataEdit;
280 };
281 
282 template <Direction DIR>
283 class UnformattedIoStatementState : public ExternalIoStatementState<DIR> {
284 public:
285   using ExternalIoStatementState<DIR>::ExternalIoStatementState;
286   bool Receive(char *, std::size_t);
287   int EndIoStatement();
288 };
289 
290 class OpenStatementState : public ExternalIoStatementBase {
291 public:
292   OpenStatementState(ExternalFileUnit &unit, bool wasExtant,
293       const char *sourceFile = nullptr, int sourceLine = 0)
294       : ExternalIoStatementBase{unit, sourceFile, sourceLine}, wasExtant_{
295                                                                    wasExtant} {}
296   bool wasExtant() const { return wasExtant_; }
297   void set_status(OpenStatus status) { status_ = status; }
298   void set_path(const char *, std::size_t, int kind); // FILE=
299   void set_position(Position position) { position_ = position; } // POSITION=
300   int EndIoStatement();
301 
302 private:
303   bool wasExtant_;
304   OpenStatus status_{OpenStatus::Unknown};
305   Position position_{Position::AsIs};
306   OwningPtr<char> path_;
307   std::size_t pathLength_;
308 };
309 
310 class CloseStatementState : public ExternalIoStatementBase {
311 public:
312   CloseStatementState(ExternalFileUnit &unit, const char *sourceFile = nullptr,
313       int sourceLine = 0)
314       : ExternalIoStatementBase{unit, sourceFile, sourceLine} {}
315   void set_status(CloseStatus status) { status_ = status; }
316   int EndIoStatement();
317 
318 private:
319   CloseStatus status_{CloseStatus::Keep};
320 };
321 
322 class NoopCloseStatementState : public IoStatementBase {
323 public:
324   NoopCloseStatementState(const char *sourceFile, int sourceLine)
325       : IoStatementBase{sourceFile, sourceLine}, ioStatementState_{*this} {}
326   IoStatementState &ioStatementState() { return ioStatementState_; }
327   void set_status(CloseStatus) {} // discards
328   MutableModes &mutableModes() { return connection_.modes; }
329   ConnectionState &GetConnectionState() { return connection_; }
330   int EndIoStatement();
331 
332 private:
333   IoStatementState ioStatementState_; // points to *this
334   ConnectionState connection_;
335 };
336 
337 extern template class InternalIoStatementState<Direction::Output>;
338 extern template class InternalIoStatementState<Direction::Input>;
339 extern template class InternalFormattedIoStatementState<Direction::Output>;
340 extern template class InternalFormattedIoStatementState<Direction::Input>;
341 extern template class InternalListIoStatementState<Direction::Output>;
342 extern template class InternalListIoStatementState<Direction::Input>;
343 extern template class ExternalIoStatementState<Direction::Output>;
344 extern template class ExternalIoStatementState<Direction::Input>;
345 extern template class ExternalFormattedIoStatementState<Direction::Output>;
346 extern template class ExternalFormattedIoStatementState<Direction::Input>;
347 extern template class ExternalListIoStatementState<Direction::Output>;
348 extern template class ExternalListIoStatementState<Direction::Input>;
349 extern template class UnformattedIoStatementState<Direction::Output>;
350 extern template class UnformattedIoStatementState<Direction::Input>;
351 extern template class FormatControl<
352     InternalFormattedIoStatementState<Direction::Output>>;
353 extern template class FormatControl<
354     InternalFormattedIoStatementState<Direction::Input>>;
355 extern template class FormatControl<
356     ExternalFormattedIoStatementState<Direction::Output>>;
357 extern template class FormatControl<
358     ExternalFormattedIoStatementState<Direction::Input>>;
359 
360 class ExternalMiscIoStatementState : public ExternalIoStatementBase {
361 public:
362   enum Which { Flush, Backspace, Endfile, Rewind };
363   ExternalMiscIoStatementState(ExternalFileUnit &unit, Which which,
364       const char *sourceFile = nullptr, int sourceLine = 0)
365       : ExternalIoStatementBase{unit, sourceFile, sourceLine}, which_{which} {}
366   int EndIoStatement();
367 
368 private:
369   Which which_;
370 };
371 
372 } // namespace Fortran::runtime::io
373 #endif // FORTRAN_RUNTIME_IO_STMT_H_
374