xref: /llvm-project/flang/runtime/emit-encoded.h (revision 2b86fb21f8402f19da7e5887a9572b3d55052991)
1 //===-- runtime/emit-encoded.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 // Templates for emitting CHARACTER values with conversion
10 
11 #ifndef FORTRAN_RUNTIME_EMIT_ENCODED_H_
12 #define FORTRAN_RUNTIME_EMIT_ENCODED_H_
13 
14 #include "connection.h"
15 #include "environment.h"
16 #include "tools.h"
17 #include "utf.h"
18 
19 namespace Fortran::runtime::io {
20 
21 template <typename CONTEXT, typename CHAR, bool NL_ADVANCES_RECORD = true>
EmitEncoded(CONTEXT & to,const CHAR * data,std::size_t chars)22 RT_API_ATTRS bool EmitEncoded(
23     CONTEXT &to, const CHAR *data, std::size_t chars) {
24   ConnectionState &connection{to.GetConnectionState()};
25   if constexpr (NL_ADVANCES_RECORD) {
26     if (connection.access == Access::Stream &&
27         connection.internalIoCharKind == 0) {
28       // Stream output: treat newlines as record advancements so that the left
29       // tab limit is correctly managed
30       while (const CHAR * nl{FindCharacter(data, CHAR{'\n'}, chars)}) {
31         auto pos{static_cast<std::size_t>(nl - data)};
32         // The [data, data + pos) does not contain the newline,
33         // so we can avoid the recursion by calling proper specialization.
34         if (!EmitEncoded<CONTEXT, CHAR, false>(to, data, pos)) {
35           return false;
36         }
37         data += pos + 1;
38         chars -= pos + 1;
39         to.AdvanceRecord();
40       }
41     }
42   }
43   if (connection.useUTF8<CHAR>()) {
44     using UnsignedChar = std::make_unsigned_t<CHAR>;
45     const UnsignedChar *uData{reinterpret_cast<const UnsignedChar *>(data)};
46     char buffer[256];
47     std::size_t at{0};
48     while (chars-- > 0) {
49       auto len{EncodeUTF8(buffer + at, *uData++)};
50       at += len;
51       if (at + maxUTF8Bytes > sizeof buffer) {
52         if (!to.Emit(buffer, at)) {
53           return false;
54         }
55         at = 0;
56       }
57     }
58     return at == 0 || to.Emit(buffer, at);
59   } else {
60     std::size_t internalKind = connection.internalIoCharKind;
61     if (internalKind == 0 || internalKind == sizeof(CHAR)) {
62       const char *rawData{reinterpret_cast<const char *>(data)};
63       return to.Emit(rawData, chars * sizeof(CHAR), sizeof(CHAR));
64     } else {
65       // CHARACTER kind conversion for internal output
66       while (chars-- > 0) {
67         char32_t buffer = *data++;
68         char *p{reinterpret_cast<char *>(&buffer)};
69         if constexpr (!isHostLittleEndian) {
70           p += sizeof(buffer) - internalKind;
71         }
72         if (!to.Emit(p, internalKind)) {
73           return false;
74         }
75       }
76       return true;
77     }
78   }
79 }
80 
81 template <typename CONTEXT>
EmitAscii(CONTEXT & to,const char * data,std::size_t chars)82 RT_API_ATTRS bool EmitAscii(CONTEXT &to, const char *data, std::size_t chars) {
83   ConnectionState &connection{to.GetConnectionState()};
84   if (connection.internalIoCharKind <= 1 &&
85       connection.access != Access::Stream) {
86     return to.Emit(data, chars);
87   } else {
88     return EmitEncoded(to, data, chars);
89   }
90 }
91 
92 template <typename CONTEXT>
EmitRepeated(CONTEXT & to,char ch,std::size_t n)93 RT_API_ATTRS bool EmitRepeated(CONTEXT &to, char ch, std::size_t n) {
94   if (n <= 0) {
95     return true;
96   }
97   ConnectionState &connection{to.GetConnectionState()};
98   if (connection.internalIoCharKind <= 1 &&
99       connection.access != Access::Stream) {
100     // faster path, no encoding needed
101     while (n-- > 0) {
102       if (!to.Emit(&ch, 1)) {
103         return false;
104       }
105     }
106   } else {
107     while (n-- > 0) {
108       if (!EmitEncoded(to, &ch, 1)) {
109         return false;
110       }
111     }
112   }
113   return true;
114 }
115 
116 } // namespace Fortran::runtime::io
117 #endif // FORTRAN_RUNTIME_EMIT_ENCODED_H_
118