Reactos
at master 146 lines 4.7 kB view raw
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 26extern "C" { void* __acrt_stdout_buffer = nullptr; } 27extern "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). 34bool __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. 54extern "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. 120extern "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}