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