xref: /reactos/sdk/lib/ucrt/stdio/fwrite.cpp (revision 04e0dc4a)
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.
_fwrite_internal(void const * const buffer,size_t const size,size_t const count,FILE * const stream,__crt_cached_ptd_host & ptd)21 extern "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 
fwrite(void const * const buffer,size_t const size,size_t const count,FILE * const stream)45 extern "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 
_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)56 extern "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 
_fwrite_nolock(void const * const buffer,size_t const element_size,size_t const element_count,FILE * const public_stream)186 extern "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 
197