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
19namespace Fortran::runtime::io {
20
21template <typename CONTEXT, typename CHAR, bool NL_ADVANCES_RECORD = true>
22RT_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
81template <typename CONTEXT>
82RT_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
92template <typename CONTEXT>
93RT_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

source code of flang/runtime/emit-encoded.h