xref: /reactos/sdk/lib/ucrt/stdio/_sftbuf.cpp (revision ffd69754)
1 //
2 // _sftbuf.cpp
3 //
4 //      Copyright (c) Microsoft Corporation.  All rights reserved.
5 //
6 // Defines functions that enable and disable temporary buffering and flushing
7 // for stdout and stderr.  When __acrt_stdio_begin_temporary_buffering_nolock()
8 // is called for one of these streams, it tests whether the stream is buffered.
9 // If the stream is not buffered, it gives it a temporary buffer, so that we can
10 // avoid making sequences of one-character writes.
11 //
12 // __acrt_stdio_end_temporary_buffering_nolock() must be called to disable
13 // temporary buffering when it is no longer needed.  This function flushes the
14 // stream before tearing down the buffer.
15 //
16 // Note that these functions are only to be used for output streams--note that
17 // __acrt_stdio_begin_temporary_buffering_nolock() sets the _IOWRITE flag.  These
18 // functions are intended for internal library use only.
19 //
20 #include <corecrt_internal_stdio.h>
21 #include <corecrt_internal_ptd_propagation.h>
22 
23 
24 
25 // Buffer pointers for stdout and stderr
26 extern "C" { void* __acrt_stdout_buffer = nullptr; }
27 extern "C" { void* __acrt_stderr_buffer = nullptr; }
28 
29 // The temporary buffer has the data of one stdio call. Stderr and Stdout use a
30 // temporary buffer for the duration of stdio output calls instead of having a
31 // full buffer and write after flush. The temporary buffer prevents the stdio
32 // functions from writing to the disk more than once per call when the stream is
33 // unbuffered (except when _IONBF is specified).
__acrt_should_use_temporary_buffer(FILE * const stream)34 bool __acrt_should_use_temporary_buffer(FILE* const stream)
35 {
36     if (stream == stderr)
37     {
38         return true;
39     }
40 
41     if (stream == stdout && _isatty(_fileno(stream)))
42     {
43         return true;
44     }
45 
46     return false;
47 }
48 
49 
50 // Sets a temporary buffer if necessary (see __acrt_should_use_temporary_buffer).
51 // On success, the buffer is initialized for the stream and 1 is returned. On
52 // failure, 0 is returned. The temporary buffer ensures that only one write to
53 // the disk occurs per stdio output call.
__acrt_stdio_begin_temporary_buffering_nolock(FILE * const public_stream)54 extern "C" bool __cdecl __acrt_stdio_begin_temporary_buffering_nolock(
55     FILE* const public_stream
56     )
57 {
58     _ASSERTE(public_stream != nullptr);
59 
60     __crt_stdio_stream const stream(public_stream);
61 
62     if (!__acrt_should_use_temporary_buffer(stream.public_stream()))
63     {
64         return false;
65     }
66 
67     void** buffer;
68     if (stream.public_stream() == stdout)
69     {
70         buffer = &__acrt_stdout_buffer;
71     }
72     else if (stream.public_stream() == stderr)
73     {
74         buffer = &__acrt_stderr_buffer;
75     }
76     else
77     {
78         return false;
79     }
80 
81     #ifndef CRTDLL
82     _cflush++; // Force library pre-termination procedure to run
83     #endif
84 
85     // Make sure the stream is not already buffered:
86     if (stream.has_any_buffer())
87     {
88         return false;
89     }
90 
91     stream.set_flags(_IOWRITE | _IOBUFFER_USER | _IOBUFFER_STBUF);
92     if (*buffer == nullptr)
93     {
94         *buffer = _malloc_crt_t(char, _INTERNAL_BUFSIZ).detach();
95     }
96 
97     if (*buffer == nullptr)
98     {
99         // If we failed to allocate a buffer, use the small character buffer:
100         stream->_base   = reinterpret_cast<char*>(&stream->_charbuf);
101         stream->_ptr    = reinterpret_cast<char*>(&stream->_charbuf);
102         stream->_cnt    = 2;
103         stream->_bufsiz = 2;
104         return true;
105     }
106 
107     // Otherwise, we have a new buffer, so set it up for use:
108     stream->_base   = reinterpret_cast<char*>(*buffer);
109     stream->_ptr    = reinterpret_cast<char*>(*buffer);
110     stream->_cnt    = _INTERNAL_BUFSIZ;
111     stream->_bufsiz = _INTERNAL_BUFSIZ;
112     return true;
113 }
114 
115 
116 
117 // If the stream currently has a temporary buffer that was set via a call to
118 // __acrt_stdio_begin_temporary_buffering_nolock(), and if the flag value is
119 // 1, this function flushes the stream and disables buffering of the stream.
__acrt_stdio_end_temporary_buffering_nolock(bool const flag,FILE * const public_stream,__crt_cached_ptd_host & ptd)120 extern "C" void __cdecl __acrt_stdio_end_temporary_buffering_nolock(
121     bool               const flag,
122     FILE*              const public_stream,
123     __crt_cached_ptd_host&   ptd
124     )
125 {
126     __crt_stdio_stream const stream(public_stream);
127 
128     if (!flag)
129         return;
130 
131     if (stream.has_temporary_buffer())
132     {
133         // Flush the stream and tear down temporary buffering:
134         __acrt_stdio_flush_nolock(stream.public_stream(), ptd);
135         stream.unset_flags(_IOBUFFER_USER | _IOBUFFER_STBUF);
136         stream->_bufsiz = 0;
137         stream->_base   = nullptr;
138         stream->_ptr    = nullptr;
139     }
140 
141     // Note: If we expand the functionality of the _IOBUFFER_STBUF bit to
142     // include other streams, we may want to clear that bit here under an
143     // 'else' clause (i.e., clear bit in the case that we leave the buffer
144     // permanently assigned.  Given our current use of the bit, the extra
145     // code is not needed.
146 }
147