|
|
@@ -1,4 +1,6 @@
|
|
|
using System;
|
|
|
+using System.Buffers.Binary;
|
|
|
+using System.Diagnostics;
|
|
|
using System.Globalization;
|
|
|
using System.IO;
|
|
|
using System.Numerics;
|
|
|
@@ -27,7 +29,7 @@ namespace Renci.SshNet.Common
|
|
|
/// <param name="buffer">The array of unsigned bytes from which to create the current stream.</param>
|
|
|
/// <exception cref="ArgumentNullException"><paramref name="buffer"/> is <see langword="null"/>.</exception>
|
|
|
public SshDataStream(byte[] buffer)
|
|
|
- : base(buffer)
|
|
|
+ : base(buffer ?? throw new ArgumentNullException(nameof(buffer)), 0, buffer.Length, writable: true, publiclyVisible: true)
|
|
|
{
|
|
|
}
|
|
|
|
|
|
@@ -39,7 +41,7 @@ namespace Renci.SshNet.Common
|
|
|
/// <param name="count">The number of bytes to load.</param>
|
|
|
/// <exception cref="ArgumentNullException"><paramref name="buffer"/> is <see langword="null"/>.</exception>
|
|
|
public SshDataStream(byte[] buffer, int offset, int count)
|
|
|
- : base(buffer, offset, count)
|
|
|
+ : base(buffer, offset, count, writable: true, publiclyVisible: true)
|
|
|
{
|
|
|
}
|
|
|
|
|
|
@@ -58,19 +60,6 @@ namespace Renci.SshNet.Common
|
|
|
}
|
|
|
|
|
|
#if NETFRAMEWORK || NETSTANDARD2_0
|
|
|
- private int Read(Span<byte> buffer)
|
|
|
- {
|
|
|
- var sharedBuffer = System.Buffers.ArrayPool<byte>.Shared.Rent(buffer.Length);
|
|
|
-
|
|
|
- var numRead = Read(sharedBuffer, 0, buffer.Length);
|
|
|
-
|
|
|
- sharedBuffer.AsSpan(0, numRead).CopyTo(buffer);
|
|
|
-
|
|
|
- System.Buffers.ArrayPool<byte>.Shared.Return(sharedBuffer);
|
|
|
-
|
|
|
- return numRead;
|
|
|
- }
|
|
|
-
|
|
|
private void Write(ReadOnlySpan<byte> buffer)
|
|
|
{
|
|
|
var sharedBuffer = System.Buffers.ArrayPool<byte>.Shared.Rent(buffer.Length);
|
|
|
@@ -90,7 +79,7 @@ namespace Renci.SshNet.Common
|
|
|
public void Write(uint value)
|
|
|
{
|
|
|
Span<byte> bytes = stackalloc byte[4];
|
|
|
- System.Buffers.Binary.BinaryPrimitives.WriteUInt32BigEndian(bytes, value);
|
|
|
+ BinaryPrimitives.WriteUInt32BigEndian(bytes, value);
|
|
|
Write(bytes);
|
|
|
}
|
|
|
|
|
|
@@ -101,7 +90,7 @@ namespace Renci.SshNet.Common
|
|
|
public void Write(ulong value)
|
|
|
{
|
|
|
Span<byte> bytes = stackalloc byte[8];
|
|
|
- System.Buffers.Binary.BinaryPrimitives.WriteUInt64BigEndian(bytes, value);
|
|
|
+ BinaryPrimitives.WriteUInt64BigEndian(bytes, value);
|
|
|
Write(bytes);
|
|
|
}
|
|
|
|
|
|
@@ -137,6 +126,7 @@ namespace Renci.SshNet.Common
|
|
|
/// <exception cref="ArgumentNullException"><paramref name="encoding"/> is <see langword="null"/>.</exception>
|
|
|
public void Write(string s, Encoding encoding)
|
|
|
{
|
|
|
+ ThrowHelper.ThrowIfNull(s);
|
|
|
ThrowHelper.ThrowIfNull(encoding);
|
|
|
|
|
|
#if NETSTANDARD2_1 || NET
|
|
|
@@ -153,12 +143,21 @@ namespace Renci.SshNet.Common
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
- /// Reads a byte array from the SSH data stream.
|
|
|
+ /// Reads a length-prefixed byte array from the SSH data stream.
|
|
|
/// </summary>
|
|
|
/// <returns>
|
|
|
/// The byte array read from the SSH data stream.
|
|
|
/// </returns>
|
|
|
public byte[] ReadBinary()
|
|
|
+ {
|
|
|
+ return ReadBinarySegment().ToArray();
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Reads a length-prefixed byte array from the SSH data stream,
|
|
|
+ /// returned as a view over the underlying buffer.
|
|
|
+ /// </summary>
|
|
|
+ internal ArraySegment<byte> ReadBinarySegment()
|
|
|
{
|
|
|
var length = ReadUInt32();
|
|
|
|
|
|
@@ -167,7 +166,23 @@ namespace Renci.SshNet.Common
|
|
|
throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Data longer than {0} is not supported.", int.MaxValue));
|
|
|
}
|
|
|
|
|
|
- return ReadBytes((int)length);
|
|
|
+ var buffer = GetRemainingBuffer().Slice(0, (int)length);
|
|
|
+
|
|
|
+ Position += length;
|
|
|
+
|
|
|
+ return buffer;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Gets a view over the remaining data in the underlying buffer.
|
|
|
+ /// </summary>
|
|
|
+ private ArraySegment<byte> GetRemainingBuffer()
|
|
|
+ {
|
|
|
+ var success = TryGetBuffer(out var buffer);
|
|
|
+
|
|
|
+ Debug.Assert(success, "Expected buffer to be publicly visible");
|
|
|
+
|
|
|
+ return buffer.Slice((int)Position);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
@@ -205,11 +220,11 @@ namespace Renci.SshNet.Common
|
|
|
/// </returns>
|
|
|
public BigInteger ReadBigInt()
|
|
|
{
|
|
|
- var data = ReadBinary();
|
|
|
-
|
|
|
#if NETSTANDARD2_1 || NET
|
|
|
+ var data = ReadBinarySegment();
|
|
|
return new BigInteger(data, isBigEndian: true);
|
|
|
#else
|
|
|
+ var data = ReadBinary();
|
|
|
Array.Reverse(data);
|
|
|
return new BigInteger(data);
|
|
|
#endif
|
|
|
@@ -223,9 +238,9 @@ namespace Renci.SshNet.Common
|
|
|
/// </returns>
|
|
|
public ushort ReadUInt16()
|
|
|
{
|
|
|
- Span<byte> bytes = stackalloc byte[2];
|
|
|
- ReadBytes(bytes);
|
|
|
- return System.Buffers.Binary.BinaryPrimitives.ReadUInt16BigEndian(bytes);
|
|
|
+ var ret = BinaryPrimitives.ReadUInt16BigEndian(GetRemainingBuffer());
|
|
|
+ Position += sizeof(ushort);
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
@@ -236,9 +251,9 @@ namespace Renci.SshNet.Common
|
|
|
/// </returns>
|
|
|
public uint ReadUInt32()
|
|
|
{
|
|
|
- Span<byte> span = stackalloc byte[4];
|
|
|
- ReadBytes(span);
|
|
|
- return System.Buffers.Binary.BinaryPrimitives.ReadUInt32BigEndian(span);
|
|
|
+ var ret = BinaryPrimitives.ReadUInt32BigEndian(GetRemainingBuffer());
|
|
|
+ Position += sizeof(uint);
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
@@ -249,9 +264,9 @@ namespace Renci.SshNet.Common
|
|
|
/// </returns>
|
|
|
public ulong ReadUInt64()
|
|
|
{
|
|
|
- Span<byte> span = stackalloc byte[8];
|
|
|
- ReadBytes(span);
|
|
|
- return System.Buffers.Binary.BinaryPrimitives.ReadUInt64BigEndian(span);
|
|
|
+ var ret = BinaryPrimitives.ReadUInt64BigEndian(GetRemainingBuffer());
|
|
|
+ Position += sizeof(ulong);
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
@@ -265,19 +280,13 @@ namespace Renci.SshNet.Common
|
|
|
{
|
|
|
encoding ??= Encoding.UTF8;
|
|
|
|
|
|
- var length = ReadUInt32();
|
|
|
-
|
|
|
- if (length > int.MaxValue)
|
|
|
- {
|
|
|
- throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Strings longer than {0} is not supported.", int.MaxValue));
|
|
|
- }
|
|
|
+ var bytes = ReadBinarySegment();
|
|
|
|
|
|
- var bytes = ReadBytes((int)length);
|
|
|
- return encoding.GetString(bytes, 0, bytes.Length);
|
|
|
+ return encoding.GetString(bytes.Array, bytes.Offset, bytes.Count);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
- /// Writes the stream contents to a byte array, regardless of the <see cref="MemoryStream.Position"/>.
|
|
|
+ /// Retrieves the stream contents as a byte array, regardless of the <see cref="MemoryStream.Position"/>.
|
|
|
/// </summary>
|
|
|
/// <returns>
|
|
|
/// This method returns the contents of the <see cref="SshDataStream"/> as a byte array.
|
|
|
@@ -288,9 +297,15 @@ namespace Renci.SshNet.Common
|
|
|
/// </remarks>
|
|
|
public override byte[] ToArray()
|
|
|
{
|
|
|
- if (Capacity == Length)
|
|
|
+ var success = TryGetBuffer(out var buffer);
|
|
|
+
|
|
|
+ Debug.Assert(success, "Expected buffer to be publicly visible");
|
|
|
+
|
|
|
+ if (buffer.Offset == 0 &&
|
|
|
+ buffer.Count == buffer.Array.Length &&
|
|
|
+ buffer.Count == Length)
|
|
|
{
|
|
|
- return GetBuffer();
|
|
|
+ return buffer.Array;
|
|
|
}
|
|
|
|
|
|
return base.ToArray();
|
|
|
@@ -315,19 +330,5 @@ namespace Renci.SshNet.Common
|
|
|
|
|
|
return data;
|
|
|
}
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Reads data into the specified <paramref name="buffer" />.
|
|
|
- /// </summary>
|
|
|
- /// <param name="buffer">The buffer to read into.</param>
|
|
|
- /// <exception cref="ArgumentOutOfRangeException"><paramref name="buffer"/> is larger than the total of bytes available.</exception>
|
|
|
- private void ReadBytes(Span<byte> buffer)
|
|
|
- {
|
|
|
- var bytesRead = Read(buffer);
|
|
|
- if (bytesRead < buffer.Length)
|
|
|
- {
|
|
|
- throw new ArgumentOutOfRangeException(nameof(buffer), string.Format(CultureInfo.InvariantCulture, "The requested length ({0}) is greater than the actual number of bytes read ({1}).", buffer.Length, bytesRead));
|
|
|
- }
|
|
|
- }
|
|
|
}
|
|
|
}
|