// // fwrite.cpp // // Copyright (c) Microsoft Corporation. All rights reserved. // // Defines fwrite() and related functions, which write unformatted data to a // stdio stream. // #include <corecrt_internal_stdio.h> #include <corecrt_internal_ptd_propagation.h> // Writes data from the provided buffer to the specified stream. The function // writes 'count' elements of 'size' size to the stream, and returns when // either all of the elements have been written or no more data can be written // (e.g. if EOF is encountered or an error occurs). // // Returns the number of "whole" elements that were written to the stream. This // may be fewer than the requested number of an error occurs or EOF is encountered. // In this case, ferror() or feof() should be used to distinguish between the two // conditions. extern "C" size_t __cdecl _fwrite_internal( void const* const buffer, size_t const size, size_t const count, FILE* const stream, __crt_cached_ptd_host& ptd ) { if (size == 0 || count == 0) { return 0; } // The _nolock version will do the rest of the validation. _UCRT_VALIDATE_RETURN(ptd, stream != nullptr, EINVAL, 0); return __acrt_lock_stream_and_call(stream, [&]() -> size_t { __acrt_stdio_temporary_buffering_guard const buffering(stream, ptd); return _fwrite_nolock_internal(buffer, size, count, stream, ptd); }); } extern "C" size_t __cdecl fwrite( void const* const buffer, size_t const size, size_t const count, FILE* const stream ) { __crt_cached_ptd_host ptd; return _fwrite_internal(buffer, size, count, stream, ptd); } extern "C" size_t __cdecl _fwrite_nolock_internal( void const* const buffer, size_t const element_size, size_t const element_count, FILE* const public_stream, __crt_cached_ptd_host& ptd ) { if (element_size == 0 || element_count == 0) { return 0; } __crt_stdio_stream const stream(public_stream); _UCRT_VALIDATE_RETURN(ptd, stream.valid(), EINVAL, 0); _UCRT_VALIDATE_RETURN(ptd, buffer != nullptr, EINVAL, 0); _UCRT_VALIDATE_RETURN(ptd, element_count <= (SIZE_MAX / element_size), EINVAL, 0); // Figure out how big the buffer is; if the stream doesn't currently have a // buffer, we assume that we'll get one with the usual internal buffer size: unsigned stream_buffer_size = stream.has_any_buffer() ? stream->_bufsiz : _INTERNAL_BUFSIZ; // The total number of bytes to be written to the stream: size_t const total_bytes = element_size * element_count; char const* data = static_cast<char const*>(buffer); // Write blocks of data from the buffer until there is no more data left: size_t remaining_bytes = total_bytes; while (remaining_bytes != 0) { // If the buffer is big and is not full, copy data into the buffer: if (stream.has_big_buffer() && stream->_cnt != 0) { if (stream->_cnt < 0) { _ASSERTE(("Inconsistent Stream Count. Flush between consecutive read and write", stream->_cnt >= 0)); stream.set_flags(_IOERROR); return (total_bytes - remaining_bytes) / element_size; } if (stream.has_any_of(_IOREAD)) { _ASSERTE(("Flush between consecutive read and write.", !stream.has_any_of(_IOREAD))); return (total_bytes - remaining_bytes) / element_size; } size_t const bytes_to_write = __min(remaining_bytes, static_cast<size_t>(stream->_cnt)); memcpy(stream->_ptr, data, bytes_to_write); remaining_bytes -= bytes_to_write; stream->_cnt -= static_cast<int>(bytes_to_write); stream->_ptr += bytes_to_write; data += bytes_to_write; } // If we have more than stream_buffer_size bytes to write, write data by // calling _write() with an integral number of stream_buffer_size blocks. else if (remaining_bytes >= stream_buffer_size) { // If we reach here and we have a big buffer, it must be full, so // flush it. If the flush fails, there's nothing we can do to // recover: if (stream.has_big_buffer() && __acrt_stdio_flush_nolock(stream.public_stream(), ptd)) { return (total_bytes - remaining_bytes) / element_size; } // Calculate the number of bytes to write. The _write API takes a // 32-bit unsigned byte count and returns -1 (UINT_MAX) on failure, // so clamp the value to UINT_MAX - 1: size_t const max_bytes_to_write = stream_buffer_size > 0 ? remaining_bytes - remaining_bytes % stream_buffer_size : remaining_bytes; unsigned const bytes_to_write = static_cast<unsigned>(__min(max_bytes_to_write, UINT_MAX - 1)); unsigned const bytes_actually_written = _write_internal(_fileno(stream.public_stream()), data, bytes_to_write, ptd); if (bytes_actually_written == UINT_MAX) // UINT_MAX == -1 { stream.set_flags(_IOERROR); return (total_bytes - remaining_bytes) / element_size; } // VSWhidbey #326224: _write can return more bytes than we requested // due to Unicode conversions in text files. We do not care how many // bytes were written as long as the number is as least as large as we // requested: unsigned const bytes_written = bytes_actually_written > bytes_to_write ? bytes_to_write : bytes_actually_written; // Update the remaining bytes and data to reflect the write: remaining_bytes -= bytes_written; data += bytes_written; if (bytes_actually_written < bytes_to_write) { stream.set_flags(_IOERROR); return (total_bytes - remaining_bytes) / element_size; } } // Otherwise, the stream does not have a buffer, or the buffer is full // and there are not enough characters to do a direct write, so use // __acrt_stdio_flush_and_write_narrow_nolock: else { // Write the first character. If this fails, there is nothing we can // do. (Note that if this fails, it will update the stream error state.) if (__acrt_stdio_flush_and_write_narrow_nolock(*data, stream.public_stream(), ptd) == EOF) { return (total_bytes - remaining_bytes) / element_size; } // Update the remaining bytes to account for the byte we just wrote: ++data; --remaining_bytes; stream_buffer_size = stream->_bufsiz > 0 ? stream->_bufsiz : 1; } } return element_count; // Success! } extern "C" size_t __cdecl _fwrite_nolock( void const* const buffer, size_t const element_size, size_t const element_count, FILE* const public_stream ) { __crt_cached_ptd_host ptd; return _fwrite_nolock_internal(buffer, element_size, element_count, public_stream, ptd); }