1
1
using System ;
2
+ using System . Buffers . Binary ;
3
+ using System . Diagnostics ;
2
4
using System . Globalization ;
3
5
using System . IO ;
4
6
using System . Numerics ;
@@ -27,7 +29,7 @@ public SshDataStream(int capacity)
27
29
/// <param name="buffer">The array of unsigned bytes from which to create the current stream.</param>
28
30
/// <exception cref="ArgumentNullException"><paramref name="buffer"/> is <see langword="null"/>.</exception>
29
31
public SshDataStream ( byte [ ] buffer )
30
- : base ( buffer )
32
+ : base ( buffer ?? throw new ArgumentNullException ( nameof ( buffer ) ) , 0 , buffer . Length , writable : true , publiclyVisible : true )
31
33
{
32
34
}
33
35
@@ -39,7 +41,7 @@ public SshDataStream(byte[] buffer)
39
41
/// <param name="count">The number of bytes to load.</param>
40
42
/// <exception cref="ArgumentNullException"><paramref name="buffer"/> is <see langword="null"/>.</exception>
41
43
public SshDataStream ( byte [ ] buffer , int offset , int count )
42
- : base ( buffer , offset , count )
44
+ : base ( buffer , offset , count , writable : true , publiclyVisible : true )
43
45
{
44
46
}
45
47
@@ -58,19 +60,6 @@ public bool IsEndOfData
58
60
}
59
61
60
62
#if NETFRAMEWORK || NETSTANDARD2_0
61
- private int Read ( Span < byte > buffer )
62
- {
63
- var sharedBuffer = System . Buffers . ArrayPool < byte > . Shared . Rent ( buffer . Length ) ;
64
-
65
- var numRead = Read ( sharedBuffer , 0 , buffer . Length ) ;
66
-
67
- sharedBuffer . AsSpan ( 0 , numRead ) . CopyTo ( buffer ) ;
68
-
69
- System . Buffers . ArrayPool < byte > . Shared . Return ( sharedBuffer ) ;
70
-
71
- return numRead ;
72
- }
73
-
74
63
private void Write ( ReadOnlySpan < byte > buffer )
75
64
{
76
65
var sharedBuffer = System . Buffers . ArrayPool < byte > . Shared . Rent ( buffer . Length ) ;
@@ -90,7 +79,7 @@ private void Write(ReadOnlySpan<byte> buffer)
90
79
public void Write ( uint value )
91
80
{
92
81
Span < byte > bytes = stackalloc byte [ 4 ] ;
93
- System . Buffers . Binary . BinaryPrimitives . WriteUInt32BigEndian ( bytes , value ) ;
82
+ BinaryPrimitives . WriteUInt32BigEndian ( bytes , value ) ;
94
83
Write ( bytes ) ;
95
84
}
96
85
@@ -101,7 +90,7 @@ public void Write(uint value)
101
90
public void Write ( ulong value )
102
91
{
103
92
Span < byte > bytes = stackalloc byte [ 8 ] ;
104
- System . Buffers . Binary . BinaryPrimitives . WriteUInt64BigEndian ( bytes , value ) ;
93
+ BinaryPrimitives . WriteUInt64BigEndian ( bytes , value ) ;
105
94
Write ( bytes ) ;
106
95
}
107
96
@@ -137,6 +126,7 @@ public void Write(byte[] data)
137
126
/// <exception cref="ArgumentNullException"><paramref name="encoding"/> is <see langword="null"/>.</exception>
138
127
public void Write ( string s , Encoding encoding )
139
128
{
129
+ ThrowHelper . ThrowIfNull ( s ) ;
140
130
ThrowHelper . ThrowIfNull ( encoding ) ;
141
131
142
132
#if NETSTANDARD2_1 || NET
@@ -153,12 +143,21 @@ public void Write(string s, Encoding encoding)
153
143
}
154
144
155
145
/// <summary>
156
- /// Reads a byte array from the SSH data stream.
146
+ /// Reads a length-prefixed byte array from the SSH data stream.
157
147
/// </summary>
158
148
/// <returns>
159
149
/// The byte array read from the SSH data stream.
160
150
/// </returns>
161
151
public byte [ ] ReadBinary ( )
152
+ {
153
+ return ReadBinarySegment ( ) . ToArray ( ) ;
154
+ }
155
+
156
+ /// <summary>
157
+ /// Reads a length-prefixed byte array from the SSH data stream,
158
+ /// returned as a view over the underlying buffer.
159
+ /// </summary>
160
+ internal ArraySegment < byte > ReadBinarySegment ( )
162
161
{
163
162
var length = ReadUInt32 ( ) ;
164
163
@@ -167,7 +166,23 @@ public byte[] ReadBinary()
167
166
throw new NotSupportedException ( string . Format ( CultureInfo . CurrentCulture , "Data longer than {0} is not supported." , int . MaxValue ) ) ;
168
167
}
169
168
170
- return ReadBytes ( ( int ) length ) ;
169
+ var buffer = GetRemainingBuffer ( ) . Slice ( 0 , ( int ) length ) ;
170
+
171
+ Position += length ;
172
+
173
+ return buffer ;
174
+ }
175
+
176
+ /// <summary>
177
+ /// Gets a view over the remaining data in the underlying buffer.
178
+ /// </summary>
179
+ private ArraySegment < byte > GetRemainingBuffer ( )
180
+ {
181
+ var success = TryGetBuffer ( out var buffer ) ;
182
+
183
+ Debug . Assert ( success , "Expected buffer to be publicly visible" ) ;
184
+
185
+ return buffer . Slice ( ( int ) Position ) ;
171
186
}
172
187
173
188
/// <summary>
@@ -205,11 +220,11 @@ public void WriteBinary(byte[] buffer, int offset, int count)
205
220
/// </returns>
206
221
public BigInteger ReadBigInt ( )
207
222
{
208
- var data = ReadBinary ( ) ;
209
-
210
223
#if NETSTANDARD2_1 || NET
224
+ var data = ReadBinarySegment ( ) ;
211
225
return new BigInteger ( data , isBigEndian : true ) ;
212
226
#else
227
+ var data = ReadBinary ( ) ;
213
228
Array . Reverse ( data ) ;
214
229
return new BigInteger ( data ) ;
215
230
#endif
@@ -223,9 +238,9 @@ public BigInteger ReadBigInt()
223
238
/// </returns>
224
239
public ushort ReadUInt16 ( )
225
240
{
226
- Span < byte > bytes = stackalloc byte [ 2 ] ;
227
- ReadBytes ( bytes ) ;
228
- return System . Buffers . Binary . BinaryPrimitives . ReadUInt16BigEndian ( bytes ) ;
241
+ var ret = BinaryPrimitives . ReadUInt16BigEndian ( GetRemainingBuffer ( ) ) ;
242
+ Position += sizeof ( ushort ) ;
243
+ return ret ;
229
244
}
230
245
231
246
/// <summary>
@@ -236,9 +251,9 @@ public ushort ReadUInt16()
236
251
/// </returns>
237
252
public uint ReadUInt32 ( )
238
253
{
239
- Span < byte > span = stackalloc byte [ 4 ] ;
240
- ReadBytes ( span ) ;
241
- return System . Buffers . Binary . BinaryPrimitives . ReadUInt32BigEndian ( span ) ;
254
+ var ret = BinaryPrimitives . ReadUInt32BigEndian ( GetRemainingBuffer ( ) ) ;
255
+ Position += sizeof ( uint ) ;
256
+ return ret ;
242
257
}
243
258
244
259
/// <summary>
@@ -249,9 +264,9 @@ public uint ReadUInt32()
249
264
/// </returns>
250
265
public ulong ReadUInt64 ( )
251
266
{
252
- Span < byte > span = stackalloc byte [ 8 ] ;
253
- ReadBytes ( span ) ;
254
- return System . Buffers . Binary . BinaryPrimitives . ReadUInt64BigEndian ( span ) ;
267
+ var ret = BinaryPrimitives . ReadUInt64BigEndian ( GetRemainingBuffer ( ) ) ;
268
+ Position += sizeof ( ulong ) ;
269
+ return ret ;
255
270
}
256
271
257
272
/// <summary>
@@ -265,19 +280,13 @@ public string ReadString(Encoding encoding = null)
265
280
{
266
281
encoding ??= Encoding . UTF8 ;
267
282
268
- var length = ReadUInt32 ( ) ;
269
-
270
- if ( length > int . MaxValue )
271
- {
272
- throw new NotSupportedException ( string . Format ( CultureInfo . CurrentCulture , "Strings longer than {0} is not supported." , int . MaxValue ) ) ;
273
- }
283
+ var bytes = ReadBinarySegment ( ) ;
274
284
275
- var bytes = ReadBytes ( ( int ) length ) ;
276
- return encoding . GetString ( bytes , 0 , bytes . Length ) ;
285
+ return encoding . GetString ( bytes . Array , bytes . Offset , bytes . Count ) ;
277
286
}
278
287
279
288
/// <summary>
280
- /// Writes the stream contents to a byte array, regardless of the <see cref="MemoryStream.Position"/>.
289
+ /// Retrieves the stream contents as a byte array, regardless of the <see cref="MemoryStream.Position"/>.
281
290
/// </summary>
282
291
/// <returns>
283
292
/// This method returns the contents of the <see cref="SshDataStream"/> as a byte array.
@@ -288,9 +297,15 @@ public string ReadString(Encoding encoding = null)
288
297
/// </remarks>
289
298
public override byte [ ] ToArray ( )
290
299
{
291
- if ( Capacity == Length )
300
+ var success = TryGetBuffer ( out var buffer ) ;
301
+
302
+ Debug . Assert ( success , "Expected buffer to be publicly visible" ) ;
303
+
304
+ if ( buffer . Offset == 0 &&
305
+ buffer . Count == buffer . Array . Length &&
306
+ buffer . Count == Length )
292
307
{
293
- return GetBuffer ( ) ;
308
+ return buffer . Array ;
294
309
}
295
310
296
311
return base . ToArray ( ) ;
@@ -315,19 +330,5 @@ internal byte[] ReadBytes(int length)
315
330
316
331
return data ;
317
332
}
318
-
319
- /// <summary>
320
- /// Reads data into the specified <paramref name="buffer" />.
321
- /// </summary>
322
- /// <param name="buffer">The buffer to read into.</param>
323
- /// <exception cref="ArgumentOutOfRangeException"><paramref name="buffer"/> is larger than the total of bytes available.</exception>
324
- private void ReadBytes ( Span < byte > buffer )
325
- {
326
- var bytesRead = Read ( buffer ) ;
327
- if ( bytesRead < buffer . Length )
328
- {
329
- 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 ) ) ;
330
- }
331
- }
332
333
}
333
334
}
0 commit comments