1 //===-- runtime/unit.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 // Fortran external I/O units
10 
11 #ifndef FORTRAN_RUNTIME_IO_UNIT_H_
12 #define FORTRAN_RUNTIME_IO_UNIT_H_
13 
14 #include "buffer.h"
15 #include "connection.h"
16 #include "file.h"
17 #include "format.h"
18 #include "io-error.h"
19 #include "io-stmt.h"
20 #include "lock.h"
21 #include "memory.h"
22 #include "terminator.h"
23 #include <cstdlib>
24 #include <cstring>
25 #include <optional>
26 #include <variant>
27 
28 namespace Fortran::runtime::io {
29 
30 class UnitMap;
31 
32 class ExternalFileUnit : public ConnectionState,
33                          public OpenFile,
34                          public FileFrame<ExternalFileUnit> {
35 public:
ExternalFileUnit(int unitNumber)36   explicit ExternalFileUnit(int unitNumber) : unitNumber_{unitNumber} {}
unitNumber()37   int unitNumber() const { return unitNumber_; }
swapEndianness()38   bool swapEndianness() const { return swapEndianness_; }
39 
40   static ExternalFileUnit *LookUp(int unit);
41   static ExternalFileUnit &LookUpOrCrash(int unit, const Terminator &);
42   static ExternalFileUnit &LookUpOrCreate(
43       int unit, const Terminator &, bool &wasExtant);
44   static ExternalFileUnit &LookUpOrCreateAnonymous(
45       int unit, Direction, bool isUnformatted, const Terminator &);
46   static ExternalFileUnit *LookUp(const char *path);
47   static ExternalFileUnit &CreateNew(int unit, const Terminator &);
48   static ExternalFileUnit *LookUpForClose(int unit);
49   static int NewUnit(const Terminator &);
50   static void CloseAll(IoErrorHandler &);
51   static void FlushAll(IoErrorHandler &);
52 
53   void OpenUnit(OpenStatus, std::optional<Action>, Position,
54       OwningPtr<char> &&path, std::size_t pathLength, Convert,
55       IoErrorHandler &);
56   void OpenAnonymousUnit(
57       OpenStatus, std::optional<Action>, Position, Convert, IoErrorHandler &);
58   void CloseUnit(CloseStatus, IoErrorHandler &);
59   void DestroyClosed();
60 
61   bool SetDirection(Direction, IoErrorHandler &);
62 
63   template <typename A, typename... X>
BeginIoStatement(X &&...xs)64   IoStatementState &BeginIoStatement(X &&...xs) {
65     // TODO: Child data transfer statements vs. locking
66     lock_.Take(); // dropped in EndIoStatement()
67     A &state{u_.emplace<A>(std::forward<X>(xs)...)};
68     if constexpr (!std::is_same_v<A, OpenStatementState>) {
69       state.mutableModes() = ConnectionState::modes;
70     }
71     io_.emplace(state);
72     return *io_;
73   }
74 
75   bool Emit(
76       const char *, std::size_t, std::size_t elementBytes, IoErrorHandler &);
77   bool Receive(char *, std::size_t, std::size_t elementBytes, IoErrorHandler &);
78   std::optional<char32_t> GetCurrentChar(IoErrorHandler &);
79   void SetLeftTabLimit();
80   void BeginReadingRecord(IoErrorHandler &);
81   void FinishReadingRecord(IoErrorHandler &);
82   bool AdvanceRecord(IoErrorHandler &);
83   void BackspaceRecord(IoErrorHandler &);
84   void FlushIfTerminal(IoErrorHandler &);
85   void Endfile(IoErrorHandler &);
86   void Rewind(IoErrorHandler &);
87   void EndIoStatement();
SetPosition(std::int64_t pos)88   void SetPosition(std::int64_t pos) {
89     frameOffsetInFile_ = pos;
90     recordOffsetInFrame_ = 0;
91     BeginRecord();
92   }
93 
94 private:
95   static UnitMap &GetUnitMap();
96   const char *FrameNextInput(IoErrorHandler &, std::size_t);
97   void BeginSequentialVariableUnformattedInputRecord(IoErrorHandler &);
98   void BeginSequentialVariableFormattedInputRecord(IoErrorHandler &);
99   void BackspaceFixedRecord(IoErrorHandler &);
100   void BackspaceVariableUnformattedRecord(IoErrorHandler &);
101   void BackspaceVariableFormattedRecord(IoErrorHandler &);
102   bool SetSequentialVariableFormattedRecordLength();
103   void DoImpliedEndfile(IoErrorHandler &);
104   void DoEndfile(IoErrorHandler &);
105 
106   int unitNumber_{-1};
107   Direction direction_{Direction::Output};
108   bool impliedEndfile_{false}; // seq. output has taken place
109   bool beganReadingRecord_{false};
110 
111   Lock lock_;
112 
113   // When an I/O statement is in progress on this unit, holds its state.
114   std::variant<std::monostate, OpenStatementState, CloseStatementState,
115       ExternalFormattedIoStatementState<Direction::Output>,
116       ExternalFormattedIoStatementState<Direction::Input>,
117       ExternalListIoStatementState<Direction::Output>,
118       ExternalListIoStatementState<Direction::Input>,
119       UnformattedIoStatementState<Direction::Output>,
120       UnformattedIoStatementState<Direction::Input>, InquireUnitState,
121       ExternalMiscIoStatementState>
122       u_;
123 
124   // Points to the active alternative (if any) in u_ for use as a Cookie
125   std::optional<IoStatementState> io_;
126 
127   // Subtle: The beginning of the frame can't be allowed to advance
128   // during a single list-directed READ due to the possibility of a
129   // multi-record CHARACTER value with a "r*" repeat count.  So we
130   // manage the frame and the current record therein separately.
131   std::int64_t frameOffsetInFile_{0};
132   std::size_t recordOffsetInFrame_{0}; // of currentRecordNumber
133 
134   bool swapEndianness_{false};
135 };
136 
137 } // namespace Fortran::runtime::io
138 #endif // FORTRAN_RUNTIME_IO_UNIT_H_
139