TextReader.cs 15 KB

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