Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 5 additions & 27 deletions src/SharpCompress/Common/AsyncBinaryReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,57 +29,35 @@ public AsyncBinaryReader(Stream stream, bool leaveOpen = false, int bufferSize =

public async ValueTask<byte> ReadByteAsync(CancellationToken ct = default)
{
await ReadExactAsync(_buffer, 0, 1, ct).ConfigureAwait(false);
await _stream.ReadExactAsync(_buffer, 0, 1, ct).ConfigureAwait(false);
return _buffer[0];
}

public async ValueTask<ushort> ReadUInt16Async(CancellationToken ct = default)
{
await ReadExactAsync(_buffer, 0, 2, ct).ConfigureAwait(false);
await _stream.ReadExactAsync(_buffer, 0, 2, ct).ConfigureAwait(false);
return BinaryPrimitives.ReadUInt16LittleEndian(_buffer);
}

public async ValueTask<uint> ReadUInt32Async(CancellationToken ct = default)
{
await ReadExactAsync(_buffer, 0, 4, ct).ConfigureAwait(false);
await _stream.ReadExactAsync(_buffer, 0, 4, ct).ConfigureAwait(false);
return BinaryPrimitives.ReadUInt32LittleEndian(_buffer);
}

public async ValueTask<ulong> ReadUInt64Async(CancellationToken ct = default)
{
await ReadExactAsync(_buffer, 0, 8, ct).ConfigureAwait(false);
await _stream.ReadExactAsync(_buffer, 0, 8, ct).ConfigureAwait(false);
return BinaryPrimitives.ReadUInt64LittleEndian(_buffer);
}

public async ValueTask<byte[]> ReadBytesAsync(int count, CancellationToken ct = default)
{
var result = new byte[count];
await ReadExactAsync(result, 0, count, ct).ConfigureAwait(false);
await _stream.ReadExactAsync(result, 0, count, ct).ConfigureAwait(false);
return result;
}

private async ValueTask ReadExactAsync(
byte[] destination,
int offset,
int length,
CancellationToken ct
)
{
var read = 0;
while (read < length)
{
var n = await _stream
.ReadAsync(destination, offset + read, length - read, ct)
.ConfigureAwait(false);
if (n == 0)
{
throw new EndOfStreamException();
}

read += n;
}
}

public void Dispose()
{
if (_disposed)
Expand Down
35 changes: 0 additions & 35 deletions src/SharpCompress/Compressors/LZMA/Utilites/Utils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,39 +53,4 @@ public static void Assert(bool expression)
throw new InvalidOperationException("Assertion failed.");
}
}

public static void ReadExact(this Stream stream, byte[] buffer, int offset, int length)
{
if (stream is null)
{
throw new ArgumentNullException(nameof(stream));
}

if (buffer is null)
{
throw new ArgumentNullException(nameof(buffer));
}

if (offset < 0 || offset > buffer.Length)
{
throw new ArgumentOutOfRangeException(nameof(offset));
}

if (length < 0 || length > buffer.Length - offset)
{
throw new ArgumentOutOfRangeException(nameof(length));
}

while (length > 0)
{
var fetched = stream.Read(buffer, offset, length);
if (fetched <= 0)
{
throw new EndOfStreamException();
}

offset += fetched;
length -= fetched;
}
}
}
37 changes: 2 additions & 35 deletions src/SharpCompress/Compressors/Xz/MultiByteIntegers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public static async Task<ulong> ReadXZIntegerAsync(
MaxBytes = 9;
}

var LastByte = await ReadByteAsync(reader, cancellationToken).ConfigureAwait(false);
var LastByte = await reader.ReadByteAsync(cancellationToken).ConfigureAwait(false);
var Output = (ulong)LastByte & 0x7F;

var i = 0;
Expand All @@ -69,7 +69,7 @@ public static async Task<ulong> ReadXZIntegerAsync(
throw new InvalidFormatException();
}

LastByte = await ReadByteAsync(reader, cancellationToken).ConfigureAwait(false);
LastByte = await reader.ReadByteAsync(cancellationToken).ConfigureAwait(false);
if (LastByte == 0)
{
throw new InvalidFormatException();
Expand All @@ -79,37 +79,4 @@ public static async Task<ulong> ReadXZIntegerAsync(
}
return Output;
}

public static async Task<byte> ReadByteAsync(
this BinaryReader reader,
CancellationToken cancellationToken = default
)
{
var buffer = new byte[1];
var bytesRead = await reader
.BaseStream.ReadAsync(buffer, 0, 1, cancellationToken)
.ConfigureAwait(false);
if (bytesRead != 1)
{
throw new EndOfStreamException();
}
return buffer[0];
}

public static async Task<byte[]> ReadBytesAsync(
this BinaryReader reader,
int count,
CancellationToken cancellationToken = default
)
{
var buffer = new byte[count];
var bytesRead = await reader
.BaseStream.ReadAsync(buffer, 0, count, cancellationToken)
.ConfigureAwait(false);
if (bytesRead != count)
{
throw new EndOfStreamException();
}
return buffer;
}
}
65 changes: 65 additions & 0 deletions src/SharpCompress/Polyfills/BinaryReaderExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using System;
using System.Buffers;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

namespace SharpCompress;

public static class BinaryReaderExtensions
{
extension(BinaryReader reader)
{
public async Task<byte> ReadByteAsync(CancellationToken cancellationToken = default)
{
var buffer = new byte[1];
await reader
.BaseStream.ReadExactAsync(buffer, 0, 1, cancellationToken)
.ConfigureAwait(false);
return buffer[0];
}

public async Task<byte[]> ReadBytesAsync(
int count,
CancellationToken cancellationToken = default
)
{
if (count < 0)
{
throw new ArgumentOutOfRangeException(nameof(count), "Count must be non-negative.");
}

if (count == 0)
{
return Array.Empty<byte>();
}

// For small allocations, direct allocation is more efficient than pooling
// due to ArrayPool overhead and the need to copy data to return array
if (count <= 256)
{
var bytes = new byte[count];
await reader
.BaseStream.ReadExactAsync(bytes, 0, count, cancellationToken)
.ConfigureAwait(false);
return bytes;
}

// For larger allocations, use ArrayPool to reduce GC pressure
var buffer = ArrayPool<byte>.Shared.Rent(count);
try
{
await reader
.BaseStream.ReadExactAsync(buffer, 0, count, cancellationToken)
.ConfigureAwait(false);
var bytes = new byte[count];
Array.Copy(buffer, 0, bytes, 0, count);
return bytes;
}
finally
{
ArrayPool<byte>.Shared.Return(buffer);
}
}
}
}
122 changes: 79 additions & 43 deletions src/SharpCompress/Polyfills/StreamExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
#if NETFRAMEWORK || NETSTANDARD2_0

using System;
using System.Buffers;
using System.IO;
Expand All @@ -8,63 +6,101 @@

namespace SharpCompress;

internal static class StreamExtensions
public static class StreamExtensions
{
internal static int Read(this Stream stream, Span<byte> buffer)
extension(Stream stream)
{
var temp = ArrayPool<byte>.Shared.Rent(buffer.Length);

try
public void Skip(long advanceAmount)
{
var read = stream.Read(temp, 0, buffer.Length);

temp.AsSpan(0, read).CopyTo(buffer);
if (stream.CanSeek)
{
stream.Position += advanceAmount;
return;
}

return read;
using var buffer = MemoryPool<byte>.Shared.Rent(Utility.TEMP_BUFFER_SIZE);
while (advanceAmount > 0)
{
var toRead = (int)Math.Min(buffer.Memory.Length, advanceAmount);
var read = stream.Read(buffer.Memory.Slice(0, toRead).Span);
if (read <= 0)
{
break;
}
advanceAmount -= read;
}
}
finally

public void Skip()
{
ArrayPool<byte>.Shared.Return(temp);
using var buffer = MemoryPool<byte>.Shared.Rent(Utility.TEMP_BUFFER_SIZE);
while (stream.Read(buffer.Memory.Span) > 0) { }
}
}

internal static void Write(this Stream stream, ReadOnlySpan<byte> buffer)
{
var temp = ArrayPool<byte>.Shared.Rent(buffer.Length);

buffer.CopyTo(temp);

try
public async Task SkipAsync(CancellationToken cancellationToken = default)
{
stream.Write(temp, 0, buffer.Length);
var array = ArrayPool<byte>.Shared.Rent(Utility.TEMP_BUFFER_SIZE);
try
{
while (true)
{
var read = await stream
.ReadAsync(array, 0, array.Length, cancellationToken)
.ConfigureAwait(false);
if (read <= 0)
{
break;
}
}
}
finally
{
ArrayPool<byte>.Shared.Return(array);
}
}
finally

internal int Read(Span<byte> buffer)
{
ArrayPool<byte>.Shared.Return(temp);
var temp = ArrayPool<byte>.Shared.Rent(buffer.Length);

try
{
var read = stream.Read(temp, 0, buffer.Length);

temp.AsSpan(0, read).CopyTo(buffer);

return read;
}
finally
{
ArrayPool<byte>.Shared.Return(temp);
}
}
}

internal static async Task ReadExactlyAsync(
this Stream stream,
byte[] buffer,
int offset,
int count,
CancellationToken cancellationToken
)
{
var totalRead = 0;
while (totalRead < count)
internal void Write(ReadOnlySpan<byte> buffer)
{
var read = await stream
.ReadAsync(buffer, offset + totalRead, count - totalRead, cancellationToken)
.ConfigureAwait(false);
if (read == 0)
var temp = ArrayPool<byte>.Shared.Rent(buffer.Length);

buffer.CopyTo(temp);

try
{
stream.Write(temp, 0, buffer.Length);
}
finally
{
throw new EndOfStreamException();
ArrayPool<byte>.Shared.Return(temp);
}
totalRead += read;
}

internal async Task ReadExactlyAsync(
byte[] buffer,
int offset,
int count,
CancellationToken cancellationToken
) =>
await stream
.ReadExactAsync(buffer, offset, count, cancellationToken)
.ConfigureAwait(false);
}
}

#endif
Loading
Loading