Reactos
at master 188 lines 5.7 kB view raw
1// 2// _filbuf.cpp 3// 4// Copyright (c) Microsoft Corporation. All rights reserved. 5// 6// Functions that re-fill a stdio stream buffer and return the next character. 7// 8#include <corecrt_internal_stdio.h> 9 10 11 12namespace { 13 14 struct filwbuf_context 15 { 16 bool _is_split_character; 17 unsigned char _leftover_low_order_byte; 18 }; 19 20} 21 22 23 24// These functions store the pre-_read() state of the stream so that it can be 25// used later when we read a character from the newly-filled buffer. 26static int get_context_nolock(__crt_stdio_stream const, char) throw() 27{ 28 return 0; 29} 30 31static filwbuf_context get_context_nolock(__crt_stdio_stream const stream, wchar_t) throw() 32{ 33 // When reading wide character elements, we must handle the case where a two 34 // byte character straddles the buffer boundary, with the low order byte at 35 // the end of the old buffer and the high order byte at the start of the new 36 // buffer. 37 // 38 // We do this here: if there is exactly one character left in the buffer, we 39 // store that and set a flag so we know to pick it up later. 40 filwbuf_context context; 41 if (stream->_cnt == 1) 42 { 43 context._is_split_character = true; 44 context._leftover_low_order_byte = static_cast<unsigned char>(*stream->_ptr); 45 } 46 else 47 { 48 context._is_split_character = false; 49 context._leftover_low_order_byte = 0; 50 } 51 return context; 52} 53 54 55// These functions test whether a buffer is valid following a call to _read(). 56static bool is_buffer_valid_nolock(__crt_stdio_stream const stream, char) throw() 57{ 58 return stream->_cnt != 0 59 && stream->_cnt != -1; 60} 61 62static bool is_buffer_valid_nolock(__crt_stdio_stream const stream, wchar_t) throw() 63{ 64 return stream->_cnt != 0 65 && stream->_cnt != 1 66 && stream->_cnt != -1; 67} 68 69 70 71// These functions read a character from the stream after the _read() has 72// completed successfully. 73static unsigned char read_character_nolock(__crt_stdio_stream const stream, int, char) throw() 74{ 75 --stream->_cnt; 76 return static_cast<unsigned char>(*stream->_ptr++); 77} 78 79 80 81static wchar_t read_character_nolock( 82 __crt_stdio_stream const stream, 83 filwbuf_context const context, 84 wchar_t 85 ) throw() 86{ 87 if (context._is_split_character) 88 { 89 // If the character was split across buffers, we read only one byte 90 // from the new buffer and or it with the leftover byte from the old 91 // buffer. 92 unsigned char high_order_byte = static_cast<unsigned char>(*stream->_ptr); 93 wchar_t result = (high_order_byte << 8) | context._leftover_low_order_byte; 94 95 --stream->_cnt; 96 ++stream->_ptr; 97 return (result); 98 } 99 else 100 { 101 wchar_t const result = 0xffff & reinterpret_cast<wchar_t const&>(*stream->_ptr); 102 103 stream->_cnt -= sizeof(wchar_t); 104 stream->_ptr += sizeof(wchar_t); 105 106 return result; 107 } 108} 109 110 111 112// Fills a buffer and reads the first character. Allocates a buffer for the 113// stream if the stream does not yet have one. This function is intended for 114// internal usage only. This function assumes that the caller has acquired 115// the lock for the stream. 116// 117// Returns the first character from the new buffer. For the wide character 118// version, the case is handled where a character straddles the old and new 119// buffers. Returns EOF if the file is string-backed or is not open for 120// reading, or if there are no more characters to be read. 121template <typename Character> 122static int __cdecl common_refill_and_read_nolock(__crt_stdio_stream const stream) throw() 123{ 124 typedef __acrt_stdio_char_traits<Character> stdio_traits; 125 126 _VALIDATE_RETURN(stream.valid(), EINVAL, stdio_traits::eof); 127 128 if (!stream.is_in_use() || stream.is_string_backed()) 129 return stdio_traits::eof; 130 131 if (stream.has_all_of(_IOWRITE)) 132 { 133 stream.set_flags(_IOERROR); 134 return stdio_traits::eof; 135 } 136 137 stream.set_flags(_IOREAD); 138 139 // Get a buffer, if necessary: 140 if (!stream.has_any_buffer()) 141 __acrt_stdio_allocate_buffer_nolock(stream.public_stream()); 142 143 auto const context = get_context_nolock(stream, Character()); 144 145 stream->_ptr = stream->_base; 146 stream->_cnt = _read(_fileno(stream.public_stream()), stream->_base, stream->_bufsiz); 147 148 if (!is_buffer_valid_nolock(stream, Character())) 149 { 150 stream.set_flags(stream->_cnt != 0 ? _IOERROR : _IOEOF); 151 stream->_cnt = 0; 152 return stdio_traits::eof; 153 } 154 155 if (!stream.has_any_of(_IOWRITE | _IOUPDATE) && 156 ((_osfile_safe(_fileno(stream.public_stream())) & (FTEXT | FEOFLAG)) == (FTEXT | FEOFLAG))) 157 { 158 stream.set_flags(_IOCTRLZ); 159 } 160 161 // Check for small _bufsiz (_SMALL_BUFSIZ). If it is small and if it is our 162 // buffer, then this must be the first call to this function after an fseek 163 // on a read-access-only stream. Restore _bufsiz to its larger value 164 // (_INTERNAL_BUFSIZ) so that the next call to this function, if one is made, 165 // will fill the whole buffer. 166 if (stream->_bufsiz == _SMALL_BUFSIZ && 167 stream.has_crt_buffer() && 168 !stream.has_all_of(_IOBUFFER_SETVBUF)) 169 { 170 stream->_bufsiz = _INTERNAL_BUFSIZ; 171 } 172 173 return read_character_nolock(stream, context, Character()); 174} 175 176 177 178extern "C" int __cdecl __acrt_stdio_refill_and_read_narrow_nolock(FILE* const stream) 179{ 180 return common_refill_and_read_nolock<char>(__crt_stdio_stream(stream)); 181} 182 183 184 185extern "C" int __cdecl __acrt_stdio_refill_and_read_wide_nolock(FILE* const stream) 186{ 187 return common_refill_and_read_nolock<wchar_t>(__crt_stdio_stream(stream)); 188}