mirror of
https://github.com/reactos/reactos.git
synced 2025-05-11 13:27:47 +00:00

Imported from https://www.nuget.org/packages/Microsoft.Windows.SDK.CRTSource/10.0.22621.3 License: MIT
196 lines
7.4 KiB
C++
196 lines
7.4 KiB
C++
//
|
|
// 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);
|
|
}
|
|
|