1
1
using System ;
2
+ using System . Buffers . Binary ;
2
3
using System . Collections . Generic ;
3
4
using System . Diagnostics ;
4
5
using System . Globalization ;
@@ -22,8 +23,8 @@ internal sealed class SftpSession : SubsystemSession, ISftpSession
22
23
23
24
private readonly Dictionary < uint , SftpRequest > _requests = new Dictionary < uint , SftpRequest > ( ) ;
24
25
private readonly ISftpResponseFactory _sftpResponseFactory ;
25
- private readonly List < byte > _data = new List < byte > ( 32 * 1024 ) ;
26
26
private readonly Encoding _encoding ;
27
+ private System . Net . ArrayBuffer _buffer = new ( 32 * 1024 ) ;
27
28
private EventWaitHandle _sftpVersionConfirmed = new AutoResetEvent ( initialState : false ) ;
28
29
private IDictionary < string , string > _supportedExtensions ;
29
30
@@ -303,125 +304,77 @@ protected override void OnChannelOpen()
303
304
304
305
protected override void OnDataReceived( byte [ ] data )
305
306
{
306
- const int packetLengthByteCount = 4 ;
307
- const int sftpMessageTypeByteCount = 1 ;
308
- const int minimumChannelDataLength = packetLengthByteCount + sftpMessageTypeByteCount ;
307
+ ArraySegment< byte > d = new ( data ) ;
309
308
310
- var offset = 0 ;
311
- var count = data. Length ;
312
-
313
- // improve performance and reduce GC pressure by not buffering channel data if the received
314
- // chunk contains the complete packet data.
315
- //
316
- // for this, the buffer should be empty and the chunk should contain at least the packet length
317
- // and the type of the SFTP message
318
- if ( _data . Count == 0 )
309
+ // If the buffer is empty then skip a copy and read packets
310
+ // directly out of the given data.
311
+ if ( _buffer . ActiveLength == 0 )
319
312
{
320
- while ( count >= minimumChannelDataLength )
313
+ while ( d . Count >= 4 )
321
314
{
322
- // extract packet length
323
- var packetDataLength = data[ offset ] << 24 | data [ offset + 1 ] << 16 | data [ offset + 2 ] << 8 |
324
- data [ offset + 3 ] ;
325
-
326
- var packetTotalLength = packetDataLength + packetLengthByteCount ;
315
+ var packetLength = BinaryPrimitives. ReadInt32BigEndian ( d ) ;
327
316
328
- // check if complete packet data (or more) is available
329
- if ( count >= packetTotalLength )
317
+ if ( d . Count - 4 < packetLength )
330
318
{
331
- // load and process SFTP message
332
- if ( ! TryLoadSftpMessage ( data , offset + packetLengthByteCount , packetDataLength ) )
333
- {
334
- return ;
335
- }
336
-
337
- // remove processed bytes from the number of bytes to process as the channel
338
- // data we received may contain (part of) another message
339
- count -= packetTotalLength ;
340
-
341
- // move offset beyond bytes we just processed
342
- offset += packetTotalLength ;
319
+ break ;
343
320
}
344
- else
321
+
322
+ if ( ! TryLoadSftpMessage ( d . Slice ( 4 , packetLength ) ) )
345
323
{
346
- // we don't have a complete message
347
- break ;
324
+ // An error occured.
325
+ return ;
348
326
}
349
- }
350
327
351
- // check if there is channel data left to process or buffer
352
- if ( count == 0 )
353
- {
354
- return ;
328
+ d = d. Slice ( 4 + packetLength ) ;
355
329
}
356
330
357
- // check if we processed part of the channel data we received
358
- if ( offset > 0 )
359
- {
360
- // add (remaining) channel data to internal data holder
361
- var remainingChannelData = new byte [ count ] ;
362
- Buffer. BlockCopy ( data , offset , remainingChannelData , 0 , count ) ;
363
- _data. AddRange ( remainingChannelData ) ;
364
- }
365
- else
331
+ if ( d . Count > 0 )
366
332
{
367
- // add (remaining) channel data to internal data holder
368
- _data. AddRange ( data ) ;
333
+ // Now buffer the remainder.
334
+ _buffer. EnsureAvailableSpace ( d . Count ) ;
335
+ d. AsSpan ( ) . CopyTo ( _buffer . AvailableSpan ) ;
336
+ _buffer. Commit ( d . Count ) ;
369
337
}
370
338
371
- // skip further processing as we'll need a new chunk to complete the message
372
339
return ;
373
340
}
374
341
375
- // add (remaining) channel data to internal data holder
376
- _data. AddRange ( data ) ;
342
+ // The buffer already had some data. Append the new data and
343
+ // proceed with reading out packets.
344
+ _buffer. EnsureAvailableSpace ( d . Count ) ;
345
+ d. AsSpan ( ) . CopyTo ( _buffer . AvailableSpan ) ;
346
+ _buffer. Commit ( d . Count ) ;
377
347
378
- while ( _data . Count >= minimumChannelDataLength )
348
+ while ( _buffer . ActiveLength >= 4 )
379
349
{
380
- // extract packet length
381
- var packetDataLength = _data[ 0 ] << 24 | _data [ 1 ] << 16 | _data [ 2 ] << 8 | _data [ 3 ] ;
350
+ d = new ArraySegment< byte > (
351
+ _buffer . DangerousGetUnderlyingBuffer ( ) ,
352
+ _buffer . ActiveStartOffset ,
353
+ _buffer . ActiveLength ) ;
382
354
383
- var packetTotalLength = packetDataLength + packetLengthByteCount ;
355
+ var packetLength = BinaryPrimitives . ReadInt32BigEndian ( d ) ;
384
356
385
- // check if complete packet data is available
386
- if ( _data . Count < packetTotalLength )
357
+ if ( d . Count - 4 < packetLength )
387
358
{
388
- // wait for complete message to arrive first
389
359
break ;
390
360
}
391
361
392
- // create buffer to hold packet data
393
- var packetData = new byte [ packetDataLength ] ;
362
+ // Note: the packet data in the buffer is safe to read from
363
+ // only for the duration of this load. If it needs to be stored,
364
+ // callees should make their own copy.
365
+ _ = TryLoadSftpMessage ( d . Slice ( 4 , packetLength ) ) ;
394
366
395
- // copy packet data and bytes for length to array
396
- _data . CopyTo ( packetLengthByteCount , packetData , 0 , packetDataLength ) ;
397
-
398
- // remove loaded data and bytes for length from _data holder
399
- if ( _data . Count == packetTotalLength )
400
- {
401
- // the only buffered data is the data we're processing
402
- _data. Clear ( ) ;
403
- }
404
- else
405
- {
406
- // remove only the data we're processing
407
- _data. RemoveRange ( 0 , packetTotalLength ) ;
408
- }
409
-
410
- // load and process SFTP message
411
- if ( ! TryLoadSftpMessage ( packetData , 0 , packetDataLength ) )
412
- {
413
- break ;
414
- }
367
+ _buffer. Discard ( 4 + packetLength ) ;
415
368
}
416
369
}
417
370
418
- private bool TryLoadSftpMessage( byte [ ] packetData , int offset , int count )
371
+ private bool TryLoadSftpMessage( ArraySegment < byte > packetData )
419
372
{
420
373
// Create SFTP message
421
- var response = _sftpResponseFactory. Create ( ProtocolVersion , packetData [ offset ] , _encoding ) ;
374
+ var response = _sftpResponseFactory . Create ( ProtocolVersion , packetData . Array [ packetData . Offset ] , _encoding ) ;
422
375
423
376
// Load message data into it
424
- response. Load ( packetData , offset + 1 , count - 1 ) ;
377
+ response . Load ( packetData . Array , packetData . Offset + 1 , packetData . Count - 1 ) ;
425
378
426
379
try
427
380
{
0 commit comments