Reactos
at master 196 lines 7.5 kB view raw
1// 2// fwrite.cpp 3// 4// Copyright (c) Microsoft Corporation. All rights reserved. 5// 6// Defines fwrite() and related functions, which write unformatted data to a 7// stdio stream. 8// 9#include <corecrt_internal_stdio.h> 10#include <corecrt_internal_ptd_propagation.h> 11 12// Writes data from the provided buffer to the specified stream. The function 13// writes 'count' elements of 'size' size to the stream, and returns when 14// either all of the elements have been written or no more data can be written 15// (e.g. if EOF is encountered or an error occurs). 16// 17// Returns the number of "whole" elements that were written to the stream. This 18// may be fewer than the requested number of an error occurs or EOF is encountered. 19// In this case, ferror() or feof() should be used to distinguish between the two 20// conditions. 21extern "C" size_t __cdecl _fwrite_internal( 22 void const* const buffer, 23 size_t const size, 24 size_t const count, 25 FILE* const stream, 26 __crt_cached_ptd_host& ptd 27 ) 28{ 29 if (size == 0 || count == 0) 30 { 31 return 0; 32 } 33 34 // The _nolock version will do the rest of the validation. 35 _UCRT_VALIDATE_RETURN(ptd, stream != nullptr, EINVAL, 0); 36 37 return __acrt_lock_stream_and_call(stream, [&]() -> size_t 38 { 39 __acrt_stdio_temporary_buffering_guard const buffering(stream, ptd); 40 41 return _fwrite_nolock_internal(buffer, size, count, stream, ptd); 42 }); 43} 44 45extern "C" size_t __cdecl fwrite( 46 void const* const buffer, 47 size_t const size, 48 size_t const count, 49 FILE* const stream 50 ) 51{ 52 __crt_cached_ptd_host ptd; 53 return _fwrite_internal(buffer, size, count, stream, ptd); 54} 55 56extern "C" size_t __cdecl _fwrite_nolock_internal( 57 void const* const buffer, 58 size_t const element_size, 59 size_t const element_count, 60 FILE* const public_stream, 61 __crt_cached_ptd_host& ptd 62 ) 63{ 64 if (element_size == 0 || element_count == 0) 65 { 66 return 0; 67 } 68 69 __crt_stdio_stream const stream(public_stream); 70 71 _UCRT_VALIDATE_RETURN(ptd, stream.valid(), EINVAL, 0); 72 _UCRT_VALIDATE_RETURN(ptd, buffer != nullptr, EINVAL, 0); 73 _UCRT_VALIDATE_RETURN(ptd, element_count <= (SIZE_MAX / element_size), EINVAL, 0); 74 75 // Figure out how big the buffer is; if the stream doesn't currently have a 76 // buffer, we assume that we'll get one with the usual internal buffer size: 77 unsigned stream_buffer_size = stream.has_any_buffer() 78 ? stream->_bufsiz 79 : _INTERNAL_BUFSIZ; 80 81 // The total number of bytes to be written to the stream: 82 size_t const total_bytes = element_size * element_count; 83 84 char const* data = static_cast<char const*>(buffer); 85 86 // Write blocks of data from the buffer until there is no more data left: 87 size_t remaining_bytes = total_bytes; 88 while (remaining_bytes != 0) 89 { 90 // If the buffer is big and is not full, copy data into the buffer: 91 if (stream.has_big_buffer() && stream->_cnt != 0) 92 { 93 if (stream->_cnt < 0) 94 { 95 _ASSERTE(("Inconsistent Stream Count. Flush between consecutive read and write", stream->_cnt >= 0)); 96 stream.set_flags(_IOERROR); 97 return (total_bytes - remaining_bytes) / element_size; 98 } 99 100 if (stream.has_any_of(_IOREAD)) 101 { 102 _ASSERTE(("Flush between consecutive read and write.", !stream.has_any_of(_IOREAD))); 103 return (total_bytes - remaining_bytes) / element_size; 104 } 105 106 size_t const bytes_to_write = __min(remaining_bytes, static_cast<size_t>(stream->_cnt)); 107 108 memcpy(stream->_ptr, data, bytes_to_write); 109 110 remaining_bytes -= bytes_to_write; 111 stream->_cnt -= static_cast<int>(bytes_to_write); 112 stream->_ptr += bytes_to_write; 113 data += bytes_to_write; 114 } 115 // If we have more than stream_buffer_size bytes to write, write data by 116 // calling _write() with an integral number of stream_buffer_size blocks. 117 else if (remaining_bytes >= stream_buffer_size) 118 { 119 // If we reach here and we have a big buffer, it must be full, so 120 // flush it. If the flush fails, there's nothing we can do to 121 // recover: 122 if (stream.has_big_buffer() && __acrt_stdio_flush_nolock(stream.public_stream(), ptd)) 123 { 124 return (total_bytes - remaining_bytes) / element_size; 125 } 126 127 // Calculate the number of bytes to write. The _write API takes a 128 // 32-bit unsigned byte count and returns -1 (UINT_MAX) on failure, 129 // so clamp the value to UINT_MAX - 1: 130 size_t const max_bytes_to_write = stream_buffer_size > 0 131 ? remaining_bytes - remaining_bytes % stream_buffer_size 132 : remaining_bytes; 133 134 unsigned const bytes_to_write = static_cast<unsigned>(__min(max_bytes_to_write, UINT_MAX - 1)); 135 136 unsigned const bytes_actually_written = _write_internal(_fileno(stream.public_stream()), data, bytes_to_write, ptd); 137 if (bytes_actually_written == UINT_MAX) // UINT_MAX == -1 138 { 139 stream.set_flags(_IOERROR); 140 return (total_bytes - remaining_bytes) / element_size; 141 } 142 143 // VSWhidbey #326224: _write can return more bytes than we requested 144 // due to Unicode conversions in text files. We do not care how many 145 // bytes were written as long as the number is as least as large as we 146 // requested: 147 unsigned const bytes_written = bytes_actually_written > bytes_to_write 148 ? bytes_to_write 149 : bytes_actually_written; 150 151 // Update the remaining bytes and data to reflect the write: 152 remaining_bytes -= bytes_written; 153 data += bytes_written; 154 155 if (bytes_actually_written < bytes_to_write) 156 { 157 stream.set_flags(_IOERROR); 158 return (total_bytes - remaining_bytes) / element_size; 159 } 160 } 161 // Otherwise, the stream does not have a buffer, or the buffer is full 162 // and there are not enough characters to do a direct write, so use 163 // __acrt_stdio_flush_and_write_narrow_nolock: 164 else 165 { 166 // Write the first character. If this fails, there is nothing we can 167 // do. (Note that if this fails, it will update the stream error state.) 168 if (__acrt_stdio_flush_and_write_narrow_nolock(*data, stream.public_stream(), ptd) == EOF) 169 { 170 return (total_bytes - remaining_bytes) / element_size; 171 } 172 173 // Update the remaining bytes to account for the byte we just wrote: 174 ++data; 175 --remaining_bytes; 176 177 stream_buffer_size = stream->_bufsiz > 0 178 ? stream->_bufsiz 179 : 1; 180 } 181 } 182 183 return element_count; // Success! 184} 185 186extern "C" size_t __cdecl _fwrite_nolock( 187 void const* const buffer, 188 size_t const element_size, 189 size_t const element_count, 190 FILE* const public_stream 191 ) 192{ 193 __crt_cached_ptd_host ptd; 194 return _fwrite_nolock_internal(buffer, element_size, element_count, public_stream, ptd); 195} 196