11using System ;
2+ using System . Buffers ;
23using System . IO ;
34using System . Threading ;
45using System . Threading . Tasks ;
@@ -28,14 +29,25 @@ protected override void Dispose(bool disposing)
2829#if DEBUG_STREAMS
2930 this . DebugDispose ( typeof ( BufferedSubStream ) ) ;
3031#endif
31- if ( disposing ) { }
32+ if ( _isDisposed )
33+ {
34+ return ;
35+ }
36+ _isDisposed = true ;
37+
38+ if ( disposing && _cache is not null )
39+ {
40+ ArrayPool < byte > . Shared . Return ( _cache ) ;
41+ _cache = null ;
42+ }
3243 base . Dispose ( disposing ) ;
3344 }
3445
3546 private int _cacheOffset ;
3647 private int _cacheLength ;
37- private readonly byte [ ] _cache = new byte [ 32 << 10 ] ;
48+ private byte [ ] ? _cache = ArrayPool < byte > . Shared . Rent ( 81920 ) ;
3849 private long origin ;
50+ private bool _isDisposed ;
3951
4052 private long BytesLeftToRead { get ; set ; }
4153
@@ -57,29 +69,51 @@ public override long Position
5769
5870 private void RefillCache ( )
5971 {
60- var count = ( int ) Math . Min ( BytesLeftToRead , _cache . Length ) ;
72+ if ( _isDisposed )
73+ {
74+ throw new ObjectDisposedException ( nameof ( BufferedSubStream ) ) ;
75+ }
76+
77+ var count = ( int ) Math . Min ( BytesLeftToRead , _cache ! . Length ) ;
6178 _cacheOffset = 0 ;
6279 if ( count == 0 )
6380 {
6481 _cacheLength = 0 ;
6582 return ;
6683 }
67- Stream . Position = origin ;
84+
85+ // Only seek if we're not already at the correct position
86+ // This avoids expensive seek operations when reading sequentially
87+ if ( Stream . CanSeek && Stream . Position != origin )
88+ {
89+ Stream . Position = origin ;
90+ }
91+
6892 _cacheLength = Stream . Read ( _cache , 0 , count ) ;
6993 origin += _cacheLength ;
7094 BytesLeftToRead -= _cacheLength ;
7195 }
7296
7397 private async ValueTask RefillCacheAsync ( CancellationToken cancellationToken )
7498 {
75- var count = ( int ) Math . Min ( BytesLeftToRead , _cache . Length ) ;
99+ if ( _isDisposed )
100+ {
101+ throw new ObjectDisposedException ( nameof ( BufferedSubStream ) ) ;
102+ }
103+
104+ var count = ( int ) Math . Min ( BytesLeftToRead , _cache ! . Length ) ;
76105 _cacheOffset = 0 ;
77106 if ( count == 0 )
78107 {
79108 _cacheLength = 0 ;
80109 return ;
81110 }
82- Stream . Position = origin ;
111+ // Only seek if we're not already at the correct position
112+ // This avoids expensive seek operations when reading sequentially
113+ if ( Stream . CanSeek && Stream . Position != origin )
114+ {
115+ Stream . Position = origin ;
116+ }
83117 _cacheLength = await Stream
84118 . ReadAsync ( _cache , 0 , count , cancellationToken )
85119 . ConfigureAwait ( false ) ;
@@ -102,7 +136,7 @@ public override int Read(byte[] buffer, int offset, int count)
102136 }
103137
104138 count = Math . Min ( count , _cacheLength - _cacheOffset ) ;
105- Buffer . BlockCopy ( _cache , _cacheOffset , buffer , offset , count ) ;
139+ Buffer . BlockCopy ( _cache ! , _cacheOffset , buffer , offset , count ) ;
106140 _cacheOffset += count ;
107141 }
108142
@@ -120,7 +154,7 @@ public override int ReadByte()
120154 }
121155 }
122156
123- return _cache [ _cacheOffset ++ ] ;
157+ return _cache ! [ _cacheOffset ++ ] ;
124158 }
125159
126160 public override async Task < int > ReadAsync (
@@ -143,7 +177,7 @@ CancellationToken cancellationToken
143177 }
144178
145179 count = Math . Min ( count , _cacheLength - _cacheOffset ) ;
146- Buffer . BlockCopy ( _cache , _cacheOffset , buffer , offset , count ) ;
180+ Buffer . BlockCopy ( _cache ! , _cacheOffset , buffer , offset , count ) ;
147181 _cacheOffset += count ;
148182 }
149183
@@ -170,7 +204,7 @@ public override async ValueTask<int> ReadAsync(
170204 }
171205
172206 count = Math . Min ( count , _cacheLength - _cacheOffset ) ;
173- _cache . AsSpan ( _cacheOffset , count ) . CopyTo ( buffer . Span ) ;
207+ _cache ! . AsSpan ( _cacheOffset , count ) . CopyTo ( buffer . Span ) ;
174208 _cacheOffset += count ;
175209 }
176210
0 commit comments