1//===----------------------------------------------------------------------===//
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#include "std_stream.h"
10
11#include <__memory/construct_at.h>
12#include <__ostream/basic_ostream.h>
13#include <fstream>
14#include <istream>
15
16#define ABI_NAMESPACE_STR _LIBCPP_TOSTRING(_LIBCPP_ABI_NAMESPACE)
17
18_LIBCPP_BEGIN_NAMESPACE_STD
19_LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
20
21// This file implements the various stream objects provided inside <iostream>. We're doing some ODR violations in here,
22// so this quite fragile. Specifically, the size of the stream objects (i.e. cout, cin etc.) needs to stay the same.
23// For that reason, we have `stream` and `stream_data` separated into two objects. The public `stream` objects only
24// contain the actual stream, while the private `stream_data` objects contains the `basic_streambuf` we're using as well
25// as the mbstate_t. `stream_data` objects are only accessible within the library, so they aren't ABI sensitive and we
26// can change them as we want.
27
28template <class StreamT>
29union stream {
30 constexpr stream() {}
31 stream(const stream&) = delete;
32 stream& operator=(const stream&) = delete;
33 constexpr ~stream() {}
34
35 StreamT value;
36};
37
38template <class StreamT, class SyncBufT, class UnsyncBufT>
39union stream_data {
40 constexpr stream_data() {}
41 constexpr ~stream_data() {}
42 struct {
43 union {
44 SyncBufT sync_buffer;
45 UnsyncBufT unsync_buffer;
46 };
47 mbstate_t mb;
48 };
49};
50
51template <class StreamT, class SyncBufT, class UnsyncBufT>
52void init_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT, SyncBufT, UnsyncBufT>& data) {
53 data.mb = {};
54 std::construct_at(&data.sync_buffer, stdstream, &data.mb);
55 std::construct_at(&stream.value, &data.sync_buffer);
56}
57
58template <class StreamT, class SyncBufT, class UnsyncBufT>
59void switch_to_sync_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT, SyncBufT, UnsyncBufT>& data) {
60 data.unsync_buffer.__adopt_file(nullptr, {}); // reset the file, so that basic_filebuf doesn't close standard streams
61 std::destroy_at(&data.unsync_buffer);
62 data.mb = {};
63 std::construct_at(&data.sync_buffer, stdstream, &data.mb);
64 stream.value.rdbuf(&data.sync_buffer);
65}
66
67template <class StreamT, class SyncBufT, class UnsyncBufT>
68void switch_to_unsync_stream(FILE* stdstream,
69 stream<StreamT>& stream,
70 stream_data<StreamT, SyncBufT, UnsyncBufT>& data,
71 ios_base::openmode mode) {
72 std::destroy_at(&data.sync_buffer);
73 std::construct_at(&data.unsync_buffer);
74 data.unsync_buffer.__adopt_file(stdstream, mode);
75 stream.value.rdbuf(&data.unsync_buffer);
76}
77
78#define CHAR_MANGLING_char "D"
79#define CHAR_MANGLING_wchar_t "_W"
80#define CHAR_MANGLING(CharT) CHAR_MANGLING_##CharT
81
82#ifdef _LIBCPP_COMPILER_CLANG_BASED
83# define STRING_DATA_CONSTINIT constinit
84#else
85# define STRING_DATA_CONSTINIT
86#endif
87
88#ifdef _LIBCPP_ABI_MICROSOFT
89# define STREAM(StreamT, SyncBufT, UnsyncBufT, CharT, var) \
90 STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, SyncBufT<CharT>, UnsyncBufT<CharT>> var##_data; \
91 _LIBCPP_EXPORTED_FROM_ABI STRING_DATA_CONSTINIT stream<StreamT<CharT>> var __asm__( \
92 "?" #var "@" ABI_NAMESPACE_STR "@std@@3V?$" #StreamT \
93 "@" CHAR_MANGLING(CharT) "U?$char_traits@" CHAR_MANGLING(CharT) "@" ABI_NAMESPACE_STR "@std@@@12@A")
94#else
95# define STREAM(StreamT, SyncBufT, UnsyncBufT, CharT, var) \
96 STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, SyncBufT<CharT>, UnsyncBufT<CharT>> var##_data; \
97 _LIBCPP_EXPORTED_FROM_ABI STRING_DATA_CONSTINIT stream<StreamT<CharT>> var
98#endif
99
100// These definitions and the declarations in <iostream> technically cause ODR violations, since they have different
101// types (stream_data and {i,o}stream respectively). This means that <iostream> should never be included in this TU.
102
103#if _LIBCPP_HAS_FILESYSTEM
104template <class CharT>
105using unsync_buffer = basic_filebuf<CharT>;
106#else
107// If we don't have access to files we can't treat the standard streams as files either - just save a dummy object in
108// that case.
109template <class>
110using unsync_buffer = char;
111#endif
112
113STREAM(basic_istream, __stdinbuf, unsync_buffer, char, cin);
114STREAM(basic_ostream, __stdoutbuf, unsync_buffer, char, cout);
115STREAM(basic_ostream, __stdoutbuf, unsync_buffer, char, cerr);
116STREAM(basic_ostream, __stdoutbuf, unsync_buffer, char, clog);
117#if _LIBCPP_HAS_WIDE_CHARACTERS
118STREAM(basic_istream, __stdinbuf, unsync_buffer, wchar_t, wcin);
119STREAM(basic_ostream, __stdoutbuf, unsync_buffer, wchar_t, wcout);
120STREAM(basic_ostream, __stdoutbuf, unsync_buffer, wchar_t, wcerr);
121STREAM(basic_ostream, __stdoutbuf, unsync_buffer, wchar_t, wclog);
122#endif // _LIBCPP_HAS_WIDE_CHARACTERS
123
124// Pretend we're inside a system header so the compiler doesn't flag the use of the init_priority
125// attribute with a value that's reserved for the implementation (we're the implementation).
126#include "iostream_init.h"
127
128// On Windows the TLS storage for locales needs to be initialized before we create
129// the standard streams, otherwise it may not be alive during program termination
130// when we flush the streams.
131static void force_locale_initialization() {
132#ifdef _WIN32
133 static bool once = []() {
134 auto loc = __locale::__newlocale(_LIBCPP_ALL_MASK, "C", 0);
135 {
136 __locale::__locale_guard g(loc); // forces initialization of locale TLS
137 ((void)g);
138 }
139 __locale::__freelocale(loc);
140 return true;
141 }();
142 ((void)once);
143#endif
144}
145
146class DoIOSInit {
147public:
148 DoIOSInit();
149 ~DoIOSInit();
150};
151
152DoIOSInit::DoIOSInit() {
153 force_locale_initialization();
154
155 init_stream(stdin, stream&: cin, data&: cin_data);
156 init_stream(stdout, stream&: cout, data&: cout_data);
157 init_stream(stderr, stream&: cerr, data&: cerr_data);
158 init_stream(stderr, stream&: clog, data&: clog_data);
159
160 cin.value.tie(tiestr: &cout.value);
161 std::unitbuf(str&: cerr.value);
162 cerr.value.tie(tiestr: &cout.value);
163
164#if _LIBCPP_HAS_WIDE_CHARACTERS
165 init_stream(stdin, stream&: wcin, data&: wcin_data);
166 init_stream(stdout, stream&: wcout, data&: wcout_data);
167 init_stream(stderr, stream&: wcerr, data&: wcerr_data);
168 init_stream(stderr, stream&: wclog, data&: wclog_data);
169
170 wcin.value.tie(tiestr: &wcout.value);
171 std::unitbuf(str&: wcerr.value);
172 wcerr.value.tie(tiestr: &wcout.value);
173#endif
174}
175
176DoIOSInit::~DoIOSInit() {
177 cout.value.flush();
178 clog.value.flush();
179
180#if _LIBCPP_HAS_WIDE_CHARACTERS
181 wcout.value.flush();
182 wclog.value.flush();
183#endif
184}
185
186ios_base::Init::Init() {
187 static DoIOSInit init_the_streams; // gets initialized once
188}
189
190ios_base::Init::~Init() {}
191
192bool ios_base::sync_with_stdio(bool sync) {
193 static bool previous_state = true;
194 bool r = previous_state;
195
196#if _LIBCPP_HAS_FILESYSTEM
197 if (sync != previous_state) {
198 if (sync) {
199 switch_to_sync_stream(stdin, stream&: cin, data&: cin_data);
200 switch_to_sync_stream(stdout, stream&: cout, data&: cout_data);
201 switch_to_sync_stream(stderr, stream&: clog, data&: clog_data);
202# if _LIBCPP_HAS_WIDE_CHARACTERS
203 switch_to_sync_stream(stdin, stream&: wcin, data&: wcin_data);
204 switch_to_sync_stream(stdout, stream&: wcout, data&: wcout_data);
205 switch_to_sync_stream(stderr, stream&: wclog, data&: wclog_data);
206# endif
207 } else {
208 switch_to_unsync_stream(stdin, stream&: cin, data&: cin_data, mode: ios::in);
209 switch_to_unsync_stream(stdout, stream&: cout, data&: cout_data, mode: ios::out);
210 switch_to_unsync_stream(stderr, stream&: clog, data&: clog_data, mode: ios::out);
211# if _LIBCPP_HAS_WIDE_CHARACTERS
212 switch_to_unsync_stream(stdin, stream&: wcin, data&: wcin_data, mode: ios::in);
213 switch_to_unsync_stream(stdout, stream&: wcout, data&: wcout_data, mode: ios::out);
214 switch_to_unsync_stream(stderr, stream&: wclog, data&: wclog_data, mode: ios::out);
215# endif
216 }
217 }
218#endif // _LIBCPP_HAS_FILESYSTEM
219
220 previous_state = sync;
221 return r;
222}
223
224_LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
225_LIBCPP_END_NAMESPACE_STD
226