# < Summary

Information

Class: | Renci.SshNet.Sftp.SftpFileStream
Assembly: | Renci.SshNet
File(s): | \home\appveyor\projects\ssh-net\src\Renci.SshNet\Sftp\SftpFileStream.cs

Line coverage
83%

Covered lines: | 547
Uncovered lines: | 112
Coverable lines: | 659
Total lines: | 1349
Line coverage: | 83%

Branch coverage
81%

Covered branches: | 191
Total branches: | 234
Branch coverage: | 81.6%

Method coverage

Feature is only available for sponsors

Upgrade to PRO version

# Metrics

Method | Branch coverage | Cyclomatic complexity | Line coverage
--- | --- | --- | ---
.ctor(...) | 100% | 1 | 100%
get_CanRead() | 100% | 1 | 100%
get_CanSeek() | 100% | 1 | 100%
get_CanWrite() | 100% | 1 | 100%
get_CanTimeout() | 100% | 1 | 100%
get_Length() | 83.33% | 6 | 88.23%
get_Position() | 50% | 2 | 71.42%
set_Position(...) | 100% | 1 | 100%
get_Name() | 100% | 1 | 100%
get_Handle() | 100% | 1 | 0%
get_Timeout() | 100% | 1 | 100%
.ctor(...) | 91.42% | 35 | 91.89%
OpenAsync() | 90.32% | 31 | 78.57%
Finalize() | 100% | 1 | 100%
Flush() | 100% | 2 | 100%
FlushAsync(...) | 50% | 2 | 75%
Read(...) | 80% | 20 | 88.05%
ReadAsync() | 75% | 20 | 82.81%
ReadByte() | 100% | 4 | 100%
Seek(...) | 60% | 30 | 61.81%
SetLength(...) | 75% | 8 | 84.61%
Write(...) | 70% | 20 | 64.81%
WriteAsync() | 75% | 20 | 73.33%
WriteByte(...) | 50% | 2 | 61.11%
Dispose(...) | 100% | 12 | 100%
GetOrCreateReadBuffer() | 100% | 2 | 100%
GetOrCreateWriteBuffer() | 100% | 2 | 100%
FlushReadBuffer() | 100% | 1 | 100%
FlushWriteBuffer() | 100% | 2 | 100%
FlushWriteBufferAsync() | 100% | 2 | 100%
SetupRead() | 100% | 4 | 100%
SetupWrite() | 100% | 4 | 100%
CheckSessionIsOpen() | 100% | 2 | 100%

# File(s)

## \home\appveyor\projects\ssh-net\src\Renci.SshNet\Sftp\SftpFileStream.cs

| # | Line |  | Line coverage
--- | --- | --- | --- | ---
|  | `1` |  | `using System;`
|  | `2` |  | `using System.Diagnostics.CodeAnalysis;`
|  | `3` |  | `using System.Globalization;`
|  | `4` |  | `using System.IO;`
|  | `5` |  | `using System.Threading;`
|  | `6` |  | `using System.Threading.Tasks;`
|  | `7` |  | ``
|  | `8` |  | `using Renci.SshNet.Common;`
|  | `9` |  | ``
|  | `10` |  | `namespace Renci.SshNet.Sftp`
|  | `11` |  | `{`
|  | `12` |  | `/// <summary>`
|  | `13` |  | `/// Exposes a <see cref="Stream"/> around a remote SFTP file, supporting both synchronous and asynchronous read and`
|  | `14` |  | `/// </summary>`
|  | `15` |  | `/// <threadsafety static="true" instance="false"/>`
|  | `16` |  | `#pragma warning disable CA1844 // Provide memory-based overrides of async methods when subclassing 'Stream'`
|  | `17` |  | `public class SftpFileStream : Stream`
|  | `18` |  | `#pragma warning restore CA1844 // Provide memory-based overrides of async methods when subclassing 'Stream'`
|  | `19` |  | `{`
| 1528 | `20` |  | `private readonly object _lock = new object();`
|  | `21` |  | `private readonly int _readBufferSize;`
|  | `22` |  | `private readonly int _writeBufferSize;`
|  | `23` |  | ``
|  | `24` |  | `// Internal state.`
|  | `25` |  | `private byte[] _handle;`
|  | `26` |  | `private ISftpSession _session;`
|  | `27` |  | ``
|  | `28` |  | `// Buffer information.`
|  | `29` |  | `private byte[] _readBuffer;`
|  | `30` |  | `private byte[] _writeBuffer;`
|  | `31` |  | `private int _bufferPosition;`
|  | `32` |  | `private int _bufferLen;`
|  | `33` |  | `private long _position;`
|  | `34` |  | `private bool _bufferOwnedByWrite;`
|  | `35` |  | `private bool _canRead;`
|  | `36` |  | `private bool _canSeek;`
|  | `37` |  | `private bool _canWrite;`
|  | `38` |  | ``
|  | `39` |  | `/// <summary>`
|  | `40` |  | `/// Gets a value indicating whether the current stream supports reading.`
|  | `41` |  | `/// </summary>`
|  | `42` |  | `/// <returns>`
|  | `43` |  | `/// <see langword="true"/> if the stream supports reading; otherwise, <see langword="false"/>.`
|  | `44` |  | `/// </returns>`
|  | `45` |  | `public override bool CanRead`
|  | `46` |  | `{`
| 2433 | `47` |  | `get { return _canRead; }`
|  | `48` |  | `}`
|  | `49` |  | ``
|  | `50` |  | `/// <summary>`
|  | `51` |  | `/// Gets a value indicating whether the current stream supports seeking.`
|  | `52` |  | `/// </summary>`
|  | `53` |  | `/// <returns>`
|  | `54` |  | `/// <see langword="true"/> if the stream supports seeking; otherwise, <see langword="false"/>.`
|  | `55` |  | `/// </returns>`
|  | `56` |  | `public override bool CanSeek`
|  | `57` |  | `{`
| 2247 | `58` |  | `get { return _canSeek; }`
|  | `59` |  | `}`
|  | `60` |  | ``
|  | `61` |  | `/// <summary>`
|  | `62` |  | `/// Gets a value indicating whether the current stream supports writing.`
|  | `63` |  | `/// </summary>`
|  | `64` |  | `/// <returns>`
|  | `65` |  | `/// <see langword="true"/> if the stream supports writing; otherwise, <see langword="false"/>.`
|  | `66` |  | `/// </returns>`
|  | `67` |  | `public override bool CanWrite`
|  | `68` |  | `{`
| 1932 | `69` |  | `get { return _canWrite; }`
|  | `70` |  | `}`
|  | `71` |  | ``
|  | `72` |  | `/// <summary>`
|  | `73` |  | `/// Gets a value indicating whether timeout properties are usable for <see cref="SftpFileStream"/>.`
|  | `74` |  | `/// </summary>`
|  | `75` |  | `/// <value>`
|  | `76` |  | `/// <see langword="true"/> in all cases.`
|  | `77` |  | `/// </value>`
|  | `78` |  | `public override bool CanTimeout`
|  | `79` |  | `{`
| 270 | `80` |  | `get { return true; }`
|  | `81` |  | `}`
|  | `82` |  | ``
|  | `83` |  | `/// <summary>`
|  | `84` |  | `/// Gets the length in bytes of the stream.`
|  | `85` |  | `/// </summary>`
|  | `86` |  | `/// <returns>A long value representing the length of the stream in bytes.</returns>`
|  | `87` |  | `/// <exception cref="NotSupportedException">A class derived from Stream does not support seeking. </exception>`
|  | `88` |  | `/// <exception cref="ObjectDisposedException">Methods were called after the stream was closed. </exception>`
|  | `89` |  | `/// <exception cref="IOException">IO operation failed. </exception>`
|  | `90` |  | `[SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations", Justification = "Be des`
|  | `91` |  | `public override long Length`
|  | `92` |  | `{`
|  | `93` |  | `get`
| 82 | `94` |  | `{`
|  | `95` |  | `// Lock down the file stream while we do this.`
| 82 | `96` |  | `lock (_lock)`
| 82 | `97` |  | `{`
| 82 | `98` |  | `CheckSessionIsOpen();`
|  | `99` |  | ``
| 82 | `100` |  | `if (!CanSeek)`
| 0 | `101` |  | `{`
| 0 | `102` |  | `throw new NotSupportedException("Seek operation is not supported.");`
|  | `103` |  | `}`
|  | `104` |  | ``
|  | `105` |  | `// Flush the write buffer, because it may`
|  | `106` |  | `// affect the length of the stream.`
| 82 | `107` |  | `if (_bufferOwnedByWrite)`
| 6 | `108` |  | `{`
| 6 | `109` |  | `FlushWriteBuffer();`
| 6 | `110` |  | `}`
|  | `111` |  | ``
|  | `112` |  | `// obtain file attributes`
| 82 | `113` |  | `var attributes = _session.RequestFStat(_handle, nullOnError: true);`
| 82 | `114` |  | `if (attributes != null)`
| 79 | `115` |  | `{`
| 79 | `116` |  | `return attributes.Size;`
|  | `117` |  | `}`
|  | `118` |  | ``
| 3 | `119` |  | `throw new IOException("Seek operation failed.");`
|  | `120` |  | `}`
| 79 | `121` |  | `}`
|  | `122` |  | `}`
|  | `123` |  | ``
|  | `124` |  | `/// <summary>`
|  | `125` |  | `/// Gets or sets the position within the current stream.`
|  | `126` |  | `/// </summary>`
|  | `127` |  | `/// <returns>The current position within the stream.</returns>`
|  | `128` |  | `/// <exception cref="IOException">An I/O error occurs. </exception>`
|  | `129` |  | `/// <exception cref="NotSupportedException">The stream does not support seeking. </exception>`
|  | `130` |  | `/// <exception cref="ObjectDisposedException">Methods were called after the stream was closed. </exception>`
|  | `131` |  | `public override long Position`
|  | `132` |  | `{`
|  | `133` |  | `get`
| 280 | `134` |  | `{`
| 280 | `135` |  | `CheckSessionIsOpen();`
|  | `136` |  | ``
| 280 | `137` |  | `if (!CanSeek)`
| 0 | `138` |  | `{`
| 0 | `139` |  | `throw new NotSupportedException("Seek operation not supported.");`
|  | `140` |  | `}`
|  | `141` |  | ``
| 280 | `142` |  | `return _position;`
| 280 | `143` |  | `}`
|  | `144` |  | `set`
| 3 | `145` |  | `{`
| 3 | `146` |  | `_ = Seek(value, SeekOrigin.Begin);`
| 3 | `147` |  | `}`
|  | `148` |  | `}`
|  | `149` |  | ``
|  | `150` |  | `/// <summary>`
|  | `151` |  | `/// Gets the name of the path that was used to construct the current <see cref="SftpFileStream"/>.`
|  | `152` |  | `/// </summary>`
|  | `153` |  | `/// <value>`
|  | `154` |  | `/// The name of the path that was used to construct the current <see cref="SftpFileStream"/>.`
|  | `155` |  | `/// </value>`
| 1528 | `156` |  | `public string Name { get; private set; }`
|  | `157` |  | ``
|  | `158` |  | `/// <summary>`
|  | `159` |  | `/// Gets the operating system file handle for the file that the current <see cref="SftpFileStream"/> encapsulate`
|  | `160` |  | `/// </summary>`
|  | `161` |  | `/// <value>`
|  | `162` |  | `/// The operating system file handle for the file that the current <see cref="SftpFileStream"/> encapsulates.`
|  | `163` |  | `/// </value>`
|  | `164` |  | `public virtual byte[] Handle`
|  | `165` |  | `{`
|  | `166` |  | `get`
| 0 | `167` |  | `{`
| 0 | `168` |  | `Flush();`
| 0 | `169` |  | `return _handle;`
| 0 | `170` |  | `}`
|  | `171` |  | `}`
|  | `172` |  | ``
|  | `173` |  | `/// <summary>`
|  | `174` |  | `/// Gets or sets the operation timeout.`
|  | `175` |  | `/// </summary>`
|  | `176` |  | `/// <value>`
|  | `177` |  | `/// The timeout.`
|  | `178` |  | `/// </value>`
| 1528 | `179` |  | `public TimeSpan Timeout { get; set; }`
|  | `180` |  | ``
| 418 | `181` |  | `private SftpFileStream(ISftpSession session, string path, FileAccess access, int bufferSize, byte[] handle, long`
| 418 | `182` |  | `{`
| 418 | `183` |  | `Timeout = TimeSpan.FromSeconds(30);`
| 418 | `184` |  | `Name = path;`
|  | `185` |  | ``
| 418 | `186` |  | `_session = session;`
| 418 | `187` |  | `_canRead = (access & FileAccess.Read) == FileAccess.Read;`
| 418 | `188` |  | `_canSeek = true;`
| 418 | `189` |  | `_canWrite = (access & FileAccess.Write) == FileAccess.Write;`
|  | `190` |  | ``
| 418 | `191` |  | `_handle = handle;`
|  | `192` |  | ``
|  | `193` |  | `/*`
|  | `194` |  | `* Instead of using the specified buffer size as is, we use it to calculate a buffer size`
|  | `195` |  | `* that ensures we always receive or send the max. number of bytes in a single SSH_FXP_READ`
|  | `196` |  | `* or SSH_FXP_WRITE message.`
|  | `197` |  | `*/`
|  | `198` |  | ``
| 418 | `199` |  | `_readBufferSize = (int) session.CalculateOptimalReadLength((uint) bufferSize);`
| 418 | `200` |  | `_writeBufferSize = (int) session.CalculateOptimalWriteLength((uint) bufferSize, _handle);`
|  | `201` |  | ``
| 418 | `202` |  | `_position = position;`
| 418 | `203` |  | `}`
|  | `204` |  | ``
| 1110 | `205` |  | `internal SftpFileStream(ISftpSession session, string path, FileMode mode, FileAccess access, int bufferSize)`
| 1110 | `206` |  | `{`
| 1110 | `207` |  | `if (session is null)`
| 0 | `208` |  | `{`
| 0 | `209` |  | `throw new SshConnectionException("Client not connected.");`
|  | `210` |  | `}`
|  | `211` |  | ``
| 1110 | `212` |  | `if (path is null)`
| 0 | `213` |  | `{`
| 0 | `214` |  | `throw new ArgumentNullException(nameof(path));`
|  | `215` |  | `}`
|  | `216` |  | ``
| 1110 | `217` |  | `if (bufferSize <= 0)`
| 0 | `218` |  | `{`
| 0 | `219` |  | `throw new ArgumentOutOfRangeException(nameof(bufferSize), "Cannot be less than or equal to zero.");`
|  | `220` |  | `}`
|  | `221` |  | ``
| 1110 | `222` |  | `Timeout = TimeSpan.FromSeconds(30);`
| 1110 | `223` |  | `Name = path;`
|  | `224` |  | ``
|  | `225` |  | `// Initialize the object state.`
| 1110 | `226` |  | `_session = session;`
| 1110 | `227` |  | `_canRead = (access & FileAccess.Read) == FileAccess.Read;`
| 1110 | `228` |  | `_canSeek = true;`
| 1110 | `229` |  | `_canWrite = (access & FileAccess.Write) == FileAccess.Write;`
|  | `230` |  | ``
| 1110 | `231` |  | `var flags = Flags.None;`
|  | `232` |  | ``
| 1110 | `233` |  | `switch (access)`
|  | `234` |  | `{`
|  | `235` |  | `case FileAccess.Read:`
| 377 | `236` |  | `flags |= Flags.Read;`
| 377 | `237` |  | `break;`
|  | `238` |  | `case FileAccess.Write:`
| 435 | `239` |  | `flags |= Flags.Write;`
| 435 | `240` |  | `break;`
|  | `241` |  | `case FileAccess.ReadWrite:`
| 295 | `242` |  | `flags |= Flags.Read;`
| 295 | `243` |  | `flags |= Flags.Write;`
| 295 | `244` |  | `break;`
|  | `245` |  | `default:`
| 3 | `246` |  | `throw new ArgumentOutOfRangeException(nameof(access));`
|  | `247` |  | `}`
|  | `248` |  | ``
| 1107 | `249` |  | `if ((access & FileAccess.Read) == FileAccess.Read && mode == FileMode.Append)`
| 6 | `250` |  | `{`
| 6 | `251` |  | `throw new ArgumentException(string.Format(CultureInfo.InvariantCulture,`
| 6 | `252` |  | `"{0} mode can be requested only when combined with write-only`
| 6 | `253` |  | `mode.ToString("G")),`
| 6 | `254` |  | `nameof(mode));`
|  | `255` |  | `}`
|  | `256` |  | ``
| 1101 | `257` |  | `if ((access & FileAccess.Write) != FileAccess.Write)`
| 374 | `258` |  | `{`
| 374 | `259` |  | `if (mode is FileMode.Create or FileMode.CreateNew or FileMode.Truncate or FileMode.Append)`
| 9 | `260` |  | `{`
| 9 | `261` |  | `throw new ArgumentException(string.Format(CultureInfo.InvariantCulture,`
| 9 | `262` |  | `"Combining {0}: {1} with {2}: {3} is invalid.",`
| 9 | `263` |  | `nameof(FileMode),`
| 9 | `264` |  | `mode,`
| 9 | `265` |  | `nameof(FileAccess),`
| 9 | `266` |  | `access),`
| 9 | `267` |  | `nameof(mode));`
|  | `268` |  | `}`
| 365 | `269` |  | `}`
|  | `270` |  | ``
| 1092 | `271` |  | `switch (mode)`
|  | `272` |  | `{`
|  | `273` |  | `case FileMode.Append:`
| 59 | `274` |  | `flags |= Flags.Append | Flags.CreateNewOrOpen;`
| 59 | `275` |  | `break;`
|  | `276` |  | `case FileMode.Create:`
| 170 | `277` |  | `_handle = _session.RequestOpen(path, flags | Flags.Truncate, nullOnError: true);`
| 170 | `278` |  | `if (_handle is null)`
| 63 | `279` |  | `{`
| 63 | `280` |  | `flags |= Flags.CreateNew;`
| 63 | `281` |  | `}`
|  | `282` |  | `else`
| 107 | `283` |  | `{`
| 107 | `284` |  | `flags |= Flags.Truncate;`
| 107 | `285` |  | `}`
|  | `286` |  | ``
| 170 | `287` |  | `break;`
|  | `288` |  | `case FileMode.CreateNew:`
| 65 | `289` |  | `flags |= Flags.CreateNew;`
| 65 | `290` |  | `break;`
|  | `291` |  | `case FileMode.Open:`
| 445 | `292` |  | `break;`
|  | `293` |  | `case FileMode.OpenOrCreate:`
| 288 | `294` |  | `flags |= Flags.CreateNewOrOpen;`
| 288 | `295` |  | `break;`
|  | `296` |  | `case FileMode.Truncate:`
| 62 | `297` |  | `flags |= Flags.Truncate;`
| 62 | `298` |  | `break;`
|  | `299` |  | `default:`
| 3 | `300` |  | `throw new ArgumentOutOfRangeException(nameof(mode));`
|  | `301` |  | `}`
|  | `302` |  | ``
| 1089 | `303` |  | `_handle ??= _session.RequestOpen(path, flags);`
|  | `304` |  | ``
|  | `305` |  | `/*`
|  | `306` |  | `* Instead of using the specified buffer size as is, we use it to calculate a buffer size`
|  | `307` |  | `* that ensures we always receive or send the max. number of bytes in a single SSH_FXP_READ`
|  | `308` |  | `* or SSH_FXP_WRITE message.`
|  | `309` |  | `*/`
|  | `310` |  | ``
| 1063 | `311` |  | `_readBufferSize = (int) session.CalculateOptimalReadLength((uint) bufferSize);`
| 1063 | `312` |  | `_writeBufferSize = (int) session.CalculateOptimalWriteLength((uint) bufferSize, _handle);`
|  | `313` |  | ``
| 1063 | `314` |  | `if (mode == FileMode.Append)`
| 53 | `315` |  | `{`
| 53 | `316` |  | `var attributes = _session.RequestFStat(_handle, nullOnError: false);`
| 53 | `317` |  | `_position = attributes.Size;`
| 53 | `318` |  | `}`
| 1063 | `319` |  | `}`
|  | `320` |  | ``
|  | `321` |  | `internal static async Task<SftpFileStream> OpenAsync(ISftpSession session, string path, FileMode mode, FileAcces`
| 439 | `322` |  | `{`
| 439 | `323` |  | `if (session is null)`
| 0 | `324` |  | `{`
| 0 | `325` |  | `throw new SshConnectionException("Client not connected.");`
|  | `326` |  | `}`
|  | `327` |  | ``
| 439 | `328` |  | `if (path is null)`
| 0 | `329` |  | `{`
| 0 | `330` |  | `throw new ArgumentNullException(nameof(path));`
|  | `331` |  | `}`
|  | `332` |  | ``
| 439 | `333` |  | `if (bufferSize <= 0)`
| 0 | `334` |  | `{`
| 0 | `335` |  | `throw new ArgumentOutOfRangeException(nameof(bufferSize), "Cannot be less than or equal to zero.");`
|  | `336` |  | `}`
|  | `337` |  | ``
| 439 | `338` |  | `var flags = Flags.None;`
|  | `339` |  | ``
| 439 | `340` |  | `switch (access)`
|  | `341` |  | `{`
|  | `342` |  | `case FileAccess.Read:`
| 108 | `343` |  | `flags |= Flags.Read;`
| 108 | `344` |  | `break;`
|  | `345` |  | `case FileAccess.Write:`
| 181 | `346` |  | `flags |= Flags.Write;`
| 181 | `347` |  | `break;`
|  | `348` |  | `case FileAccess.ReadWrite:`
| 147 | `349` |  | `flags |= Flags.Read;`
| 147 | `350` |  | `flags |= Flags.Write;`
| 147 | `351` |  | `break;`
|  | `352` |  | `default:`
| 3 | `353` |  | `throw new ArgumentOutOfRangeException(nameof(access));`
|  | `354` |  | `}`
|  | `355` |  | ``
| 436 | `356` |  | `if ((access & FileAccess.Read) == FileAccess.Read && mode == FileMode.Append)`
| 6 | `357` |  | `{`
| 6 | `358` |  | `throw new ArgumentException(string.Format(CultureInfo.InvariantCulture,`
| 6 | `359` |  | `"{0} mode can be requested only when combined with write-only`
| 6 | `360` |  | `mode.ToString("G")),`
| 6 | `361` |  | `nameof(mode));`
|  | `362` |  | `}`
|  | `363` |  | ``
| 430 | `364` |  | `if ((access & FileAccess.Write) != FileAccess.Write)`
| 105 | `365` |  | `{`
| 105 | `366` |  | `if (mode is FileMode.Create or FileMode.CreateNew or FileMode.Truncate or FileMode.Append)`
| 9 | `367` |  | `{`
| 9 | `368` |  | `throw new ArgumentException(string.Format(CultureInfo.InvariantCulture,`
| 9 | `369` |  | `"Combining {0}: {1} with {2}: {3} is invalid.",`
| 9 | `370` |  | `nameof(FileMode),`
| 9 | `371` |  | `mode,`
| 9 | `372` |  | `nameof(FileAccess),`
| 9 | `373` |  | `access),`
| 9 | `374` |  | `nameof(mode));`
|  | `375` |  | `}`
| 96 | `376` |  | `}`
|  | `377` |  | ``
| 421 | `378` |  | `switch (mode)`
|  | `379` |  | `{`
|  | `380` |  | `case FileMode.Append:`
| 27 | `381` |  | `flags |= Flags.Append | Flags.CreateNewOrOpen;`
| 27 | `382` |  | `break;`
|  | `383` |  | `case FileMode.Create:`
| 105 | `384` |  | `flags |= Flags.CreateNewOrOpen | Flags.Truncate;`
| 105 | `385` |  | `break;`
|  | `386` |  | `case FileMode.CreateNew:`
| 49 | `387` |  | `flags |= Flags.CreateNew;`
| 49 | `388` |  | `break;`
|  | `389` |  | `case FileMode.Open:`
| 117 | `390` |  | `break;`
|  | `391` |  | `case FileMode.OpenOrCreate:`
| 72 | `392` |  | `flags |= Flags.CreateNewOrOpen;`
| 72 | `393` |  | `break;`
|  | `394` |  | `case FileMode.Truncate:`
| 48 | `395` |  | `flags |= Flags.Truncate;`
| 48 | `396` |  | `break;`
|  | `397` |  | `default:`
| 3 | `398` |  | `throw new ArgumentOutOfRangeException(nameof(mode));`
|  | `399` |  | `}`
|  | `400` |  | ``
| 418 | `401` |  | `var handle = await session.RequestOpenAsync(path, flags, cancellationToken).ConfigureAwait(false);`
|  | `402` |  | ``
| 418 | `403` |  | `long position = 0;`
| 418 | `404` |  | `if (mode == FileMode.Append)`
| 27 | `405` |  | `{`
|  | `406` |  | `try`
| 27 | `407` |  | `{`
| 27 | `408` |  | `var attributes = await session.RequestFStatAsync(handle, cancellationToken).ConfigureAwait(false);`
| 27 | `409` |  | `position = attributes.Size;`
| 27 | `410` |  | `}`
| 0 | `411` |  | `catch`
| 0 | `412` |  | `{`
|  | `413` |  | `try`
| 0 | `414` |  | `{`
| 0 | `415` |  | `await session.RequestCloseAsync(handle, cancellationToken).ConfigureAwait(false);`
| 0 | `416` |  | `}`
| 0 | `417` |  | `catch`
| 0 | `418` |  | `{`
|  | `419` |  | `// The original exception is presumably more informative, so we just ignore this one.`
| 0 | `420` |  | `}`
|  | `421` |  | ``
| 0 | `422` |  | `throw;`
|  | `423` |  | `}`
|  | `424` |  | `}`
|  | `425` |  | ``
| 418 | `426` |  | `return new SftpFileStream(session, path, access, bufferSize, handle, position);`
| 418 | `427` |  | `}`
|  | `428` |  | ``
|  | `429` |  | `/// <summary>`
|  | `430` |  | `/// Finalizes an instance of the <see cref="SftpFileStream"/> class.`
|  | `431` |  | `/// </summary>`
|  | `432` |  | `~SftpFileStream()`
| 2344 | `433` |  | `{`
| 1172 | `434` |  | `Dispose(disposing: false);`
| 2344 | `435` |  | `}`
|  | `436` |  | ``
|  | `437` |  | `/// <summary>`
|  | `438` |  | `/// Clears all buffers for this stream and causes any buffered data to be written to the file.`
|  | `439` |  | `/// </summary>`
|  | `440` |  | `/// <exception cref="IOException">An I/O error occurs. </exception>`
|  | `441` |  | `/// <exception cref="ObjectDisposedException">Stream is closed.</exception>`
|  | `442` |  | `public override void Flush()`
| 105 | `443` |  | `{`
| 105 | `444` |  | `lock (_lock)`
| 105 | `445` |  | `{`
| 105 | `446` |  | `CheckSessionIsOpen();`
|  | `447` |  | ``
| 102 | `448` |  | `if (_bufferOwnedByWrite)`
| 81 | `449` |  | `{`
| 81 | `450` |  | `FlushWriteBuffer();`
| 81 | `451` |  | `}`
|  | `452` |  | `else`
| 21 | `453` |  | `{`
| 21 | `454` |  | `FlushReadBuffer();`
| 21 | `455` |  | `}`
| 102 | `456` |  | `}`
| 102 | `457` |  | `}`
|  | `458` |  | ``
|  | `459` |  | `/// <summary>`
|  | `460` |  | `/// Asynchronously clears all buffers for this stream and causes any buffered data to be written to the file.`
|  | `461` |  | `/// </summary>`
|  | `462` |  | `/// <param name="cancellationToken">The <see cref="CancellationToken"/> to observe.</param>`
|  | `463` |  | `/// <returns>A <see cref="Task"/> that represents the asynchronous flush operation.</returns>`
|  | `464` |  | `/// <exception cref="IOException">An I/O error occurs. </exception>`
|  | `465` |  | `/// <exception cref="ObjectDisposedException">Stream is closed.</exception>`
|  | `466` |  | `public override Task FlushAsync(CancellationToken cancellationToken)`
| 3 | `467` |  | `{`
| 3 | `468` |  | `CheckSessionIsOpen();`
|  | `469` |  | ``
| 3 | `470` |  | `if (_bufferOwnedByWrite)`
| 3 | `471` |  | `{`
| 3 | `472` |  | `return FlushWriteBufferAsync(cancellationToken);`
|  | `473` |  | `}`
|  | `474` |  | ``
| 0 | `475` |  | `FlushReadBuffer();`
|  | `476` |  | ``
| 0 | `477` |  | `return Task.CompletedTask;`
| 3 | `478` |  | `}`
|  | `479` |  | ``
|  | `480` |  | `/// <summary>`
|  | `481` |  | `/// Reads a sequence of bytes from the current stream and advances the position within the stream by the`
|  | `482` |  | `/// number of bytes read.`
|  | `483` |  | `/// </summary>`
|  | `484` |  | `/// <param name="buffer">An array of bytes. When this method returns, the buffer contains the specified byte arr`
|  | `485` |  | `/// <param name="offset">The zero-based byte offset in <paramref name="buffer"/> at which to begin storing the d`
|  | `486` |  | `/// <param name="count">The maximum number of bytes to be read from the current stream.</param>`
|  | `487` |  | `/// <returns>`
|  | `488` |  | `/// The total number of bytes read into the buffer. This can be less than the number of bytes requested`
|  | `489` |  | `/// if that many bytes are not currently available, or zero (0) if the end of the stream has been reached.`
|  | `490` |  | `/// </returns>`
|  | `491` |  | `/// <exception cref="ArgumentException">The sum of <paramref name="offset"/> and <paramref name="count"/> is lar`
|  | `492` |  | `/// <exception cref="ArgumentNullException"><paramref name="buffer"/> is <see langword="null"/>. </exception>`
|  | `493` |  | `/// <exception cref="ArgumentOutOfRangeException"><paramref name="offset"/> or <paramref name="count"/> is negat`
|  | `494` |  | `/// <exception cref="IOException">An I/O error occurs. </exception>`
|  | `495` |  | `/// <exception cref="NotSupportedException">The stream does not support reading. </exception>`
|  | `496` |  | `/// <exception cref="ObjectDisposedException">Methods were called after the stream was closed. </exception>`
|  | `497` |  | `/// <remarks>`
|  | `498` |  | `/// <para>`
|  | `499` |  | `/// This method attempts to read up to <paramref name="count"/> bytes. This either from the buffer, from the`
|  | `500` |  | `/// server (using one or more <c>SSH_FXP_READ</c> requests) or using a combination of both.`
|  | `501` |  | `/// </para>`
|  | `502` |  | `/// <para>`
|  | `503` |  | `/// The read loop is interrupted when either <paramref name="count"/> bytes are read, the server returns zero`
|  | `504` |  | `/// bytes (EOF) or less bytes than the read buffer size.`
|  | `505` |  | `/// </para>`
|  | `506` |  | `/// <para>`
|  | `507` |  | `/// When a server returns less number of bytes than the read buffer size, this <c>may</c> indicate that EOF has`
|  | `508` |  | `/// been reached. A subsequent (<c>SSH_FXP_READ</c>) server request is necessary to make sure EOF has effectivel`
|  | `509` |  | `/// been reached. Breaking out of the read loop avoids reading from the server twice to determine EOF: once in`
|  | `510` |  | `/// the read loop, and once upon the next <see cref="Read"/> or <see cref="ReadByte"/> invocation.`
|  | `511` |  | `/// </para>`
|  | `512` |  | `/// </remarks>`
|  | `513` |  | `public override int Read(byte[] buffer, int offset, int count)`
| 414 | `514` |  | `{`
| 414 | `515` |  | `var readLen = 0;`
|  | `516` |  | ``
| 414 | `517` |  | `if (buffer is null)`
| 0 | `518` |  | `{`
| 0 | `519` |  | `throw new ArgumentNullException(nameof(buffer));`
|  | `520` |  | `}`
|  | `521` |  | ``
| 414 | `522` |  | `if (offset < 0)`
| 0 | `523` |  | `{`
| 0 | `524` |  | `throw new ArgumentOutOfRangeException(nameof(offset));`
|  | `525` |  | `}`
|  | `526` |  | ``
| 414 | `527` |  | `if (count < 0)`
| 0 | `528` |  | `{`
| 0 | `529` |  | `throw new ArgumentOutOfRangeException(nameof(count));`
|  | `530` |  | `}`
|  | `531` |  | ``
| 414 | `532` |  | `if ((buffer.Length - offset) < count)`
| 0 | `533` |  | `{`
| 0 | `534` |  | `throw new ArgumentException("Invalid array range.");`
|  | `535` |  | `}`
|  | `536` |  | ``
|  | `537` |  | `// Lock down the file stream while we do this.`
| 414 | `538` |  | `lock (_lock)`
| 414 | `539` |  | `{`
| 414 | `540` |  | `CheckSessionIsOpen();`
|  | `541` |  | ``
|  | `542` |  | `// Set up for the read operation.`
| 414 | `543` |  | `SetupRead();`
|  | `544` |  | ``
|  | `545` |  | `// Read data into the caller's buffer.`
| 699 | `546` |  | `while (count > 0)`
| 592 | `547` |  | `{`
|  | `548` |  | `// How much data do we have available in the buffer?`
| 592 | `549` |  | `var bytesAvailableInBuffer = _bufferLen - _bufferPosition;`
| 592 | `550` |  | `if (bytesAvailableInBuffer <= 0)`
| 536 | `551` |  | `{`
| 536 | `552` |  | `var data = _session.RequestRead(_handle, (ulong) _position, (uint) _readBufferSize);`
|  | `553` |  | ``
| 536 | `554` |  | `if (data.Length == 0)`
| 48 | `555` |  | `{`
| 48 | `556` |  | `_bufferPosition = 0;`
| 48 | `557` |  | `_bufferLen = 0;`
|  | `558` |  | ``
| 48 | `559` |  | `break;`
|  | `560` |  | `}`
|  | `561` |  | ``
| 488 | `562` |  | `var bytesToWriteToCallerBuffer = count;`
| 488 | `563` |  | `if (bytesToWriteToCallerBuffer >= data.Length)`
| 367 | `564` |  | `{`
|  | `565` |  | `// write all data read to caller-provided buffer`
| 367 | `566` |  | `bytesToWriteToCallerBuffer = data.Length;`
|  | `567` |  | ``
|  | `568` |  | `// reset buffer since we will skip buffering`
| 367 | `569` |  | `_bufferPosition = 0;`
| 367 | `570` |  | `_bufferLen = 0;`
| 367 | `571` |  | `}`
|  | `572` |  | `else`
| 121 | `573` |  | `{`
|  | `574` |  | `// determine number of bytes that we should write into read buffer`
| 121 | `575` |  | `var bytesToWriteToReadBuffer = data.Length - bytesToWriteToCallerBuffer;`
|  | `576` |  | ``
|  | `577` |  | `// write remaining bytes to read buffer`
| 121 | `578` |  | `Buffer.BlockCopy(data, count, GetOrCreateReadBuffer(), 0, bytesToWriteToReadBuffer);`
|  | `579` |  | ``
|  | `580` |  | `// update position in read buffer`
| 121 | `581` |  | `_bufferPosition = 0;`
|  | `582` |  | ``
|  | `583` |  | `// update number of bytes in read buffer`
| 121 | `584` |  | `_bufferLen = bytesToWriteToReadBuffer;`
| 121 | `585` |  | `}`
|  | `586` |  | ``
|  | `587` |  | `// write bytes to caller-provided buffer`
| 488 | `588` |  | `Buffer.BlockCopy(data, 0, buffer, offset, bytesToWriteToCallerBuffer);`
|  | `589` |  | ``
|  | `590` |  | `// update stream position`
| 488 | `591` |  | `_position += bytesToWriteToCallerBuffer;`
|  | `592` |  | ``
|  | `593` |  | `// record total number of bytes read into caller-provided buffer`
| 488 | `594` |  | `readLen += bytesToWriteToCallerBuffer;`
|  | `595` |  | ``
|  | `596` |  | `// break out of the read loop when the server returned less than the request number of bytes`
|  | `597` |  | `// as that *may* indicate that we've reached EOF`
|  | `598` |  | `//`
|  | `599` |  | `// doing this avoids reading from server twice to determine EOF: once in the read loop, and`
|  | `600` |  | `// once upon the next Read or ReadByte invocation by the caller`
| 488 | `601` |  | `if (data.Length < _readBufferSize)`
| 238 | `602` |  | `{`
| 238 | `603` |  | `break;`
|  | `604` |  | `}`
|  | `605` |  | ``
|  | `606` |  | `// advance offset to start writing bytes into caller-provided buffer`
| 250 | `607` |  | `offset += bytesToWriteToCallerBuffer;`
|  | `608` |  | ``
|  | `609` |  | `// update number of bytes left to read into caller-provided buffer`
| 250 | `610` |  | `count -= bytesToWriteToCallerBuffer;`
| 250 | `611` |  | `}`
|  | `612` |  | `else`
| 56 | `613` |  | `{`
|  | `614` |  | `// limit the number of bytes to use from read buffer to the caller-request number of bytes`
| 56 | `615` |  | `if (bytesAvailableInBuffer > count)`
| 25 | `616` |  | `{`
| 25 | `617` |  | `bytesAvailableInBuffer = count;`
| 25 | `618` |  | `}`
|  | `619` |  | ``
|  | `620` |  | `// copy data from read buffer to the caller-provided buffer`
| 56 | `621` |  | `Buffer.BlockCopy(GetOrCreateReadBuffer(), _bufferPosition, buffer, offset, bytesAvailableInBuffe`
|  | `622` |  | ``
|  | `623` |  | `// update position in read buffer`
| 56 | `624` |  | `_bufferPosition += bytesAvailableInBuffer;`
|  | `625` |  | ``
|  | `626` |  | `// update stream position`
| 56 | `627` |  | `_position += bytesAvailableInBuffer;`
|  | `628` |  | ``
|  | `629` |  | `// record total number of bytes read into caller-provided buffer`
| 56 | `630` |  | `readLen += bytesAvailableInBuffer;`
|  | `631` |  | ``
|  | `632` |  | `// advance offset to start writing bytes into caller-provided buffer`
| 56 | `633` |  | `offset += bytesAvailableInBuffer;`
|  | `634` |  | ``
|  | `635` |  | `// update number of bytes left to read`
| 56 | `636` |  | `count -= bytesAvailableInBuffer;`
| 56 | `637` |  | `}`
| 306 | `638` |  | `}`
| 393 | `639` |  | `}`
|  | `640` |  | ``
|  | `641` |  | `// return the number of bytes that were read to the caller.`
| 393 | `642` |  | `return readLen;`
| 393 | `643` |  | `}`
|  | `644` |  | ``
|  | `645` |  | `/// <summary>`
|  | `646` |  | `/// Asynchronously reads a sequence of bytes from the current stream and advances the position within the stream`
|  | `647` |  | `/// number of bytes read.`
|  | `648` |  | `/// </summary>`
|  | `649` |  | `/// <param name="buffer">An array of bytes. When this method returns, the buffer contains the specified byte arr`
|  | `650` |  | `/// <param name="offset">The zero-based byte offset in <paramref name="buffer"/> at which to begin storing the d`
|  | `651` |  | `/// <param name="count">The maximum number of bytes to be read from the current stream.</param>`
|  | `652` |  | `/// <param name="cancellationToken">The <see cref="CancellationToken" /> to observe.</param>`
|  | `653` |  | `/// <returns>A <see cref="Task" /> that represents the asynchronous read operation.</returns>`
|  | `654` |  | `public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationTo`
| 108 | `655` |  | `{`
| 108 | `656` |  | `var readLen = 0;`
|  | `657` |  | ``
| 108 | `658` |  | `if (buffer is null)`
| 0 | `659` |  | `{`
| 0 | `660` |  | `throw new ArgumentNullException(nameof(buffer));`
|  | `661` |  | `}`
|  | `662` |  | ``
| 108 | `663` |  | `if (offset < 0)`
| 0 | `664` |  | `{`
| 0 | `665` |  | `throw new ArgumentOutOfRangeException(nameof(offset));`
|  | `666` |  | `}`
|  | `667` |  | ``
| 108 | `668` |  | `if (count < 0)`
| 0 | `669` |  | `{`
| 0 | `670` |  | `throw new ArgumentOutOfRangeException(nameof(count));`
|  | `671` |  | `}`
|  | `672` |  | ``
| 108 | `673` |  | `if ((buffer.Length - offset) < count)`
| 0 | `674` |  | `{`
| 0 | `675` |  | `throw new ArgumentException("Invalid array range.");`
|  | `676` |  | `}`
|  | `677` |  | ``
| 108 | `678` |  | `CheckSessionIsOpen();`
|  | `679` |  | ``
|  | `680` |  | `// Set up for the read operation.`
| 108 | `681` |  | `SetupRead();`
|  | `682` |  | ``
|  | `683` |  | `// Read data into the caller's buffer.`
| 144 | `684` |  | `while (count > 0)`
| 108 | `685` |  | `{`
|  | `686` |  | `// How much data do we have available in the buffer?`
| 108 | `687` |  | `var bytesAvailableInBuffer = _bufferLen - _bufferPosition;`
| 108 | `688` |  | `if (bytesAvailableInBuffer <= 0)`
| 96 | `689` |  | `{`
| 96 | `690` |  | `var data = await _session.RequestReadAsync(_handle, (ulong)_position, (uint)_readBufferSize, cancell`
|  | `691` |  | ``
| 96 | `692` |  | `if (data.Length == 0)`
| 12 | `693` |  | `{`
| 12 | `694` |  | `_bufferPosition = 0;`
| 12 | `695` |  | `_bufferLen = 0;`
|  | `696` |  | ``
| 12 | `697` |  | `break;`
|  | `698` |  | `}`
|  | `699` |  | ``
| 84 | `700` |  | `var bytesToWriteToCallerBuffer = count;`
| 84 | `701` |  | `if (bytesToWriteToCallerBuffer >= data.Length)`
| 54 | `702` |  | `{`
|  | `703` |  | `// write all data read to caller-provided buffer`
| 54 | `704` |  | `bytesToWriteToCallerBuffer = data.Length;`
|  | `705` |  | ``
|  | `706` |  | `// reset buffer since we will skip buffering`
| 54 | `707` |  | `_bufferPosition = 0;`
| 54 | `708` |  | `_bufferLen = 0;`
| 54 | `709` |  | `}`
|  | `710` |  | `else`
| 30 | `711` |  | `{`
|  | `712` |  | `// determine number of bytes that we should write into read buffer`
| 30 | `713` |  | `var bytesToWriteToReadBuffer = data.Length - bytesToWriteToCallerBuffer;`
|  | `714` |  | ``
|  | `715` |  | `// write remaining bytes to read buffer`
| 30 | `716` |  | `Buffer.BlockCopy(data, count, GetOrCreateReadBuffer(), 0, bytesToWriteToReadBuffer);`
|  | `717` |  | ``
|  | `718` |  | `// update position in read buffer`
| 30 | `719` |  | `_bufferPosition = 0;`
|  | `720` |  | ``
|  | `721` |  | `// update number of bytes in read buffer`
| 30 | `722` |  | `_bufferLen = bytesToWriteToReadBuffer;`
| 30 | `723` |  | `}`
|  | `724` |  | ``
|  | `725` |  | `// write bytes to caller-provided buffer`
| 84 | `726` |  | `Buffer.BlockCopy(data, 0, buffer, offset, bytesToWriteToCallerBuffer);`
|  | `727` |  | ``
|  | `728` |  | `// update stream position`
| 84 | `729` |  | `_position += bytesToWriteToCallerBuffer;`
|  | `730` |  | ``
|  | `731` |  | `// record total number of bytes read into caller-provided buffer`
| 84 | `732` |  | `readLen += bytesToWriteToCallerBuffer;`
|  | `733` |  | ``
|  | `734` |  | `// break out of the read loop when the server returned less than the request number of bytes`
|  | `735` |  | `// as that *may* indicate that we've reached EOF`
|  | `736` |  | `//`
|  | `737` |  | `// doing this avoids reading from server twice to determine EOF: once in the read loop, and`
|  | `738` |  | `// once upon the next Read or ReadByte invocation by the caller`
| 84 | `739` |  | `if (data.Length < _readBufferSize)`
| 39 | `740` |  | `{`
| 39 | `741` |  | `break;`
|  | `742` |  | `}`
|  | `743` |  | ``
|  | `744` |  | `// advance offset to start writing bytes into caller-provided buffer`
| 45 | `745` |  | `offset += bytesToWriteToCallerBuffer;`
|  | `746` |  | ``
|  | `747` |  | `// update number of bytes left to read into caller-provided buffer`
| 45 | `748` |  | `count -= bytesToWriteToCallerBuffer;`
| 45 | `749` |  | `}`
|  | `750` |  | `else`
| 12 | `751` |  | `{`
|  | `752` |  | `// limit the number of bytes to use from read buffer to the caller-request number of bytes`
| 12 | `753` |  | `if (bytesAvailableInBuffer > count)`
| 0 | `754` |  | `{`
| 0 | `755` |  | `bytesAvailableInBuffer = count;`
| 0 | `756` |  | `}`
|  | `757` |  | ``
|  | `758` |  | `// copy data from read buffer to the caller-provided buffer`
| 12 | `759` |  | `Buffer.BlockCopy(GetOrCreateReadBuffer(), _bufferPosition, buffer, offset, bytesAvailableInBuffer);`
|  | `760` |  | ``
|  | `761` |  | `// update position in read buffer`
| 12 | `762` |  | `_bufferPosition += bytesAvailableInBuffer;`
|  | `763` |  | ``
|  | `764` |  | `// update stream position`
| 12 | `765` |  | `_position += bytesAvailableInBuffer;`
|  | `766` |  | ``
|  | `767` |  | `// record total number of bytes read into caller-provided buffer`
| 12 | `768` |  | `readLen += bytesAvailableInBuffer;`
|  | `769` |  | ``
|  | `770` |  | `// advance offset to start writing bytes into caller-provided buffer`
| 12 | `771` |  | `offset += bytesAvailableInBuffer;`
|  | `772` |  | ``
|  | `773` |  | `// update number of bytes left to read`
| 12 | `774` |  | `count -= bytesAvailableInBuffer;`
| 12 | `775` |  | `}`
| 57 | `776` |  | `}`
|  | `777` |  | ``
|  | `778` |  | `// return the number of bytes that were read to the caller.`
| 87 | `779` |  | `return readLen;`
| 87 | `780` |  | `}`
|  | `781` |  | ``
|  | `782` |  | `/// <summary>`
|  | `783` |  | `/// Reads a byte from the stream and advances the position within the stream by one byte, or returns -1 if at th`
|  | `784` |  | `/// </summary>`
|  | `785` |  | `/// <returns>`
|  | `786` |  | `/// The unsigned byte cast to an <see cref="int"/>, or -1 if at the end of the stream.`
|  | `787` |  | `/// </returns>`
|  | `788` |  | `/// <exception cref="NotSupportedException">The stream does not support reading. </exception>`
|  | `789` |  | `/// <exception cref="ObjectDisposedException">Methods were called after the stream was closed. </exception>`
|  | `790` |  | `/// <exception cref="IOException">Read operation failed.</exception>`
|  | `791` |  | `public override int ReadByte()`
| 160 | `792` |  | `{`
|  | `793` |  | `// Lock down the file stream while we do this.`
| 160 | `794` |  | `lock (_lock)`
| 160 | `795` |  | `{`
| 160 | `796` |  | `CheckSessionIsOpen();`
|  | `797` |  | ``
|  | `798` |  | `// Setup the object for reading.`
| 160 | `799` |  | `SetupRead();`
|  | `800` |  | ``
|  | `801` |  | `byte[] readBuffer;`
|  | `802` |  | ``
|  | `803` |  | `// Read more data into the internal buffer if necessary.`
| 139 | `804` |  | `if (_bufferPosition >= _bufferLen)`
| 112 | `805` |  | `{`
| 112 | `806` |  | `var data = _session.RequestRead(_handle, (ulong) _position, (uint) _readBufferSize);`
| 112 | `807` |  | `if (data.Length == 0)`
| 43 | `808` |  | `{`
|  | `809` |  | `// We've reached EOF.`
| 43 | `810` |  | `return -1;`
|  | `811` |  | `}`
|  | `812` |  | ``
| 69 | `813` |  | `readBuffer = GetOrCreateReadBuffer();`
| 69 | `814` |  | `Buffer.BlockCopy(data, 0, readBuffer, 0, data.Length);`
|  | `815` |  | ``
| 69 | `816` |  | `_bufferPosition = 0;`
| 69 | `817` |  | `_bufferLen = data.Length;`
| 69 | `818` |  | `}`
|  | `819` |  | `else`
| 27 | `820` |  | `{`
| 27 | `821` |  | `readBuffer = GetOrCreateReadBuffer();`
| 27 | `822` |  | `}`
|  | `823` |  | ``
|  | `824` |  | `// Extract the next byte from the buffer.`
| 96 | `825` |  | `++_position;`
|  | `826` |  | ``
| 96 | `827` |  | `return readBuffer[_bufferPosition++];`
|  | `828` |  | `}`
| 139 | `829` |  | `}`
|  | `830` |  | ``
|  | `831` |  | `/// <summary>`
|  | `832` |  | `/// Sets the position within the current stream.`
|  | `833` |  | `/// </summary>`
|  | `834` |  | `/// <param name="offset">A byte offset relative to the <paramref name="origin"/> parameter.</param>`
|  | `835` |  | `/// <param name="origin">A value of type <see cref="SeekOrigin"/> indicating the reference point used to obtain`
|  | `836` |  | `/// <returns>`
|  | `837` |  | `/// The new position within the current stream.`
|  | `838` |  | `/// </returns>`
|  | `839` |  | `/// <exception cref="IOException">An I/O error occurs. </exception>`
|  | `840` |  | `/// <exception cref="NotSupportedException">The stream does not support seeking, such as if the stream is constr`
|  | `841` |  | `/// <exception cref="ObjectDisposedException">Methods were called after the stream was closed. </exception>`
|  | `842` |  | `public override long Seek(long offset, SeekOrigin origin)`
| 107 | `843` |  | `{`
|  | `844` |  | `long newPosn;`
|  | `845` |  | ``
|  | `846` |  | `// Lock down the file stream while we do this.`
| 107 | `847` |  | `lock (_lock)`
| 107 | `848` |  | `{`
| 107 | `849` |  | `CheckSessionIsOpen();`
|  | `850` |  | ``
| 107 | `851` |  | `if (!CanSeek)`
| 0 | `852` |  | `{`
| 0 | `853` |  | `throw new NotSupportedException("Seek is not supported.");`
|  | `854` |  | `}`
|  | `855` |  | ``
|  | `856` |  | `// Don't do anything if the position won't be moving.`
| 107 | `857` |  | `if (origin == SeekOrigin.Begin && offset == _position)`
| 10 | `858` |  | `{`
| 10 | `859` |  | `return offset;`
|  | `860` |  | `}`
|  | `861` |  | ``
| 97 | `862` |  | `if (origin == SeekOrigin.Current && offset == 0)`
| 0 | `863` |  | `{`
| 0 | `864` |  | `return _position;`
|  | `865` |  | `}`
|  | `866` |  | ``
|  | `867` |  | `// The behaviour depends upon the read/write mode.`
| 97 | `868` |  | `if (_bufferOwnedByWrite)`
| 31 | `869` |  | `{`
|  | `870` |  | `// Flush the write buffer and then seek.`
| 31 | `871` |  | `FlushWriteBuffer();`
| 31 | `872` |  | `}`
|  | `873` |  | `else`
| 66 | `874` |  | `{`
|  | `875` |  | `// Determine if the seek is to somewhere inside`
|  | `876` |  | `// the current read buffer bounds.`
| 66 | `877` |  | `if (origin == SeekOrigin.Begin)`
| 58 | `878` |  | `{`
| 58 | `879` |  | `newPosn = _position - _bufferPosition;`
| 58 | `880` |  | `if (offset >= newPosn && offset < (newPosn + _bufferLen))`
| 0 | `881` |  | `{`
| 0 | `882` |  | `_bufferPosition = (int)(offset - newPosn);`
| 0 | `883` |  | `_position = offset;`
| 0 | `884` |  | `return _position;`
|  | `885` |  | `}`
| 58 | `886` |  | `}`
| 8 | `887` |  | `else if (origin == SeekOrigin.Current)`
| 0 | `888` |  | `{`
| 0 | `889` |  | `newPosn = _position + offset;`
| 0 | `890` |  | `if (newPosn >= (_position - _bufferPosition) &&`
| 0 | `891` |  | `newPosn < (_position - _bufferPosition + _bufferLen))`
| 0 | `892` |  | `{`
| 0 | `893` |  | `_bufferPosition = (int) (newPosn - (_position - _bufferPosition));`
| 0 | `894` |  | `_position = newPosn;`
| 0 | `895` |  | `return _position;`
|  | `896` |  | `}`
| 0 | `897` |  | `}`
|  | `898` |  | ``
|  | `899` |  | `// Abandon the read buffer.`
| 66 | `900` |  | `_bufferPosition = 0;`
| 66 | `901` |  | `_bufferLen = 0;`
| 66 | `902` |  | `}`
|  | `903` |  | ``
|  | `904` |  | `// Seek to the new position.`
| 97 | `905` |  | `switch (origin)`
|  | `906` |  | `{`
|  | `907` |  | `case SeekOrigin.Begin:`
| 60 | `908` |  | `newPosn = offset;`
| 60 | `909` |  | `break;`
|  | `910` |  | `case SeekOrigin.Current:`
| 0 | `911` |  | `newPosn = _position + offset;`
| 0 | `912` |  | `break;`
|  | `913` |  | `case SeekOrigin.End:`
| 37 | `914` |  | `var attributes = _session.RequestFStat(_handle, nullOnError: false);`
| 37 | `915` |  | `newPosn = attributes.Size + offset;`
| 37 | `916` |  | `break;`
|  | `917` |  | `default:`
| 0 | `918` |  | `throw new ArgumentException("Invalid seek origin.", nameof(origin));`
|  | `919` |  | `}`
|  | `920` |  | ``
| 97 | `921` |  | `if (newPosn < 0)`
| 9 | `922` |  | `{`
| 9 | `923` |  | `throw new EndOfStreamException();`
|  | `924` |  | `}`
|  | `925` |  | ``
| 88 | `926` |  | `_position = newPosn;`
| 88 | `927` |  | `return _position;`
|  | `928` |  | `}`
| 98 | `929` |  | `}`
|  | `930` |  | ``
|  | `931` |  | `/// <summary>`
|  | `932` |  | `/// Sets the length of the current stream.`
|  | `933` |  | `/// </summary>`
|  | `934` |  | `/// <param name="value">The desired length of the current stream in bytes.</param>`
|  | `935` |  | `/// <exception cref="IOException">An I/O error occurs.</exception>`
|  | `936` |  | `/// <exception cref="NotSupportedException">The stream does not support both writing and seeking.</exception>`
|  | `937` |  | `/// <exception cref="ObjectDisposedException">Methods were called after the stream was closed.</exception>`
|  | `938` |  | `/// <exception cref="ArgumentOutOfRangeException"><paramref name="value"/> must be greater than zero.</exception`
|  | `939` |  | `/// <remarks>`
|  | `940` |  | `/// <para>`
|  | `941` |  | `/// Buffers are first flushed.`
|  | `942` |  | `/// </para>`
|  | `943` |  | `/// <para>`
|  | `944` |  | `/// If the specified value is less than the current length of the stream, the stream is truncated and - if the`
|  | `945` |  | `/// current position is greater than the new length - the current position is moved to the last byte of the stre`
|  | `946` |  | `/// </para>`
|  | `947` |  | `/// <para>`
|  | `948` |  | `/// If the given value is greater than the current length of the stream, the stream is expanded and the current`
|  | `949` |  | `/// position remains the same.`
|  | `950` |  | `/// </para>`
|  | `951` |  | `/// </remarks>`
|  | `952` |  | `public override void SetLength(long value)`
| 133 | `953` |  | `{`
| 133 | `954` |  | `if (value < 0)`
| 0 | `955` |  | `{`
| 0 | `956` |  | `throw new ArgumentOutOfRangeException(nameof(value));`
|  | `957` |  | `}`
|  | `958` |  | ``
|  | `959` |  | `// Lock down the file stream while we do this.`
| 133 | `960` |  | `lock (_lock)`
| 133 | `961` |  | `{`
| 133 | `962` |  | `CheckSessionIsOpen();`
|  | `963` |  | ``
| 124 | `964` |  | `if (!CanSeek)`
| 0 | `965` |  | `{`
| 0 | `966` |  | `throw new NotSupportedException("Seek is not supported.");`
|  | `967` |  | `}`
|  | `968` |  | ``
| 124 | `969` |  | `if (_bufferOwnedByWrite)`
| 37 | `970` |  | `{`
| 37 | `971` |  | `FlushWriteBuffer();`
| 37 | `972` |  | `}`
|  | `973` |  | `else`
| 87 | `974` |  | `{`
| 87 | `975` |  | `SetupWrite();`
| 84 | `976` |  | `}`
|  | `977` |  | ``
| 121 | `978` |  | `var attributes = _session.RequestFStat(_handle, nullOnError: false);`
| 121 | `979` |  | `attributes.Size = value;`
| 121 | `980` |  | `_session.RequestFSetStat(_handle, attributes);`
|  | `981` |  | ``
| 121 | `982` |  | `if (_position > value)`
| 35 | `983` |  | `{`
| 35 | `984` |  | `_position = value;`
| 35 | `985` |  | `}`
| 121 | `986` |  | `}`
| 121 | `987` |  | `}`
|  | `988` |  | ``
|  | `989` |  | `/// <summary>`
|  | `990` |  | `/// Writes a sequence of bytes to the current stream and advances the current position within this stream by the`
|  | `991` |  | `/// </summary>`
|  | `992` |  | `/// <param name="buffer">An array of bytes. This method copies <paramref name="count"/> bytes from <paramref nam`
|  | `993` |  | `/// <param name="offset">The zero-based byte offset in <paramref name="buffer"/> at which to begin copying bytes`
|  | `994` |  | `/// <param name="count">The number of bytes to be written to the current stream.</param>`
|  | `995` |  | `/// <exception cref="ArgumentException">The sum of <paramref name="offset"/> and <paramref name="count"/> is gre`
|  | `996` |  | `/// <exception cref="ArgumentNullException"><paramref name="buffer"/> is <see langword="null"/>.</exception>`
|  | `997` |  | `/// <exception cref="ArgumentOutOfRangeException"><paramref name="offset"/> or <paramref name="count"/> is negat`
|  | `998` |  | `/// <exception cref="IOException">An I/O error occurs.</exception>`
|  | `999` |  | `/// <exception cref="NotSupportedException">The stream does not support writing.</exception>`
|  | `1000` |  | `/// <exception cref="ObjectDisposedException">Methods were called after the stream was closed.</exception>`
|  | `1001` |  | `public override void Write(byte[] buffer, int offset, int count)`
| 280 | `1002` |  | `{`
| 280 | `1003` |  | `if (buffer is null)`
| 0 | `1004` |  | `{`
| 0 | `1005` |  | `throw new ArgumentNullException(nameof(buffer));`
|  | `1006` |  | `}`
|  | `1007` |  | ``
| 280 | `1008` |  | `if (offset < 0)`
| 0 | `1009` |  | `{`
| 0 | `1010` |  | `throw new ArgumentOutOfRangeException(nameof(offset));`
|  | `1011` |  | `}`
|  | `1012` |  | ``
| 280 | `1013` |  | `if (count < 0)`
| 0 | `1014` |  | `{`
| 0 | `1015` |  | `throw new ArgumentOutOfRangeException(nameof(count));`
|  | `1016` |  | `}`
|  | `1017` |  | ``
| 280 | `1018` |  | `if ((buffer.Length - offset) < count)`
| 0 | `1019` |  | `{`
| 0 | `1020` |  | `throw new ArgumentException("Invalid array range.");`
|  | `1021` |  | `}`
|  | `1022` |  | ``
|  | `1023` |  | `// Lock down the file stream while we do this.`
| 280 | `1024` |  | `lock (_lock)`
| 280 | `1025` |  | `{`
| 280 | `1026` |  | `CheckSessionIsOpen();`
|  | `1027` |  | ``
|  | `1028` |  | `// Setup this object for writing.`
| 280 | `1029` |  | `SetupWrite();`
|  | `1030` |  | ``
|  | `1031` |  | `// Write data to the file stream.`
| 607 | `1032` |  | `while (count > 0)`
| 333 | `1033` |  | `{`
|  | `1034` |  | `// Determine how many bytes we can write to the buffer.`
| 333 | `1035` |  | `var tempLen = _writeBufferSize - _bufferPosition;`
| 333 | `1036` |  | `if (tempLen <= 0)`
| 0 | `1037` |  | `{`
|  | `1038` |  | `// flush write buffer, and mark it empty`
| 0 | `1039` |  | `FlushWriteBuffer();`
|  | `1040` |  | ``
|  | `1041` |  | `// we can now write or buffer the full buffer size`
| 0 | `1042` |  | `tempLen = _writeBufferSize;`
| 0 | `1043` |  | `}`
|  | `1044` |  | ``
|  | `1045` |  | `// limit the number of bytes to write to the actual number of bytes requested`
| 333 | `1046` |  | `if (tempLen > count)`
| 208 | `1047` |  | `{`
| 208 | `1048` |  | `tempLen = count;`
| 208 | `1049` |  | `}`
|  | `1050` |  | ``
|  | `1051` |  | `// Can we short-cut the internal buffer?`
| 333 | `1052` |  | `if (_bufferPosition == 0 && tempLen == _writeBufferSize)`
| 125 | `1053` |  | `{`
| 125 | `1054` |  | `using (var wait = new AutoResetEvent(initialState: false))`
| 125 | `1055` |  | `{`
| 125 | `1056` |  | `_session.RequestWrite(_handle, (ulong) _position, buffer, offset, tempLen, wait);`
| 125 | `1057` |  | `}`
| 125 | `1058` |  | `}`
|  | `1059` |  | `else`
| 208 | `1060` |  | `{`
|  | `1061` |  | `// No: copy the data to the write buffer first.`
| 208 | `1062` |  | `Buffer.BlockCopy(buffer, offset, GetOrCreateWriteBuffer(), _bufferPosition, tempLen);`
| 208 | `1063` |  | `_bufferPosition += tempLen;`
| 208 | `1064` |  | `}`
|  | `1065` |  | ``
|  | `1066` |  | `// Advance the buffer and stream positions.`
| 333 | `1067` |  | `_position += tempLen;`
| 333 | `1068` |  | `offset += tempLen;`
| 333 | `1069` |  | `count -= tempLen;`
| 333 | `1070` |  | `}`
|  | `1071` |  | ``
|  | `1072` |  | `// If the buffer is full, then do a speculative flush now,`
|  | `1073` |  | `// rather than waiting for the next call to this method.`
| 274 | `1074` |  | `if (_bufferPosition >= _writeBufferSize)`
| 0 | `1075` |  | `{`
| 0 | `1076` |  | `using (var wait = new AutoResetEvent(initialState: false))`
| 0 | `1077` |  | `{`
| 0 | `1078` |  | `_session.RequestWrite(_handle, (ulong) (_position - _bufferPosition), GetOrCreateWriteBuffer(),`
| 0 | `1079` |  | `}`
|  | `1080` |  | ``
| 0 | `1081` |  | `_bufferPosition = 0;`
| 0 | `1082` |  | `}`
| 274 | `1083` |  | `}`
| 274 | `1084` |  | `}`
|  | `1085` |  | ``
|  | `1086` |  | `/// <summary>`
|  | `1087` |  | `/// Asynchronously writes a sequence of bytes to the current stream and advances the current position within thi`
|  | `1088` |  | `/// </summary>`
|  | `1089` |  | `/// <param name="buffer">An array of bytes. This method copies <paramref name="count"/> bytes from <paramref nam`
|  | `1090` |  | `/// <param name="offset">The zero-based byte offset in <paramref name="buffer"/> at which to begin copying bytes`
|  | `1091` |  | `/// <param name="count">The number of bytes to be written to the current stream.</param>`
|  | `1092` |  | `/// <param name="cancellationToken">The <see cref="CancellationToken"/> to observe.</param>`
|  | `1093` |  | `/// <returns>A <see cref="Task"/> that represents the asynchronous write operation.</returns>`
|  | `1094` |  | `/// <exception cref="ArgumentException">The sum of <paramref name="offset"/> and <paramref name="count"/> is gre`
|  | `1095` |  | `/// <exception cref="ArgumentNullException"><paramref name="buffer"/> is <see langword="null"/>.</exception>`
|  | `1096` |  | `/// <exception cref="ArgumentOutOfRangeException"><paramref name="offset"/> or <paramref name="count"/> is negat`
|  | `1097` |  | `/// <exception cref="IOException">An I/O error occurs.</exception>`
|  | `1098` |  | `/// <exception cref="NotSupportedException">The stream does not support writing.</exception>`
|  | `1099` |  | `/// <exception cref="ObjectDisposedException">Methods were called after the stream was closed.</exception>`
|  | `1100` |  | `public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)`
| 62 | `1101` |  | `{`
| 62 | `1102` |  | `if (buffer is null)`
| 0 | `1103` |  | `{`
| 0 | `1104` |  | `throw new ArgumentNullException(nameof(buffer));`
|  | `1105` |  | `}`
|  | `1106` |  | ``
| 62 | `1107` |  | `if (offset < 0)`
| 0 | `1108` |  | `{`
| 0 | `1109` |  | `throw new ArgumentOutOfRangeException(nameof(offset));`
|  | `1110` |  | `}`
|  | `1111` |  | ``
| 62 | `1112` |  | `if (count < 0)`
| 0 | `1113` |  | `{`
| 0 | `1114` |  | `throw new ArgumentOutOfRangeException(nameof(count));`
|  | `1115` |  | `}`
|  | `1116` |  | ``
| 62 | `1117` |  | `if ((buffer.Length - offset) < count)`
| 0 | `1118` |  | `{`
| 0 | `1119` |  | `throw new ArgumentException("Invalid array range.");`
|  | `1120` |  | `}`
|  | `1121` |  | ``
| 62 | `1122` |  | `CheckSessionIsOpen();`
|  | `1123` |  | ``
|  | `1124` |  | `// Setup this object for writing.`
| 62 | `1125` |  | `SetupWrite();`
|  | `1126` |  | ``
|  | `1127` |  | `// Write data to the file stream.`
| 162 | `1128` |  | `while (count > 0)`
| 106 | `1129` |  | `{`
|  | `1130` |  | `// Determine how many bytes we can write to the buffer.`
| 106 | `1131` |  | `var tempLen = _writeBufferSize - _bufferPosition;`
| 106 | `1132` |  | `if (tempLen <= 0)`
| 7 | `1133` |  | `{`
|  | `1134` |  | `// flush write buffer, and mark it empty`
| 7 | `1135` |  | `await FlushWriteBufferAsync(cancellationToken).ConfigureAwait(false);`
|  | `1136` |  | ``
|  | `1137` |  | `// we can now write or buffer the full buffer size`
| 7 | `1138` |  | `tempLen = _writeBufferSize;`
| 7 | `1139` |  | `}`
|  | `1140` |  | ``
|  | `1141` |  | `// limit the number of bytes to write to the actual number of bytes requested`
| 106 | `1142` |  | `if (tempLen > count)`
| 17 | `1143` |  | `{`
| 17 | `1144` |  | `tempLen = count;`
| 17 | `1145` |  | `}`
|  | `1146` |  | ``
|  | `1147` |  | `// Can we short-cut the internal buffer?`
| 106 | `1148` |  | `if (_bufferPosition == 0 && tempLen == _writeBufferSize)`
| 82 | `1149` |  | `{`
| 82 | `1150` |  | `await _session.RequestWriteAsync(_handle, (ulong)_position, buffer, offset, tempLen, cancellationTok`
| 82 | `1151` |  | `}`
|  | `1152` |  | `else`
| 24 | `1153` |  | `{`
|  | `1154` |  | `// No: copy the data to the write buffer first.`
| 24 | `1155` |  | `Buffer.BlockCopy(buffer, offset, GetOrCreateWriteBuffer(), _bufferPosition, tempLen);`
| 24 | `1156` |  | `_bufferPosition += tempLen;`
| 24 | `1157` |  | `}`
|  | `1158` |  | ``
|  | `1159` |  | `// Advance the buffer and stream positions.`
| 106 | `1160` |  | `_position += tempLen;`
| 106 | `1161` |  | `offset += tempLen;`
| 106 | `1162` |  | `count -= tempLen;`
| 106 | `1163` |  | `}`
|  | `1164` |  | ``
|  | `1165` |  | `// If the buffer is full, then do a speculative flush now,`
|  | `1166` |  | `// rather than waiting for the next call to this method.`
| 56 | `1167` |  | `if (_bufferPosition >= _writeBufferSize)`
| 0 | `1168` |  | `{`
| 0 | `1169` |  | `await _session.RequestWriteAsync(_handle, (ulong)(_position - _bufferPosition), GetOrCreateWriteBuffer()`
| 0 | `1170` |  | `_bufferPosition = 0;`
| 0 | `1171` |  | `}`
| 56 | `1172` |  | `}`
|  | `1173` |  | ``
|  | `1174` |  | `/// <summary>`
|  | `1175` |  | `/// Writes a byte to the current position in the stream and advances the position within the stream by one byte.`
|  | `1176` |  | `/// </summary>`
|  | `1177` |  | `/// <param name="value">The byte to write to the stream.</param>`
|  | `1178` |  | `/// <exception cref="IOException">An I/O error occurs. </exception>`
|  | `1179` |  | `/// <exception cref="NotSupportedException">The stream does not support writing, or the stream is already closed`
|  | `1180` |  | `/// <exception cref="ObjectDisposedException">Methods were called after the stream was closed. </exception>`
|  | `1181` |  | `public override void WriteByte(byte value)`
| 39 | `1182` |  | `{`
|  | `1183` |  | `// Lock down the file stream while we do this.`
| 39 | `1184` |  | `lock (_lock)`
| 39 | `1185` |  | `{`
| 39 | `1186` |  | `CheckSessionIsOpen();`
|  | `1187` |  | ``
|  | `1188` |  | `// Setup the object for writing.`
| 39 | `1189` |  | `SetupWrite();`
|  | `1190` |  | ``
| 33 | `1191` |  | `var writeBuffer = GetOrCreateWriteBuffer();`
|  | `1192` |  | ``
|  | `1193` |  | `// Flush the current buffer if it is full.`
| 33 | `1194` |  | `if (_bufferPosition >= _writeBufferSize)`
| 0 | `1195` |  | `{`
| 0 | `1196` |  | `using (var wait = new AutoResetEvent(initialState: false))`
| 0 | `1197` |  | `{`
| 0 | `1198` |  | `_session.RequestWrite(_handle, (ulong) (_position - _bufferPosition), writeBuffer, 0, _bufferPos`
| 0 | `1199` |  | `}`
|  | `1200` |  | ``
| 0 | `1201` |  | `_bufferPosition = 0;`
| 0 | `1202` |  | `}`
|  | `1203` |  | ``
|  | `1204` |  | `// Write the byte into the buffer and advance the posn.`
| 33 | `1205` |  | `writeBuffer[_bufferPosition++] = value;`
| 33 | `1206` |  | `++_position;`
| 33 | `1207` |  | `}`
| 33 | `1208` |  | `}`
|  | `1209` |  | ``
|  | `1210` |  | `/// <summary>`
|  | `1211` |  | `/// Releases the unmanaged resources used by the <see cref="Stream"/> and optionally releases the managed resour`
|  | `1212` |  | `/// </summary>`
|  | `1213` |  | `/// <param name="disposing"><see langword="true"/> to release both managed and unmanaged resources; <see langwor`
|  | `1214` |  | `protected override void Dispose(bool disposing)`
| 1554 | `1215` |  | `{`
| 1554 | `1216` |  | `base.Dispose(disposing);`
|  | `1217` |  | ``
| 1554 | `1218` |  | `if (_session != null)`
| 1528 | `1219` |  | `{`
| 1528 | `1220` |  | `if (disposing)`
| 356 | `1221` |  | `{`
| 356 | `1222` |  | `lock (_lock)`
| 356 | `1223` |  | `{`
| 356 | `1224` |  | `if (_session != null)`
| 356 | `1225` |  | `{`
| 356 | `1226` |  | `_canRead = false;`
| 356 | `1227` |  | `_canSeek = false;`
| 356 | `1228` |  | `_canWrite = false;`
|  | `1229` |  | ``
| 356 | `1230` |  | `if (_handle != null)`
| 356 | `1231` |  | `{`
| 356 | `1232` |  | `if (_session.IsOpen)`
| 344 | `1233` |  | `{`
| 344 | `1234` |  | `if (_bufferOwnedByWrite)`
| 131 | `1235` |  | `{`
| 131 | `1236` |  | `FlushWriteBuffer();`
| 131 | `1237` |  | `}`
|  | `1238` |  | ``
| 344 | `1239` |  | `_session.RequestClose(_handle);`
| 344 | `1240` |  | `}`
|  | `1241` |  | ``
| 356 | `1242` |  | `_handle = null;`
| 356 | `1243` |  | `}`
|  | `1244` |  | ``
| 356 | `1245` |  | `_session = null;`
| 356 | `1246` |  | `}`
| 356 | `1247` |  | `}`
| 356 | `1248` |  | `}`
| 1528 | `1249` |  | `}`
| 1554 | `1250` |  | `}`
|  | `1251` |  | ``
|  | `1252` |  | `private byte[] GetOrCreateReadBuffer()`
| 315 | `1253` |  | `{`
| 315 | `1254` |  | `_readBuffer ??= new byte[_readBufferSize];`
| 315 | `1255` |  | `return _readBuffer;`
| 315 | `1256` |  | `}`
|  | `1257` |  | ``
|  | `1258` |  | `private byte[] GetOrCreateWriteBuffer()`
| 265 | `1259` |  | `{`
| 265 | `1260` |  | `_writeBuffer ??= new byte[_writeBufferSize];`
| 265 | `1261` |  | `return _writeBuffer;`
| 265 | `1262` |  | `}`
|  | `1263` |  | ``
|  | `1264` |  | `/// <summary>`
|  | `1265` |  | `/// Flushes the read data from the buffer.`
|  | `1266` |  | `/// </summary>`
|  | `1267` |  | `private void FlushReadBuffer()`
| 392 | `1268` |  | `{`
| 392 | `1269` |  | `_bufferPosition = 0;`
| 392 | `1270` |  | `_bufferLen = 0;`
| 392 | `1271` |  | `}`
|  | `1272` |  | ``
|  | `1273` |  | `/// <summary>`
|  | `1274` |  | `/// Flush any buffered write data to the file.`
|  | `1275` |  | `/// </summary>`
|  | `1276` |  | `private void FlushWriteBuffer()`
| 310 | `1277` |  | `{`
| 310 | `1278` |  | `if (_bufferPosition > 0)`
| 191 | `1279` |  | `{`
| 191 | `1280` |  | `using (var wait = new AutoResetEvent(initialState: false))`
| 191 | `1281` |  | `{`
| 191 | `1282` |  | `_session.RequestWrite(_handle, (ulong) (_position - _bufferPosition), _writeBuffer, 0, _bufferPositi`
| 191 | `1283` |  | `}`
|  | `1284` |  | ``
| 191 | `1285` |  | `_bufferPosition = 0;`
| 191 | `1286` |  | `}`
| 310 | `1287` |  | `}`
|  | `1288` |  | ``
|  | `1289` |  | `private async Task FlushWriteBufferAsync(CancellationToken cancellationToken)`
| 10 | `1290` |  | `{`
| 10 | `1291` |  | `if (_bufferPosition > 0)`
| 10 | `1292` |  | `{`
| 10 | `1293` |  | `await _session.RequestWriteAsync(_handle, (ulong)(_position - _bufferPosition), _writeBuffer, 0, _buffer`
| 10 | `1294` |  | `_bufferPosition = 0;`
| 10 | `1295` |  | `}`
| 10 | `1296` |  | `}`
|  | `1297` |  | ``
|  | `1298` |  | `/// <summary>`
|  | `1299` |  | `/// Setups the read.`
|  | `1300` |  | `/// </summary>`
|  | `1301` |  | `private void SetupRead()`
| 682 | `1302` |  | `{`
| 682 | `1303` |  | `if (!CanRead)`
| 63 | `1304` |  | `{`
| 63 | `1305` |  | `throw new NotSupportedException("Read not supported.");`
|  | `1306` |  | `}`
|  | `1307` |  | ``
| 619 | `1308` |  | `if (_bufferOwnedByWrite)`
| 24 | `1309` |  | `{`
| 24 | `1310` |  | `FlushWriteBuffer();`
| 24 | `1311` |  | `_bufferOwnedByWrite = false;`
| 24 | `1312` |  | `}`
| 619 | `1313` |  | `}`
|  | `1314` |  | ``
|  | `1315` |  | `/// <summary>`
|  | `1316` |  | `/// Setups the write.`
|  | `1317` |  | `/// </summary>`
|  | `1318` |  | `private void SetupWrite()`
| 468 | `1319` |  | `{`
| 468 | `1320` |  | `if (!CanWrite)`
| 21 | `1321` |  | `{`
| 21 | `1322` |  | `throw new NotSupportedException("Write not supported.");`
|  | `1323` |  | `}`
|  | `1324` |  | ``
| 447 | `1325` |  | `if (!_bufferOwnedByWrite)`
| 371 | `1326` |  | `{`
| 371 | `1327` |  | `FlushReadBuffer();`
| 371 | `1328` |  | `_bufferOwnedByWrite = true;`
| 371 | `1329` |  | `}`
| 447 | `1330` |  | `}`
|  | `1331` |  | ``
|  | `1332` |  | `private void CheckSessionIsOpen()`
| 1773 | `1333` |  | `{`
|  | `1334` |  | `#if NET7_0_OR_GREATER`
| 1411 | `1335` |  | `ObjectDisposedException.ThrowIf(_session is null, this);`
|  | `1336` |  | `#else`
| 362 | `1337` |  | `if (_session is null)`
| 2 | `1338` |  | `{`
| 2 | `1339` |  | `throw new ObjectDisposedException(GetType().FullName);`
|  | `1340` |  | `}`
|  | `1341` |  | `#endif // NET7_0_OR_GREATER`
|  | `1342` |  | ``
| 1767 | `1343` |  | `if (!_session.IsOpen)`
| 6 | `1344` |  | `{`
| 6 | `1345` |  | `throw new ObjectDisposedException(GetType().FullName, "Cannot access a closed SFTP session.");`
|  | `1346` |  | `}`
| 1761 | `1347` |  | `}`
|  | `1348` |  | `}`
|  | `1349` |  | `}`

# Methods/Properties

.ctor(Renci.SshNet.Sftp.ISftpSession,System.String,System.IO.FileAccess,System.Int32,System.Byte[],System.Int64)
get_CanRead()
get_CanSeek()
get_CanWrite()
get_CanTimeout()
get_Length()
get_Position()
set_Position(System.Int64)
get_Name()
get_Handle()
get_Timeout()
.ctor(Renci.SshNet.Sftp.ISftpSession,System.String,System.IO.FileMode,System.IO.FileAccess,System.Int32)
OpenAsync()
Finalize()
Flush()
FlushAsync(System.Threading.CancellationToken)
Read(System.Byte[],System.Int32,System.Int32)
ReadAsync()
ReadByte()
Seek(System.Int64,System.IO.SeekOrigin)
SetLength(System.Int64)
Write(System.Byte[],System.Int32,System.Int32)
WriteAsync()
WriteByte(System.Byte)
Dispose(System.Boolean)
GetOrCreateReadBuffer()
GetOrCreateWriteBuffer()
FlushReadBuffer()
FlushWriteBuffer()
FlushWriteBufferAsync()
SetupRead()
SetupWrite()
CheckSessionIsOpen()