1315 lines
54 KiB
C#
1315 lines
54 KiB
C#
// ==++==
|
||
//
|
||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
//
|
||
// ==--==
|
||
/*============================================================
|
||
**
|
||
** Class: StreamReader
|
||
**
|
||
** <OWNER>gpaperin</OWNER>
|
||
**
|
||
**
|
||
** Purpose: For reading text from streams in a particular
|
||
** encoding.
|
||
**
|
||
**
|
||
===========================================================*/
|
||
|
||
using System;
|
||
using System.Text;
|
||
using System.Runtime.InteropServices;
|
||
using System.Runtime.Versioning;
|
||
using System.Diagnostics.CodeAnalysis;
|
||
using System.Diagnostics.Contracts;
|
||
using System.Security.Permissions;
|
||
#if FEATURE_ASYNC_IO
|
||
using System.Threading.Tasks;
|
||
#endif
|
||
|
||
namespace System.IO
|
||
{
|
||
// This class implements a TextReader for reading characters to a Stream.
|
||
// This is designed for character input in a particular Encoding,
|
||
// whereas the Stream class is designed for byte input and output.
|
||
//
|
||
[Serializable]
|
||
[System.Runtime.InteropServices.ComVisible(true)]
|
||
public class StreamReader : TextReader
|
||
{
|
||
// StreamReader.Null is threadsafe.
|
||
public new static readonly StreamReader Null = new NullStreamReader();
|
||
|
||
// Using a 1K byte buffer and a 4K FileStream buffer works out pretty well
|
||
// perf-wise. On even a 40 MB text file, any perf loss by using a 4K
|
||
// buffer is negated by the win of allocating a smaller byte[], which
|
||
// saves construction time. This does break adaptive buffering,
|
||
// but this is slightly faster.
|
||
internal static int DefaultBufferSize
|
||
{
|
||
get
|
||
{
|
||
#if FEATURE_LEGACYNETCF
|
||
// Quirk for Mango app compatibility
|
||
if (CompatibilitySwitches.IsAppEarlierThanWindowsPhone8)
|
||
{
|
||
return 4096;
|
||
}
|
||
#endif // FEATURE_LEGACYNETCF
|
||
return 1024;
|
||
}
|
||
}
|
||
|
||
private const int DefaultFileStreamBufferSize = 4096;
|
||
private const int MinBufferSize = 128;
|
||
|
||
private Stream stream;
|
||
private Encoding encoding;
|
||
private Decoder decoder;
|
||
private byte[] byteBuffer;
|
||
private char[] charBuffer;
|
||
private byte[] _preamble; // Encoding's preamble, which identifies this encoding.
|
||
private int charPos;
|
||
private int charLen;
|
||
// Record the number of valid bytes in the byteBuffer, for a few checks.
|
||
private int byteLen;
|
||
// This is used only for preamble detection
|
||
private int bytePos;
|
||
|
||
// This is the maximum number of chars we can get from one call to
|
||
// ReadBuffer. Used so ReadBuffer can tell when to copy data into
|
||
// a user's char[] directly, instead of our internal char[].
|
||
private int _maxCharsPerBuffer;
|
||
|
||
// We will support looking for byte order marks in the stream and trying
|
||
// to decide what the encoding might be from the byte order marks, IF they
|
||
// exist. But that's all we'll do.
|
||
private bool _detectEncoding;
|
||
|
||
// Whether we must still check for the encoding's given preamble at the
|
||
// beginning of this file.
|
||
private bool _checkPreamble;
|
||
|
||
// Whether the stream is most likely not going to give us back as much
|
||
// data as we want the next time we call it. We must do the computation
|
||
// before we do any byte order mark handling and save the result. Note
|
||
// that we need this to allow users to handle streams used for an
|
||
// interactive protocol, where they block waiting for the remote end
|
||
// to send a response, like logging in on a Unix machine.
|
||
private bool _isBlocked;
|
||
|
||
// The intent of this field is to leave open the underlying stream when
|
||
// disposing of this StreamReader. A name like _leaveOpen is better,
|
||
// but this type is serializable, and this field's name was _closable.
|
||
private bool _closable; // Whether to close the underlying stream.
|
||
|
||
#if FEATURE_ASYNC_IO
|
||
// We don't guarantee thread safety on StreamReader, but we should at
|
||
// least prevent users from trying to read anything while an Async
|
||
// read from the same thread is in progress.
|
||
[NonSerialized]
|
||
private volatile Task _asyncReadTask;
|
||
|
||
private void CheckAsyncTaskInProgress()
|
||
{
|
||
// We are not locking the access to _asyncReadTask because this is not meant to guarantee thread safety.
|
||
// We are simply trying to deter calling any Read APIs while an async Read from the same thread is in progress.
|
||
|
||
Task t = _asyncReadTask;
|
||
|
||
if (t != null && !t.IsCompleted)
|
||
throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_AsyncIOInProgress"));
|
||
}
|
||
#endif
|
||
|
||
// StreamReader by default will ignore illegal UTF8 characters. We don't want to
|
||
// throw here because we want to be able to read ill-formed data without choking.
|
||
// The high level goal is to be tolerant of encoding errors when we read and very strict
|
||
// when we write. Hence, default StreamWriter encoding will throw on error.
|
||
|
||
internal StreamReader() {
|
||
}
|
||
|
||
public StreamReader(Stream stream)
|
||
: this(stream, true) {
|
||
}
|
||
|
||
public StreamReader(Stream stream, bool detectEncodingFromByteOrderMarks)
|
||
: this(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks, DefaultBufferSize, false) {
|
||
}
|
||
|
||
public StreamReader(Stream stream, Encoding encoding)
|
||
: this(stream, encoding, true, DefaultBufferSize, false) {
|
||
}
|
||
|
||
public StreamReader(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks)
|
||
: this(stream, encoding, detectEncodingFromByteOrderMarks, DefaultBufferSize, false) {
|
||
}
|
||
|
||
// Creates a new StreamReader for the given stream. The
|
||
// character encoding is set by encoding and the buffer size,
|
||
// in number of 16-bit characters, is set by bufferSize.
|
||
//
|
||
// Note that detectEncodingFromByteOrderMarks is a very
|
||
// loose attempt at detecting the encoding by looking at the first
|
||
// 3 bytes of the stream. It will recognize UTF-8, little endian
|
||
// unicode, and big endian unicode text, but that's it. If neither
|
||
// of those three match, it will use the Encoding you provided.
|
||
//
|
||
public StreamReader(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize)
|
||
: this(stream, encoding, detectEncodingFromByteOrderMarks, bufferSize, false) {
|
||
}
|
||
|
||
public StreamReader(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize, bool leaveOpen)
|
||
{
|
||
if (stream == null || encoding == null)
|
||
throw new ArgumentNullException((stream == null ? "stream" : "encoding"));
|
||
if (!stream.CanRead)
|
||
throw new ArgumentException(Environment.GetResourceString("Argument_StreamNotReadable"));
|
||
if (bufferSize <= 0)
|
||
throw new ArgumentOutOfRangeException("bufferSize", Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum"));
|
||
Contract.EndContractBlock();
|
||
|
||
Init(stream, encoding, detectEncodingFromByteOrderMarks, bufferSize, leaveOpen);
|
||
}
|
||
|
||
#if FEATURE_LEGACYNETCF
|
||
[System.Security.SecuritySafeCritical]
|
||
#endif // FEATURE_LEGACYNETCF
|
||
[ResourceExposure(ResourceScope.Machine)]
|
||
[ResourceConsumption(ResourceScope.Machine)]
|
||
public StreamReader(String path)
|
||
: this(path, true) {
|
||
#if FEATURE_LEGACYNETCF
|
||
if(CompatibilitySwitches.IsAppEarlierThanWindowsPhone8) {
|
||
System.Reflection.Assembly callingAssembly = System.Reflection.Assembly.GetCallingAssembly();
|
||
if(callingAssembly != null && !callingAssembly.IsProfileAssembly) {
|
||
string caller = new System.Diagnostics.StackFrame(1).GetMethod().FullName;
|
||
string callee = System.Reflection.MethodBase.GetCurrentMethod().FullName;
|
||
throw new MethodAccessException(String.Format(
|
||
System.Globalization.CultureInfo.CurrentCulture,
|
||
Environment.GetResourceString("Arg_MethodAccessException_WithCaller"),
|
||
caller,
|
||
callee));
|
||
}
|
||
}
|
||
#endif // FEATURE_LEGACYNETCF
|
||
}
|
||
|
||
[ResourceExposure(ResourceScope.Machine)]
|
||
[ResourceConsumption(ResourceScope.Machine)]
|
||
public StreamReader(String path, bool detectEncodingFromByteOrderMarks)
|
||
: this(path, Encoding.UTF8, detectEncodingFromByteOrderMarks, DefaultBufferSize) {
|
||
}
|
||
|
||
[ResourceExposure(ResourceScope.Machine)]
|
||
[ResourceConsumption(ResourceScope.Machine)]
|
||
public StreamReader(String path, Encoding encoding)
|
||
: this(path, encoding, true, DefaultBufferSize) {
|
||
}
|
||
|
||
[ResourceExposure(ResourceScope.Machine)]
|
||
[ResourceConsumption(ResourceScope.Machine)]
|
||
public StreamReader(String path, Encoding encoding, bool detectEncodingFromByteOrderMarks)
|
||
: this(path, encoding, detectEncodingFromByteOrderMarks, DefaultBufferSize) {
|
||
}
|
||
|
||
[System.Security.SecuritySafeCritical]
|
||
[ResourceExposure(ResourceScope.Machine)]
|
||
[ResourceConsumption(ResourceScope.Machine)]
|
||
public StreamReader(String path, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize)
|
||
: this(path, encoding, detectEncodingFromByteOrderMarks, bufferSize, true) {
|
||
}
|
||
|
||
[System.Security.SecurityCritical]
|
||
[ResourceExposure(ResourceScope.Machine)]
|
||
[ResourceConsumption(ResourceScope.Machine)]
|
||
internal StreamReader(String path, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize, bool checkHost)
|
||
{
|
||
// Don't open a Stream before checking for invalid arguments,
|
||
// or we'll create a FileStream on disk and we won't close it until
|
||
// the finalizer runs, causing problems for applications.
|
||
if (path==null || encoding==null)
|
||
throw new ArgumentNullException((path==null ? "path" : "encoding"));
|
||
if (path.Length==0)
|
||
throw new ArgumentException(Environment.GetResourceString("Argument_EmptyPath"));
|
||
if (bufferSize <= 0)
|
||
throw new ArgumentOutOfRangeException("bufferSize", Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum"));
|
||
Contract.EndContractBlock();
|
||
|
||
Stream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, DefaultFileStreamBufferSize, FileOptions.SequentialScan, Path.GetFileName(path), false, false, checkHost);
|
||
Init(stream, encoding, detectEncodingFromByteOrderMarks, bufferSize, false);
|
||
}
|
||
|
||
private void Init(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize, bool leaveOpen) {
|
||
this.stream = stream;
|
||
this.encoding = encoding;
|
||
decoder = encoding.GetDecoder();
|
||
if (bufferSize < MinBufferSize) bufferSize = MinBufferSize;
|
||
byteBuffer = new byte[bufferSize];
|
||
_maxCharsPerBuffer = encoding.GetMaxCharCount(bufferSize);
|
||
charBuffer = new char[_maxCharsPerBuffer];
|
||
byteLen = 0;
|
||
bytePos = 0;
|
||
_detectEncoding = detectEncodingFromByteOrderMarks;
|
||
_preamble = encoding.GetPreamble();
|
||
_checkPreamble = (_preamble.Length > 0);
|
||
_isBlocked = false;
|
||
_closable = !leaveOpen;
|
||
}
|
||
|
||
// Init used by NullStreamReader, to delay load encoding
|
||
internal void Init(Stream stream)
|
||
{
|
||
this.stream = stream;
|
||
_closable = true;
|
||
}
|
||
|
||
public override void Close()
|
||
{
|
||
Dispose(true);
|
||
}
|
||
|
||
protected override void Dispose(bool disposing)
|
||
{
|
||
// Dispose of our resources if this StreamReader is closable.
|
||
// Note that Console.In should be left open.
|
||
try {
|
||
// Note that Stream.Close() can potentially throw here. So we need to
|
||
// ensure cleaning up internal resources, inside the finally block.
|
||
if (!LeaveOpen && disposing && (stream != null))
|
||
stream.Close();
|
||
}
|
||
finally {
|
||
if (!LeaveOpen && (stream != null)) {
|
||
stream = null;
|
||
encoding = null;
|
||
decoder = null;
|
||
byteBuffer = null;
|
||
charBuffer = null;
|
||
charPos = 0;
|
||
charLen = 0;
|
||
base.Dispose(disposing);
|
||
}
|
||
}
|
||
}
|
||
|
||
public virtual Encoding CurrentEncoding {
|
||
get { return encoding; }
|
||
}
|
||
|
||
public virtual Stream BaseStream {
|
||
get { return stream; }
|
||
}
|
||
|
||
internal bool LeaveOpen {
|
||
get { return !_closable; }
|
||
}
|
||
|
||
// DiscardBufferedData tells StreamReader to throw away its internal
|
||
// buffer contents. This is useful if the user needs to seek on the
|
||
// underlying stream to a known location then wants the StreamReader
|
||
// to start reading from this new point. This method should be called
|
||
// very sparingly, if ever, since it can lead to very poor performance.
|
||
// However, it may be the only way of handling some scenarios where
|
||
// users need to re-read the contents of a StreamReader a second time.
|
||
public void DiscardBufferedData()
|
||
{
|
||
|
||
#if FEATURE_ASYNC_IO
|
||
CheckAsyncTaskInProgress();
|
||
#endif
|
||
|
||
byteLen = 0;
|
||
charLen = 0;
|
||
charPos = 0;
|
||
// in general we'd like to have an invariant that encoding isn't null. However,
|
||
// for startup improvements for NullStreamReader, we want to delay load encoding.
|
||
if (encoding != null) {
|
||
decoder = encoding.GetDecoder();
|
||
}
|
||
_isBlocked = false;
|
||
}
|
||
|
||
public bool EndOfStream {
|
||
get {
|
||
if (stream == null)
|
||
__Error.ReaderClosed();
|
||
|
||
#if FEATURE_ASYNC_IO
|
||
CheckAsyncTaskInProgress();
|
||
#endif
|
||
|
||
if (charPos < charLen)
|
||
return false;
|
||
|
||
// This may block on pipes!
|
||
int numRead = ReadBuffer();
|
||
return numRead == 0;
|
||
}
|
||
}
|
||
|
||
[Pure]
|
||
public override int Peek() {
|
||
if (stream == null)
|
||
__Error.ReaderClosed();
|
||
|
||
#if FEATURE_ASYNC_IO
|
||
CheckAsyncTaskInProgress();
|
||
#endif
|
||
|
||
if (charPos == charLen)
|
||
{
|
||
if (_isBlocked || ReadBuffer() == 0) return -1;
|
||
}
|
||
return charBuffer[charPos];
|
||
}
|
||
|
||
#if MONO
|
||
//
|
||
// Used internally by our console, as it previously depended on Peek() being a
|
||
// routine that would not block.
|
||
//
|
||
internal bool DataAvailable ()
|
||
{
|
||
return charPos < charLen;
|
||
}
|
||
#endif
|
||
|
||
public override int Read() {
|
||
if (stream == null)
|
||
__Error.ReaderClosed();
|
||
|
||
#if FEATURE_ASYNC_IO
|
||
CheckAsyncTaskInProgress();
|
||
#endif
|
||
|
||
if (charPos == charLen) {
|
||
if (ReadBuffer() == 0) return -1;
|
||
}
|
||
int result = charBuffer[charPos];
|
||
charPos++;
|
||
return result;
|
||
}
|
||
|
||
public override int Read([In, Out] char[] buffer, int index, int count)
|
||
{
|
||
if (buffer==null)
|
||
throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer"));
|
||
if (index < 0 || count < 0)
|
||
throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
|
||
if (buffer.Length - index < count)
|
||
throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
|
||
Contract.EndContractBlock();
|
||
|
||
if (stream == null)
|
||
__Error.ReaderClosed();
|
||
|
||
#if FEATURE_ASYNC_IO
|
||
CheckAsyncTaskInProgress();
|
||
#endif
|
||
|
||
int charsRead = 0;
|
||
// As a perf optimization, if we had exactly one buffer's worth of
|
||
// data read in, let's try writing directly to the user's buffer.
|
||
bool readToUserBuffer = false;
|
||
while (count > 0) {
|
||
int n = charLen - charPos;
|
||
if (n == 0) n = ReadBuffer(buffer, index + charsRead, count, out readToUserBuffer);
|
||
if (n == 0) break; // We're at EOF
|
||
if (n > count) n = count;
|
||
if (!readToUserBuffer) {
|
||
Buffer.InternalBlockCopy(charBuffer, charPos * 2, buffer, (index + charsRead) * 2, n*2);
|
||
charPos += n;
|
||
}
|
||
charsRead += n;
|
||
count -= n;
|
||
// This function shouldn't block for an indefinite amount of time,
|
||
// or reading from a network stream won't work right. If we got
|
||
// fewer bytes than we requested, then we want to break right here.
|
||
if (_isBlocked)
|
||
break;
|
||
}
|
||
|
||
return charsRead;
|
||
}
|
||
|
||
public override String ReadToEnd()
|
||
{
|
||
if (stream == null)
|
||
__Error.ReaderClosed();
|
||
|
||
#if FEATURE_ASYNC_IO
|
||
CheckAsyncTaskInProgress();
|
||
#endif
|
||
|
||
// Call ReadBuffer, then pull data out of charBuffer.
|
||
StringBuilder sb = new StringBuilder(charLen - charPos);
|
||
do {
|
||
sb.Append(charBuffer, charPos, charLen - charPos);
|
||
charPos = charLen; // Note we consumed these characters
|
||
ReadBuffer();
|
||
} while (charLen > 0);
|
||
return sb.ToString();
|
||
}
|
||
|
||
public override int ReadBlock([In, Out] char[] buffer, int index, int count)
|
||
{
|
||
if (buffer==null)
|
||
throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer"));
|
||
if (index < 0 || count < 0)
|
||
throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
|
||
if (buffer.Length - index < count)
|
||
throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
|
||
Contract.EndContractBlock();
|
||
|
||
if (stream == null)
|
||
__Error.ReaderClosed();
|
||
|
||
#if FEATURE_ASYNC_IO
|
||
CheckAsyncTaskInProgress();
|
||
#endif
|
||
|
||
return base.ReadBlock(buffer, index, count);
|
||
}
|
||
|
||
// Trims n bytes from the front of the buffer.
|
||
private void CompressBuffer(int n)
|
||
{
|
||
Contract.Assert(byteLen >= n, "CompressBuffer was called with a number of bytes greater than the current buffer length. Are two threads using this StreamReader at the same time?");
|
||
Buffer.InternalBlockCopy(byteBuffer, n, byteBuffer, 0, byteLen - n);
|
||
byteLen -= n;
|
||
}
|
||
|
||
private void DetectEncoding()
|
||
{
|
||
if (byteLen < 2)
|
||
return;
|
||
_detectEncoding = false;
|
||
bool changedEncoding = false;
|
||
if (byteBuffer[0]==0xFE && byteBuffer[1]==0xFF) {
|
||
// Big Endian Unicode
|
||
|
||
encoding = new UnicodeEncoding(true, true);
|
||
CompressBuffer(2);
|
||
changedEncoding = true;
|
||
}
|
||
|
||
else if (byteBuffer[0]==0xFF && byteBuffer[1]==0xFE) {
|
||
// Little Endian Unicode, or possibly little endian UTF32
|
||
if (byteLen < 4 || byteBuffer[2] != 0 || byteBuffer[3] != 0) {
|
||
encoding = new UnicodeEncoding(false, true);
|
||
CompressBuffer(2);
|
||
changedEncoding = true;
|
||
}
|
||
#if FEATURE_UTF32
|
||
else {
|
||
encoding = new UTF32Encoding(false, true);
|
||
CompressBuffer(4);
|
||
changedEncoding = true;
|
||
}
|
||
#endif
|
||
}
|
||
|
||
else if (byteLen >= 3 && byteBuffer[0]==0xEF && byteBuffer[1]==0xBB && byteBuffer[2]==0xBF) {
|
||
// UTF-8
|
||
encoding = Encoding.UTF8;
|
||
CompressBuffer(3);
|
||
changedEncoding = true;
|
||
}
|
||
#if FEATURE_UTF32
|
||
else if (byteLen >= 4 && byteBuffer[0] == 0 && byteBuffer[1] == 0 &&
|
||
byteBuffer[2] == 0xFE && byteBuffer[3] == 0xFF) {
|
||
// Big Endian UTF32
|
||
encoding = new UTF32Encoding(true, true);
|
||
CompressBuffer(4);
|
||
changedEncoding = true;
|
||
}
|
||
#endif
|
||
else if (byteLen == 2)
|
||
_detectEncoding = true;
|
||
// Note: in the future, if we change this algorithm significantly,
|
||
// we can support checking for the preamble of the given encoding.
|
||
|
||
if (changedEncoding) {
|
||
decoder = encoding.GetDecoder();
|
||
_maxCharsPerBuffer = encoding.GetMaxCharCount(byteBuffer.Length);
|
||
charBuffer = new char[_maxCharsPerBuffer];
|
||
}
|
||
}
|
||
|
||
// Trims the preamble bytes from the byteBuffer. This routine can be called multiple times
|
||
// and we will buffer the bytes read until the preamble is matched or we determine that
|
||
// there is no match. If there is no match, every byte read previously will be available
|
||
// for further consumption. If there is a match, we will compress the buffer for the
|
||
// leading preamble bytes
|
||
private bool IsPreamble()
|
||
{
|
||
if (!_checkPreamble)
|
||
return _checkPreamble;
|
||
|
||
Contract.Assert(bytePos <= _preamble.Length, "_compressPreamble was called with the current bytePos greater than the preamble buffer length. Are two threads using this StreamReader at the same time?");
|
||
int len = (byteLen >= (_preamble.Length))? (_preamble.Length - bytePos) : (byteLen - bytePos);
|
||
|
||
for(int i=0; i<len; i++, bytePos++) {
|
||
if (byteBuffer[bytePos] != _preamble[bytePos]) {
|
||
bytePos = 0;
|
||
_checkPreamble = false;
|
||
break;
|
||
}
|
||
}
|
||
|
||
Contract.Assert(bytePos <= _preamble.Length, "possible bug in _compressPreamble. Are two threads using this StreamReader at the same time?");
|
||
|
||
if (_checkPreamble) {
|
||
if (bytePos == _preamble.Length) {
|
||
// We have a match
|
||
CompressBuffer(_preamble.Length);
|
||
bytePos = 0;
|
||
_checkPreamble = false;
|
||
_detectEncoding = false;
|
||
}
|
||
}
|
||
|
||
return _checkPreamble;
|
||
}
|
||
|
||
internal virtual int ReadBuffer() {
|
||
charLen = 0;
|
||
charPos = 0;
|
||
|
||
if (!_checkPreamble)
|
||
byteLen = 0;
|
||
do {
|
||
if (_checkPreamble) {
|
||
Contract.Assert(bytePos <= _preamble.Length, "possible bug in _compressPreamble. Are two threads using this StreamReader at the same time?");
|
||
int len = stream.Read(byteBuffer, bytePos, byteBuffer.Length - bytePos);
|
||
Contract.Assert(len >= 0, "Stream.Read returned a negative number! This is a bug in your stream class.");
|
||
|
||
if (len == 0) {
|
||
// EOF but we might have buffered bytes from previous
|
||
// attempt to detect preamble that needs to be decoded now
|
||
if (byteLen > 0)
|
||
{
|
||
charLen += decoder.GetChars(byteBuffer, 0, byteLen, charBuffer, charLen);
|
||
// Need to zero out the byteLen after we consume these bytes so that we don't keep infinitely hitting this code path
|
||
bytePos = byteLen = 0;
|
||
}
|
||
|
||
return charLen;
|
||
}
|
||
|
||
byteLen += len;
|
||
}
|
||
else {
|
||
Contract.Assert(bytePos == 0, "bytePos can be non zero only when we are trying to _checkPreamble. Are two threads using this StreamReader at the same time?");
|
||
byteLen = stream.Read(byteBuffer, 0, byteBuffer.Length);
|
||
Contract.Assert(byteLen >= 0, "Stream.Read returned a negative number! This is a bug in your stream class.");
|
||
|
||
if (byteLen == 0) // We're at EOF
|
||
return charLen;
|
||
}
|
||
|
||
// _isBlocked == whether we read fewer bytes than we asked for.
|
||
// Note we must check it here because CompressBuffer or
|
||
// DetectEncoding will change byteLen.
|
||
_isBlocked = (byteLen < byteBuffer.Length);
|
||
|
||
// Check for preamble before detect encoding. This is not to override the
|
||
// user suppplied Encoding for the one we implicitly detect. The user could
|
||
// customize the encoding which we will loose, such as ThrowOnError on UTF8
|
||
if (IsPreamble())
|
||
continue;
|
||
|
||
// If we're supposed to detect the encoding and haven't done so yet,
|
||
// do it. Note this may need to be called more than once.
|
||
if (_detectEncoding && byteLen >= 2)
|
||
DetectEncoding();
|
||
|
||
charLen += decoder.GetChars(byteBuffer, 0, byteLen, charBuffer, charLen);
|
||
} while (charLen == 0);
|
||
//Console.WriteLine("ReadBuffer called. chars: "+charLen);
|
||
return charLen;
|
||
}
|
||
|
||
|
||
// This version has a perf optimization to decode data DIRECTLY into the
|
||
// user's buffer, bypassing StreamReader's own buffer.
|
||
// This gives a > 20% perf improvement for our encodings across the board,
|
||
// but only when asking for at least the number of characters that one
|
||
// buffer's worth of bytes could produce.
|
||
// This optimization, if run, will break SwitchEncoding, so we must not do
|
||
// this on the first call to ReadBuffer.
|
||
private int ReadBuffer(char[] userBuffer, int userOffset, int desiredChars, out bool readToUserBuffer)
|
||
{
|
||
charLen = 0;
|
||
charPos = 0;
|
||
|
||
if (!_checkPreamble)
|
||
byteLen = 0;
|
||
|
||
int charsRead = 0;
|
||
|
||
// As a perf optimization, we can decode characters DIRECTLY into a
|
||
// user's char[]. We absolutely must not write more characters
|
||
// into the user's buffer than they asked for. Calculating
|
||
// encoding.GetMaxCharCount(byteLen) each time is potentially very
|
||
// expensive - instead, cache the number of chars a full buffer's
|
||
// worth of data may produce. Yes, this makes the perf optimization
|
||
// less aggressive, in that all reads that asked for fewer than AND
|
||
// returned fewer than _maxCharsPerBuffer chars won't get the user
|
||
// buffer optimization. This affects reads where the end of the
|
||
// Stream comes in the middle somewhere, and when you ask for
|
||
// fewer chars than your buffer could produce.
|
||
readToUserBuffer = desiredChars >= _maxCharsPerBuffer;
|
||
|
||
do {
|
||
Contract.Assert(charsRead == 0);
|
||
|
||
if (_checkPreamble) {
|
||
Contract.Assert(bytePos <= _preamble.Length, "possible bug in _compressPreamble. Are two threads using this StreamReader at the same time?");
|
||
int len = stream.Read(byteBuffer, bytePos, byteBuffer.Length - bytePos);
|
||
Contract.Assert(len >= 0, "Stream.Read returned a negative number! This is a bug in your stream class.");
|
||
|
||
if (len == 0) {
|
||
// EOF but we might have buffered bytes from previous
|
||
// attempt to detect preamble that needs to be decoded now
|
||
if (byteLen > 0) {
|
||
if (readToUserBuffer) {
|
||
charsRead = decoder.GetChars(byteBuffer, 0, byteLen, userBuffer, userOffset + charsRead);
|
||
charLen = 0; // StreamReader's buffer is empty.
|
||
}
|
||
else {
|
||
charsRead = decoder.GetChars(byteBuffer, 0, byteLen, charBuffer, charsRead);
|
||
charLen += charsRead; // Number of chars in StreamReader's buffer.
|
||
}
|
||
}
|
||
|
||
return charsRead;
|
||
}
|
||
|
||
byteLen += len;
|
||
}
|
||
else {
|
||
Contract.Assert(bytePos == 0, "bytePos can be non zero only when we are trying to _checkPreamble. Are two threads using this StreamReader at the same time?");
|
||
|
||
byteLen = stream.Read(byteBuffer, 0, byteBuffer.Length);
|
||
|
||
Contract.Assert(byteLen >= 0, "Stream.Read returned a negative number! This is a bug in your stream class.");
|
||
|
||
if (byteLen == 0) // EOF
|
||
break;
|
||
}
|
||
|
||
// _isBlocked == whether we read fewer bytes than we asked for.
|
||
// Note we must check it here because CompressBuffer or
|
||
// DetectEncoding will change byteLen.
|
||
_isBlocked = (byteLen < byteBuffer.Length);
|
||
|
||
// Check for preamble before detect encoding. This is not to override the
|
||
// user suppplied Encoding for the one we implicitly detect. The user could
|
||
// customize the encoding which we will loose, such as ThrowOnError on UTF8
|
||
// Note: we don't need to recompute readToUserBuffer optimization as IsPreamble
|
||
// doesn't change the encoding or affect _maxCharsPerBuffer
|
||
if (IsPreamble())
|
||
continue;
|
||
|
||
// On the first call to ReadBuffer, if we're supposed to detect the encoding, do it.
|
||
if (_detectEncoding && byteLen >= 2) {
|
||
DetectEncoding();
|
||
// DetectEncoding changes some buffer state. Recompute this.
|
||
readToUserBuffer = desiredChars >= _maxCharsPerBuffer;
|
||
}
|
||
|
||
charPos = 0;
|
||
if (readToUserBuffer) {
|
||
charsRead += decoder.GetChars(byteBuffer, 0, byteLen, userBuffer, userOffset + charsRead);
|
||
charLen = 0; // StreamReader's buffer is empty.
|
||
}
|
||
else {
|
||
charsRead = decoder.GetChars(byteBuffer, 0, byteLen, charBuffer, charsRead);
|
||
charLen += charsRead; // Number of chars in StreamReader's buffer.
|
||
}
|
||
} while (charsRead == 0);
|
||
|
||
_isBlocked &= charsRead < desiredChars;
|
||
|
||
//Console.WriteLine("ReadBuffer: charsRead: "+charsRead+" readToUserBuffer: "+readToUserBuffer);
|
||
return charsRead;
|
||
}
|
||
|
||
|
||
// Reads a line. A line is defined as a sequence of characters followed by
|
||
// a carriage return ('\r'), a line feed ('\n'), or a carriage return
|
||
// immediately followed by a line feed. The resulting string does not
|
||
// contain the terminating carriage return and/or line feed. The returned
|
||
// value is null if the end of the input stream has been reached.
|
||
//
|
||
public override String ReadLine()
|
||
{
|
||
if (stream == null)
|
||
__Error.ReaderClosed();
|
||
|
||
#if FEATURE_ASYNC_IO
|
||
CheckAsyncTaskInProgress();
|
||
#endif
|
||
|
||
if (charPos == charLen)
|
||
{
|
||
if (ReadBuffer() == 0) return null;
|
||
}
|
||
|
||
StringBuilder sb = null;
|
||
do {
|
||
int i = charPos;
|
||
do {
|
||
char ch = charBuffer[i];
|
||
// Note the following common line feed chars:
|
||
// \n - UNIX \r\n - DOS \r - Mac
|
||
if (ch == '\r' || ch == '\n') {
|
||
String s;
|
||
if (sb != null) {
|
||
sb.Append(charBuffer, charPos, i - charPos);
|
||
s = sb.ToString();
|
||
}
|
||
else {
|
||
s = new String(charBuffer, charPos, i - charPos);
|
||
}
|
||
charPos = i + 1;
|
||
if (ch == '\r' && (charPos < charLen || ReadBuffer() > 0)) {
|
||
if (charBuffer[charPos] == '\n') charPos++;
|
||
}
|
||
return s;
|
||
}
|
||
i++;
|
||
} while (i < charLen);
|
||
i = charLen - charPos;
|
||
if (sb == null) sb = new StringBuilder(i + 80);
|
||
sb.Append(charBuffer, charPos, i);
|
||
} while (ReadBuffer() > 0);
|
||
return sb.ToString();
|
||
}
|
||
|
||
#if FEATURE_ASYNC_IO
|
||
#region Task based Async APIs
|
||
[HostProtection(ExternalThreading=true)]
|
||
[ComVisible(false)]
|
||
public override Task<String> ReadLineAsync()
|
||
{
|
||
// If we have been inherited into a subclass, the following implementation could be incorrect
|
||
// since it does not call through to Read() which a subclass might have overriden.
|
||
// To be safe we will only use this implementation in cases where we know it is safe to do so,
|
||
// and delegate to our base class (which will call into Read) when we are not sure.
|
||
if (this.GetType() != typeof(StreamReader))
|
||
return base.ReadLineAsync();
|
||
|
||
if (stream == null)
|
||
__Error.ReaderClosed();
|
||
|
||
CheckAsyncTaskInProgress();
|
||
|
||
Task<String> task = ReadLineAsyncInternal();
|
||
_asyncReadTask = task;
|
||
|
||
return task;
|
||
}
|
||
|
||
private async Task<String> ReadLineAsyncInternal()
|
||
{
|
||
if (CharPos_Prop == CharLen_Prop && (await ReadBufferAsync().ConfigureAwait(false)) == 0)
|
||
return null;
|
||
|
||
StringBuilder sb = null;
|
||
|
||
do
|
||
{
|
||
char[] tmpCharBuffer = CharBuffer_Prop;
|
||
int tmpCharLen = CharLen_Prop;
|
||
int tmpCharPos = CharPos_Prop;
|
||
int i = tmpCharPos;
|
||
|
||
do
|
||
{
|
||
char ch = tmpCharBuffer[i];
|
||
|
||
// Note the following common line feed chars:
|
||
// \n - UNIX \r\n - DOS \r - Mac
|
||
if (ch == '\r' || ch == '\n')
|
||
{
|
||
String s;
|
||
|
||
if (sb != null)
|
||
{
|
||
sb.Append(tmpCharBuffer, tmpCharPos, i - tmpCharPos);
|
||
s = sb.ToString();
|
||
}
|
||
else
|
||
{
|
||
s = new String(tmpCharBuffer, tmpCharPos, i - tmpCharPos);
|
||
}
|
||
|
||
CharPos_Prop = tmpCharPos = i + 1;
|
||
|
||
if (ch == '\r' && (tmpCharPos < tmpCharLen || (await ReadBufferAsync().ConfigureAwait(false)) > 0))
|
||
{
|
||
tmpCharPos = CharPos_Prop;
|
||
if (CharBuffer_Prop[tmpCharPos] == '\n')
|
||
CharPos_Prop = ++tmpCharPos;
|
||
}
|
||
|
||
return s;
|
||
}
|
||
|
||
i++;
|
||
|
||
} while (i < tmpCharLen);
|
||
|
||
i = tmpCharLen - tmpCharPos;
|
||
if (sb == null) sb = new StringBuilder(i + 80);
|
||
sb.Append(tmpCharBuffer, tmpCharPos, i);
|
||
|
||
} while (await ReadBufferAsync().ConfigureAwait(false) > 0);
|
||
|
||
return sb.ToString();
|
||
}
|
||
|
||
[HostProtection(ExternalThreading=true)]
|
||
[ComVisible(false)]
|
||
public override Task<String> ReadToEndAsync()
|
||
{
|
||
// If we have been inherited into a subclass, the following implementation could be incorrect
|
||
// since it does not call through to Read() which a subclass might have overriden.
|
||
// To be safe we will only use this implementation in cases where we know it is safe to do so,
|
||
// and delegate to our base class (which will call into Read) when we are not sure.
|
||
if (this.GetType() != typeof(StreamReader))
|
||
return base.ReadToEndAsync();
|
||
|
||
if (stream == null)
|
||
__Error.ReaderClosed();
|
||
|
||
CheckAsyncTaskInProgress();
|
||
|
||
Task<String> task = ReadToEndAsyncInternal();
|
||
_asyncReadTask = task;
|
||
|
||
return task;
|
||
}
|
||
|
||
private async Task<String> ReadToEndAsyncInternal()
|
||
{
|
||
// Call ReadBuffer, then pull data out of charBuffer.
|
||
StringBuilder sb = new StringBuilder(CharLen_Prop - CharPos_Prop);
|
||
do
|
||
{
|
||
int tmpCharPos = CharPos_Prop;
|
||
sb.Append(CharBuffer_Prop, tmpCharPos, CharLen_Prop - tmpCharPos);
|
||
CharPos_Prop = CharLen_Prop; // We consumed these characters
|
||
await ReadBufferAsync().ConfigureAwait(false);
|
||
} while (CharLen_Prop > 0);
|
||
|
||
return sb.ToString();
|
||
}
|
||
|
||
[HostProtection(ExternalThreading=true)]
|
||
[ComVisible(false)]
|
||
public override Task<int> ReadAsync(char[] buffer, int index, int count)
|
||
{
|
||
if (buffer==null)
|
||
throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer"));
|
||
if (index < 0 || count < 0)
|
||
throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
|
||
if (buffer.Length - index < count)
|
||
throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
|
||
Contract.EndContractBlock();
|
||
|
||
// If we have been inherited into a subclass, the following implementation could be incorrect
|
||
// since it does not call through to Read() which a subclass might have overriden.
|
||
// To be safe we will only use this implementation in cases where we know it is safe to do so,
|
||
// and delegate to our base class (which will call into Read) when we are not sure.
|
||
if (this.GetType() != typeof(StreamReader))
|
||
return base.ReadAsync(buffer, index, count);
|
||
|
||
if (stream == null)
|
||
__Error.ReaderClosed();
|
||
|
||
CheckAsyncTaskInProgress();
|
||
|
||
Task<int> task = ReadAsyncInternal(buffer, index, count);
|
||
_asyncReadTask = task;
|
||
|
||
return task;
|
||
}
|
||
|
||
internal override async Task<int> ReadAsyncInternal(char[] buffer, int index, int count)
|
||
{
|
||
if (CharPos_Prop == CharLen_Prop && (await ReadBufferAsync().ConfigureAwait(false)) == 0)
|
||
return 0;
|
||
|
||
int charsRead = 0;
|
||
|
||
// As a perf optimization, if we had exactly one buffer's worth of
|
||
// data read in, let's try writing directly to the user's buffer.
|
||
bool readToUserBuffer = false;
|
||
|
||
Byte[] tmpByteBuffer = ByteBuffer_Prop;
|
||
Stream tmpStream = Stream_Prop;
|
||
|
||
while (count > 0)
|
||
{
|
||
// n is the cha----ters avaialbe in _charBuffer
|
||
int n = CharLen_Prop - CharPos_Prop;
|
||
|
||
// charBuffer is empty, let's read from the stream
|
||
if (n == 0)
|
||
{
|
||
CharLen_Prop = 0;
|
||
CharPos_Prop = 0;
|
||
|
||
if (!CheckPreamble_Prop)
|
||
ByteLen_Prop = 0;
|
||
|
||
readToUserBuffer = count >= MaxCharsPerBuffer_Prop;
|
||
|
||
// We loop here so that we read in enough bytes to yield at least 1 char.
|
||
// We break out of the loop if the stream is blocked (EOF is reached).
|
||
do
|
||
{
|
||
Contract.Assert(n == 0);
|
||
|
||
if (CheckPreamble_Prop)
|
||
{
|
||
Contract.Assert(BytePos_Prop <= Preamble_Prop.Length, "possible bug in _compressPreamble. Are two threads using this StreamReader at the same time?");
|
||
int tmpBytePos = BytePos_Prop;
|
||
int len = await tmpStream.ReadAsync(tmpByteBuffer, tmpBytePos, tmpByteBuffer.Length - tmpBytePos).ConfigureAwait(false);
|
||
Contract.Assert(len >= 0, "Stream.Read returned a negative number! This is a bug in your stream class.");
|
||
|
||
if (len == 0)
|
||
{
|
||
// EOF but we might have buffered bytes from previous
|
||
// attempts to detect preamble that needs to be decoded now
|
||
if (ByteLen_Prop > 0)
|
||
{
|
||
if (readToUserBuffer)
|
||
{
|
||
n = Decoder_Prop.GetChars(tmpByteBuffer, 0, ByteLen_Prop, buffer, index + charsRead);
|
||
CharLen_Prop = 0; // StreamReader's buffer is empty.
|
||
}
|
||
else
|
||
{
|
||
n = Decoder_Prop.GetChars(tmpByteBuffer, 0, ByteLen_Prop, CharBuffer_Prop, 0);
|
||
CharLen_Prop += n; // Number of chars in StreamReader's buffer.
|
||
}
|
||
}
|
||
|
||
// How can part of the preamble yield any chars?
|
||
Contract.Assert(n == 0);
|
||
|
||
IsBlocked_Prop = true;
|
||
break;
|
||
}
|
||
else
|
||
{
|
||
ByteLen_Prop += len;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
Contract.Assert(BytePos_Prop == 0, "_bytePos can be non zero only when we are trying to _checkPreamble. Are two threads using this StreamReader at the same time?");
|
||
|
||
ByteLen_Prop = await tmpStream.ReadAsync(tmpByteBuffer, 0, tmpByteBuffer.Length).ConfigureAwait(false);
|
||
|
||
Contract.Assert(ByteLen_Prop >= 0, "Stream.Read returned a negative number! This is a bug in your stream class.");
|
||
|
||
if (ByteLen_Prop == 0) // EOF
|
||
{
|
||
IsBlocked_Prop = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// _isBlocked == whether we read fewer bytes than we asked for.
|
||
// Note we must check it here because CompressBuffer or
|
||
// DetectEncoding will change _byteLen.
|
||
IsBlocked_Prop = (ByteLen_Prop < tmpByteBuffer.Length);
|
||
|
||
// Check for preamble before detect encoding. This is not to override the
|
||
// user suppplied Encoding for the one we implicitly detect. The user could
|
||
// customize the encoding which we will loose, such as ThrowOnError on UTF8
|
||
// Note: we don't need to recompute readToUserBuffer optimization as IsPreamble
|
||
// doesn't change the encoding or affect _maxCharsPerBuffer
|
||
if (IsPreamble())
|
||
continue;
|
||
|
||
// On the first call to ReadBuffer, if we're supposed to detect the encoding, do it.
|
||
if (DetectEncoding_Prop && ByteLen_Prop >= 2)
|
||
{
|
||
DetectEncoding();
|
||
// DetectEncoding changes some buffer state. Recompute this.
|
||
readToUserBuffer = count >= MaxCharsPerBuffer_Prop;
|
||
}
|
||
|
||
Contract.Assert(n == 0);
|
||
|
||
CharPos_Prop = 0;
|
||
if (readToUserBuffer)
|
||
{
|
||
n += Decoder_Prop.GetChars(tmpByteBuffer, 0, ByteLen_Prop, buffer, index + charsRead);
|
||
|
||
// Why did the bytes yield no chars?
|
||
Contract.Assert(n > 0);
|
||
|
||
CharLen_Prop = 0; // StreamReader's buffer is empty.
|
||
}
|
||
else
|
||
{
|
||
n = Decoder_Prop.GetChars(tmpByteBuffer, 0, ByteLen_Prop, CharBuffer_Prop, 0);
|
||
|
||
// Why did the bytes yield no chars?
|
||
Contract.Assert(n > 0);
|
||
|
||
CharLen_Prop += n; // Number of chars in StreamReader's buffer.
|
||
}
|
||
|
||
} while (n == 0);
|
||
|
||
if (n == 0) break; // We're at EOF
|
||
} // if (n == 0)
|
||
|
||
// Got more chars in charBuffer than the user requested
|
||
if (n > count)
|
||
n = count;
|
||
|
||
if (!readToUserBuffer)
|
||
{
|
||
Buffer.InternalBlockCopy(CharBuffer_Prop, CharPos_Prop * 2, buffer, (index + charsRead) * 2, n * 2);
|
||
CharPos_Prop += n;
|
||
}
|
||
|
||
charsRead += n;
|
||
count -= n;
|
||
|
||
// This function shouldn't block for an indefinite amount of time,
|
||
// or reading from a network stream won't work right. If we got
|
||
// fewer bytes than we requested, then we want to break right here.
|
||
if (IsBlocked_Prop)
|
||
break;
|
||
} // while (count > 0)
|
||
|
||
return charsRead;
|
||
}
|
||
|
||
[HostProtection(ExternalThreading=true)]
|
||
[ComVisible(false)]
|
||
public override Task<int> ReadBlockAsync(char[] buffer, int index, int count)
|
||
{
|
||
if (buffer==null)
|
||
throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer"));
|
||
if (index < 0 || count < 0)
|
||
throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
|
||
if (buffer.Length - index < count)
|
||
throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
|
||
Contract.EndContractBlock();
|
||
|
||
// If we have been inherited into a subclass, the following implementation could be incorrect
|
||
// since it does not call through to Read() which a subclass might have overriden.
|
||
// To be safe we will only use this implementation in cases where we know it is safe to do so,
|
||
// and delegate to our base class (which will call into Read) when we are not sure.
|
||
if (this.GetType() != typeof(StreamReader))
|
||
return base.ReadBlockAsync(buffer, index, count);
|
||
|
||
if (stream == null)
|
||
__Error.ReaderClosed();
|
||
|
||
CheckAsyncTaskInProgress();
|
||
|
||
Task<int> task = base.ReadBlockAsync(buffer, index, count);
|
||
_asyncReadTask = task;
|
||
|
||
return task;
|
||
}
|
||
|
||
#region Private properties for async method performance
|
||
// Access to instance fields of MarshalByRefObject-derived types requires special JIT helpers that check
|
||
// if the instance operated on is remote. This is optimised for fields on <20>this<69> but if a method is Async
|
||
// and is thus lifted to a state machine type, access will be slow.
|
||
// As a workaround, we either cache instance fields in locals or use properties to access such fields.
|
||
|
||
// See Dev11 bug #370300 for more info.
|
||
|
||
private Int32 CharLen_Prop {
|
||
get { return charLen; }
|
||
set { charLen = value; }
|
||
}
|
||
|
||
private Int32 CharPos_Prop {
|
||
get { return charPos; }
|
||
set { charPos = value; }
|
||
}
|
||
|
||
private Int32 ByteLen_Prop {
|
||
get { return byteLen; }
|
||
set { byteLen = value; }
|
||
}
|
||
|
||
private Int32 BytePos_Prop {
|
||
get { return bytePos; }
|
||
set { bytePos = value; }
|
||
}
|
||
|
||
private Byte[] Preamble_Prop {
|
||
get { return _preamble; }
|
||
}
|
||
|
||
private bool CheckPreamble_Prop {
|
||
get { return _checkPreamble; }
|
||
}
|
||
|
||
private Decoder Decoder_Prop {
|
||
get { return decoder; }
|
||
}
|
||
|
||
private bool DetectEncoding_Prop {
|
||
get { return _detectEncoding; }
|
||
}
|
||
|
||
private Char[] CharBuffer_Prop {
|
||
get { return charBuffer; }
|
||
}
|
||
|
||
private Byte[] ByteBuffer_Prop {
|
||
get { return byteBuffer; }
|
||
}
|
||
|
||
private bool IsBlocked_Prop {
|
||
get { return _isBlocked; }
|
||
set { _isBlocked = value; }
|
||
}
|
||
|
||
private Stream Stream_Prop {
|
||
get { return stream; }
|
||
}
|
||
|
||
private Int32 MaxCharsPerBuffer_Prop {
|
||
get { return _maxCharsPerBuffer; }
|
||
}
|
||
#endregion Private properties for async method performance
|
||
private async Task<int> ReadBufferAsync()
|
||
{
|
||
CharLen_Prop = 0;
|
||
CharPos_Prop = 0;
|
||
Byte[] tmpByteBuffer = ByteBuffer_Prop;
|
||
Stream tmpStream = Stream_Prop;
|
||
|
||
if (!CheckPreamble_Prop)
|
||
ByteLen_Prop = 0;
|
||
do {
|
||
if (CheckPreamble_Prop) {
|
||
Contract.Assert(BytePos_Prop <= Preamble_Prop.Length, "possible bug in _compressPreamble. Are two threads using this StreamReader at the same time?");
|
||
int tmpBytePos = BytePos_Prop;
|
||
int len = await tmpStream.ReadAsync(tmpByteBuffer, tmpBytePos, tmpByteBuffer.Length - tmpBytePos).ConfigureAwait(false);
|
||
Contract.Assert(len >= 0, "Stream.Read returned a negative number! This is a bug in your stream class.");
|
||
|
||
if (len == 0) {
|
||
// EOF but we might have buffered bytes from previous
|
||
// attempt to detect preamble that needs to be decoded now
|
||
if (ByteLen_Prop > 0)
|
||
{
|
||
CharLen_Prop += Decoder_Prop.GetChars(tmpByteBuffer, 0, ByteLen_Prop, CharBuffer_Prop, CharLen_Prop);
|
||
// Need to zero out the _byteLen after we consume these bytes so that we don't keep infinitely hitting this code path
|
||
BytePos_Prop = 0; ByteLen_Prop = 0;
|
||
}
|
||
|
||
return CharLen_Prop;
|
||
}
|
||
|
||
ByteLen_Prop += len;
|
||
}
|
||
else {
|
||
Contract.Assert(BytePos_Prop == 0, "_bytePos can be non zero only when we are trying to _checkPreamble. Are two threads using this StreamReader at the same time?");
|
||
ByteLen_Prop = await tmpStream.ReadAsync(tmpByteBuffer, 0, tmpByteBuffer.Length).ConfigureAwait(false);
|
||
Contract.Assert(ByteLen_Prop >= 0, "Stream.Read returned a negative number! Bug in stream class.");
|
||
|
||
if (ByteLen_Prop == 0) // We're at EOF
|
||
return CharLen_Prop;
|
||
}
|
||
|
||
// _isBlocked == whether we read fewer bytes than we asked for.
|
||
// Note we must check it here because CompressBuffer or
|
||
// DetectEncoding will change _byteLen.
|
||
IsBlocked_Prop = (ByteLen_Prop < tmpByteBuffer.Length);
|
||
|
||
// Check for preamble before detect encoding. This is not to override the
|
||
// user suppplied Encoding for the one we implicitly detect. The user could
|
||
// customize the encoding which we will loose, such as ThrowOnError on UTF8
|
||
if (IsPreamble())
|
||
continue;
|
||
|
||
// If we're supposed to detect the encoding and haven't done so yet,
|
||
// do it. Note this may need to be called more than once.
|
||
if (DetectEncoding_Prop && ByteLen_Prop >= 2)
|
||
DetectEncoding();
|
||
|
||
CharLen_Prop += Decoder_Prop.GetChars(tmpByteBuffer, 0, ByteLen_Prop, CharBuffer_Prop, CharLen_Prop);
|
||
} while (CharLen_Prop == 0);
|
||
|
||
return CharLen_Prop;
|
||
}
|
||
#endregion
|
||
#endif //FEATURE_ASYNC_IO
|
||
|
||
|
||
// No data, class doesn't need to be serializable.
|
||
// Note this class is threadsafe.
|
||
private class NullStreamReader : StreamReader
|
||
{
|
||
// Instantiating Encoding causes unnecessary perf hit.
|
||
internal NullStreamReader() {
|
||
Init(Stream.Null);
|
||
}
|
||
|
||
public override Stream BaseStream {
|
||
get { return Stream.Null; }
|
||
}
|
||
|
||
public override Encoding CurrentEncoding {
|
||
get { return Encoding.Unicode; }
|
||
}
|
||
|
||
protected override void Dispose(bool disposing)
|
||
{
|
||
// Do nothing - this is essentially unclosable.
|
||
}
|
||
|
||
public override int Peek()
|
||
{
|
||
return -1;
|
||
}
|
||
|
||
public override int Read()
|
||
{
|
||
return -1;
|
||
}
|
||
|
||
[SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
|
||
public override int Read(char[] buffer, int index, int count) {
|
||
return 0;
|
||
}
|
||
|
||
public override String ReadLine() {
|
||
return null;
|
||
}
|
||
|
||
public override String ReadToEnd()
|
||
{
|
||
return String.Empty;
|
||
}
|
||
|
||
internal override int ReadBuffer()
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
}
|
||
}
|
||
}
|