|
@@ -26,9 +26,12 @@ namespace Renci.SshNet
|
|
|
|
|
|
|
|
private readonly object _sync = new object();
|
|
private readonly object _sync = new object();
|
|
|
|
|
|
|
|
- private byte[] _buffer;
|
|
|
|
|
- private int _head; // The index from which the data starts in _buffer.
|
|
|
|
|
- private int _tail; // The index at which to add new data into _buffer.
|
|
|
|
|
|
|
+ private readonly byte[] _writeBuffer;
|
|
|
|
|
+ private int _writeLength; // The length of the data in _writeBuffer.
|
|
|
|
|
+
|
|
|
|
|
+ private byte[] _readBuffer;
|
|
|
|
|
+ private int _readHead; // The index from which the data starts in _readBuffer.
|
|
|
|
|
+ private int _readTail; // The index at which to add new data into _readBuffer.
|
|
|
private bool _disposed;
|
|
private bool _disposed;
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
@@ -54,7 +57,7 @@ namespace Renci.SshNet
|
|
|
lock (_sync)
|
|
lock (_sync)
|
|
|
{
|
|
{
|
|
|
AssertValid();
|
|
AssertValid();
|
|
|
- return _tail != _head;
|
|
|
|
|
|
|
+ return _readTail != _readHead;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -64,11 +67,11 @@ namespace Renci.SshNet
|
|
|
private void AssertValid()
|
|
private void AssertValid()
|
|
|
{
|
|
{
|
|
|
Debug.Assert(Monitor.IsEntered(_sync), $"Should be in lock on {nameof(_sync)}");
|
|
Debug.Assert(Monitor.IsEntered(_sync), $"Should be in lock on {nameof(_sync)}");
|
|
|
- Debug.Assert(_head >= 0, $"{nameof(_head)} should be non-negative but is {_head}");
|
|
|
|
|
- Debug.Assert(_tail >= 0, $"{nameof(_tail)} should be non-negative but is {_tail}");
|
|
|
|
|
- Debug.Assert(_head < _buffer.Length || _buffer.Length == 0, $"{nameof(_head)} should be < {nameof(_buffer)}.Length but is {_head}");
|
|
|
|
|
- Debug.Assert(_tail <= _buffer.Length, $"{nameof(_tail)} should be <= {nameof(_buffer)}.Length but is {_tail}");
|
|
|
|
|
- Debug.Assert(_head <= _tail, $"Should have {nameof(_head)} <= {nameof(_tail)} but have {_head} <= {_tail}");
|
|
|
|
|
|
|
+ Debug.Assert(_readHead >= 0, $"{nameof(_readHead)} should be non-negative but is {_readHead}");
|
|
|
|
|
+ Debug.Assert(_readTail >= 0, $"{nameof(_readTail)} should be non-negative but is {_readTail}");
|
|
|
|
|
+ Debug.Assert(_readHead < _readBuffer.Length || _readBuffer.Length == 0, $"{nameof(_readHead)} should be < {nameof(_readBuffer)}.Length but is {_readHead}");
|
|
|
|
|
+ Debug.Assert(_readTail <= _readBuffer.Length, $"{nameof(_readTail)} should be <= {nameof(_readBuffer)}.Length but is {_readTail}");
|
|
|
|
|
+ Debug.Assert(_readHead <= _readTail, $"Should have {nameof(_readHead)} <= {nameof(_readTail)} but have {_readHead} <= {_readTail}");
|
|
|
}
|
|
}
|
|
|
#pragma warning restore MA0076 // Do not use implicit culture-sensitive ToString in interpolated strings
|
|
#pragma warning restore MA0076 // Do not use implicit culture-sensitive ToString in interpolated strings
|
|
|
|
|
|
|
@@ -108,7 +111,8 @@ namespace Renci.SshNet
|
|
|
_session.Disconnected += Session_Disconnected;
|
|
_session.Disconnected += Session_Disconnected;
|
|
|
_session.ErrorOccured += Session_ErrorOccured;
|
|
_session.ErrorOccured += Session_ErrorOccured;
|
|
|
|
|
|
|
|
- _buffer = new byte[bufferSize];
|
|
|
|
|
|
|
+ _readBuffer = new byte[bufferSize];
|
|
|
|
|
+ _writeBuffer = new byte[bufferSize];
|
|
|
|
|
|
|
|
try
|
|
try
|
|
|
{
|
|
{
|
|
@@ -178,6 +182,15 @@ namespace Renci.SshNet
|
|
|
/// </summary>
|
|
/// </summary>
|
|
|
public override void Flush()
|
|
public override void Flush()
|
|
|
{
|
|
{
|
|
|
|
|
+ ThrowIfDisposed();
|
|
|
|
|
+
|
|
|
|
|
+ Debug.Assert(_writeLength >= 0 && _writeLength <= _writeBuffer.Length);
|
|
|
|
|
+
|
|
|
|
|
+ if (_writeLength > 0)
|
|
|
|
|
+ {
|
|
|
|
|
+ _channel.SendData(_writeBuffer, 0, _writeLength);
|
|
|
|
|
+ _writeLength = 0;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
@@ -191,7 +204,7 @@ namespace Renci.SshNet
|
|
|
lock (_sync)
|
|
lock (_sync)
|
|
|
{
|
|
{
|
|
|
AssertValid();
|
|
AssertValid();
|
|
|
- return _tail - _head;
|
|
|
|
|
|
|
+ return _readTail - _readHead;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -326,22 +339,22 @@ namespace Renci.SshNet
|
|
|
AssertValid();
|
|
AssertValid();
|
|
|
|
|
|
|
|
var searchHead = lookback == -1
|
|
var searchHead = lookback == -1
|
|
|
- ? _head
|
|
|
|
|
- : Math.Max(_tail - lookback, _head);
|
|
|
|
|
|
|
+ ? _readHead
|
|
|
|
|
+ : Math.Max(_readTail - lookback, _readHead);
|
|
|
|
|
|
|
|
- Debug.Assert(_head <= searchHead && searchHead <= _tail);
|
|
|
|
|
|
|
+ Debug.Assert(_readHead <= searchHead && searchHead <= _readTail);
|
|
|
|
|
|
|
|
#if NETFRAMEWORK || NETSTANDARD2_0
|
|
#if NETFRAMEWORK || NETSTANDARD2_0
|
|
|
- var indexOfMatch = _buffer.IndexOf(expectBytes, searchHead, _tail - searchHead);
|
|
|
|
|
|
|
+ var indexOfMatch = _readBuffer.IndexOf(expectBytes, searchHead, _readTail - searchHead);
|
|
|
#else
|
|
#else
|
|
|
- var indexOfMatch = _buffer.AsSpan(searchHead, _tail - searchHead).IndexOf(expectBytes);
|
|
|
|
|
|
|
+ var indexOfMatch = _readBuffer.AsSpan(searchHead, _readTail - searchHead).IndexOf(expectBytes);
|
|
|
#endif
|
|
#endif
|
|
|
|
|
|
|
|
if (indexOfMatch >= 0)
|
|
if (indexOfMatch >= 0)
|
|
|
{
|
|
{
|
|
|
- var returnText = _encoding.GetString(_buffer, _head, searchHead - _head + indexOfMatch + expectBytes.Length);
|
|
|
|
|
|
|
+ var returnText = _encoding.GetString(_readBuffer, _readHead, searchHead - _readHead + indexOfMatch + expectBytes.Length);
|
|
|
|
|
|
|
|
- _head = searchHead + indexOfMatch + expectBytes.Length;
|
|
|
|
|
|
|
+ _readHead = searchHead + indexOfMatch + expectBytes.Length;
|
|
|
|
|
|
|
|
AssertValid();
|
|
AssertValid();
|
|
|
|
|
|
|
@@ -415,7 +428,7 @@ namespace Renci.SshNet
|
|
|
{
|
|
{
|
|
|
AssertValid();
|
|
AssertValid();
|
|
|
|
|
|
|
|
- var bufferText = _encoding.GetString(_buffer, _head, _tail - _head);
|
|
|
|
|
|
|
+ var bufferText = _encoding.GetString(_readBuffer, _readHead, _readTail - _readHead);
|
|
|
|
|
|
|
|
var searchStart = lookback == -1
|
|
var searchStart = lookback == -1
|
|
|
? 0
|
|
? 0
|
|
@@ -438,7 +451,7 @@ namespace Renci.SshNet
|
|
|
{
|
|
{
|
|
|
var returnText = bufferText.Substring(0, match.Index + match.Length);
|
|
var returnText = bufferText.Substring(0, match.Index + match.Length);
|
|
|
#endif
|
|
#endif
|
|
|
- _head += _encoding.GetByteCount(returnText);
|
|
|
|
|
|
|
+ _readHead += _encoding.GetByteCount(returnText);
|
|
|
|
|
|
|
|
AssertValid();
|
|
AssertValid();
|
|
|
|
|
|
|
@@ -604,29 +617,29 @@ namespace Renci.SshNet
|
|
|
AssertValid();
|
|
AssertValid();
|
|
|
|
|
|
|
|
#if NETFRAMEWORK || NETSTANDARD2_0
|
|
#if NETFRAMEWORK || NETSTANDARD2_0
|
|
|
- var indexOfCr = _buffer.IndexOf(_carriageReturnBytes, _head, _tail - _head);
|
|
|
|
|
|
|
+ var indexOfCr = _readBuffer.IndexOf(_carriageReturnBytes, _readHead, _readTail - _readHead);
|
|
|
#else
|
|
#else
|
|
|
- var indexOfCr = _buffer.AsSpan(_head, _tail - _head).IndexOf(_carriageReturnBytes);
|
|
|
|
|
|
|
+ var indexOfCr = _readBuffer.AsSpan(_readHead, _readTail - _readHead).IndexOf(_carriageReturnBytes);
|
|
|
#endif
|
|
#endif
|
|
|
if (indexOfCr >= 0)
|
|
if (indexOfCr >= 0)
|
|
|
{
|
|
{
|
|
|
// We have found \r. We only need to search for \n up to and just after the \r
|
|
// We have found \r. We only need to search for \n up to and just after the \r
|
|
|
// (in order to consume \r\n if we can).
|
|
// (in order to consume \r\n if we can).
|
|
|
#if NETFRAMEWORK || NETSTANDARD2_0
|
|
#if NETFRAMEWORK || NETSTANDARD2_0
|
|
|
- var indexOfLf = indexOfCr + _carriageReturnBytes.Length + _lineFeedBytes.Length <= _tail - _head
|
|
|
|
|
- ? _buffer.IndexOf(_lineFeedBytes, _head, indexOfCr + _carriageReturnBytes.Length + _lineFeedBytes.Length)
|
|
|
|
|
- : _buffer.IndexOf(_lineFeedBytes, _head, indexOfCr);
|
|
|
|
|
|
|
+ var indexOfLf = indexOfCr + _carriageReturnBytes.Length + _lineFeedBytes.Length <= _readTail - _readHead
|
|
|
|
|
+ ? _readBuffer.IndexOf(_lineFeedBytes, _readHead, indexOfCr + _carriageReturnBytes.Length + _lineFeedBytes.Length)
|
|
|
|
|
+ : _readBuffer.IndexOf(_lineFeedBytes, _readHead, indexOfCr);
|
|
|
#else
|
|
#else
|
|
|
- var indexOfLf = indexOfCr + _carriageReturnBytes.Length + _lineFeedBytes.Length <= _tail - _head
|
|
|
|
|
- ? _buffer.AsSpan(_head, indexOfCr + _carriageReturnBytes.Length + _lineFeedBytes.Length).IndexOf(_lineFeedBytes)
|
|
|
|
|
- : _buffer.AsSpan(_head, indexOfCr).IndexOf(_lineFeedBytes);
|
|
|
|
|
|
|
+ var indexOfLf = indexOfCr + _carriageReturnBytes.Length + _lineFeedBytes.Length <= _readTail - _readHead
|
|
|
|
|
+ ? _readBuffer.AsSpan(_readHead, indexOfCr + _carriageReturnBytes.Length + _lineFeedBytes.Length).IndexOf(_lineFeedBytes)
|
|
|
|
|
+ : _readBuffer.AsSpan(_readHead, indexOfCr).IndexOf(_lineFeedBytes);
|
|
|
#endif
|
|
#endif
|
|
|
if (indexOfLf >= 0 && indexOfLf < indexOfCr)
|
|
if (indexOfLf >= 0 && indexOfLf < indexOfCr)
|
|
|
{
|
|
{
|
|
|
// If there is \n before the \r, then return up to the \n
|
|
// If there is \n before the \r, then return up to the \n
|
|
|
- var returnText = _encoding.GetString(_buffer, _head, indexOfLf);
|
|
|
|
|
|
|
+ var returnText = _encoding.GetString(_readBuffer, _readHead, indexOfLf);
|
|
|
|
|
|
|
|
- _head += indexOfLf + _lineFeedBytes.Length;
|
|
|
|
|
|
|
+ _readHead += indexOfLf + _lineFeedBytes.Length;
|
|
|
|
|
|
|
|
AssertValid();
|
|
AssertValid();
|
|
|
|
|
|
|
@@ -635,9 +648,9 @@ namespace Renci.SshNet
|
|
|
else if (indexOfLf == indexOfCr + _carriageReturnBytes.Length)
|
|
else if (indexOfLf == indexOfCr + _carriageReturnBytes.Length)
|
|
|
{
|
|
{
|
|
|
// If we have \r\n, then consume both
|
|
// If we have \r\n, then consume both
|
|
|
- var returnText = _encoding.GetString(_buffer, _head, indexOfCr);
|
|
|
|
|
|
|
+ var returnText = _encoding.GetString(_readBuffer, _readHead, indexOfCr);
|
|
|
|
|
|
|
|
- _head += indexOfCr + _carriageReturnBytes.Length + _lineFeedBytes.Length;
|
|
|
|
|
|
|
+ _readHead += indexOfCr + _carriageReturnBytes.Length + _lineFeedBytes.Length;
|
|
|
|
|
|
|
|
AssertValid();
|
|
AssertValid();
|
|
|
|
|
|
|
@@ -646,9 +659,9 @@ namespace Renci.SshNet
|
|
|
else
|
|
else
|
|
|
{
|
|
{
|
|
|
// Return up to the \r
|
|
// Return up to the \r
|
|
|
- var returnText = _encoding.GetString(_buffer, _head, indexOfCr);
|
|
|
|
|
|
|
+ var returnText = _encoding.GetString(_readBuffer, _readHead, indexOfCr);
|
|
|
|
|
|
|
|
- _head += indexOfCr + _carriageReturnBytes.Length;
|
|
|
|
|
|
|
+ _readHead += indexOfCr + _carriageReturnBytes.Length;
|
|
|
|
|
|
|
|
AssertValid();
|
|
AssertValid();
|
|
|
|
|
|
|
@@ -659,15 +672,15 @@ namespace Renci.SshNet
|
|
|
{
|
|
{
|
|
|
// There is no \r. What about \n?
|
|
// There is no \r. What about \n?
|
|
|
#if NETFRAMEWORK || NETSTANDARD2_0
|
|
#if NETFRAMEWORK || NETSTANDARD2_0
|
|
|
- var indexOfLf = _buffer.IndexOf(_lineFeedBytes, _head, _tail - _head);
|
|
|
|
|
|
|
+ var indexOfLf = _readBuffer.IndexOf(_lineFeedBytes, _readHead, _readTail - _readHead);
|
|
|
#else
|
|
#else
|
|
|
- var indexOfLf = _buffer.AsSpan(_head, _tail - _head).IndexOf(_lineFeedBytes);
|
|
|
|
|
|
|
+ var indexOfLf = _readBuffer.AsSpan(_readHead, _readTail - _readHead).IndexOf(_lineFeedBytes);
|
|
|
#endif
|
|
#endif
|
|
|
if (indexOfLf >= 0)
|
|
if (indexOfLf >= 0)
|
|
|
{
|
|
{
|
|
|
- var returnText = _encoding.GetString(_buffer, _head, indexOfLf);
|
|
|
|
|
|
|
+ var returnText = _encoding.GetString(_readBuffer, _readHead, indexOfLf);
|
|
|
|
|
|
|
|
- _head += indexOfLf + _lineFeedBytes.Length;
|
|
|
|
|
|
|
+ _readHead += indexOfLf + _lineFeedBytes.Length;
|
|
|
|
|
|
|
|
AssertValid();
|
|
AssertValid();
|
|
|
|
|
|
|
@@ -677,11 +690,11 @@ namespace Renci.SshNet
|
|
|
|
|
|
|
|
if (_disposed)
|
|
if (_disposed)
|
|
|
{
|
|
{
|
|
|
- var lastLine = _head == _tail
|
|
|
|
|
|
|
+ var lastLine = _readHead == _readTail
|
|
|
? null
|
|
? null
|
|
|
- : _encoding.GetString(_buffer, _head, _tail - _head);
|
|
|
|
|
|
|
+ : _encoding.GetString(_readBuffer, _readHead, _readTail - _readHead);
|
|
|
|
|
|
|
|
- _head = _tail = 0;
|
|
|
|
|
|
|
+ _readHead = _readTail = 0;
|
|
|
|
|
|
|
|
return lastLine;
|
|
return lastLine;
|
|
|
}
|
|
}
|
|
@@ -719,6 +732,18 @@ namespace Renci.SshNet
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private void ThrowIfDisposed()
|
|
|
|
|
+ {
|
|
|
|
|
+#if NET7_0_OR_GREATER
|
|
|
|
|
+ ObjectDisposedException.ThrowIf(_disposed, this);
|
|
|
|
|
+#else
|
|
|
|
|
+ if (_disposed)
|
|
|
|
|
+ {
|
|
|
|
|
+ throw new ObjectDisposedException(GetType().FullName);
|
|
|
|
|
+ }
|
|
|
|
|
+#endif // NET7_0_OR_GREATER
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
/// <summary>
|
|
/// <summary>
|
|
|
/// Reads all of the text currently available in the shell.
|
|
/// Reads all of the text currently available in the shell.
|
|
|
/// </summary>
|
|
/// </summary>
|
|
@@ -731,9 +756,9 @@ namespace Renci.SshNet
|
|
|
{
|
|
{
|
|
|
AssertValid();
|
|
AssertValid();
|
|
|
|
|
|
|
|
- var text = _encoding.GetString(_buffer, _head, _tail - _head);
|
|
|
|
|
|
|
+ var text = _encoding.GetString(_readBuffer, _readHead, _readTail - _readHead);
|
|
|
|
|
|
|
|
- _head = _tail = 0;
|
|
|
|
|
|
|
+ _readHead = _readTail = 0;
|
|
|
|
|
|
|
|
return text;
|
|
return text;
|
|
|
}
|
|
}
|
|
@@ -744,18 +769,18 @@ namespace Renci.SshNet
|
|
|
{
|
|
{
|
|
|
lock (_sync)
|
|
lock (_sync)
|
|
|
{
|
|
{
|
|
|
- while (_head == _tail && !_disposed)
|
|
|
|
|
|
|
+ while (_readHead == _readTail && !_disposed)
|
|
|
{
|
|
{
|
|
|
_ = Monitor.Wait(_sync);
|
|
_ = Monitor.Wait(_sync);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
AssertValid();
|
|
AssertValid();
|
|
|
|
|
|
|
|
- var bytesRead = Math.Min(count, _tail - _head);
|
|
|
|
|
|
|
+ var bytesRead = Math.Min(count, _readTail - _readHead);
|
|
|
|
|
|
|
|
- Buffer.BlockCopy(_buffer, _head, buffer, offset, bytesRead);
|
|
|
|
|
|
|
+ Buffer.BlockCopy(_readBuffer, _readHead, buffer, offset, bytesRead);
|
|
|
|
|
|
|
|
- _head += bytesRead;
|
|
|
|
|
|
|
+ _readHead += bytesRead;
|
|
|
|
|
|
|
|
AssertValid();
|
|
AssertValid();
|
|
|
|
|
|
|
@@ -768,13 +793,8 @@ namespace Renci.SshNet
|
|
|
/// </summary>
|
|
/// </summary>
|
|
|
/// <param name="text">The text to be written to the shell.</param>
|
|
/// <param name="text">The text to be written to the shell.</param>
|
|
|
/// <remarks>
|
|
/// <remarks>
|
|
|
- /// <para>
|
|
|
|
|
/// If <paramref name="text"/> is <see langword="null"/>, nothing is written.
|
|
/// If <paramref name="text"/> is <see langword="null"/>, nothing is written.
|
|
|
- /// </para>
|
|
|
|
|
- /// <para>
|
|
|
|
|
- /// Data is not buffered before being written to the shell. If you have text to send in many pieces,
|
|
|
|
|
- /// consider wrapping this stream in a <see cref="StreamWriter"/>.
|
|
|
|
|
- /// </para>
|
|
|
|
|
|
|
+ /// Otherwise, <see cref="Flush"/> is called after writing the data to the buffer.
|
|
|
/// </remarks>
|
|
/// </remarks>
|
|
|
/// <exception cref="ObjectDisposedException">The stream is closed.</exception>
|
|
/// <exception cref="ObjectDisposedException">The stream is closed.</exception>
|
|
|
public void Write(string? text)
|
|
public void Write(string? text)
|
|
@@ -787,31 +807,31 @@ namespace Renci.SshNet
|
|
|
var data = _encoding.GetBytes(text);
|
|
var data = _encoding.GetBytes(text);
|
|
|
|
|
|
|
|
Write(data, 0, data.Length);
|
|
Write(data, 0, data.Length);
|
|
|
|
|
+ Flush();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /// <summary>
|
|
|
|
|
- /// Writes a sequence of bytes to the shell.
|
|
|
|
|
- /// </summary>
|
|
|
|
|
- /// <param name="buffer">An array of bytes. This method sends <paramref name="count"/> bytes from buffer to the shell.</param>
|
|
|
|
|
- /// <param name="offset">The zero-based byte offset in <paramref name="buffer"/> at which to begin sending bytes to the shell.</param>
|
|
|
|
|
- /// <param name="count">The number of bytes to be sent to the shell.</param>
|
|
|
|
|
- /// <remarks>
|
|
|
|
|
- /// Data is not buffered before being written to the shell. If you have data to send in many pieces,
|
|
|
|
|
- /// consider wrapping this stream in a <see cref="BufferedStream"/>.
|
|
|
|
|
- /// </remarks>
|
|
|
|
|
- /// <exception cref="ObjectDisposedException">The stream is closed.</exception>
|
|
|
|
|
|
|
+ /// <inheritdoc/>
|
|
|
public override void Write(byte[] buffer, int offset, int count)
|
|
public override void Write(byte[] buffer, int offset, int count)
|
|
|
{
|
|
{
|
|
|
-#if NET7_0_OR_GREATER
|
|
|
|
|
- ObjectDisposedException.ThrowIf(_disposed, this);
|
|
|
|
|
-#else
|
|
|
|
|
- if (_disposed)
|
|
|
|
|
|
|
+ ThrowIfDisposed();
|
|
|
|
|
+
|
|
|
|
|
+ while (count > 0)
|
|
|
{
|
|
{
|
|
|
- throw new ObjectDisposedException(GetType().FullName);
|
|
|
|
|
- }
|
|
|
|
|
-#endif // NET7_0_OR_GREATER
|
|
|
|
|
|
|
+ if (_writeLength == _writeBuffer.Length)
|
|
|
|
|
+ {
|
|
|
|
|
+ Flush();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ var bytesToCopy = Math.Min(count, _writeBuffer.Length - _writeLength);
|
|
|
|
|
+
|
|
|
|
|
+ Buffer.BlockCopy(buffer, offset, _writeBuffer, _writeLength, bytesToCopy);
|
|
|
|
|
|
|
|
- _channel.SendData(buffer, offset, count);
|
|
|
|
|
|
|
+ offset += bytesToCopy;
|
|
|
|
|
+ count -= bytesToCopy;
|
|
|
|
|
+ _writeLength += bytesToCopy;
|
|
|
|
|
+
|
|
|
|
|
+ Debug.Assert(_writeLength >= 0 && _writeLength <= _writeBuffer.Length);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
@@ -820,6 +840,7 @@ namespace Renci.SshNet
|
|
|
/// <param name="line">The line to be written to the shell.</param>
|
|
/// <param name="line">The line to be written to the shell.</param>
|
|
|
/// <remarks>
|
|
/// <remarks>
|
|
|
/// If <paramref name="line"/> is <see langword="null"/>, only the line terminator is written.
|
|
/// If <paramref name="line"/> is <see langword="null"/>, only the line terminator is written.
|
|
|
|
|
+ /// <see cref="Flush"/> is called once the data is written.
|
|
|
/// </remarks>
|
|
/// </remarks>
|
|
|
/// <exception cref="ObjectDisposedException">The stream is closed.</exception>
|
|
/// <exception cref="ObjectDisposedException">The stream is closed.</exception>
|
|
|
public void WriteLine(string line)
|
|
public void WriteLine(string line)
|
|
@@ -883,38 +904,38 @@ namespace Renci.SshNet
|
|
|
|
|
|
|
|
// Ensure sufficient buffer space and copy the new data in.
|
|
// Ensure sufficient buffer space and copy the new data in.
|
|
|
|
|
|
|
|
- if (_buffer.Length - _tail >= e.Data.Length)
|
|
|
|
|
|
|
+ if (_readBuffer.Length - _readTail >= e.Data.Length)
|
|
|
{
|
|
{
|
|
|
// If there is enough space after _tail for the new data,
|
|
// If there is enough space after _tail for the new data,
|
|
|
// then copy the data there.
|
|
// then copy the data there.
|
|
|
- Buffer.BlockCopy(e.Data, 0, _buffer, _tail, e.Data.Length);
|
|
|
|
|
- _tail += e.Data.Length;
|
|
|
|
|
|
|
+ Buffer.BlockCopy(e.Data, 0, _readBuffer, _readTail, e.Data.Length);
|
|
|
|
|
+ _readTail += e.Data.Length;
|
|
|
}
|
|
}
|
|
|
else
|
|
else
|
|
|
{
|
|
{
|
|
|
// We can't fit the new data after _tail.
|
|
// We can't fit the new data after _tail.
|
|
|
|
|
|
|
|
- var newLength = _tail - _head + e.Data.Length;
|
|
|
|
|
|
|
+ var newLength = _readTail - _readHead + e.Data.Length;
|
|
|
|
|
|
|
|
- if (newLength <= _buffer.Length)
|
|
|
|
|
|
|
+ if (newLength <= _readBuffer.Length)
|
|
|
{
|
|
{
|
|
|
// If there is sufficient space at the start of the buffer,
|
|
// If there is sufficient space at the start of the buffer,
|
|
|
// then move the current data to the start of the buffer.
|
|
// then move the current data to the start of the buffer.
|
|
|
- Buffer.BlockCopy(_buffer, _head, _buffer, 0, _tail - _head);
|
|
|
|
|
|
|
+ Buffer.BlockCopy(_readBuffer, _readHead, _readBuffer, 0, _readTail - _readHead);
|
|
|
}
|
|
}
|
|
|
else
|
|
else
|
|
|
{
|
|
{
|
|
|
// Otherwise, we're gonna need a bigger buffer.
|
|
// Otherwise, we're gonna need a bigger buffer.
|
|
|
- var newBuffer = new byte[_buffer.Length * 2];
|
|
|
|
|
- Buffer.BlockCopy(_buffer, _head, newBuffer, 0, _tail - _head);
|
|
|
|
|
- _buffer = newBuffer;
|
|
|
|
|
|
|
+ var newBuffer = new byte[_readBuffer.Length * 2];
|
|
|
|
|
+ Buffer.BlockCopy(_readBuffer, _readHead, newBuffer, 0, _readTail - _readHead);
|
|
|
|
|
+ _readBuffer = newBuffer;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Copy the new data into the freed-up space.
|
|
// Copy the new data into the freed-up space.
|
|
|
- Buffer.BlockCopy(e.Data, 0, _buffer, _tail - _head, e.Data.Length);
|
|
|
|
|
|
|
+ Buffer.BlockCopy(e.Data, 0, _readBuffer, _readTail - _readHead, e.Data.Length);
|
|
|
|
|
|
|
|
- _head = 0;
|
|
|
|
|
- _tail = newLength;
|
|
|
|
|
|
|
+ _readHead = 0;
|
|
|
|
|
+ _readTail = newLength;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
AssertValid();
|
|
AssertValid();
|