// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
+using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
else
baseStream.Read(bytes, 0, size);
}
+
+ [Fact]
+ public async Task Parallel_CompressDecompressMultipleStreamsConcurrently()
+ {
+ const int ParallelOperations = 20;
+ const int DataSize = 10 * 1024;
+
+ var sourceData = new byte[DataSize];
+ new Random().NextBytes(sourceData);
+
+ await Task.WhenAll(Enumerable.Range(0, ParallelOperations).Select(_ => Task.Run(async () =>
+ {
+ var compressedStream = new MemoryStream();
+ using (Stream ds = CreateStream(compressedStream, CompressionMode.Compress, leaveOpen: true))
+ {
+ await ds.WriteAsync(sourceData, 0, sourceData.Length);
+ }
+
+ compressedStream.Position = 0;
+
+ var decompressedStream = new MemoryStream();
+ using (Stream ds = CreateStream(compressedStream, CompressionMode.Decompress, leaveOpen: true))
+ {
+ await ds.CopyToAsync(decompressedStream);
+ }
+
+ Assert.Equal(sourceData, decompressedStream.ToArray());
+ })));
+ }
}
internal sealed class BadWrappedStream : MemoryStream
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+using System.Buffers;
using System.Diagnostics;
-using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
{
private const int DefaultInternalBufferSize = (1 << 16) - 16; //65520;
private Stream _stream;
- private readonly byte[] _buffer;
+ private byte[] _buffer;
private readonly bool _leaveOpen;
private readonly CompressionMode _mode;
_mode = mode;
_stream = stream;
_leaveOpen = leaveOpen;
- _buffer = new byte[DefaultInternalBufferSize];
+ _buffer = ArrayPool<byte>.Shared.Rent(DefaultInternalBufferSize);
}
private void EnsureNotDisposed()
{
if (_stream == null)
- throw new ObjectDisposedException("stream", SR.ObjectDisposed_StreamClosed);
+ throw new ObjectDisposedException(GetType().Name, SR.ObjectDisposed_StreamClosed);
}
protected override void Dispose(bool disposing)
}
finally
{
- _stream = null;
- _encoder.Dispose();
- _decoder.Dispose();
+ ReleaseStateForDispose();
base.Dispose(disposing);
}
}
}
finally
{
- _stream = null;
- _encoder.Dispose();
- _decoder.Dispose();
+ ReleaseStateForDispose();
+ }
+ }
+
+ private void ReleaseStateForDispose()
+ {
+ _stream = null;
+ _encoder.Dispose();
+ _decoder.Dispose();
+
+ byte[] buffer = _buffer;
+ if (buffer != null)
+ {
+ _buffer = null;
+ if (!AsyncOperationIsActive)
+ {
+ ArrayPool<byte>.Shared.Return(buffer);
+ }
}
}
public override Stream CreateStream(Stream stream, CompressionLevel level) => new BrotliStream(stream, level);
public override Stream CreateStream(Stream stream, CompressionLevel level, bool leaveOpen) => new BrotliStream(stream, level, leaveOpen);
public override Stream BaseStream(Stream stream) => ((BrotliStream)stream).BaseStream;
- public override int BufferSize { get => 65520; }
+
+ // The tests are relying on an implementation detail of BrotliStream, using knowledge of its internal buffer size
+ // in various test calculations. Currently the implementation is using the ArrayPool, which will round up to a
+ // power-of-2. If the buffer size employed changes (which could also mean that ArrayPool<byte>.Shared starts giving
+ // out different array sizes), the tests will need to be tweaked.
+ public override int BufferSize => 1 << 16;
+
protected override string CompressedTestFile(string uncompressedPath) => Path.Combine("BrotliTestData", Path.GetFileName(uncompressedPath) + ".br");
[Fact]