TextReader.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. // See the LICENSE file in the project root for more information.
  4. using System.Text;
  5. using System.Threading;
  6. using System.Threading.Tasks;
  7. using System.Diagnostics;
  8. using System.Runtime.CompilerServices;
  9. using System.Runtime.InteropServices;
  10. using System.Buffers;
  11. namespace System.IO
  12. {
  13. // This abstract base class represents a reader that can read a sequential
  14. // stream of characters. This is not intended for reading bytes -
  15. // there are methods on the Stream class to read bytes.
  16. // A subclass must minimally implement the Peek() and Read() methods.
  17. //
  18. // This class is intended for character input, not bytes.
  19. // There are methods on the Stream class for reading bytes.
  20. public abstract partial class TextReader : MarshalByRefObject, IDisposable
  21. {
  22. public static readonly TextReader Null = new NullTextReader();
  23. protected TextReader() { }
  24. public virtual void Close()
  25. {
  26. Dispose(true);
  27. GC.SuppressFinalize(this);
  28. }
  29. public void Dispose()
  30. {
  31. Dispose(true);
  32. GC.SuppressFinalize(this);
  33. }
  34. protected virtual void Dispose(bool disposing)
  35. {
  36. }
  37. // Returns the next available character without actually reading it from
  38. // the input stream. The current position of the TextReader is not changed by
  39. // this operation. The returned value is -1 if no further characters are
  40. // available.
  41. //
  42. // This default method simply returns -1.
  43. //
  44. public virtual int Peek()
  45. {
  46. return -1;
  47. }
  48. // Reads the next character from the input stream. The returned value is
  49. // -1 if no further characters are available.
  50. //
  51. // This default method simply returns -1.
  52. //
  53. public virtual int Read()
  54. {
  55. return -1;
  56. }
  57. // Reads a block of characters. This method will read up to
  58. // count characters from this TextReader into the
  59. // buffer character array starting at position
  60. // index. Returns the actual number of characters read.
  61. //
  62. public virtual int Read(char[] buffer, int index, int count)
  63. {
  64. if (buffer == null)
  65. {
  66. throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
  67. }
  68. if (index < 0)
  69. {
  70. throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_NeedNonNegNum);
  71. }
  72. if (count < 0)
  73. {
  74. throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
  75. }
  76. if (buffer.Length - index < count)
  77. {
  78. throw new ArgumentException(SR.Argument_InvalidOffLen);
  79. }
  80. int n;
  81. for (n = 0; n < count; n++)
  82. {
  83. int ch = Read();
  84. if (ch == -1) break;
  85. buffer[index + n] = (char)ch;
  86. }
  87. return n;
  88. }
  89. // Reads a span of characters. This method will read up to
  90. // count characters from this TextReader into the
  91. // span of characters Returns the actual number of characters read.
  92. //
  93. public virtual int Read(Span<char> buffer)
  94. {
  95. char[] array = ArrayPool<char>.Shared.Rent(buffer.Length);
  96. try
  97. {
  98. int numRead = Read(array, 0, buffer.Length);
  99. if ((uint)numRead > (uint)buffer.Length)
  100. {
  101. throw new IOException(SR.IO_InvalidReadLength);
  102. }
  103. new Span<char>(array, 0, numRead).CopyTo(buffer);
  104. return numRead;
  105. }
  106. finally
  107. {
  108. ArrayPool<char>.Shared.Return(array);
  109. }
  110. }
  111. // Reads all characters from the current position to the end of the
  112. // TextReader, and returns them as one string.
  113. public virtual string ReadToEnd()
  114. {
  115. char[] chars = new char[4096];
  116. int len;
  117. StringBuilder sb = new StringBuilder(4096);
  118. while ((len = Read(chars, 0, chars.Length)) != 0)
  119. {
  120. sb.Append(chars, 0, len);
  121. }
  122. return sb.ToString();
  123. }
  124. // Blocking version of read. Returns only when count
  125. // characters have been read or the end of the file was reached.
  126. //
  127. public virtual int ReadBlock(char[] buffer, int index, int count)
  128. {
  129. int i, n = 0;
  130. do
  131. {
  132. n += (i = Read(buffer, index + n, count - n));
  133. } while (i > 0 && n < count);
  134. return n;
  135. }
  136. // Blocking version of read for span of characters. Returns only when count
  137. // characters have been read or the end of the file was reached.
  138. //
  139. public virtual int ReadBlock(Span<char> buffer)
  140. {
  141. char[] array = ArrayPool<char>.Shared.Rent(buffer.Length);
  142. try
  143. {
  144. int numRead = ReadBlock(array, 0, buffer.Length);
  145. if ((uint)numRead > (uint)buffer.Length)
  146. {
  147. throw new IOException(SR.IO_InvalidReadLength);
  148. }
  149. new Span<char>(array, 0, numRead).CopyTo(buffer);
  150. return numRead;
  151. }
  152. finally
  153. {
  154. ArrayPool<char>.Shared.Return(array);
  155. }
  156. }
  157. // Reads a line. A line is defined as a sequence of characters followed by
  158. // a carriage return ('\r'), a line feed ('\n'), or a carriage return
  159. // immediately followed by a line feed. The resulting string does not
  160. // contain the terminating carriage return and/or line feed. The returned
  161. // value is null if the end of the input stream has been reached.
  162. //
  163. public virtual string ReadLine()
  164. {
  165. StringBuilder sb = new StringBuilder();
  166. while (true)
  167. {
  168. int ch = Read();
  169. if (ch == -1) break;
  170. if (ch == '\r' || ch == '\n')
  171. {
  172. if (ch == '\r' && Peek() == '\n')
  173. {
  174. Read();
  175. }
  176. return sb.ToString();
  177. }
  178. sb.Append((char)ch);
  179. }
  180. if (sb.Length > 0)
  181. {
  182. return sb.ToString();
  183. }
  184. return null;
  185. }
  186. #region Task based Async APIs
  187. public virtual Task<string> ReadLineAsync()
  188. {
  189. return Task<string>.Factory.StartNew(state =>
  190. {
  191. return ((TextReader)state).ReadLine();
  192. },
  193. this, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
  194. }
  195. public async virtual Task<string> ReadToEndAsync()
  196. {
  197. var sb = new StringBuilder(4096);
  198. char[] chars = ArrayPool<char>.Shared.Rent(4096);
  199. try
  200. {
  201. int len;
  202. while ((len = await ReadAsyncInternal(chars, default).ConfigureAwait(false)) != 0)
  203. {
  204. sb.Append(chars, 0, len);
  205. }
  206. }
  207. finally
  208. {
  209. ArrayPool<char>.Shared.Return(chars);
  210. }
  211. return sb.ToString();
  212. }
  213. public virtual Task<int> ReadAsync(char[] buffer, int index, int count)
  214. {
  215. if (buffer == null)
  216. {
  217. throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
  218. }
  219. if (index < 0 || count < 0)
  220. {
  221. throw new ArgumentOutOfRangeException((index < 0 ? nameof(index): nameof(count)), SR.ArgumentOutOfRange_NeedNonNegNum);
  222. }
  223. if (buffer.Length - index < count)
  224. {
  225. throw new ArgumentException(SR.Argument_InvalidOffLen);
  226. }
  227. return ReadAsyncInternal(new Memory<char>(buffer, index, count), default).AsTask();
  228. }
  229. public virtual ValueTask<int> ReadAsync(Memory<char> buffer, CancellationToken cancellationToken = default) =>
  230. new ValueTask<int>(MemoryMarshal.TryGetArray(buffer, out ArraySegment<char> array) ?
  231. ReadAsync(array.Array, array.Offset, array.Count) :
  232. Task<int>.Factory.StartNew(state =>
  233. {
  234. var t = (Tuple<TextReader, Memory<char>>)state;
  235. return t.Item1.Read(t.Item2.Span);
  236. }, Tuple.Create(this, buffer), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default));
  237. internal virtual ValueTask<int> ReadAsyncInternal(Memory<char> buffer, CancellationToken cancellationToken)
  238. {
  239. var tuple = new Tuple<TextReader, Memory<char>>(this, buffer);
  240. return new ValueTask<int>(Task<int>.Factory.StartNew(state =>
  241. {
  242. var t = (Tuple<TextReader, Memory<char>>)state;
  243. return t.Item1.Read(t.Item2.Span);
  244. },
  245. tuple, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default));
  246. }
  247. public virtual Task<int> ReadBlockAsync(char[] buffer, int index, int count)
  248. {
  249. if (buffer == null)
  250. {
  251. throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
  252. }
  253. if (index < 0 || count < 0)
  254. {
  255. throw new ArgumentOutOfRangeException((index < 0 ? nameof(index): nameof(count)), SR.ArgumentOutOfRange_NeedNonNegNum);
  256. }
  257. if (buffer.Length - index < count)
  258. {
  259. throw new ArgumentException(SR.Argument_InvalidOffLen);
  260. }
  261. return ReadBlockAsyncInternal(new Memory<char>(buffer, index, count), default).AsTask();
  262. }
  263. public virtual ValueTask<int> ReadBlockAsync(Memory<char> buffer, CancellationToken cancellationToken = default) =>
  264. new ValueTask<int>(MemoryMarshal.TryGetArray(buffer, out ArraySegment<char> array) ?
  265. ReadBlockAsync(array.Array, array.Offset, array.Count) :
  266. Task<int>.Factory.StartNew(state =>
  267. {
  268. var t = (Tuple<TextReader, Memory<char>>)state;
  269. return t.Item1.ReadBlock(t.Item2.Span);
  270. }, Tuple.Create(this, buffer), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default));
  271. internal async ValueTask<int> ReadBlockAsyncInternal(Memory<char> buffer, CancellationToken cancellationToken)
  272. {
  273. int n = 0, i;
  274. do
  275. {
  276. i = await ReadAsyncInternal(buffer.Slice(n), cancellationToken).ConfigureAwait(false);
  277. n += i;
  278. } while (i > 0 && n < buffer.Length);
  279. return n;
  280. }
  281. #endregion
  282. private sealed class NullTextReader : TextReader
  283. {
  284. public NullTextReader() { }
  285. public override int Read(char[] buffer, int index, int count)
  286. {
  287. return 0;
  288. }
  289. public override string ReadLine()
  290. {
  291. return null;
  292. }
  293. }
  294. public static TextReader Synchronized(TextReader reader)
  295. {
  296. if (reader == null)
  297. throw new ArgumentNullException(nameof(reader));
  298. return reader is SyncTextReader ? reader : new SyncTextReader(reader);
  299. }
  300. internal sealed class SyncTextReader : TextReader
  301. {
  302. internal readonly TextReader _in;
  303. internal SyncTextReader(TextReader t)
  304. {
  305. _in = t;
  306. }
  307. [MethodImpl(MethodImplOptions.Synchronized)]
  308. public override void Close() => _in.Close();
  309. [MethodImpl(MethodImplOptions.Synchronized)]
  310. protected override void Dispose(bool disposing)
  311. {
  312. // Explicitly pick up a potentially methodimpl'ed Dispose
  313. if (disposing)
  314. ((IDisposable)_in).Dispose();
  315. }
  316. [MethodImpl(MethodImplOptions.Synchronized)]
  317. public override int Peek() => _in.Peek();
  318. [MethodImpl(MethodImplOptions.Synchronized)]
  319. public override int Read() => _in.Read();
  320. [MethodImpl(MethodImplOptions.Synchronized)]
  321. public override int Read(char[] buffer, int index, int count) => _in.Read(buffer, index, count);
  322. [MethodImpl(MethodImplOptions.Synchronized)]
  323. public override int ReadBlock(char[] buffer, int index, int count) => _in.ReadBlock(buffer, index, count);
  324. [MethodImpl(MethodImplOptions.Synchronized)]
  325. public override string ReadLine() => _in.ReadLine();
  326. [MethodImpl(MethodImplOptions.Synchronized)]
  327. public override string ReadToEnd() => _in.ReadToEnd();
  328. //
  329. // On SyncTextReader all APIs should run synchronously, even the async ones.
  330. //
  331. [MethodImpl(MethodImplOptions.Synchronized)]
  332. public override Task<string> ReadLineAsync() => Task.FromResult(ReadLine());
  333. [MethodImpl(MethodImplOptions.Synchronized)]
  334. public override Task<string> ReadToEndAsync() => Task.FromResult(ReadToEnd());
  335. [MethodImpl(MethodImplOptions.Synchronized)]
  336. public override Task<int> ReadBlockAsync(char[] buffer, int index, int count)
  337. {
  338. if (buffer == null)
  339. throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
  340. if (index < 0 || count < 0)
  341. throw new ArgumentOutOfRangeException((index < 0 ? nameof(index) : nameof(count)), SR.ArgumentOutOfRange_NeedNonNegNum);
  342. if (buffer.Length - index < count)
  343. throw new ArgumentException(SR.Argument_InvalidOffLen);
  344. return Task.FromResult(ReadBlock(buffer, index, count));
  345. }
  346. [MethodImpl(MethodImplOptions.Synchronized)]
  347. public override Task<int> ReadAsync(char[] buffer, int index, int count)
  348. {
  349. if (buffer == null)
  350. throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
  351. if (index < 0 || count < 0)
  352. throw new ArgumentOutOfRangeException((index < 0 ? nameof(index) : nameof(count)), SR.ArgumentOutOfRange_NeedNonNegNum);
  353. if (buffer.Length - index < count)
  354. throw new ArgumentException(SR.Argument_InvalidOffLen);
  355. return Task.FromResult(Read(buffer, index, count));
  356. }
  357. }
  358. }
  359. }