[flang-commits] [flang] [llvm] [flang-rt] Split large unformatted sequential records into subrecords (PR #213003)

via flang-commits flang-commits at lists.llvm.org
Fri Oct 16 03:17:59 PDT 2026


================
@@ -607,67 +711,130 @@ void ExternalFileUnit::EndIoStatement() {
 void ExternalFileUnit::BeginSequentialVariableUnformattedInputRecord(
     IoErrorHandler &handler) {
   RUNTIME_CHECK(handler, access == Access::Sequential);
-  std::uint32_t header{0}, footer{0};
-  std::size_t need{recordOffsetInFrame_ + sizeof header};
-  std::size_t got{ReadFrame(frameOffsetInFile_, need, handler)};
-  // Try to emit informative errors to help debug corrupted files.
+  unformattedDiskExtent_ = 0;
+  streamRead_ = false;
+  FileOffset recordStart{
+      frameOffsetInFile_ + static_cast<FileOffset>(recordOffsetInFrame_)};
   const char *error{nullptr};
-  if (got < need) {
-    if (got == recordOffsetInFrame_) {
-      HitEndOnRead(handler);
+  std::int32_t header{0}, footer{0}, firstHeader{0};
+  std::size_t totalPayload{0};
+  FileOffset filePos{recordStart};
+
+  enum class MarkerRead { Ok, Eof, Short };
+  enum class ScanOutcome { Ok, Eof, Fail };
+  const char *scanError{nullptr};
+
+  auto readMarkerAt = [&](FileOffset &pos, std::int32_t &marker, bool swap) {
+    char buf[kRecMarkSize];
+    auto got{Read(pos, buf, kRecMarkSize, kRecMarkSize, handler)};
+    if (got < kRecMarkSize) {
+      return got == 0 ? MarkerRead::Eof : MarkerRead::Short;
+    }
+    pos += kRecMarkSize;
+    marker = LoadRecordMarker(buf, swap);
+    return MarkerRead::Ok;
+  };
+
+  auto scanRecord = [&](bool swap, std::int32_t &outHeader,
+                        std::int32_t &outFooter, std::int32_t &outFirstHeader,
+                        std::size_t &outTotalPayload,
+                        FileOffset &outFilePos) -> ScanOutcome {
+    scanError = nullptr;
+    outFilePos = recordStart;
+    outTotalPayload = 0;
+    switch (readMarkerAt(outFilePos, outHeader, swap)) {
+    case MarkerRead::Ok:
+      break;
+    case MarkerRead::Eof:
+      return ScanOutcome::Eof;
+    case MarkerRead::Short:
+      scanError = "Unformatted variable-length sequential file input failed at "
+                  "record #%jd (file offset %jd): truncated record header";
+      return ScanOutcome::Fail;
+    }
+    outFirstHeader = outHeader;
+    do {
+      outTotalPayload += SubrecordPayloadBytes(outHeader);
+      outFilePos += static_cast<FileOffset>(SubrecordPayloadBytes(outHeader));
+      if (readMarkerAt(outFilePos, outFooter, swap) != MarkerRead::Ok) {
+        scanError =
+            "Unformatted variable-length sequential file input failed at "
+            "record #%jd (file offset %jd): truncated record footer";
+        return ScanOutcome::Fail;
+      }
+      if (SubrecordPayloadBytes(outFooter) !=
+          SubrecordPayloadBytes(outHeader)) {
+        scanError =
+            "Unformatted variable-length sequential file input failed at "
+            "record #%jd (file offset %jd): record header has length %jd "
+            "that does not match record footer (%jd)";
+        return ScanOutcome::Fail;
+      }
+      if (outHeader >= 0) {
+        return ScanOutcome::Ok;
+      }
+      if (readMarkerAt(outFilePos, outHeader, swap) != MarkerRead::Ok) {
+        scanError =
+            "Unformatted variable-length sequential file input failed at "
+            "record #%jd (file offset %jd): truncated record header";
+        return ScanOutcome::Fail;
+      }
+    } while (true);
+  };
+
+  auto outcome{scanRecord(
+      swapEndianness_, header, footer, firstHeader, totalPayload, filePos)};
+  if (outcome == ScanOutcome::Eof) {
+    HitEndOnRead(handler);
+    return;
+  }
+  if (outcome == ScanOutcome::Fail) {
+    const char *primaryError{scanError};
+    if (frameOffsetInFile_ == 0 && recordOffsetInFrame_ == 0 && [&]() {
+          std::int32_t swappedHeader{0}, swappedFooter{0},
+              swappedFirstHeader{0};
+          std::size_t swappedTotalPayload{0};
+          FileOffset swappedFilePos{recordStart};
+          return scanRecord(!swapEndianness_, swappedHeader, swappedFooter,
+                     swappedFirstHeader, swappedTotalPayload,
+                     swappedFilePos) == ScanOutcome::Ok;
+        }()) {
+      error =
+          "Unformatted variable-length sequential file input failed on the "
+          "first record, probably due to a need for byte order data "
+          "conversion; consider adding CONVERT='SWAP' to the OPEN statement "
+          "or adding FORT_CONVERT=SWAP to the execution environment";
     } else {
-      error = "Unformatted variable-length sequential file input failed at "
-              "record #%jd (file offset %jd): truncated record header";
+      error = primaryError
+          ? primaryError
+          : "Unformatted variable-length sequential file input "
+            "failed at record #%jd (file offset %jd): truncated "
+            "record header";
     }
----------------
ejose02 wrote:

Done. set recordLength before footer check so we advance after a bad record.

https://github.com/llvm/llvm-project/pull/213003


More information about the flang-commits mailing list