[flang][runtime] Reset the left tab limit when flushing output
authorPeter Klausler <pklausler@nvidia.com>
Wed, 12 Apr 2023 19:32:37 +0000 (12:32 -0700)
committerPeter Klausler <pklausler@nvidia.com>
Thu, 13 Apr 2023 16:43:40 +0000 (09:43 -0700)
When flushing output to a non-positionable tty or socket file, reset the
left tab limit.  Otherwise, non-advancing output to that file will contain
an increasing amount of leading spaces in each flush.  Also, detect
newline characters in stream output, and treat them as record
advancement.

Differential Revision: https://reviews.llvm.org/D148157

flang/runtime/emit-encoded.h
flang/runtime/tools.h
flang/runtime/unit.cpp

index 192c7ec..864848c 100644 (file)
@@ -13,6 +13,7 @@
 
 #include "connection.h"
 #include "environment.h"
+#include "tools.h"
 #include "utf.h"
 
 namespace Fortran::runtime::io {
@@ -20,6 +21,20 @@ namespace Fortran::runtime::io {
 template <typename CONTEXT, typename CHAR>
 bool EmitEncoded(CONTEXT &to, const CHAR *data, std::size_t chars) {
   ConnectionState &connection{to.GetConnectionState()};
+  if (connection.access == Access::Stream &&
+      connection.internalIoCharKind == 0) {
+    // Stream output: treat newlines as record advancements so that the left tab
+    // limit is correctly managed
+    while (const CHAR * nl{FindCharacter(data, CHAR{'\n'}, chars)}) {
+      auto pos{static_cast<std::size_t>(nl - data)};
+      if (!EmitEncoded(to, data, pos)) {
+        return false;
+      }
+      data += pos + 1;
+      chars -= pos + 1;
+      to.AdvanceRecord();
+    }
+  }
   if (connection.useUTF8<CHAR>()) {
     using UnsignedChar = std::make_unsigned_t<CHAR>;
     const UnsignedChar *uData{reinterpret_cast<const UnsignedChar *>(data)};
@@ -61,7 +76,8 @@ bool EmitEncoded(CONTEXT &to, const CHAR *data, std::size_t chars) {
 template <typename CONTEXT>
 bool EmitAscii(CONTEXT &to, const char *data, std::size_t chars) {
   ConnectionState &connection{to.GetConnectionState()};
-  if (connection.internalIoCharKind <= 1) {
+  if (connection.internalIoCharKind <= 1 &&
+      connection.access != Access::Stream) {
     return to.Emit(data, chars);
   } else {
     return EmitEncoded(to, data, chars);
@@ -74,7 +90,9 @@ bool EmitRepeated(CONTEXT &to, char ch, std::size_t n) {
     return true;
   }
   ConnectionState &connection{to.GetConnectionState()};
-  if (connection.internalIoCharKind <= 1) {
+  if (connection.internalIoCharKind <= 1 &&
+      connection.access != Access::Stream) {
+    // faster path, no encoding needed
     while (n-- > 0) {
       if (!to.Emit(&ch, 1)) {
         return false;
index a9c942d..d96d848 100644 (file)
@@ -13,6 +13,7 @@
 #include "flang/Runtime/cpp-type.h"
 #include "flang/Runtime/descriptor.h"
 #include "flang/Runtime/memory.h"
+#include <cstring>
 #include <functional>
 #include <map>
 #include <type_traits>
@@ -356,5 +357,24 @@ using AccumulationType = CppTypeFor<CAT,
         ? std::max(KIND, static_cast<int>(sizeof(double)))
         : KIND>;
 
+// memchr() for any character type
+template <typename CHAR>
+static inline const CHAR *FindCharacter(
+    const CHAR *data, CHAR ch, std::size_t chars) {
+  const CHAR *end{data + chars};
+  for (const CHAR *p{data}; p < end; ++p) {
+    if (*p == ch) {
+      return p;
+    }
+  }
+  return nullptr;
+}
+
+template <>
+inline const char *FindCharacter(const char *data, char ch, std::size_t chars) {
+  return reinterpret_cast<const char *>(
+      std::memchr(data, static_cast<int>(ch), chars));
+}
+
 } // namespace Fortran::runtime
 #endif // FORTRAN_RUNTIME_TOOLS_H_
index 092a0a2..af2c835 100644 (file)
@@ -9,6 +9,7 @@
 #include "unit.h"
 #include "io-error.h"
 #include "lock.h"
+#include "tools.h"
 #include "unit-map.h"
 #include <cstdio>
 #include <limits>
@@ -417,8 +418,7 @@ bool ExternalFileUnit::SetVariableFormattedRecordLength() {
   } else if (FrameLength() > recordOffsetInFrame_) {
     const char *record{Frame() + recordOffsetInFrame_};
     std::size_t bytes{FrameLength() - recordOffsetInFrame_};
-    if (const char *nl{
-            reinterpret_cast<const char *>(std::memchr(record, '\n', bytes))}) {
+    if (const char *nl{FindCharacter(record, '\n', bytes)}) {
       recordLength = nl - record;
       if (*recordLength > 0 && record[*recordLength - 1] == '\r') {
         --*recordLength;
@@ -621,6 +621,7 @@ void ExternalFileUnit::FlushOutput(IoErrorHandler &handler) {
       // needs to advance frameOffsetInFile_ to prevent attempts at
       // impossible seeks
       CommitWrites();
+      leftTabLimit.reset();
     }
   }
   Flush(handler);