mirror of
https://github.com/reactos/reactos.git
synced 2025-05-23 11:04:52 +00:00

Imported from https://www.nuget.org/packages/Microsoft.Windows.SDK.CRTSource/10.0.22621.3 License: MIT
188 lines
5.5 KiB
C++
188 lines
5.5 KiB
C++
//
|
|
// _filbuf.cpp
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
// Functions that re-fill a stdio stream buffer and return the next character.
|
|
//
|
|
#include <corecrt_internal_stdio.h>
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
struct filwbuf_context
|
|
{
|
|
bool _is_split_character;
|
|
unsigned char _leftover_low_order_byte;
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
// These functions store the pre-_read() state of the stream so that it can be
|
|
// used later when we read a character from the newly-filled buffer.
|
|
static int get_context_nolock(__crt_stdio_stream const, char) throw()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static filwbuf_context get_context_nolock(__crt_stdio_stream const stream, wchar_t) throw()
|
|
{
|
|
// When reading wide character elements, we must handle the case where a two
|
|
// byte character straddles the buffer boundary, with the low order byte at
|
|
// the end of the old buffer and the high order byte at the start of the new
|
|
// buffer.
|
|
//
|
|
// We do this here: if there is exactly one character left in the buffer, we
|
|
// store that and set a flag so we know to pick it up later.
|
|
filwbuf_context context;
|
|
if (stream->_cnt == 1)
|
|
{
|
|
context._is_split_character = true;
|
|
context._leftover_low_order_byte = static_cast<unsigned char>(*stream->_ptr);
|
|
}
|
|
else
|
|
{
|
|
context._is_split_character = false;
|
|
context._leftover_low_order_byte = 0;
|
|
}
|
|
return context;
|
|
}
|
|
|
|
|
|
// These functions test whether a buffer is valid following a call to _read().
|
|
static bool is_buffer_valid_nolock(__crt_stdio_stream const stream, char) throw()
|
|
{
|
|
return stream->_cnt != 0
|
|
&& stream->_cnt != -1;
|
|
}
|
|
|
|
static bool is_buffer_valid_nolock(__crt_stdio_stream const stream, wchar_t) throw()
|
|
{
|
|
return stream->_cnt != 0
|
|
&& stream->_cnt != 1
|
|
&& stream->_cnt != -1;
|
|
}
|
|
|
|
|
|
|
|
// These functions read a character from the stream after the _read() has
|
|
// completed successfully.
|
|
static unsigned char read_character_nolock(__crt_stdio_stream const stream, int, char) throw()
|
|
{
|
|
--stream->_cnt;
|
|
return static_cast<unsigned char>(*stream->_ptr++);
|
|
}
|
|
|
|
|
|
|
|
static wchar_t read_character_nolock(
|
|
__crt_stdio_stream const stream,
|
|
filwbuf_context const context,
|
|
wchar_t
|
|
) throw()
|
|
{
|
|
if (context._is_split_character)
|
|
{
|
|
// If the character was split across buffers, we read only one byte
|
|
// from the new buffer and or it with the leftover byte from the old
|
|
// buffer.
|
|
unsigned char high_order_byte = static_cast<unsigned char>(*stream->_ptr);
|
|
wchar_t result = (high_order_byte << 8) | context._leftover_low_order_byte;
|
|
|
|
--stream->_cnt;
|
|
++stream->_ptr;
|
|
return (result);
|
|
}
|
|
else
|
|
{
|
|
wchar_t const result = 0xffff & reinterpret_cast<wchar_t const&>(*stream->_ptr);
|
|
|
|
stream->_cnt -= sizeof(wchar_t);
|
|
stream->_ptr += sizeof(wchar_t);
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Fills a buffer and reads the first character. Allocates a buffer for the
|
|
// stream if the stream does not yet have one. This function is intended for
|
|
// internal usage only. This function assumes that the caller has acquired
|
|
// the lock for the stream.
|
|
//
|
|
// Returns the first character from the new buffer. For the wide character
|
|
// version, the case is handled where a character straddles the old and new
|
|
// buffers. Returns EOF if the file is string-backed or is not open for
|
|
// reading, or if there are no more characters to be read.
|
|
template <typename Character>
|
|
static int __cdecl common_refill_and_read_nolock(__crt_stdio_stream const stream) throw()
|
|
{
|
|
typedef __acrt_stdio_char_traits<Character> stdio_traits;
|
|
|
|
_VALIDATE_RETURN(stream.valid(), EINVAL, stdio_traits::eof);
|
|
|
|
if (!stream.is_in_use() || stream.is_string_backed())
|
|
return stdio_traits::eof;
|
|
|
|
if (stream.has_all_of(_IOWRITE))
|
|
{
|
|
stream.set_flags(_IOERROR);
|
|
return stdio_traits::eof;
|
|
}
|
|
|
|
stream.set_flags(_IOREAD);
|
|
|
|
// Get a buffer, if necessary:
|
|
if (!stream.has_any_buffer())
|
|
__acrt_stdio_allocate_buffer_nolock(stream.public_stream());
|
|
|
|
auto const context = get_context_nolock(stream, Character());
|
|
|
|
stream->_ptr = stream->_base;
|
|
stream->_cnt = _read(_fileno(stream.public_stream()), stream->_base, stream->_bufsiz);
|
|
|
|
if (!is_buffer_valid_nolock(stream, Character()))
|
|
{
|
|
stream.set_flags(stream->_cnt != 0 ? _IOERROR : _IOEOF);
|
|
stream->_cnt = 0;
|
|
return stdio_traits::eof;
|
|
}
|
|
|
|
if (!stream.has_any_of(_IOWRITE | _IOUPDATE) &&
|
|
((_osfile_safe(_fileno(stream.public_stream())) & (FTEXT | FEOFLAG)) == (FTEXT | FEOFLAG)))
|
|
{
|
|
stream.set_flags(_IOCTRLZ);
|
|
}
|
|
|
|
// Check for small _bufsiz (_SMALL_BUFSIZ). If it is small and if it is our
|
|
// buffer, then this must be the first call to this function after an fseek
|
|
// on a read-access-only stream. Restore _bufsiz to its larger value
|
|
// (_INTERNAL_BUFSIZ) so that the next call to this function, if one is made,
|
|
// will fill the whole buffer.
|
|
if (stream->_bufsiz == _SMALL_BUFSIZ &&
|
|
stream.has_crt_buffer() &&
|
|
!stream.has_all_of(_IOBUFFER_SETVBUF))
|
|
{
|
|
stream->_bufsiz = _INTERNAL_BUFSIZ;
|
|
}
|
|
|
|
return read_character_nolock(stream, context, Character());
|
|
}
|
|
|
|
|
|
|
|
extern "C" int __cdecl __acrt_stdio_refill_and_read_narrow_nolock(FILE* const stream)
|
|
{
|
|
return common_refill_and_read_nolock<char>(__crt_stdio_stream(stream));
|
|
}
|
|
|
|
|
|
|
|
extern "C" int __cdecl __acrt_stdio_refill_and_read_wide_nolock(FILE* const stream)
|
|
{
|
|
return common_refill_and_read_nolock<wchar_t>(__crt_stdio_stream(stream));
|
|
}
|