@@ -208,39 +208,41 @@ private Stream CreateTempFile()
208
208
FileOptions . Asynchronous | FileOptions . DeleteOnClose | FileOptions . SequentialScan ) ;
209
209
}
210
210
211
- public override int Read ( byte [ ] buffer , int offset , int count )
211
+ public override int Read ( Span < byte > buffer )
212
212
{
213
213
ThrowIfDisposed ( ) ;
214
- if ( _buffer . Position < _buffer . Length || _completelyBuffered )
214
+
215
+ if ( _completelyBuffered )
215
216
{
216
217
// Just read from the buffer
217
- return _buffer . Read ( buffer , offset , ( int ) Math . Min ( count , _buffer . Length - _buffer . Position ) ) ;
218
+ return _buffer . Read ( buffer ) ;
218
219
}
219
220
220
- int read = _inner . Read ( buffer , offset , count ) ;
221
+ var read = _inner . Read ( buffer ) ;
221
222
222
223
if ( _bufferLimit . HasValue && _bufferLimit - read < _buffer . Length )
223
224
{
224
- Dispose ( ) ;
225
225
throw new IOException ( "Buffer limit exceeded." ) ;
226
226
}
227
227
228
- if ( _inMemory && _buffer . Length + read > _memoryThreshold )
228
+ // We're about to go over the threshold, switch to a file
229
+ if ( _inMemory && _memoryThreshold - read < _buffer . Length )
229
230
{
230
231
_inMemory = false ;
231
232
var oldBuffer = _buffer ;
232
233
_buffer = CreateTempFile ( ) ;
233
234
if ( _rentedBuffer == null )
234
235
{
236
+ // Copy data from the in memory buffer to the file stream using a pooled buffer
235
237
oldBuffer . Position = 0 ;
236
238
var rentedBuffer = _bytePool . Rent ( Math . Min ( ( int ) oldBuffer . Length , _maxRentedBufferSize ) ) ;
237
239
try
238
240
{
239
- var copyRead = oldBuffer . Read ( rentedBuffer , 0 , rentedBuffer . Length ) ;
241
+ var copyRead = oldBuffer . Read ( rentedBuffer ) ;
240
242
while ( copyRead > 0 )
241
243
{
242
- _buffer . Write ( rentedBuffer , 0 , copyRead ) ;
243
- copyRead = oldBuffer . Read ( rentedBuffer , 0 , rentedBuffer . Length ) ;
244
+ _buffer . Write ( rentedBuffer . AsSpan ( 0 , copyRead ) ) ;
245
+ copyRead = oldBuffer . Read ( rentedBuffer ) ;
244
246
}
245
247
}
246
248
finally
@@ -250,15 +252,15 @@ public override int Read(byte[] buffer, int offset, int count)
250
252
}
251
253
else
252
254
{
253
- _buffer . Write ( _rentedBuffer , 0 , ( int ) oldBuffer . Length ) ;
255
+ _buffer . Write ( _rentedBuffer . AsSpan ( 0 , ( int ) oldBuffer . Length ) ) ;
254
256
_bytePool . Return ( _rentedBuffer ) ;
255
257
_rentedBuffer = null ;
256
258
}
257
259
}
258
260
259
261
if ( read > 0 )
260
262
{
261
- _buffer . Write ( buffer , offset , read ) ;
263
+ _buffer . Write ( buffer . Slice ( 0 , read ) ) ;
262
264
}
263
265
else
264
266
{
@@ -268,24 +270,34 @@ public override int Read(byte[] buffer, int offset, int count)
268
270
return read ;
269
271
}
270
272
271
- public override async Task < int > ReadAsync ( byte [ ] buffer , int offset , int count , CancellationToken cancellationToken )
273
+ public override int Read ( byte [ ] buffer , int offset , int count )
274
+ {
275
+ return Read ( buffer . AsSpan ( offset , count ) ) ;
276
+ }
277
+
278
+ public override Task < int > ReadAsync ( byte [ ] buffer , int offset , int count , CancellationToken cancellationToken )
279
+ {
280
+ return ReadAsync ( buffer . AsMemory ( offset , count ) , cancellationToken ) . AsTask ( ) ;
281
+ }
282
+
283
+ public override async ValueTask < int > ReadAsync ( Memory < byte > buffer , CancellationToken cancellationToken = default )
272
284
{
273
285
ThrowIfDisposed ( ) ;
274
- if ( _buffer . Position < _buffer . Length || _completelyBuffered )
286
+
287
+ if ( _completelyBuffered )
275
288
{
276
289
// Just read from the buffer
277
- return await _buffer . ReadAsync ( buffer , offset , ( int ) Math . Min ( count , _buffer . Length - _buffer . Position ) , cancellationToken ) ;
290
+ return await _buffer . ReadAsync ( buffer , cancellationToken ) ;
278
291
}
279
292
280
- int read = await _inner . ReadAsync ( buffer , offset , count , cancellationToken ) ;
293
+ var read = await _inner . ReadAsync ( buffer , cancellationToken ) ;
281
294
282
295
if ( _bufferLimit . HasValue && _bufferLimit - read < _buffer . Length )
283
296
{
284
- Dispose ( ) ;
285
297
throw new IOException ( "Buffer limit exceeded." ) ;
286
298
}
287
299
288
- if ( _inMemory && _buffer . Length + read > _memoryThreshold )
300
+ if ( _inMemory && _memoryThreshold - read < _buffer . Length )
289
301
{
290
302
_inMemory = false ;
291
303
var oldBuffer = _buffer ;
@@ -297,11 +309,11 @@ public override async Task<int> ReadAsync(byte[] buffer, int offset, int count,
297
309
try
298
310
{
299
311
// oldBuffer is a MemoryStream, no need to do async reads.
300
- var copyRead = oldBuffer . Read ( rentedBuffer , 0 , rentedBuffer . Length ) ;
312
+ var copyRead = oldBuffer . Read ( rentedBuffer ) ;
301
313
while ( copyRead > 0 )
302
314
{
303
- await _buffer . WriteAsync ( rentedBuffer , 0 , copyRead , cancellationToken ) ;
304
- copyRead = oldBuffer . Read ( rentedBuffer , 0 , rentedBuffer . Length ) ;
315
+ await _buffer . WriteAsync ( rentedBuffer . AsMemory ( 0 , copyRead ) , cancellationToken ) ;
316
+ copyRead = oldBuffer . Read ( rentedBuffer ) ;
305
317
}
306
318
}
307
319
finally
@@ -311,15 +323,15 @@ public override async Task<int> ReadAsync(byte[] buffer, int offset, int count,
311
323
}
312
324
else
313
325
{
314
- await _buffer . WriteAsync ( _rentedBuffer , 0 , ( int ) oldBuffer . Length , cancellationToken ) ;
326
+ await _buffer . WriteAsync ( _rentedBuffer . AsMemory ( 0 , ( int ) oldBuffer . Length ) , cancellationToken ) ;
315
327
_bytePool . Return ( _rentedBuffer ) ;
316
328
_rentedBuffer = null ;
317
329
}
318
330
}
319
331
320
332
if ( read > 0 )
321
333
{
322
- await _buffer . WriteAsync ( buffer , offset , read , cancellationToken ) ;
334
+ await _buffer . WriteAsync ( buffer . Slice ( 0 , read ) , cancellationToken ) ;
323
335
}
324
336
else
325
337
{
@@ -349,6 +361,39 @@ public override void Flush()
349
361
throw new NotSupportedException ( ) ;
350
362
}
351
363
364
+ public override Task CopyToAsync ( Stream destination , int bufferSize , CancellationToken cancellationToken )
365
+ {
366
+ // If we're completed buffered then copy from the underlying source
367
+ if ( _completelyBuffered )
368
+ {
369
+ return _buffer . CopyToAsync ( destination , bufferSize , cancellationToken ) ;
370
+ }
371
+
372
+ async Task CopyToAsyncImpl ( )
373
+ {
374
+ // At least a 4K buffer
375
+ byte [ ] buffer = _bytePool . Rent ( Math . Min ( bufferSize , 4096 ) ) ;
376
+ try
377
+ {
378
+ while ( true )
379
+ {
380
+ int bytesRead = await ReadAsync ( buffer , cancellationToken ) ;
381
+ if ( bytesRead == 0 )
382
+ {
383
+ break ;
384
+ }
385
+ await destination . WriteAsync ( buffer . AsMemory ( 0 , bytesRead ) , cancellationToken ) ;
386
+ }
387
+ }
388
+ finally
389
+ {
390
+ _bytePool . Return ( buffer ) ;
391
+ }
392
+ }
393
+
394
+ return CopyToAsyncImpl ( ) ;
395
+ }
396
+
352
397
protected override void Dispose ( bool disposing )
353
398
{
354
399
if ( ! _disposed )
0 commit comments