StreamWriter.cs 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140
  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.Diagnostics;
  5. using System.Runtime.CompilerServices;
  6. using System.Runtime.InteropServices;
  7. using System.Text;
  8. using System.Threading;
  9. using System.Threading.Tasks;
  10. namespace System.IO
  11. {
  12. // This class implements a TextWriter for writing characters to a Stream.
  13. // This is designed for character output in a particular Encoding,
  14. // whereas the Stream class is designed for byte input and output.
  15. public class StreamWriter : TextWriter
  16. {
  17. // For UTF-8, the values of 1K for the default buffer size and 4K for the
  18. // file stream buffer size are reasonable & give very reasonable
  19. // performance for in terms of construction time for the StreamWriter and
  20. // write perf. Note that for UTF-8, we end up allocating a 4K byte buffer,
  21. // which means we take advantage of adaptive buffering code.
  22. // The performance using UnicodeEncoding is acceptable.
  23. private const int DefaultBufferSize = 1024; // char[]
  24. private const int DefaultFileStreamBufferSize = 4096;
  25. private const int MinBufferSize = 128;
  26. private const int DontCopyOnWriteLineThreshold = 512;
  27. // Bit bucket - Null has no backing store. Non closable.
  28. public new static readonly StreamWriter Null = new StreamWriter(Stream.Null, UTF8NoBOM, MinBufferSize, true);
  29. private Stream _stream;
  30. private Encoding _encoding;
  31. private Encoder _encoder;
  32. private byte[] _byteBuffer;
  33. private char[] _charBuffer;
  34. private int _charPos;
  35. private int _charLen;
  36. private bool _autoFlush;
  37. private bool _haveWrittenPreamble;
  38. private bool _closable;
  39. // We don't guarantee thread safety on StreamWriter, but we should at
  40. // least prevent users from trying to write anything while an Async
  41. // write from the same thread is in progress.
  42. private Task _asyncWriteTask = Task.CompletedTask;
  43. private void CheckAsyncTaskInProgress()
  44. {
  45. // We are not locking the access to _asyncWriteTask because this is not meant to guarantee thread safety.
  46. // We are simply trying to deter calling any Write APIs while an async Write from the same thread is in progress.
  47. if (!_asyncWriteTask.IsCompleted)
  48. {
  49. ThrowAsyncIOInProgress();
  50. }
  51. }
  52. private static void ThrowAsyncIOInProgress() =>
  53. throw new InvalidOperationException(SR.InvalidOperation_AsyncIOInProgress);
  54. // The high level goal is to be tolerant of encoding errors when we read and very strict
  55. // when we write. Hence, default StreamWriter encoding will throw on encoding error.
  56. // Note: when StreamWriter throws on invalid encoding chars (for ex, high surrogate character
  57. // D800-DBFF without a following low surrogate character DC00-DFFF), it will cause the
  58. // internal StreamWriter's state to be irrecoverable as it would have buffered the
  59. // illegal chars and any subsequent call to Flush() would hit the encoding error again.
  60. // Even Close() will hit the exception as it would try to flush the unwritten data.
  61. // Maybe we can add a DiscardBufferedData() method to get out of such situation (like
  62. // StreamReader though for different reason). Either way, the buffered data will be lost!
  63. private static Encoding UTF8NoBOM => EncodingCache.UTF8NoBOM;
  64. internal StreamWriter() : base(null)
  65. { // Ask for CurrentCulture all the time
  66. }
  67. public StreamWriter(Stream stream)
  68. : this(stream, UTF8NoBOM, DefaultBufferSize, false)
  69. {
  70. }
  71. public StreamWriter(Stream stream, Encoding encoding)
  72. : this(stream, encoding, DefaultBufferSize, false)
  73. {
  74. }
  75. // Creates a new StreamWriter for the given stream. The
  76. // character encoding is set by encoding and the buffer size,
  77. // in number of 16-bit characters, is set by bufferSize.
  78. //
  79. public StreamWriter(Stream stream, Encoding encoding, int bufferSize)
  80. : this(stream, encoding, bufferSize, false)
  81. {
  82. }
  83. public StreamWriter(Stream stream, Encoding encoding, int bufferSize, bool leaveOpen)
  84. : base(null) // Ask for CurrentCulture all the time
  85. {
  86. if (stream == null || encoding == null)
  87. {
  88. throw new ArgumentNullException(stream == null ? nameof(stream) : nameof(encoding));
  89. }
  90. if (!stream.CanWrite)
  91. {
  92. throw new ArgumentException(SR.Argument_StreamNotWritable);
  93. }
  94. if (bufferSize <= 0)
  95. {
  96. throw new ArgumentOutOfRangeException(nameof(bufferSize), SR.ArgumentOutOfRange_NeedPosNum);
  97. }
  98. Init(stream, encoding, bufferSize, leaveOpen);
  99. }
  100. public StreamWriter(string path)
  101. : this(path, false, UTF8NoBOM, DefaultBufferSize)
  102. {
  103. }
  104. public StreamWriter(string path, bool append)
  105. : this(path, append, UTF8NoBOM, DefaultBufferSize)
  106. {
  107. }
  108. public StreamWriter(string path, bool append, Encoding encoding)
  109. : this(path, append, encoding, DefaultBufferSize)
  110. {
  111. }
  112. public StreamWriter(string path, bool append, Encoding encoding, int bufferSize)
  113. {
  114. if (path == null)
  115. throw new ArgumentNullException(nameof(path));
  116. if (encoding == null)
  117. throw new ArgumentNullException(nameof(encoding));
  118. if (path.Length == 0)
  119. throw new ArgumentException(SR.Argument_EmptyPath);
  120. if (bufferSize <= 0)
  121. throw new ArgumentOutOfRangeException(nameof(bufferSize), SR.ArgumentOutOfRange_NeedPosNum);
  122. Stream stream = new FileStream(path, append ? FileMode.Append : FileMode.Create, FileAccess.Write, FileShare.Read,
  123. DefaultFileStreamBufferSize, FileOptions.SequentialScan);
  124. Init(stream, encoding, bufferSize, shouldLeaveOpen: false);
  125. }
  126. private void Init(Stream streamArg, Encoding encodingArg, int bufferSize, bool shouldLeaveOpen)
  127. {
  128. _stream = streamArg;
  129. _encoding = encodingArg;
  130. _encoder = _encoding.GetEncoder();
  131. if (bufferSize < MinBufferSize)
  132. {
  133. bufferSize = MinBufferSize;
  134. }
  135. _charBuffer = new char[bufferSize];
  136. _byteBuffer = new byte[_encoding.GetMaxByteCount(bufferSize)];
  137. _charLen = bufferSize;
  138. // If we're appending to a Stream that already has data, don't write
  139. // the preamble.
  140. if (_stream.CanSeek && _stream.Position > 0)
  141. {
  142. _haveWrittenPreamble = true;
  143. }
  144. _closable = !shouldLeaveOpen;
  145. }
  146. public override void Close()
  147. {
  148. Dispose(true);
  149. GC.SuppressFinalize(this);
  150. }
  151. protected override void Dispose(bool disposing)
  152. {
  153. try
  154. {
  155. // We need to flush any buffered data if we are being closed/disposed.
  156. // Also, we never close the handles for stdout & friends. So we can safely
  157. // write any buffered data to those streams even during finalization, which
  158. // is generally the right thing to do.
  159. if (_stream != null)
  160. {
  161. // Note: flush on the underlying stream can throw (ex., low disk space)
  162. if (disposing /* || (LeaveOpen && stream is __ConsoleStream) */)
  163. {
  164. CheckAsyncTaskInProgress();
  165. Flush(true, true);
  166. }
  167. }
  168. }
  169. finally
  170. {
  171. CloseStreamFromDispose(disposing);
  172. }
  173. }
  174. private void CloseStreamFromDispose(bool disposing)
  175. {
  176. // Dispose of our resources if this StreamWriter is closable.
  177. if (!LeaveOpen && _stream != null)
  178. {
  179. try
  180. {
  181. // Attempt to close the stream even if there was an IO error from Flushing.
  182. // Note that Stream.Close() can potentially throw here (may or may not be
  183. // due to the same Flush error). In this case, we still need to ensure
  184. // cleaning up internal resources, hence the finally block.
  185. if (disposing)
  186. {
  187. _stream.Close();
  188. }
  189. }
  190. finally
  191. {
  192. _stream = null;
  193. _byteBuffer = null;
  194. _charBuffer = null;
  195. _encoding = null;
  196. _encoder = null;
  197. _charLen = 0;
  198. base.Dispose(disposing);
  199. }
  200. }
  201. }
  202. public override ValueTask DisposeAsync() =>
  203. GetType() != typeof(StreamWriter) ?
  204. base.DisposeAsync() :
  205. DisposeAsyncCore();
  206. private async ValueTask DisposeAsyncCore()
  207. {
  208. // Same logic as in Dispose(), but with async flushing.
  209. Debug.Assert(GetType() == typeof(StreamWriter));
  210. try
  211. {
  212. if (_stream != null)
  213. {
  214. await FlushAsync().ConfigureAwait(false);
  215. }
  216. }
  217. finally
  218. {
  219. CloseStreamFromDispose(disposing: true);
  220. }
  221. GC.SuppressFinalize(this);
  222. }
  223. public override void Flush()
  224. {
  225. CheckAsyncTaskInProgress();
  226. Flush(true, true);
  227. }
  228. private void Flush(bool flushStream, bool flushEncoder)
  229. {
  230. // flushEncoder should be true at the end of the file and if
  231. // the user explicitly calls Flush (though not if AutoFlush is true).
  232. // This is required to flush any dangling characters from our UTF-7
  233. // and UTF-8 encoders.
  234. if (_stream == null)
  235. {
  236. throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed);
  237. }
  238. // Perf boost for Flush on non-dirty writers.
  239. if (_charPos == 0 && !flushStream && !flushEncoder)
  240. {
  241. return;
  242. }
  243. if (!_haveWrittenPreamble)
  244. {
  245. _haveWrittenPreamble = true;
  246. ReadOnlySpan<byte> preamble = _encoding.Preamble;
  247. if (preamble.Length > 0)
  248. {
  249. _stream.Write(preamble);
  250. }
  251. }
  252. int count = _encoder.GetBytes(_charBuffer, 0, _charPos, _byteBuffer, 0, flushEncoder);
  253. _charPos = 0;
  254. if (count > 0)
  255. {
  256. _stream.Write(_byteBuffer, 0, count);
  257. }
  258. // By definition, calling Flush should flush the stream, but this is
  259. // only necessary if we passed in true for flushStream. The Web
  260. // Services guys have some perf tests where flushing needlessly hurts.
  261. if (flushStream)
  262. {
  263. _stream.Flush();
  264. }
  265. }
  266. public virtual bool AutoFlush
  267. {
  268. get { return _autoFlush; }
  269. set
  270. {
  271. CheckAsyncTaskInProgress();
  272. _autoFlush = value;
  273. if (value)
  274. {
  275. Flush(true, false);
  276. }
  277. }
  278. }
  279. public virtual Stream BaseStream
  280. {
  281. get { return _stream; }
  282. }
  283. internal bool LeaveOpen
  284. {
  285. get { return !_closable; }
  286. }
  287. internal bool HaveWrittenPreamble
  288. {
  289. set { _haveWrittenPreamble = value; }
  290. }
  291. public override Encoding Encoding
  292. {
  293. get { return _encoding; }
  294. }
  295. public override void Write(char value)
  296. {
  297. CheckAsyncTaskInProgress();
  298. if (_charPos == _charLen)
  299. {
  300. Flush(false, false);
  301. }
  302. _charBuffer[_charPos] = value;
  303. _charPos++;
  304. if (_autoFlush)
  305. {
  306. Flush(true, false);
  307. }
  308. }
  309. [MethodImpl(MethodImplOptions.NoInlining)] // prevent WriteSpan from bloating call sites
  310. public override void Write(char[] buffer)
  311. {
  312. WriteSpan(buffer, appendNewLine: false);
  313. }
  314. [MethodImpl(MethodImplOptions.NoInlining)] // prevent WriteSpan from bloating call sites
  315. public override void Write(char[] buffer, int index, int count)
  316. {
  317. if (buffer == null)
  318. {
  319. throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
  320. }
  321. if (index < 0)
  322. {
  323. throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_NeedNonNegNum);
  324. }
  325. if (count < 0)
  326. {
  327. throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
  328. }
  329. if (buffer.Length - index < count)
  330. {
  331. throw new ArgumentException(SR.Argument_InvalidOffLen);
  332. }
  333. WriteSpan(buffer.AsSpan(index, count), appendNewLine: false);
  334. }
  335. [MethodImpl(MethodImplOptions.NoInlining)] // prevent WriteSpan from bloating call sites
  336. public override void Write(ReadOnlySpan<char> buffer)
  337. {
  338. if (GetType() == typeof(StreamWriter))
  339. {
  340. WriteSpan(buffer, appendNewLine: false);
  341. }
  342. else
  343. {
  344. // If a derived class may have overridden existing Write behavior,
  345. // we need to make sure we use it.
  346. base.Write(buffer);
  347. }
  348. }
  349. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  350. private unsafe void WriteSpan(ReadOnlySpan<char> buffer, bool appendNewLine)
  351. {
  352. CheckAsyncTaskInProgress();
  353. if (buffer.Length <= 4 && // Threshold of 4 chosen based on perf experimentation
  354. buffer.Length <= _charLen - _charPos)
  355. {
  356. // For very short buffers and when we don't need to worry about running out of space
  357. // in the char buffer, just copy the chars individually.
  358. for (int i = 0; i < buffer.Length; i++)
  359. {
  360. _charBuffer[_charPos++] = buffer[i];
  361. }
  362. }
  363. else
  364. {
  365. // For larger buffers or when we may run out of room in the internal char buffer, copy in chunks.
  366. // Use unsafe code until https://github.com/dotnet/coreclr/issues/13827 is addressed, as spans are
  367. // resulting in significant overhead (even when the if branch above is taken rather than this
  368. // else) due to temporaries that need to be cleared. Given the use of unsafe code, we also
  369. // make local copies of instance state to protect against potential concurrent misuse.
  370. char[] charBuffer = _charBuffer;
  371. if (charBuffer == null)
  372. {
  373. throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed);
  374. }
  375. fixed (char* bufferPtr = &MemoryMarshal.GetReference(buffer))
  376. fixed (char* dstPtr = &charBuffer[0])
  377. {
  378. char* srcPtr = bufferPtr;
  379. int count = buffer.Length;
  380. int dstPos = _charPos; // use a local copy of _charPos for safety
  381. while (count > 0)
  382. {
  383. if (dstPos == charBuffer.Length)
  384. {
  385. Flush(false, false);
  386. dstPos = 0;
  387. }
  388. int n = Math.Min(charBuffer.Length - dstPos, count);
  389. int bytesToCopy = n * sizeof(char);
  390. Buffer.MemoryCopy(srcPtr, dstPtr + dstPos, bytesToCopy, bytesToCopy);
  391. _charPos += n;
  392. dstPos += n;
  393. srcPtr += n;
  394. count -= n;
  395. }
  396. }
  397. }
  398. if (appendNewLine)
  399. {
  400. char[] coreNewLine = CoreNewLine;
  401. for (int i = 0; i < coreNewLine.Length; i++) // Generally 1 (\n) or 2 (\r\n) iterations
  402. {
  403. if (_charPos == _charLen)
  404. {
  405. Flush(false, false);
  406. }
  407. _charBuffer[_charPos] = coreNewLine[i];
  408. _charPos++;
  409. }
  410. }
  411. if (_autoFlush)
  412. {
  413. Flush(true, false);
  414. }
  415. }
  416. [MethodImpl(MethodImplOptions.NoInlining)] // prevent WriteSpan from bloating call sites
  417. public override void Write(string value)
  418. {
  419. WriteSpan(value, appendNewLine: false);
  420. }
  421. [MethodImpl(MethodImplOptions.NoInlining)] // prevent WriteSpan from bloating call sites
  422. public override void WriteLine(string value)
  423. {
  424. CheckAsyncTaskInProgress();
  425. WriteSpan(value, appendNewLine: true);
  426. }
  427. [MethodImpl(MethodImplOptions.NoInlining)] // prevent WriteSpan from bloating call sites
  428. public override void WriteLine(ReadOnlySpan<char> value)
  429. {
  430. if (GetType() == typeof(StreamWriter))
  431. {
  432. CheckAsyncTaskInProgress();
  433. WriteSpan(value, appendNewLine: true);
  434. }
  435. else
  436. {
  437. // If a derived class may have overridden existing WriteLine behavior,
  438. // we need to make sure we use it.
  439. base.WriteLine(value);
  440. }
  441. }
  442. private void WriteFormatHelper(string format, ParamsArray args, bool appendNewLine)
  443. {
  444. StringBuilder sb =
  445. StringBuilderCache.Acquire(format.Length + args.Length * 8)
  446. .AppendFormatHelper(null, format, args);
  447. StringBuilder.ChunkEnumerator chunks = sb.GetChunks();
  448. bool more = chunks.MoveNext();
  449. while (more)
  450. {
  451. ReadOnlySpan<char> current = chunks.Current.Span;
  452. more = chunks.MoveNext();
  453. // If final chunk, include the newline if needed
  454. WriteSpan(current, appendNewLine: more ? false : appendNewLine);
  455. }
  456. StringBuilderCache.Release(sb);
  457. }
  458. public override void Write(string format, object arg0)
  459. {
  460. if (GetType() == typeof(StreamWriter))
  461. {
  462. WriteFormatHelper(format, new ParamsArray(arg0), appendNewLine: false);
  463. }
  464. else
  465. {
  466. base.Write(format, arg0);
  467. }
  468. }
  469. public override void Write(string format, object arg0, object arg1)
  470. {
  471. if (GetType() == typeof(StreamWriter))
  472. {
  473. WriteFormatHelper(format, new ParamsArray(arg0, arg1), appendNewLine: false);
  474. }
  475. else
  476. {
  477. base.Write(format, arg0, arg1);
  478. }
  479. }
  480. public override void Write(string format, object arg0, object arg1, object arg2)
  481. {
  482. if (GetType() == typeof(StreamWriter))
  483. {
  484. WriteFormatHelper(format, new ParamsArray(arg0, arg1, arg2), appendNewLine: false);
  485. }
  486. else
  487. {
  488. base.Write(format, arg0, arg1, arg2);
  489. }
  490. }
  491. public override void Write(string format, params object[] arg)
  492. {
  493. if (GetType() == typeof(StreamWriter))
  494. {
  495. WriteFormatHelper(format, new ParamsArray(arg), appendNewLine: false);
  496. }
  497. else
  498. {
  499. base.Write(format, arg);
  500. }
  501. }
  502. public override void WriteLine(string format, object arg0)
  503. {
  504. if (GetType() == typeof(StreamWriter))
  505. {
  506. WriteFormatHelper(format, new ParamsArray(arg0), appendNewLine: true);
  507. }
  508. else
  509. {
  510. base.WriteLine(format, arg0);
  511. }
  512. }
  513. public override void WriteLine(string format, object arg0, object arg1)
  514. {
  515. if (GetType() == typeof(StreamWriter))
  516. {
  517. WriteFormatHelper(format, new ParamsArray(arg0, arg1), appendNewLine: true);
  518. }
  519. else
  520. {
  521. base.WriteLine(format, arg0, arg1);
  522. }
  523. }
  524. public override void WriteLine(string format, object arg0, object arg1, object arg2)
  525. {
  526. if (GetType() == typeof(StreamWriter))
  527. {
  528. WriteFormatHelper(format, new ParamsArray(arg0, arg1, arg2), appendNewLine: true);
  529. }
  530. else
  531. {
  532. base.WriteLine(format, arg0, arg1, arg2);
  533. }
  534. }
  535. public override void WriteLine(string format, params object[] arg)
  536. {
  537. if (GetType() == typeof(StreamWriter))
  538. {
  539. WriteFormatHelper(format, new ParamsArray(arg), appendNewLine: true);
  540. }
  541. else
  542. {
  543. base.WriteLine(format, arg);
  544. }
  545. }
  546. #region Task based Async APIs
  547. public override Task WriteAsync(char value)
  548. {
  549. // If we have been inherited into a subclass, the following implementation could be incorrect
  550. // since it does not call through to Write() which a subclass might have overridden.
  551. // To be safe we will only use this implementation in cases where we know it is safe to do so,
  552. // and delegate to our base class (which will call into Write) when we are not sure.
  553. if (GetType() != typeof(StreamWriter))
  554. {
  555. return base.WriteAsync(value);
  556. }
  557. if (_stream == null)
  558. {
  559. throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed);
  560. }
  561. CheckAsyncTaskInProgress();
  562. Task task = WriteAsyncInternal(this, value, _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: false);
  563. _asyncWriteTask = task;
  564. return task;
  565. }
  566. // We pass in private instance fields of this MarshalByRefObject-derived type as local params
  567. // to ensure performant access inside the state machine that corresponds this async method.
  568. // Fields that are written to must be assigned at the end of the method *and* before instance invocations.
  569. private static async Task WriteAsyncInternal(StreamWriter _this, char value,
  570. char[] charBuffer, int charPos, int charLen, char[] coreNewLine,
  571. bool autoFlush, bool appendNewLine)
  572. {
  573. if (charPos == charLen)
  574. {
  575. await _this.FlushAsyncInternal(false, false, charBuffer, charPos).ConfigureAwait(false);
  576. Debug.Assert(_this._charPos == 0);
  577. charPos = 0;
  578. }
  579. charBuffer[charPos] = value;
  580. charPos++;
  581. if (appendNewLine)
  582. {
  583. for (int i = 0; i < coreNewLine.Length; i++) // Expect 2 iterations, no point calling BlockCopy
  584. {
  585. if (charPos == charLen)
  586. {
  587. await _this.FlushAsyncInternal(false, false, charBuffer, charPos).ConfigureAwait(false);
  588. Debug.Assert(_this._charPos == 0);
  589. charPos = 0;
  590. }
  591. charBuffer[charPos] = coreNewLine[i];
  592. charPos++;
  593. }
  594. }
  595. if (autoFlush)
  596. {
  597. await _this.FlushAsyncInternal(true, false, charBuffer, charPos).ConfigureAwait(false);
  598. Debug.Assert(_this._charPos == 0);
  599. charPos = 0;
  600. }
  601. _this.CharPos_Prop = charPos;
  602. }
  603. public override Task WriteAsync(string value)
  604. {
  605. // If we have been inherited into a subclass, the following implementation could be incorrect
  606. // since it does not call through to Write() which a subclass might have overridden.
  607. // To be safe we will only use this implementation in cases where we know it is safe to do so,
  608. // and delegate to our base class (which will call into Write) when we are not sure.
  609. if (GetType() != typeof(StreamWriter))
  610. {
  611. return base.WriteAsync(value);
  612. }
  613. if (value != null)
  614. {
  615. if (_stream == null)
  616. {
  617. throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed);
  618. }
  619. CheckAsyncTaskInProgress();
  620. Task task = WriteAsyncInternal(this, value, _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: false);
  621. _asyncWriteTask = task;
  622. return task;
  623. }
  624. else
  625. {
  626. return Task.CompletedTask;
  627. }
  628. }
  629. // We pass in private instance fields of this MarshalByRefObject-derived type as local params
  630. // to ensure performant access inside the state machine that corresponds this async method.
  631. // Fields that are written to must be assigned at the end of the method *and* before instance invocations.
  632. private static async Task WriteAsyncInternal(StreamWriter _this, string value,
  633. char[] charBuffer, int charPos, int charLen, char[] coreNewLine,
  634. bool autoFlush, bool appendNewLine)
  635. {
  636. Debug.Assert(value != null);
  637. int count = value.Length;
  638. int index = 0;
  639. while (count > 0)
  640. {
  641. if (charPos == charLen)
  642. {
  643. await _this.FlushAsyncInternal(false, false, charBuffer, charPos).ConfigureAwait(false);
  644. Debug.Assert(_this._charPos == 0);
  645. charPos = 0;
  646. }
  647. int n = charLen - charPos;
  648. if (n > count)
  649. {
  650. n = count;
  651. }
  652. Debug.Assert(n > 0, "StreamWriter::Write(String) isn't making progress! This is most likely a race condition in user code.");
  653. value.CopyTo(index, charBuffer, charPos, n);
  654. charPos += n;
  655. index += n;
  656. count -= n;
  657. }
  658. if (appendNewLine)
  659. {
  660. for (int i = 0; i < coreNewLine.Length; i++) // Expect 2 iterations, no point calling BlockCopy
  661. {
  662. if (charPos == charLen)
  663. {
  664. await _this.FlushAsyncInternal(false, false, charBuffer, charPos).ConfigureAwait(false);
  665. Debug.Assert(_this._charPos == 0);
  666. charPos = 0;
  667. }
  668. charBuffer[charPos] = coreNewLine[i];
  669. charPos++;
  670. }
  671. }
  672. if (autoFlush)
  673. {
  674. await _this.FlushAsyncInternal(true, false, charBuffer, charPos).ConfigureAwait(false);
  675. Debug.Assert(_this._charPos == 0);
  676. charPos = 0;
  677. }
  678. _this.CharPos_Prop = charPos;
  679. }
  680. public override Task WriteAsync(char[] buffer, int index, int count)
  681. {
  682. if (buffer == null)
  683. {
  684. throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
  685. }
  686. if (index < 0)
  687. {
  688. throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_NeedNonNegNum);
  689. }
  690. if (count < 0)
  691. {
  692. throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
  693. }
  694. if (buffer.Length - index < count)
  695. {
  696. throw new ArgumentException(SR.Argument_InvalidOffLen);
  697. }
  698. // If we have been inherited into a subclass, the following implementation could be incorrect
  699. // since it does not call through to Write() which a subclass might have overridden.
  700. // To be safe we will only use this implementation in cases where we know it is safe to do so,
  701. // and delegate to our base class (which will call into Write) when we are not sure.
  702. if (GetType() != typeof(StreamWriter))
  703. {
  704. return base.WriteAsync(buffer, index, count);
  705. }
  706. if (_stream == null)
  707. {
  708. throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed);
  709. }
  710. CheckAsyncTaskInProgress();
  711. Task task = WriteAsyncInternal(this, new ReadOnlyMemory<char>(buffer, index, count), _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: false, cancellationToken: default);
  712. _asyncWriteTask = task;
  713. return task;
  714. }
  715. public override Task WriteAsync(ReadOnlyMemory<char> buffer, CancellationToken cancellationToken = default)
  716. {
  717. if (GetType() != typeof(StreamWriter))
  718. {
  719. // If a derived type may have overridden existing WriteASync(char[], ...) behavior, make sure we use it.
  720. return base.WriteAsync(buffer, cancellationToken);
  721. }
  722. if (_stream == null)
  723. {
  724. throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed);
  725. }
  726. CheckAsyncTaskInProgress();
  727. if (cancellationToken.IsCancellationRequested)
  728. {
  729. return Task.FromCanceled(cancellationToken);
  730. }
  731. Task task = WriteAsyncInternal(this, buffer, _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: false, cancellationToken: cancellationToken);
  732. _asyncWriteTask = task;
  733. return task;
  734. }
  735. // We pass in private instance fields of this MarshalByRefObject-derived type as local params
  736. // to ensure performant access inside the state machine that corresponds this async method.
  737. // Fields that are written to must be assigned at the end of the method *and* before instance invocations.
  738. private static async Task WriteAsyncInternal(StreamWriter _this, ReadOnlyMemory<char> source,
  739. char[] charBuffer, int charPos, int charLen, char[] coreNewLine,
  740. bool autoFlush, bool appendNewLine, CancellationToken cancellationToken)
  741. {
  742. int copied = 0;
  743. while (copied < source.Length)
  744. {
  745. if (charPos == charLen)
  746. {
  747. await _this.FlushAsyncInternal(false, false, charBuffer, charPos, cancellationToken).ConfigureAwait(false);
  748. Debug.Assert(_this._charPos == 0);
  749. charPos = 0;
  750. }
  751. int n = Math.Min(charLen - charPos, source.Length - copied);
  752. Debug.Assert(n > 0, "StreamWriter::Write(char[], int, int) isn't making progress! This is most likely a race condition in user code.");
  753. source.Span.Slice(copied, n).CopyTo(new Span<char>(charBuffer, charPos, n));
  754. charPos += n;
  755. copied += n;
  756. }
  757. if (appendNewLine)
  758. {
  759. for (int i = 0; i < coreNewLine.Length; i++) // Expect 2 iterations, no point calling BlockCopy
  760. {
  761. if (charPos == charLen)
  762. {
  763. await _this.FlushAsyncInternal(false, false, charBuffer, charPos, cancellationToken).ConfigureAwait(false);
  764. Debug.Assert(_this._charPos == 0);
  765. charPos = 0;
  766. }
  767. charBuffer[charPos] = coreNewLine[i];
  768. charPos++;
  769. }
  770. }
  771. if (autoFlush)
  772. {
  773. await _this.FlushAsyncInternal(true, false, charBuffer, charPos, cancellationToken).ConfigureAwait(false);
  774. Debug.Assert(_this._charPos == 0);
  775. charPos = 0;
  776. }
  777. _this.CharPos_Prop = charPos;
  778. }
  779. public override Task WriteLineAsync()
  780. {
  781. // If we have been inherited into a subclass, the following implementation could be incorrect
  782. // since it does not call through to Write() which a subclass might have overridden.
  783. // To be safe we will only use this implementation in cases where we know it is safe to do so,
  784. // and delegate to our base class (which will call into Write) when we are not sure.
  785. if (GetType() != typeof(StreamWriter))
  786. {
  787. return base.WriteLineAsync();
  788. }
  789. if (_stream == null)
  790. {
  791. throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed);
  792. }
  793. CheckAsyncTaskInProgress();
  794. Task task = WriteAsyncInternal(this, ReadOnlyMemory<char>.Empty, _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: true, cancellationToken: default);
  795. _asyncWriteTask = task;
  796. return task;
  797. }
  798. public override Task WriteLineAsync(char value)
  799. {
  800. // If we have been inherited into a subclass, the following implementation could be incorrect
  801. // since it does not call through to Write() which a subclass might have overridden.
  802. // To be safe we will only use this implementation in cases where we know it is safe to do so,
  803. // and delegate to our base class (which will call into Write) when we are not sure.
  804. if (GetType() != typeof(StreamWriter))
  805. {
  806. return base.WriteLineAsync(value);
  807. }
  808. if (_stream == null)
  809. {
  810. throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed);
  811. }
  812. CheckAsyncTaskInProgress();
  813. Task task = WriteAsyncInternal(this, value, _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: true);
  814. _asyncWriteTask = task;
  815. return task;
  816. }
  817. public override Task WriteLineAsync(string value)
  818. {
  819. if (value == null)
  820. {
  821. return WriteLineAsync();
  822. }
  823. // If we have been inherited into a subclass, the following implementation could be incorrect
  824. // since it does not call through to Write() which a subclass might have overridden.
  825. // To be safe we will only use this implementation in cases where we know it is safe to do so,
  826. // and delegate to our base class (which will call into Write) when we are not sure.
  827. if (GetType() != typeof(StreamWriter))
  828. {
  829. return base.WriteLineAsync(value);
  830. }
  831. if (_stream == null)
  832. {
  833. throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed);
  834. }
  835. CheckAsyncTaskInProgress();
  836. Task task = WriteAsyncInternal(this, value, _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: true);
  837. _asyncWriteTask = task;
  838. return task;
  839. }
  840. public override Task WriteLineAsync(char[] buffer, int index, int count)
  841. {
  842. if (buffer == null)
  843. {
  844. throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
  845. }
  846. if (index < 0)
  847. {
  848. throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_NeedNonNegNum);
  849. }
  850. if (count < 0)
  851. {
  852. throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
  853. }
  854. if (buffer.Length - index < count)
  855. {
  856. throw new ArgumentException(SR.Argument_InvalidOffLen);
  857. }
  858. // If we have been inherited into a subclass, the following implementation could be incorrect
  859. // since it does not call through to Write() which a subclass might have overridden.
  860. // To be safe we will only use this implementation in cases where we know it is safe to do so,
  861. // and delegate to our base class (which will call into Write) when we are not sure.
  862. if (GetType() != typeof(StreamWriter))
  863. {
  864. return base.WriteLineAsync(buffer, index, count);
  865. }
  866. if (_stream == null)
  867. {
  868. throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed);
  869. }
  870. CheckAsyncTaskInProgress();
  871. Task task = WriteAsyncInternal(this, new ReadOnlyMemory<char>(buffer, index, count), _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: true, cancellationToken: default);
  872. _asyncWriteTask = task;
  873. return task;
  874. }
  875. public override Task WriteLineAsync(ReadOnlyMemory<char> buffer, CancellationToken cancellationToken = default)
  876. {
  877. if (GetType() != typeof(StreamWriter))
  878. {
  879. return base.WriteLineAsync(buffer, cancellationToken);
  880. }
  881. if (_stream == null)
  882. {
  883. throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed);
  884. }
  885. CheckAsyncTaskInProgress();
  886. if (cancellationToken.IsCancellationRequested)
  887. {
  888. return Task.FromCanceled(cancellationToken);
  889. }
  890. Task task = WriteAsyncInternal(this, buffer, _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: true, cancellationToken: cancellationToken);
  891. _asyncWriteTask = task;
  892. return task;
  893. }
  894. public override Task FlushAsync()
  895. {
  896. // If we have been inherited into a subclass, the following implementation could be incorrect
  897. // since it does not call through to Flush() which a subclass might have overridden. To be safe
  898. // we will only use this implementation in cases where we know it is safe to do so,
  899. // and delegate to our base class (which will call into Flush) when we are not sure.
  900. if (GetType() != typeof(StreamWriter))
  901. {
  902. return base.FlushAsync();
  903. }
  904. // flushEncoder should be true at the end of the file and if
  905. // the user explicitly calls Flush (though not if AutoFlush is true).
  906. // This is required to flush any dangling characters from our UTF-7
  907. // and UTF-8 encoders.
  908. if (_stream == null)
  909. {
  910. throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed);
  911. }
  912. CheckAsyncTaskInProgress();
  913. Task task = FlushAsyncInternal(true, true, _charBuffer, _charPos);
  914. _asyncWriteTask = task;
  915. return task;
  916. }
  917. private int CharPos_Prop
  918. {
  919. set { _charPos = value; }
  920. }
  921. private bool HaveWrittenPreamble_Prop
  922. {
  923. set { _haveWrittenPreamble = value; }
  924. }
  925. private Task FlushAsyncInternal(bool flushStream, bool flushEncoder,
  926. char[] sCharBuffer, int sCharPos, CancellationToken cancellationToken = default)
  927. {
  928. if (cancellationToken.IsCancellationRequested)
  929. {
  930. return Task.FromCanceled(cancellationToken);
  931. }
  932. // Perf boost for Flush on non-dirty writers.
  933. if (sCharPos == 0 && !flushStream && !flushEncoder)
  934. {
  935. return Task.CompletedTask;
  936. }
  937. Task flushTask = FlushAsyncInternal(this, flushStream, flushEncoder, sCharBuffer, sCharPos, _haveWrittenPreamble,
  938. _encoding, _encoder, _byteBuffer, _stream, cancellationToken);
  939. _charPos = 0;
  940. return flushTask;
  941. }
  942. // We pass in private instance fields of this MarshalByRefObject-derived type as local params
  943. // to ensure performant access inside the state machine that corresponds this async method.
  944. private static async Task FlushAsyncInternal(StreamWriter _this, bool flushStream, bool flushEncoder,
  945. char[] charBuffer, int charPos, bool haveWrittenPreamble,
  946. Encoding encoding, Encoder encoder, byte[] byteBuffer, Stream stream, CancellationToken cancellationToken)
  947. {
  948. if (!haveWrittenPreamble)
  949. {
  950. _this.HaveWrittenPreamble_Prop = true;
  951. byte[] preamble = encoding.GetPreamble();
  952. if (preamble.Length > 0)
  953. {
  954. await stream.WriteAsync(new ReadOnlyMemory<byte>(preamble), cancellationToken).ConfigureAwait(false);
  955. }
  956. }
  957. int count = encoder.GetBytes(charBuffer, 0, charPos, byteBuffer, 0, flushEncoder);
  958. if (count > 0)
  959. {
  960. await stream.WriteAsync(new ReadOnlyMemory<byte>(byteBuffer, 0, count), cancellationToken).ConfigureAwait(false);
  961. }
  962. // By definition, calling Flush should flush the stream, but this is
  963. // only necessary if we passed in true for flushStream. The Web
  964. // Services guys have some perf tests where flushing needlessly hurts.
  965. if (flushStream)
  966. {
  967. await stream.FlushAsync(cancellationToken).ConfigureAwait(false);
  968. }
  969. }
  970. #endregion
  971. } // class StreamWriter
  972. } // namespace