Skip to content

Commit e9f101c

Browse files
authored
Override more Stream members on System.IO.Compression streams (#54518)
1 parent 419506b commit e9f101c

File tree

5 files changed

+211
-22
lines changed

5 files changed

+211
-22
lines changed

src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateManaged/DeflateManagedStream.cs

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Diagnostics;
5-
using System.Runtime.CompilerServices;
5+
using System.Runtime.InteropServices;
66
using System.Threading;
77
using System.Threading.Tasks;
88

@@ -97,19 +97,22 @@ public override void SetLength(long value)
9797
public override int Read(byte[] buffer, int offset, int count)
9898
{
9999
ValidateBufferArguments(buffer, offset, count);
100+
return Read(new Span<byte>(buffer, offset, count));
101+
}
102+
103+
public override int Read(Span<byte> buffer)
104+
{
100105
EnsureNotDisposed();
101106

102-
int bytesRead;
103-
int currentOffset = offset;
104-
int remainingCount = count;
107+
int initialLength = buffer.Length;
105108

109+
int bytesRead;
106110
while (true)
107111
{
108-
bytesRead = _inflater.Inflate(buffer, currentOffset, remainingCount);
109-
currentOffset += bytesRead;
110-
remainingCount -= bytesRead;
112+
bytesRead = _inflater.Inflate(buffer);
113+
buffer = buffer.Slice(bytesRead);
111114

112-
if (remainingCount == 0)
115+
if (buffer.Length == 0)
113116
{
114117
break;
115118
}
@@ -136,7 +139,13 @@ public override int Read(byte[] buffer, int offset, int count)
136139
_inflater.SetInput(_buffer, 0, bytes);
137140
}
138141

139-
return count - remainingCount;
142+
return initialLength - buffer.Length;
143+
}
144+
145+
public override int ReadByte()
146+
{
147+
byte b = default;
148+
return Read(MemoryMarshal.CreateSpan(ref b, 1)) == 1 ? b : -1;
140149
}
141150

142151
private void EnsureNotDisposed()
@@ -169,7 +178,7 @@ private ValueTask<int> ReadAsyncInternal(Memory<byte> buffer, CancellationToken
169178
try
170179
{
171180
// Try to read decompressed data in output buffer
172-
int bytesRead = _inflater.Inflate(buffer);
181+
int bytesRead = _inflater.Inflate(buffer.Span);
173182
if (bytesRead != 0)
174183
{
175184
// If decompression output buffer is not empty, return immediately.
@@ -224,7 +233,7 @@ private async ValueTask<int> ReadAsyncCore(ValueTask<int> readTask, Memory<byte>
224233

225234
// Feed the data from base stream into decompression engine
226235
_inflater.SetInput(_buffer, 0, bytesRead);
227-
bytesRead = _inflater.Inflate(buffer);
236+
bytesRead = _inflater.Inflate(buffer.Span);
228237

229238
if (bytesRead == 0 && !_inflater.Finished())
230239
{

src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateManaged/InflaterManaged.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ public void SetInput(byte[] inputBytes, int offset, int length) =>
9595

9696
public int AvailableOutput => _output.AvailableBytes;
9797

98-
public int Inflate(Memory<byte> bytes)
98+
public int Inflate(Span<byte> bytes)
9999
{
100100
// copy bytes from output to outputbytes if we have available bytes
101101
// if buffer is not filled up. keep decoding until no input are available
@@ -139,7 +139,7 @@ public int Inflate(Memory<byte> bytes)
139139
return count;
140140
}
141141

142-
public int Inflate(byte[] bytes, int offset, int length) => Inflate(bytes.AsMemory(offset, length));
142+
public int Inflate(byte[] bytes, int offset, int length) => Inflate(bytes.AsSpan(offset, length));
143143

144144
//Each block of compressed data begins with 3 header bits
145145
// containing the following data:

src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateManaged/OutputWindow.cs

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ public int CopyFrom(InputBuffer input, int length)
118118
public int AvailableBytes => _bytesUsed;
119119

120120
/// <summary>Copy the decompressed bytes to output buffer.</summary>
121-
public int CopyTo(Memory<byte> output)
121+
public int CopyTo(Span<byte> output)
122122
{
123123
int copy_end;
124124

@@ -140,19 +140,13 @@ public int CopyTo(Memory<byte> output)
140140
{
141141
// this means we need to copy two parts separately
142142
// copy the taillen bytes from the end of the output window
143-
_window.AsSpan(WindowSize - tailLen, tailLen).CopyTo(output.Span);
143+
_window.AsSpan(WindowSize - tailLen, tailLen).CopyTo(output);
144144
output = output.Slice(tailLen, copy_end);
145145
}
146-
_window.AsSpan(copy_end - output.Length, output.Length).CopyTo(output.Span);
146+
_window.AsSpan(copy_end - output.Length, output.Length).CopyTo(output);
147147
_bytesUsed -= copied;
148148
Debug.Assert(_bytesUsed >= 0, "check this function and find why we copied more bytes than we have");
149149
return copied;
150150
}
151-
152-
/// <summary>Copy the decompressed bytes to output array.</summary>
153-
public int CopyTo(byte[] output, int offset, int length)
154-
{
155-
return CopyTo(output.AsMemory(offset, length));
156-
}
157151
}
158152
}

src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.cs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@
44
using System.Collections.Generic;
55
using System.Diagnostics;
66
using System.Diagnostics.CodeAnalysis;
7+
using System.Runtime.InteropServices;
78
using System.Text;
9+
using System.Threading;
10+
using System.Threading.Tasks;
811

912
namespace System.IO.Compression
1013
{
@@ -1222,6 +1225,38 @@ public override void Write(ReadOnlySpan<byte> source)
12221225
_position += source.Length;
12231226
}
12241227

1228+
public override void WriteByte(byte value) =>
1229+
Write(MemoryMarshal.CreateReadOnlySpan(ref value, 1));
1230+
1231+
public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
1232+
{
1233+
ValidateBufferArguments(buffer, offset, count);
1234+
return WriteAsync(new ReadOnlyMemory<byte>(buffer, offset, count), cancellationToken).AsTask();
1235+
}
1236+
1237+
public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
1238+
{
1239+
ThrowIfDisposed();
1240+
Debug.Assert(CanWrite);
1241+
1242+
return !buffer.IsEmpty ?
1243+
Core(buffer, cancellationToken) :
1244+
default;
1245+
1246+
async ValueTask Core(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken)
1247+
{
1248+
if (!_everWritten)
1249+
{
1250+
_everWritten = true;
1251+
// write local header, we are good to go
1252+
_usedZip64inLH = _entry.WriteLocalFileHeader(isEmptyFile: false);
1253+
}
1254+
1255+
await _crcSizeStream.WriteAsync(buffer, cancellationToken).ConfigureAwait(false);
1256+
_position += buffer.Length;
1257+
}
1258+
}
1259+
12251260
public override void Flush()
12261261
{
12271262
ThrowIfDisposed();
@@ -1230,6 +1265,14 @@ public override void Flush()
12301265
_crcSizeStream.Flush();
12311266
}
12321267

1268+
public override Task FlushAsync(CancellationToken cancellationToken)
1269+
{
1270+
ThrowIfDisposed();
1271+
Debug.Assert(CanWrite);
1272+
1273+
return _crcSizeStream.FlushAsync(cancellationToken);
1274+
}
1275+
12331276
protected override void Dispose(bool disposing)
12341277
{
12351278
if (disposing && !_isDisposed)

src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCustomStreams.cs

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Diagnostics;
5+
using System.Runtime.InteropServices;
6+
using System.Threading;
7+
using System.Threading.Tasks;
58

69
namespace System.IO.Compression
710
{
@@ -95,6 +98,38 @@ public override int Read(byte[] buffer, int offset, int count)
9598
return _baseStream.Read(buffer, offset, count);
9699
}
97100

101+
public override int Read(Span<byte> buffer)
102+
{
103+
ThrowIfDisposed();
104+
ThrowIfCantRead();
105+
106+
return _baseStream.Read(buffer);
107+
}
108+
109+
public override int ReadByte()
110+
{
111+
ThrowIfDisposed();
112+
ThrowIfCantRead();
113+
114+
return _baseStream.ReadByte();
115+
}
116+
117+
public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
118+
{
119+
ThrowIfDisposed();
120+
ThrowIfCantRead();
121+
122+
return _baseStream.ReadAsync(buffer, offset, count, cancellationToken);
123+
}
124+
125+
public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
126+
{
127+
ThrowIfDisposed();
128+
ThrowIfCantRead();
129+
130+
return _baseStream.ReadAsync(buffer, cancellationToken);
131+
}
132+
98133
public override long Seek(long offset, SeekOrigin origin)
99134
{
100135
ThrowIfDisposed();
@@ -128,6 +163,30 @@ public override void Write(ReadOnlySpan<byte> source)
128163
_baseStream.Write(source);
129164
}
130165

166+
public override void WriteByte(byte value)
167+
{
168+
ThrowIfDisposed();
169+
ThrowIfCantWrite();
170+
171+
_baseStream.WriteByte(value);
172+
}
173+
174+
public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
175+
{
176+
ThrowIfDisposed();
177+
ThrowIfCantWrite();
178+
179+
return _baseStream.WriteAsync(buffer, offset, count, cancellationToken);
180+
}
181+
182+
public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
183+
{
184+
ThrowIfDisposed();
185+
ThrowIfCantWrite();
186+
187+
return _baseStream.WriteAsync(buffer, cancellationToken);
188+
}
189+
131190
public override void Flush()
132191
{
133192
ThrowIfDisposed();
@@ -136,6 +195,14 @@ public override void Flush()
136195
_baseStream.Flush();
137196
}
138197

198+
public override Task FlushAsync(CancellationToken cancellationToken)
199+
{
200+
ThrowIfDisposed();
201+
ThrowIfCantWrite();
202+
203+
return _baseStream.FlushAsync(cancellationToken);
204+
}
205+
139206
protected override void Dispose(bool disposing)
140207
{
141208
if (disposing && !_isDisposed)
@@ -259,6 +326,43 @@ public override int Read(Span<byte> destination)
259326
return ret;
260327
}
261328

329+
public override int ReadByte()
330+
{
331+
byte b = default;
332+
return Read(MemoryMarshal.CreateSpan(ref b, 1)) == 1 ? b : -1;
333+
}
334+
335+
public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
336+
{
337+
ValidateBufferArguments(buffer, offset, count);
338+
return ReadAsync(new Memory<byte>(buffer, offset, count), cancellationToken).AsTask();
339+
}
340+
341+
public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
342+
{
343+
ThrowIfDisposed();
344+
ThrowIfCantRead();
345+
return Core(buffer, cancellationToken);
346+
347+
async ValueTask<int> Core(Memory<byte> buffer, CancellationToken cancellationToken)
348+
{
349+
if (_superStream.Position != _positionInSuperStream)
350+
{
351+
_superStream.Seek(_positionInSuperStream, SeekOrigin.Begin);
352+
}
353+
354+
if (_positionInSuperStream > _endInSuperStream - buffer.Length)
355+
{
356+
buffer = buffer.Slice(0, (int)(_endInSuperStream - _positionInSuperStream));
357+
}
358+
359+
int ret = await _superStream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false);
360+
361+
_positionInSuperStream += ret;
362+
return ret;
363+
}
364+
}
365+
262366
public override long Seek(long offset, SeekOrigin origin)
263367
{
264368
ThrowIfDisposed();
@@ -437,6 +541,39 @@ public override void Write(ReadOnlySpan<byte> source)
437541
_position += source.Length;
438542
}
439543

544+
public override void WriteByte(byte value) =>
545+
Write(MemoryMarshal.CreateReadOnlySpan(ref value, 1));
546+
547+
public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
548+
{
549+
ValidateBufferArguments(buffer, offset, count);
550+
return WriteAsync(new ReadOnlyMemory<byte>(buffer, offset, count), cancellationToken).AsTask();
551+
}
552+
553+
public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
554+
{
555+
ThrowIfDisposed();
556+
Debug.Assert(CanWrite);
557+
558+
return !buffer.IsEmpty ?
559+
Core(buffer, cancellationToken) :
560+
default;
561+
562+
async ValueTask Core(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
563+
{
564+
if (!_everWritten)
565+
{
566+
_initialPosition = _baseBaseStream.Position;
567+
_everWritten = true;
568+
}
569+
570+
_checksum = Crc32Helper.UpdateCrc32(_checksum, buffer.Span);
571+
572+
await _baseStream.WriteAsync(buffer, cancellationToken).ConfigureAwait(false);
573+
_position += buffer.Length;
574+
}
575+
}
576+
440577
public override void Flush()
441578
{
442579
ThrowIfDisposed();
@@ -447,6 +584,12 @@ public override void Flush()
447584
_baseStream.Flush();
448585
}
449586

587+
public override Task FlushAsync(CancellationToken cancellationToken)
588+
{
589+
ThrowIfDisposed();
590+
return _baseStream.FlushAsync(cancellationToken);
591+
}
592+
450593
protected override void Dispose(bool disposing)
451594
{
452595
if (disposing && !_isDisposed)

0 commit comments

Comments
 (0)