|
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
/*============================================================
**
** Class: TextReader
**
** <OWNER>Microsoft</OWNER>
**
**
** Purpose: Abstract base class for all Text-only Readers.
** Subclasses will include StreamReader & StringReader.
**
**
===========================================================*/
using System;
using System.Text;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Reflection;
using System.Security.Permissions;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
#if FEATURE_ASYNC_IO
using System.Threading;
using System.Threading.Tasks;
#endif
namespace System.IO {
// This abstract base class represents a reader that can read a sequential
// stream of characters. This is not intended for reading bytes -
// there are methods on the Stream class to read bytes.
// A subclass must minimally implement the Peek() and Read() methods.
//
// This class is intended for character input, not bytes.
// There are methods on the Stream class for reading bytes.
[Serializable]
[ComVisible(true)]
#if FEATURE_REMOTING
public abstract class TextReader : MarshalByRefObject, IDisposable {
#else // FEATURE_REMOTING
public abstract class TextReader : IDisposable {
#endif // FEATURE_REMOTING
#if FEATURE_ASYNC_IO
[NonSerialized]
private static Func<object, string> _ReadLineDelegate = state => ((TextReader)state).ReadLine();
[NonSerialized]
private static Func<object, int> _ReadDelegate = state =>
{
Tuple<TextReader, char[], int, int> tuple = (Tuple<TextReader, char[], int, int>)state;
return tuple.Item1.Read(tuple.Item2, tuple.Item3, tuple.Item4);
};
#endif //FEATURE_ASYNC_IO
public static readonly TextReader Null = new NullTextReader();
protected TextReader() {}
// Closes this TextReader and releases any system resources associated with the
// TextReader. Following a call to Close, any operations on the TextReader
// may raise exceptions.
//
// This default method is empty, but descendant classes can override the
// method to provide the appropriate functionality.
public virtual void Close()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
}
// Returns the next available character without actually reading it from
// the input stream. The current position of the TextReader is not changed by
// this operation. The returned value is -1 if no further characters are
// available.
//
// This default method simply returns -1.
//
[Pure]
public virtual int Peek()
{
Contract.Ensures(Contract.Result<int>() >= -1);
return -1;
}
// Reads the next character from the input stream. The returned value is
// -1 if no further characters are available.
//
// This default method simply returns -1.
//
public virtual int Read()
{
Contract.Ensures(Contract.Result<int>() >= -1);
return -1;
}
// Reads a block of characters. This method will read up to
// count characters from this TextReader into the
// buffer character array starting at position
// index. Returns the actual number of characters read.
//
public virtual int Read([In, Out] char[] buffer, int index, int count)
{
if (buffer==null)
throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer"));
if (index < 0)
throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
if (count < 0)
throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
if (buffer.Length - index < count)
throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
Contract.Ensures(Contract.Result<int>() >= 0);
Contract.Ensures(Contract.Result<int>() <= Contract.OldValue(count));
Contract.EndContractBlock();
int n = 0;
do {
int ch = Read();
if (ch == -1) break;
buffer[index + n++] = (char)ch;
} while (n < count);
return n;
}
// Reads all characters from the current position to the end of the
// TextReader, and returns them as one string.
public virtual String ReadToEnd()
{
Contract.Ensures(Contract.Result<String>() != null);
char[] chars = new char[4096];
int len;
StringBuilder sb = new StringBuilder(4096);
while((len=Read(chars, 0, chars.Length)) != 0)
{
sb.Append(chars, 0, len);
}
return sb.ToString();
}
// Blocking version of read. Returns only when count
// characters have been read or the end of the file was reached.
//
public virtual int ReadBlock([In, Out] char[] buffer, int index, int count)
{
Contract.Ensures(Contract.Result<int>() >= 0);
Contract.Ensures(Contract.Result<int>() <= count);
int i, n = 0;
do {
n += (i = Read(buffer, index + n, count - n));
} while (i > 0 && n < count);
return n;
}
// 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 virtual String ReadLine()
{
StringBuilder sb = new StringBuilder();
while (true) {
int ch = Read();
if (ch == -1) break;
if (ch == '\r' || ch == '\n')
{
if (ch == '\r' && Peek() == '\n') Read();
return sb.ToString();
}
sb.Append((char)ch);
}
if (sb.Length > 0) return sb.ToString();
return null;
}
#if FEATURE_ASYNC_IO
#region Task based Async APIs
[HostProtection(ExternalThreading=true)]
[ComVisible(false)]
public virtual Task<String> ReadLineAsync()
{
return Task<String>.Factory.StartNew(_ReadLineDelegate, this, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
}
[HostProtection(ExternalThreading=true)]
[ComVisible(false)]
public async virtual Task<String> ReadToEndAsync()
{
char[] chars = new char[4096];
int len;
StringBuilder sb = new StringBuilder(4096);
while((len = await ReadAsyncInternal(chars, 0, chars.Length).ConfigureAwait(false)) != 0)
{
sb.Append(chars, 0, len);
}
return sb.ToString();
}
[HostProtection(ExternalThreading=true)]
[ComVisible(false)]
public virtual 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();
return ReadAsyncInternal(buffer, index, count);
}
internal virtual Task<int> ReadAsyncInternal(char[] buffer, int index, int count)
{
Contract.Requires(buffer != null);
Contract.Requires(index >= 0);
Contract.Requires(count >= 0);
Contract.Requires(buffer.Length - index >= count);
Tuple<TextReader, char[], int, int> tuple = new Tuple<TextReader, char[], int, int>(this, buffer, index, count);
return Task<int>.Factory.StartNew(_ReadDelegate, tuple, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
}
[HostProtection(ExternalThreading=true)]
[ComVisible(false)]
public virtual 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();
return ReadBlockAsyncInternal(buffer, index, count);
}
[HostProtection(ExternalThreading=true)]
private async Task<int> ReadBlockAsyncInternal(char[] buffer, int index, int count)
{
Contract.Requires(buffer != null);
Contract.Requires(index >= 0);
Contract.Requires(count >= 0);
Contract.Requires(buffer.Length - index >= count);
int i, n = 0;
do
{
i = await ReadAsyncInternal(buffer, index + n, count - n).ConfigureAwait(false);
n += i;
} while (i > 0 && n < count);
return n;
}
#endregion
#endif //FEATURE_ASYNC_IO
[HostProtection(Synchronization=true)]
public static TextReader Synchronized(TextReader reader)
{
if (reader==null)
throw new ArgumentNullException("reader");
Contract.Ensures(Contract.Result<TextReader>() != null);
Contract.EndContractBlock();
if (reader is SyncTextReader)
return reader;
return new SyncTextReader(reader);
}
[Serializable]
private sealed class NullTextReader : TextReader
{
public NullTextReader(){}
[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;
}
}
[Serializable]
internal sealed class SyncTextReader : TextReader
{
internal TextReader _in;
internal SyncTextReader(TextReader t)
{
_in = t;
}
[MethodImplAttribute(MethodImplOptions.Synchronized)]
public override void Close()
{
// So that any overriden Close() gets run
_in.Close();
}
[MethodImplAttribute(MethodImplOptions.Synchronized)]
protected override void Dispose(bool disposing)
{
// Explicitly pick up a potentially methodimpl'ed Dispose
if (disposing)
((IDisposable)_in).Dispose();
}
[MethodImplAttribute(MethodImplOptions.Synchronized)]
public override int Peek()
{
return _in.Peek();
}
[MethodImplAttribute(MethodImplOptions.Synchronized)]
public override int Read()
{
return _in.Read();
}
[SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
[MethodImplAttribute(MethodImplOptions.Synchronized)]
public override int Read([In, Out] char[] buffer, int index, int count)
{
return _in.Read(buffer, index, count);
}
[MethodImplAttribute(MethodImplOptions.Synchronized)]
public override int ReadBlock([In, Out] char[] buffer, int index, int count)
{
return _in.ReadBlock(buffer, index, count);
}
[MethodImplAttribute(MethodImplOptions.Synchronized)]
public override String ReadLine()
{
return _in.ReadLine();
}
[MethodImplAttribute(MethodImplOptions.Synchronized)]
public override String ReadToEnd()
{
return _in.ReadToEnd();
}
#if FEATURE_ASYNC_IO
//
// On SyncTextReader all APIs should run synchronously, even the async ones.
//
[ComVisible(false)]
[MethodImplAttribute(MethodImplOptions.Synchronized)]
public override Task<String> ReadLineAsync()
{
return Task.FromResult(ReadLine());
}
[ComVisible(false)]
[MethodImplAttribute(MethodImplOptions.Synchronized)]
public override Task<String> ReadToEndAsync()
{
return Task.FromResult(ReadToEnd());
}
[ComVisible(false)]
[MethodImplAttribute(MethodImplOptions.Synchronized)]
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();
return Task.FromResult(ReadBlock(buffer, index, count));
}
[ComVisible(false)]
[MethodImplAttribute(MethodImplOptions.Synchronized)]
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();
return Task.FromResult(Read(buffer, index, count));
}
#endif //FEATURE_ASYNC_IO
}
}
}
|