llvm-project

Форк
0
795 строк · 26.9 Кб
1
//===-- runtime/unit.cpp --------------------------------------------------===//
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
// Implementation of ExternalFileUnit common for both
10
// RT_USE_PSEUDO_FILE_UNIT=0 and RT_USE_PSEUDO_FILE_UNIT=1.
11
//
12
//===----------------------------------------------------------------------===//
13
#include "unit.h"
14
#include "io-error.h"
15
#include "lock.h"
16
#include "tools.h"
17
#include <limits>
18
#include <utility>
19

20
namespace Fortran::runtime::io {
21

22
#ifndef FLANG_RUNTIME_NO_GLOBAL_VAR_DEFS
23
RT_OFFLOAD_VAR_GROUP_BEGIN
24
RT_VAR_ATTRS ExternalFileUnit *defaultInput{nullptr}; // unit 5
25
RT_VAR_ATTRS ExternalFileUnit *defaultOutput{nullptr}; // unit 6
26
RT_VAR_ATTRS ExternalFileUnit *errorOutput{nullptr}; // unit 0 extension
27
RT_OFFLOAD_VAR_GROUP_END
28
#endif // FLANG_RUNTIME_NO_GLOBAL_VAR_DEFS
29

30
RT_OFFLOAD_API_GROUP_BEGIN
31

32
static inline RT_API_ATTRS void SwapEndianness(
33
    char *data, std::size_t bytes, std::size_t elementBytes) {
34
  if (elementBytes > 1) {
35
    auto half{elementBytes >> 1};
36
    for (std::size_t j{0}; j + elementBytes <= bytes; j += elementBytes) {
37
      for (std::size_t k{0}; k < half; ++k) {
38
        RT_DIAG_PUSH
39
        RT_DIAG_DISABLE_CALL_HOST_FROM_DEVICE_WARN
40
        std::swap(data[j + k], data[j + elementBytes - 1 - k]);
41
        RT_DIAG_POP
42
      }
43
    }
44
  }
45
}
46

47
bool ExternalFileUnit::Emit(const char *data, std::size_t bytes,
48
    std::size_t elementBytes, IoErrorHandler &handler) {
49
  auto furthestAfter{std::max(furthestPositionInRecord,
50
      positionInRecord + static_cast<std::int64_t>(bytes))};
51
  if (openRecl) {
52
    // Check for fixed-length record overrun, but allow for
53
    // sequential record termination.
54
    int extra{0};
55
    int header{0};
56
    if (access == Access::Sequential) {
57
      if (isUnformatted.value_or(false)) {
58
        // record header + footer
59
        header = static_cast<int>(sizeof(std::uint32_t));
60
        extra = 2 * header;
61
      } else {
62
#ifdef _WIN32
63
        if (!isWindowsTextFile()) {
64
          ++extra; // carriage return (CR)
65
        }
66
#endif
67
        ++extra; // newline (LF)
68
      }
69
    }
70
    if (furthestAfter > extra + *openRecl) {
71
      handler.SignalError(IostatRecordWriteOverrun,
72
          "Attempt to write %zd bytes to position %jd in a fixed-size record "
73
          "of %jd bytes",
74
          bytes, static_cast<std::intmax_t>(positionInRecord - header),
75
          static_cast<std::intmax_t>(*openRecl));
76
      return false;
77
    }
78
  }
79
  if (recordLength) {
80
    // It is possible for recordLength to have a value now for a
81
    // variable-length output record if the previous operation
82
    // was a BACKSPACE or non advancing input statement.
83
    recordLength.reset();
84
    beganReadingRecord_ = false;
85
  }
86
  if (IsAfterEndfile()) {
87
    handler.SignalError(IostatWriteAfterEndfile);
88
    return false;
89
  }
90
  CheckDirectAccess(handler);
91
  WriteFrame(frameOffsetInFile_, recordOffsetInFrame_ + furthestAfter, handler);
92
  if (positionInRecord > furthestPositionInRecord) {
93
    std::memset(Frame() + recordOffsetInFrame_ + furthestPositionInRecord, ' ',
94
        positionInRecord - furthestPositionInRecord);
95
  }
96
  char *to{Frame() + recordOffsetInFrame_ + positionInRecord};
97
  std::memcpy(to, data, bytes);
98
  if (swapEndianness_) {
99
    SwapEndianness(to, bytes, elementBytes);
100
  }
101
  positionInRecord += bytes;
102
  furthestPositionInRecord = furthestAfter;
103
  anyWriteSinceLastPositioning_ = true;
104
  return true;
105
}
106

107
bool ExternalFileUnit::Receive(char *data, std::size_t bytes,
108
    std::size_t elementBytes, IoErrorHandler &handler) {
109
  RUNTIME_CHECK(handler, direction_ == Direction::Input);
110
  auto furthestAfter{std::max(furthestPositionInRecord,
111
      positionInRecord + static_cast<std::int64_t>(bytes))};
112
  if (furthestAfter > recordLength.value_or(furthestAfter)) {
113
    handler.SignalError(IostatRecordReadOverrun,
114
        "Attempt to read %zd bytes at position %jd in a record of %jd bytes",
115
        bytes, static_cast<std::intmax_t>(positionInRecord),
116
        static_cast<std::intmax_t>(*recordLength));
117
    return false;
118
  }
119
  auto need{recordOffsetInFrame_ + furthestAfter};
120
  auto got{ReadFrame(frameOffsetInFile_, need, handler)};
121
  if (got >= need) {
122
    std::memcpy(data, Frame() + recordOffsetInFrame_ + positionInRecord, bytes);
123
    if (swapEndianness_) {
124
      SwapEndianness(data, bytes, elementBytes);
125
    }
126
    positionInRecord += bytes;
127
    furthestPositionInRecord = furthestAfter;
128
    return true;
129
  } else {
130
    HitEndOnRead(handler);
131
    return false;
132
  }
133
}
134

135
std::size_t ExternalFileUnit::GetNextInputBytes(
136
    const char *&p, IoErrorHandler &handler) {
137
  RUNTIME_CHECK(handler, direction_ == Direction::Input);
138
  std::size_t length{1};
139
  if (auto recl{EffectiveRecordLength()}) {
140
    if (positionInRecord < *recl) {
141
      length = *recl - positionInRecord;
142
    } else {
143
      p = nullptr;
144
      return 0;
145
    }
146
  }
147
  p = FrameNextInput(handler, length);
148
  return p ? length : 0;
149
}
150

151
const char *ExternalFileUnit::FrameNextInput(
152
    IoErrorHandler &handler, std::size_t bytes) {
153
  RUNTIME_CHECK(handler, isUnformatted.has_value() && !*isUnformatted);
154
  if (static_cast<std::int64_t>(positionInRecord + bytes) <=
155
      recordLength.value_or(positionInRecord + bytes)) {
156
    auto at{recordOffsetInFrame_ + positionInRecord};
157
    auto need{static_cast<std::size_t>(at + bytes)};
158
    auto got{ReadFrame(frameOffsetInFile_, need, handler)};
159
    SetVariableFormattedRecordLength();
160
    if (got >= need) {
161
      return Frame() + at;
162
    }
163
    HitEndOnRead(handler);
164
  }
165
  return nullptr;
166
}
167

168
bool ExternalFileUnit::SetVariableFormattedRecordLength() {
169
  if (recordLength || access == Access::Direct) {
170
    return true;
171
  } else if (FrameLength() > recordOffsetInFrame_) {
172
    const char *record{Frame() + recordOffsetInFrame_};
173
    std::size_t bytes{FrameLength() - recordOffsetInFrame_};
174
    if (const char *nl{FindCharacter(record, '\n', bytes)}) {
175
      recordLength = nl - record;
176
      if (*recordLength > 0 && record[*recordLength - 1] == '\r') {
177
        --*recordLength;
178
      }
179
      return true;
180
    }
181
  }
182
  return false;
183
}
184

185
bool ExternalFileUnit::BeginReadingRecord(IoErrorHandler &handler) {
186
  RUNTIME_CHECK(handler, direction_ == Direction::Input);
187
  if (!beganReadingRecord_) {
188
    beganReadingRecord_ = true;
189
    // Don't use IsAtEOF() to check for an EOF condition here, just detect
190
    // it from a failed or short read from the file.  IsAtEOF() could be
191
    // wrong for formatted input if actual newline characters had been
192
    // written in-band by previous WRITEs before a REWIND.  In fact,
193
    // now that we know that the unit is being used for input (again),
194
    // it's best to reset endfileRecordNumber and ensure IsAtEOF() will
195
    // now be true on return only if it gets set by HitEndOnRead().
196
    endfileRecordNumber.reset();
197
    if (access == Access::Direct) {
198
      CheckDirectAccess(handler);
199
      auto need{static_cast<std::size_t>(recordOffsetInFrame_ + *openRecl)};
200
      auto got{ReadFrame(frameOffsetInFile_, need, handler)};
201
      if (got >= need) {
202
        recordLength = openRecl;
203
      } else {
204
        recordLength.reset();
205
        HitEndOnRead(handler);
206
      }
207
    } else {
208
      if (anyWriteSinceLastPositioning_ && access == Access::Sequential) {
209
        // Most Fortran implementations allow a READ after a WRITE;
210
        // the read then just hits an EOF.
211
        DoEndfile<false, Direction::Input>(handler);
212
      }
213
      recordLength.reset();
214
      RUNTIME_CHECK(handler, isUnformatted.has_value());
215
      if (*isUnformatted) {
216
        if (access == Access::Sequential) {
217
          BeginSequentialVariableUnformattedInputRecord(handler);
218
        }
219
      } else { // formatted sequential or stream
220
        BeginVariableFormattedInputRecord(handler);
221
      }
222
    }
223
  }
224
  RUNTIME_CHECK(handler,
225
      recordLength.has_value() || !IsRecordFile() || handler.InError());
226
  return !handler.InError();
227
}
228

229
void ExternalFileUnit::FinishReadingRecord(IoErrorHandler &handler) {
230
  RUNTIME_CHECK(handler, direction_ == Direction::Input && beganReadingRecord_);
231
  beganReadingRecord_ = false;
232
  if (handler.GetIoStat() == IostatEnd ||
233
      (IsRecordFile() && !recordLength.has_value())) {
234
    // Avoid bogus crashes in END/ERR circumstances; but
235
    // still increment the current record number so that
236
    // an attempted read of an endfile record, followed by
237
    // a BACKSPACE, will still be at EOF.
238
    ++currentRecordNumber;
239
  } else if (IsRecordFile()) {
240
    recordOffsetInFrame_ += *recordLength;
241
    if (access != Access::Direct) {
242
      RUNTIME_CHECK(handler, isUnformatted.has_value());
243
      recordLength.reset();
244
      if (isUnformatted.value_or(false)) {
245
        // Retain footer in frame for more efficient BACKSPACE
246
        frameOffsetInFile_ += recordOffsetInFrame_;
247
        recordOffsetInFrame_ = sizeof(std::uint32_t);
248
      } else { // formatted
249
        if (FrameLength() > recordOffsetInFrame_ &&
250
            Frame()[recordOffsetInFrame_] == '\r') {
251
          ++recordOffsetInFrame_;
252
        }
253
        if (FrameLength() > recordOffsetInFrame_ &&
254
            Frame()[recordOffsetInFrame_] == '\n') {
255
          ++recordOffsetInFrame_;
256
        }
257
        if (!pinnedFrame || mayPosition()) {
258
          frameOffsetInFile_ += recordOffsetInFrame_;
259
          recordOffsetInFrame_ = 0;
260
        }
261
      }
262
    }
263
    ++currentRecordNumber;
264
  } else { // unformatted stream
265
    furthestPositionInRecord =
266
        std::max(furthestPositionInRecord, positionInRecord);
267
    frameOffsetInFile_ += recordOffsetInFrame_ + furthestPositionInRecord;
268
    recordOffsetInFrame_ = 0;
269
  }
270
  BeginRecord();
271
}
272

273
bool ExternalFileUnit::AdvanceRecord(IoErrorHandler &handler) {
274
  if (direction_ == Direction::Input) {
275
    FinishReadingRecord(handler);
276
    return BeginReadingRecord(handler);
277
  } else { // Direction::Output
278
    bool ok{true};
279
    RUNTIME_CHECK(handler, isUnformatted.has_value());
280
    positionInRecord = furthestPositionInRecord;
281
    if (access == Access::Direct) {
282
      if (furthestPositionInRecord <
283
          openRecl.value_or(furthestPositionInRecord)) {
284
        // Pad remainder of fixed length record
285
        WriteFrame(
286
            frameOffsetInFile_, recordOffsetInFrame_ + *openRecl, handler);
287
        std::memset(Frame() + recordOffsetInFrame_ + furthestPositionInRecord,
288
            isUnformatted.value_or(false) ? 0 : ' ',
289
            *openRecl - furthestPositionInRecord);
290
        furthestPositionInRecord = *openRecl;
291
      }
292
    } else if (*isUnformatted) {
293
      if (access == Access::Sequential) {
294
        // Append the length of a sequential unformatted variable-length record
295
        // as its footer, then overwrite the reserved first four bytes of the
296
        // record with its length as its header.  These four bytes were skipped
297
        // over in BeginUnformattedIO<Output>().
298
        // TODO: Break very large records up into subrecords with negative
299
        // headers &/or footers
300
        std::uint32_t length;
301
        length = furthestPositionInRecord - sizeof length;
302
        ok = ok &&
303
            Emit(reinterpret_cast<const char *>(&length), sizeof length,
304
                sizeof length, handler);
305
        positionInRecord = 0;
306
        ok = ok &&
307
            Emit(reinterpret_cast<const char *>(&length), sizeof length,
308
                sizeof length, handler);
309
      } else {
310
        // Unformatted stream: nothing to do
311
      }
312
    } else if (handler.GetIoStat() != IostatOk &&
313
        furthestPositionInRecord == 0) {
314
      // Error in formatted variable length record, and no output yet; do
315
      // nothing, like most other Fortran compilers do.
316
      return true;
317
    } else {
318
      // Terminate formatted variable length record
319
      const char *lineEnding{"\n"};
320
      std::size_t lineEndingBytes{1};
321
#ifdef _WIN32
322
      if (!isWindowsTextFile()) {
323
        lineEnding = "\r\n";
324
        lineEndingBytes = 2;
325
      }
326
#endif
327
      ok = ok && Emit(lineEnding, lineEndingBytes, 1, handler);
328
    }
329
    leftTabLimit.reset();
330
    if (IsAfterEndfile()) {
331
      return false;
332
    }
333
    CommitWrites();
334
    ++currentRecordNumber;
335
    if (access != Access::Direct) {
336
      impliedEndfile_ = IsRecordFile();
337
      if (IsAtEOF()) {
338
        endfileRecordNumber.reset();
339
      }
340
    }
341
    return ok;
342
  }
343
}
344

345
void ExternalFileUnit::BackspaceRecord(IoErrorHandler &handler) {
346
  if (access == Access::Direct || !IsRecordFile()) {
347
    handler.SignalError(IostatBackspaceNonSequential,
348
        "BACKSPACE(UNIT=%d) on direct-access file or unformatted stream",
349
        unitNumber());
350
  } else {
351
    if (IsAfterEndfile()) {
352
      // BACKSPACE after explicit ENDFILE
353
      currentRecordNumber = *endfileRecordNumber;
354
    } else if (leftTabLimit && direction_ == Direction::Input) {
355
      // BACKSPACE after non-advancing input
356
      leftTabLimit.reset();
357
    } else {
358
      DoImpliedEndfile(handler);
359
      if (frameOffsetInFile_ + recordOffsetInFrame_ > 0) {
360
        --currentRecordNumber;
361
        if (openRecl && access == Access::Direct) {
362
          BackspaceFixedRecord(handler);
363
        } else {
364
          RUNTIME_CHECK(handler, isUnformatted.has_value());
365
          if (isUnformatted.value_or(false)) {
366
            BackspaceVariableUnformattedRecord(handler);
367
          } else {
368
            BackspaceVariableFormattedRecord(handler);
369
          }
370
        }
371
      }
372
    }
373
    BeginRecord();
374
    anyWriteSinceLastPositioning_ = false;
375
  }
376
}
377

378
void ExternalFileUnit::FlushOutput(IoErrorHandler &handler) {
379
  if (!mayPosition()) {
380
    auto frameAt{FrameAt()};
381
    if (frameOffsetInFile_ >= frameAt &&
382
        frameOffsetInFile_ <
383
            static_cast<std::int64_t>(frameAt + FrameLength())) {
384
      // A Flush() that's about to happen to a non-positionable file
385
      // needs to advance frameOffsetInFile_ to prevent attempts at
386
      // impossible seeks
387
      CommitWrites();
388
      leftTabLimit.reset();
389
    }
390
  }
391
  Flush(handler);
392
}
393

394
void ExternalFileUnit::FlushIfTerminal(IoErrorHandler &handler) {
395
  if (isTerminal()) {
396
    FlushOutput(handler);
397
  }
398
}
399

400
void ExternalFileUnit::Endfile(IoErrorHandler &handler) {
401
  if (access == Access::Direct) {
402
    handler.SignalError(IostatEndfileDirect,
403
        "ENDFILE(UNIT=%d) on direct-access file", unitNumber());
404
  } else if (!mayWrite()) {
405
    handler.SignalError(IostatEndfileUnwritable,
406
        "ENDFILE(UNIT=%d) on read-only file", unitNumber());
407
  } else if (IsAfterEndfile()) {
408
    // ENDFILE after ENDFILE
409
  } else {
410
    DoEndfile(handler);
411
    if (IsRecordFile() && access != Access::Direct) {
412
      // Explicit ENDFILE leaves position *after* the endfile record
413
      RUNTIME_CHECK(handler, endfileRecordNumber.has_value());
414
      currentRecordNumber = *endfileRecordNumber + 1;
415
    }
416
  }
417
}
418

419
void ExternalFileUnit::Rewind(IoErrorHandler &handler) {
420
  if (access == Access::Direct) {
421
    handler.SignalError(IostatRewindNonSequential,
422
        "REWIND(UNIT=%d) on non-sequential file", unitNumber());
423
  } else {
424
    DoImpliedEndfile(handler);
425
    SetPosition(0, handler);
426
    currentRecordNumber = 1;
427
    leftTabLimit.reset();
428
    anyWriteSinceLastPositioning_ = false;
429
  }
430
}
431

432
void ExternalFileUnit::SetPosition(std::int64_t pos, IoErrorHandler &handler) {
433
  frameOffsetInFile_ = pos;
434
  recordOffsetInFrame_ = 0;
435
  if (access == Access::Direct) {
436
    directAccessRecWasSet_ = true;
437
  }
438
  BeginRecord();
439
}
440

441
bool ExternalFileUnit::SetStreamPos(
442
    std::int64_t oneBasedPos, IoErrorHandler &handler) {
443
  if (access != Access::Stream) {
444
    handler.SignalError("POS= may not appear unless ACCESS='STREAM'");
445
    return false;
446
  }
447
  if (oneBasedPos < 1) { // POS=1 is beginning of file (12.6.2.11)
448
    handler.SignalError(
449
        "POS=%zd is invalid", static_cast<std::intmax_t>(oneBasedPos));
450
    return false;
451
  }
452
  // A backwards POS= implies truncation after writing, at least in
453
  // Intel and NAG.
454
  if (static_cast<std::size_t>(oneBasedPos - 1) <
455
      frameOffsetInFile_ + recordOffsetInFrame_) {
456
    DoImpliedEndfile(handler);
457
  }
458
  SetPosition(oneBasedPos - 1, handler);
459
  // We no longer know which record we're in.  Set currentRecordNumber to
460
  // a large value from whence we can both advance and backspace.
461
  currentRecordNumber = std::numeric_limits<std::int64_t>::max() / 2;
462
  endfileRecordNumber.reset();
463
  return true;
464
}
465

466
bool ExternalFileUnit::SetDirectRec(
467
    std::int64_t oneBasedRec, IoErrorHandler &handler) {
468
  if (access != Access::Direct) {
469
    handler.SignalError("REC= may not appear unless ACCESS='DIRECT'");
470
    return false;
471
  }
472
  if (!openRecl) {
473
    handler.SignalError("RECL= was not specified");
474
    return false;
475
  }
476
  if (oneBasedRec < 1) {
477
    handler.SignalError(
478
        "REC=%zd is invalid", static_cast<std::intmax_t>(oneBasedRec));
479
    return false;
480
  }
481
  currentRecordNumber = oneBasedRec;
482
  SetPosition((oneBasedRec - 1) * *openRecl, handler);
483
  return true;
484
}
485

486
void ExternalFileUnit::EndIoStatement() {
487
  io_.reset();
488
  u_.emplace<std::monostate>();
489
  lock_.Drop();
490
}
491

492
void ExternalFileUnit::BeginSequentialVariableUnformattedInputRecord(
493
    IoErrorHandler &handler) {
494
  RUNTIME_CHECK(handler, access == Access::Sequential);
495
  std::int32_t header{0}, footer{0};
496
  std::size_t need{recordOffsetInFrame_ + sizeof header};
497
  std::size_t got{ReadFrame(frameOffsetInFile_, need, handler)};
498
  // Try to emit informative errors to help debug corrupted files.
499
  const char *error{nullptr};
500
  if (got < need) {
501
    if (got == recordOffsetInFrame_) {
502
      HitEndOnRead(handler);
503
    } else {
504
      error = "Unformatted variable-length sequential file input failed at "
505
              "record #%jd (file offset %jd): truncated record header";
506
    }
507
  } else {
508
    header = ReadHeaderOrFooter(recordOffsetInFrame_);
509
    recordLength = sizeof header + header; // does not include footer
510
    need = recordOffsetInFrame_ + *recordLength + sizeof footer;
511
    got = ReadFrame(frameOffsetInFile_, need, handler);
512
    if (got < need) {
513
      error = "Unformatted variable-length sequential file input failed at "
514
              "record #%jd (file offset %jd): hit EOF reading record with "
515
              "length %jd bytes";
516
    } else {
517
      footer = ReadHeaderOrFooter(recordOffsetInFrame_ + *recordLength);
518
      if (footer != header) {
519
        error = "Unformatted variable-length sequential file input failed at "
520
                "record #%jd (file offset %jd): record header has length %jd "
521
                "that does not match record footer (%jd)";
522
      }
523
    }
524
  }
525
  if (error) {
526
    handler.SignalError(error, static_cast<std::intmax_t>(currentRecordNumber),
527
        static_cast<std::intmax_t>(frameOffsetInFile_),
528
        static_cast<std::intmax_t>(header), static_cast<std::intmax_t>(footer));
529
    // TODO: error recovery
530
  }
531
  positionInRecord = sizeof header;
532
}
533

534
void ExternalFileUnit::BeginVariableFormattedInputRecord(
535
    IoErrorHandler &handler) {
536
  if (this == defaultInput) {
537
    if (defaultOutput) {
538
      defaultOutput->FlushOutput(handler);
539
    }
540
    if (errorOutput) {
541
      errorOutput->FlushOutput(handler);
542
    }
543
  }
544
  std::size_t length{0};
545
  do {
546
    std::size_t need{length + 1};
547
    length =
548
        ReadFrame(frameOffsetInFile_, recordOffsetInFrame_ + need, handler) -
549
        recordOffsetInFrame_;
550
    if (length < need) {
551
      if (length > 0) {
552
        // final record w/o \n
553
        recordLength = length;
554
        unterminatedRecord = true;
555
      } else {
556
        HitEndOnRead(handler);
557
      }
558
      break;
559
    }
560
  } while (!SetVariableFormattedRecordLength());
561
}
562

563
void ExternalFileUnit::BackspaceFixedRecord(IoErrorHandler &handler) {
564
  RUNTIME_CHECK(handler, openRecl.has_value());
565
  if (frameOffsetInFile_ < *openRecl) {
566
    handler.SignalError(IostatBackspaceAtFirstRecord);
567
  } else {
568
    frameOffsetInFile_ -= *openRecl;
569
  }
570
}
571

572
void ExternalFileUnit::BackspaceVariableUnformattedRecord(
573
    IoErrorHandler &handler) {
574
  std::int32_t header{0};
575
  auto headerBytes{static_cast<std::int64_t>(sizeof header)};
576
  frameOffsetInFile_ += recordOffsetInFrame_;
577
  recordOffsetInFrame_ = 0;
578
  if (frameOffsetInFile_ <= headerBytes) {
579
    handler.SignalError(IostatBackspaceAtFirstRecord);
580
    return;
581
  }
582
  // Error conditions here cause crashes, not file format errors, because the
583
  // validity of the file structure before the current record will have been
584
  // checked informatively in NextSequentialVariableUnformattedInputRecord().
585
  std::size_t got{
586
      ReadFrame(frameOffsetInFile_ - headerBytes, headerBytes, handler)};
587
  if (static_cast<std::int64_t>(got) < headerBytes) {
588
    handler.SignalError(IostatShortRead);
589
    return;
590
  }
591
  recordLength = ReadHeaderOrFooter(0);
592
  if (frameOffsetInFile_ < *recordLength + 2 * headerBytes) {
593
    handler.SignalError(IostatBadUnformattedRecord);
594
    return;
595
  }
596
  frameOffsetInFile_ -= *recordLength + 2 * headerBytes;
597
  auto need{static_cast<std::size_t>(
598
      recordOffsetInFrame_ + sizeof header + *recordLength)};
599
  got = ReadFrame(frameOffsetInFile_, need, handler);
600
  if (got < need) {
601
    handler.SignalError(IostatShortRead);
602
    return;
603
  }
604
  header = ReadHeaderOrFooter(recordOffsetInFrame_);
605
  if (header != *recordLength) {
606
    handler.SignalError(IostatBadUnformattedRecord);
607
    return;
608
  }
609
}
610

611
// There's no portable memrchr(), unfortunately, and strrchr() would
612
// fail on a record with a NUL, so we have to do it the hard way.
613
static RT_API_ATTRS const char *FindLastNewline(
614
    const char *str, std::size_t length) {
615
  for (const char *p{str + length}; p >= str; p--) {
616
    if (*p == '\n') {
617
      return p;
618
    }
619
  }
620
  return nullptr;
621
}
622

623
void ExternalFileUnit::BackspaceVariableFormattedRecord(
624
    IoErrorHandler &handler) {
625
  // File offset of previous record's newline
626
  auto prevNL{
627
      frameOffsetInFile_ + static_cast<std::int64_t>(recordOffsetInFrame_) - 1};
628
  if (prevNL < 0) {
629
    handler.SignalError(IostatBackspaceAtFirstRecord);
630
    return;
631
  }
632
  while (true) {
633
    if (frameOffsetInFile_ < prevNL) {
634
      if (const char *p{
635
              FindLastNewline(Frame(), prevNL - 1 - frameOffsetInFile_)}) {
636
        recordOffsetInFrame_ = p - Frame() + 1;
637
        recordLength = prevNL - (frameOffsetInFile_ + recordOffsetInFrame_);
638
        break;
639
      }
640
    }
641
    if (frameOffsetInFile_ == 0) {
642
      recordOffsetInFrame_ = 0;
643
      recordLength = prevNL;
644
      break;
645
    }
646
    frameOffsetInFile_ -= std::min<std::int64_t>(frameOffsetInFile_, 1024);
647
    auto need{static_cast<std::size_t>(prevNL + 1 - frameOffsetInFile_)};
648
    auto got{ReadFrame(frameOffsetInFile_, need, handler)};
649
    if (got < need) {
650
      handler.SignalError(IostatShortRead);
651
      return;
652
    }
653
  }
654
  if (Frame()[recordOffsetInFrame_ + *recordLength] != '\n') {
655
    handler.SignalError(IostatMissingTerminator);
656
    return;
657
  }
658
  if (*recordLength > 0 &&
659
      Frame()[recordOffsetInFrame_ + *recordLength - 1] == '\r') {
660
    --*recordLength;
661
  }
662
}
663

664
void ExternalFileUnit::DoImpliedEndfile(IoErrorHandler &handler) {
665
  if (access != Access::Direct) {
666
    if (!impliedEndfile_ && leftTabLimit && direction_ == Direction::Output) {
667
      // Flush a partial record after non-advancing output
668
      impliedEndfile_ = true;
669
    }
670
    if (impliedEndfile_ && mayPosition()) {
671
      DoEndfile(handler);
672
    }
673
  }
674
  impliedEndfile_ = false;
675
}
676

677
template <bool ANY_DIR, Direction DIR>
678
void ExternalFileUnit::DoEndfile(IoErrorHandler &handler) {
679
  if (IsRecordFile() && access != Access::Direct) {
680
    furthestPositionInRecord =
681
        std::max(positionInRecord, furthestPositionInRecord);
682
    if (leftTabLimit) { // last I/O was non-advancing
683
      if (access == Access::Sequential && direction_ == Direction::Output) {
684
        if constexpr (ANY_DIR || DIR == Direction::Output) {
685
          // When DoEndfile() is called from BeginReadingRecord(),
686
          // this call to AdvanceRecord() may appear as a recursion
687
          // though it may never happen. Expose the call only
688
          // under the constexpr direction check.
689
          AdvanceRecord(handler);
690
        } else {
691
          // This check always fails if we are here.
692
          RUNTIME_CHECK(handler, direction_ != Direction::Output);
693
        }
694
      } else { // Access::Stream or input
695
        leftTabLimit.reset();
696
        ++currentRecordNumber;
697
      }
698
    }
699
    endfileRecordNumber = currentRecordNumber;
700
  }
701
  frameOffsetInFile_ += recordOffsetInFrame_ + furthestPositionInRecord;
702
  recordOffsetInFrame_ = 0;
703
  FlushOutput(handler);
704
  Truncate(frameOffsetInFile_, handler);
705
  TruncateFrame(frameOffsetInFile_, handler);
706
  BeginRecord();
707
  impliedEndfile_ = false;
708
  anyWriteSinceLastPositioning_ = false;
709
}
710

711
template void ExternalFileUnit::DoEndfile(IoErrorHandler &handler);
712
template void ExternalFileUnit::DoEndfile<false, Direction::Output>(
713
    IoErrorHandler &handler);
714
template void ExternalFileUnit::DoEndfile<false, Direction::Input>(
715
    IoErrorHandler &handler);
716

717
void ExternalFileUnit::CommitWrites() {
718
  frameOffsetInFile_ +=
719
      recordOffsetInFrame_ + recordLength.value_or(furthestPositionInRecord);
720
  recordOffsetInFrame_ = 0;
721
  BeginRecord();
722
}
723

724
bool ExternalFileUnit::CheckDirectAccess(IoErrorHandler &handler) {
725
  if (access == Access::Direct) {
726
    RUNTIME_CHECK(handler, openRecl);
727
    if (!directAccessRecWasSet_) {
728
      handler.SignalError(
729
          "No REC= was specified for a data transfer with ACCESS='DIRECT'");
730
      return false;
731
    }
732
  }
733
  return true;
734
}
735

736
void ExternalFileUnit::HitEndOnRead(IoErrorHandler &handler) {
737
  handler.SignalEnd();
738
  if (IsRecordFile() && access != Access::Direct) {
739
    endfileRecordNumber = currentRecordNumber;
740
  }
741
}
742

743
ChildIo &ExternalFileUnit::PushChildIo(IoStatementState &parent) {
744
  OwningPtr<ChildIo> current{std::move(child_)};
745
  Terminator &terminator{parent.GetIoErrorHandler()};
746
  OwningPtr<ChildIo> next{New<ChildIo>{terminator}(parent, std::move(current))};
747
  child_.reset(next.release());
748
  return *child_;
749
}
750

751
void ExternalFileUnit::PopChildIo(ChildIo &child) {
752
  if (child_.get() != &child) {
753
    child.parent().GetIoErrorHandler().Crash(
754
        "ChildIo being popped is not top of stack");
755
  }
756
  child_.reset(child.AcquirePrevious().release()); // deletes top child
757
}
758

759
std::int32_t ExternalFileUnit::ReadHeaderOrFooter(std::int64_t frameOffset) {
760
  std::int32_t word;
761
  char *wordPtr{reinterpret_cast<char *>(&word)};
762
  std::memcpy(wordPtr, Frame() + frameOffset, sizeof word);
763
  if (swapEndianness_) {
764
    SwapEndianness(wordPtr, sizeof word, sizeof word);
765
  }
766
  return word;
767
}
768

769
void ChildIo::EndIoStatement() {
770
  io_.reset();
771
  u_.emplace<std::monostate>();
772
}
773

774
Iostat ChildIo::CheckFormattingAndDirection(
775
    bool unformatted, Direction direction) {
776
  bool parentIsInput{!parent_.get_if<IoDirectionState<Direction::Output>>()};
777
  bool parentIsFormatted{parentIsInput
778
          ? parent_.get_if<FormattedIoStatementState<Direction::Input>>() !=
779
              nullptr
780
          : parent_.get_if<FormattedIoStatementState<Direction::Output>>() !=
781
              nullptr};
782
  bool parentIsUnformatted{!parentIsFormatted};
783
  if (unformatted != parentIsUnformatted) {
784
    return unformatted ? IostatUnformattedChildOnFormattedParent
785
                       : IostatFormattedChildOnUnformattedParent;
786
  } else if (parentIsInput != (direction == Direction::Input)) {
787
    return parentIsInput ? IostatChildOutputToInputParent
788
                         : IostatChildInputFromOutputParent;
789
  } else {
790
    return IostatOk;
791
  }
792
}
793

794
RT_OFFLOAD_API_GROUP_END
795
} // namespace Fortran::runtime::io
796

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.