add basics for async bzip2

This commit is contained in:
Adam Hathcock
2025-10-30 14:42:46 +00:00
parent dba68187ac
commit bc06f3179d
4 changed files with 299 additions and 1 deletions

View File

@@ -1,5 +1,7 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.IO;
namespace SharpCompress.Compressors.BZip2;
@@ -96,13 +98,37 @@ public sealed class BZip2Stream : Stream, IStreamStack
public override void SetLength(long value) => stream.SetLength(value);
#if !NETFRAMEWORK&& !NETSTANDARD2_0
#if !NETFRAMEWORK && !NETSTANDARD2_0
public override int Read(Span<byte> buffer) => stream.Read(buffer);
public override void Write(ReadOnlySpan<byte> buffer) => stream.Write(buffer);
public override async ValueTask<int> ReadAsync(
Memory<byte> buffer,
CancellationToken cancellationToken = default
) => await stream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false);
public override async ValueTask WriteAsync(
ReadOnlyMemory<byte> buffer,
CancellationToken cancellationToken = default
) => await stream.WriteAsync(buffer, cancellationToken).ConfigureAwait(false);
#endif
public override async Task<int> ReadAsync(
byte[] buffer,
int offset,
int count,
CancellationToken cancellationToken = default
) => await stream.ReadAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
public override async Task WriteAsync(
byte[] buffer,
int offset,
int count,
CancellationToken cancellationToken = default
) => await stream.WriteAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
public override void Write(byte[] buffer, int offset, int count) =>
stream.Write(buffer, offset, count);

View File

@@ -2,6 +2,8 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.IO;
/*
@@ -1127,6 +1129,28 @@ internal class CBZip2InputStream : Stream, IStreamStack
return k;
}
public override async Task<int> ReadAsync(
byte[] buffer,
int offset,
int count,
CancellationToken cancellationToken = default
)
{
var c = -1;
int k;
for (k = 0; k < count; ++k)
{
cancellationToken.ThrowIfCancellationRequested();
c = ReadByte();
if (c == -1)
{
break;
}
buffer[k + offset] = (byte)c;
}
return await Task.FromResult(k);
}
public override long Seek(long offset, SeekOrigin origin) => 0;
public override void SetLength(long value) { }

View File

@@ -1,5 +1,7 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.IO;
/*
@@ -2022,6 +2024,21 @@ internal sealed class CBZip2OutputStream : Stream, IStreamStack
}
}
public override async Task WriteAsync(
byte[] buffer,
int offset,
int count,
CancellationToken cancellationToken = default
)
{
for (var k = 0; k < count; ++k)
{
cancellationToken.ThrowIfCancellationRequested();
WriteByte(buffer[k + offset]);
}
await Task.CompletedTask;
}
public override bool CanRead => false;
public override bool CanSeek => false;

View File

@@ -0,0 +1,231 @@
using System;
using System.Buffers;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using SharpCompress.Compressors.BZip2;
using Xunit;
namespace SharpCompress.Test.BZip2;
public class BZip2StreamAsyncTests
{
private byte[] CreateTestData(int size)
{
var data = new byte[size];
// Create compressible data with repetitive pattern
for (int i = 0; i < size; i++)
{
data[i] = (byte)('A' + (i % 26));
}
return data;
}
[Fact]
public async Task BZip2CompressDecompressAsyncTest()
{
var testData = CreateTestData(10000);
byte[] compressed;
// Compress
using (var memoryStream = new MemoryStream())
{
using (
var bzip2Stream = new BZip2Stream(
memoryStream,
SharpCompress.Compressors.CompressionMode.Compress,
false
)
)
{
await bzip2Stream.WriteAsync(testData, 0, testData.Length);
(bzip2Stream as BZip2Stream)?.Finish();
}
compressed = memoryStream.ToArray();
}
// Verify compression occurred
Assert.True(compressed.Length > 0);
Assert.True(compressed.Length < testData.Length);
// Decompress
byte[] decompressed;
using (var memoryStream = new MemoryStream(compressed))
{
using (
var bzip2Stream = new BZip2Stream(
memoryStream,
SharpCompress.Compressors.CompressionMode.Decompress,
false
)
)
{
decompressed = new byte[testData.Length];
var totalRead = 0;
int bytesRead;
while (
(
bytesRead = await bzip2Stream.ReadAsync(
decompressed,
totalRead,
testData.Length - totalRead
)
) > 0
)
{
totalRead += bytesRead;
}
}
}
// Verify decompression
Assert.Equal(testData, decompressed);
}
[Fact]
public async Task BZip2ReadAsyncWithCancellationTest()
{
var testData = Encoding.ASCII.GetBytes(new string('A', 5000)); // Repetitive data compresses well
byte[] compressed;
// Compress
using (var memoryStream = new MemoryStream())
{
using (
var bzip2Stream = new BZip2Stream(
memoryStream,
SharpCompress.Compressors.CompressionMode.Compress,
false
)
)
{
await bzip2Stream.WriteAsync(testData, 0, testData.Length);
(bzip2Stream as BZip2Stream)?.Finish();
}
compressed = memoryStream.ToArray();
}
// Decompress with cancellation support
using (var memoryStream = new MemoryStream(compressed))
{
using (
var bzip2Stream = new BZip2Stream(
memoryStream,
SharpCompress.Compressors.CompressionMode.Decompress,
false
)
)
{
var buffer = new byte[1024];
var cts = new System.Threading.CancellationTokenSource();
// Read should complete without cancellation
var bytesRead = await bzip2Stream.ReadAsync(buffer, 0, buffer.Length, cts.Token);
Assert.True(bytesRead > 0);
}
}
}
[Fact]
public async Task BZip2MultipleAsyncWritesTest()
{
using (var memoryStream = new MemoryStream())
{
using (
var bzip2Stream = new BZip2Stream(
memoryStream,
SharpCompress.Compressors.CompressionMode.Compress,
false
)
)
{
var data1 = Encoding.ASCII.GetBytes("Hello ");
var data2 = Encoding.ASCII.GetBytes("World");
var data3 = Encoding.ASCII.GetBytes("!");
await bzip2Stream.WriteAsync(data1, 0, data1.Length);
await bzip2Stream.WriteAsync(data2, 0, data2.Length);
await bzip2Stream.WriteAsync(data3, 0, data3.Length);
(bzip2Stream as BZip2Stream)?.Finish();
}
var compressed = memoryStream.ToArray();
Assert.True(compressed.Length > 0);
// Decompress and verify
using (var readStream = new MemoryStream(compressed))
{
using (
var bzip2Stream = new BZip2Stream(
readStream,
SharpCompress.Compressors.CompressionMode.Decompress,
false
)
)
{
var result = new StringBuilder();
var buffer = new byte[256];
int bytesRead;
while ((bytesRead = await bzip2Stream.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
result.Append(Encoding.ASCII.GetString(buffer, 0, bytesRead));
}
Assert.Equal("Hello World!", result.ToString());
}
}
}
}
[Fact]
public async Task BZip2LargeDataAsyncTest()
{
var largeData = CreateTestData(100000);
// Compress
byte[] compressed;
using (var memoryStream = new MemoryStream())
{
using (
var bzip2Stream = new BZip2Stream(
memoryStream,
SharpCompress.Compressors.CompressionMode.Compress,
false
)
)
{
await bzip2Stream.WriteAsync(largeData, 0, largeData.Length);
(bzip2Stream as BZip2Stream)?.Finish();
}
compressed = memoryStream.ToArray();
}
// Decompress
byte[] decompressed;
using (var memoryStream = new MemoryStream(compressed))
{
using (
var bzip2Stream = new BZip2Stream(
memoryStream,
SharpCompress.Compressors.CompressionMode.Decompress,
false
)
)
{
decompressed = new byte[largeData.Length];
var totalRead = 0;
int bytesRead;
var buffer = new byte[4096];
while ((bytesRead = await bzip2Stream.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
Array.Copy(buffer, 0, decompressed, totalRead, bytesRead);
totalRead += bytesRead;
}
}
}
// Verify
Assert.Equal(largeData, decompressed);
}
}