mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-09-23 07:25:11 +00:00
Add helper methods to consolidate ArrayPool+ReadFully/ReadExact patterns
Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com>
This commit is contained in:
@@ -185,21 +185,11 @@ public partial class GZipArchive
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
var header = ArrayPool<byte>.Shared.Rent(10);
|
||||
try
|
||||
{
|
||||
await stream.ReadFullyAsync(header, 0, 10, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (header[0] != 0x1F || header[1] != 0x8B || header[2] != 8)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(header);
|
||||
}
|
||||
var result = await stream.TryWithRentedBufferReadFullyAsync(
|
||||
10,
|
||||
header => header[0] == 0x1F && header[1] == 0x8B && header[2] == 8,
|
||||
cancellationToken
|
||||
);
|
||||
return result.success && result.result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,38 +173,22 @@ public partial class SevenZipArchive
|
||||
|
||||
private static ReadOnlySpan<byte> Signature => [(byte)'7', (byte)'z', 0xBC, 0xAF, 0x27, 0x1C];
|
||||
|
||||
private static bool SignatureMatch(Stream stream)
|
||||
{
|
||||
var buffer = ArrayPool<byte>.Shared.Rent(6);
|
||||
try
|
||||
{
|
||||
stream.ReadExact(buffer, 0, 6);
|
||||
return buffer.AsSpan().Slice(0, 6).SequenceEqual(Signature);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(buffer);
|
||||
}
|
||||
}
|
||||
private static bool SignatureMatch(Stream stream) =>
|
||||
stream.WithRentedBufferReadExact(
|
||||
6,
|
||||
buffer => buffer.AsSpan().Slice(0, 6).SequenceEqual(Signature)
|
||||
);
|
||||
|
||||
private static async ValueTask<bool> SignatureMatchAsync(
|
||||
Stream stream,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
var buffer = ArrayPool<byte>.Shared.Rent(6);
|
||||
try
|
||||
{
|
||||
if (!await stream.ReadFullyAsync(buffer, 0, 6, cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return buffer.AsSpan().Slice(0, 6).SequenceEqual(Signature);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(buffer);
|
||||
}
|
||||
var result = await stream.TryWithRentedBufferReadFullyAsync(
|
||||
6,
|
||||
buffer => buffer.AsSpan().Slice(0, 6).SequenceEqual(Signature),
|
||||
cancellationToken
|
||||
);
|
||||
return result.success && result.result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,15 +16,19 @@ internal partial class WinzipAesCryptoStream
|
||||
return;
|
||||
}
|
||||
_isDisposed = true;
|
||||
// Read out last 10 auth bytes asynchronously
|
||||
byte[] authBytes = ArrayPool<byte>.Shared.Rent(10);
|
||||
try
|
||||
{
|
||||
await _stream.ReadFullyAsync(authBytes, 0, 10).ConfigureAwait(false);
|
||||
// Read out last 10 auth bytes asynchronously
|
||||
await _stream
|
||||
.WithRentedBufferReadFullyAsync(
|
||||
10,
|
||||
_ => 0, // Just consume the bytes, don't need the result
|
||||
CancellationToken.None
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(authBytes);
|
||||
await _stream.DisposeAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Buffers;
|
||||
using System.Buffers.Binary;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SharpCompress.Common.Zip;
|
||||
@@ -71,15 +72,14 @@ internal partial class WinzipAesCryptoStream : Stream
|
||||
// Read out last 10 auth bytes - catch exceptions for async-only streams
|
||||
if (Utility.UseSyncOverAsyncDispose())
|
||||
{
|
||||
var ten = ArrayPool<byte>.Shared.Rent(10);
|
||||
try
|
||||
{
|
||||
_stream.ReadFullyAsync(ten, 0, 10).GetAwaiter().GetResult();
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(ten);
|
||||
}
|
||||
_stream
|
||||
.WithRentedBufferReadFullyAsync(
|
||||
10,
|
||||
_ => 0, // Just consume the bytes, don't need the result
|
||||
CancellationToken.None
|
||||
)
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -33,19 +33,11 @@ public class ArcFactory : Factory, IReaderFactory
|
||||
//Hyper - archive, check the next two bytes for "HP" or "ST"(or look below for
|
||||
//"HYP").Also the ZOO archiver also does put a 01Ah at the start of the file,
|
||||
//see the ZOO entry below.
|
||||
var buffer = ArrayPool<byte>.Shared.Rent(2);
|
||||
try
|
||||
{
|
||||
if (stream.ReadFully(buffer.AsSpan(0, 2)))
|
||||
{
|
||||
return buffer[0] == 0x1A && buffer[1] < 10; //rather thin, but this is all we have
|
||||
}
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(buffer);
|
||||
}
|
||||
return stream.TryWithRentedBufferReadFully(
|
||||
2,
|
||||
buffer => buffer[0] == 0x1A && buffer[1] < 10, //rather thin, but this is all we have
|
||||
out var result
|
||||
) && result;
|
||||
}
|
||||
|
||||
public IReader OpenReader(Stream stream, ReaderOptions? options) =>
|
||||
@@ -73,15 +65,10 @@ public class ArcFactory : Factory, IReaderFactory
|
||||
//Hyper - archive, check the next two bytes for "HP" or "ST"(or look below for
|
||||
//"HYP").Also the ZOO archiver also does put a 01Ah at the start of the file,
|
||||
//see the ZOO entry below.
|
||||
var buffer = ArrayPool<byte>.Shared.Rent(2);
|
||||
try
|
||||
{
|
||||
await stream.ReadExactAsync(buffer, 0, 2, cancellationToken);
|
||||
return buffer[0] == 0x1A && buffer[1] < 10; //rather thin, but this is all we have
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(buffer);
|
||||
}
|
||||
return await stream.WithRentedBufferReadExactAsync(
|
||||
2,
|
||||
buffer => buffer[0] == 0x1A && buffer[1] < 10, //rather thin, but this is all we have
|
||||
cancellationToken
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -8,6 +9,107 @@ namespace SharpCompress;
|
||||
|
||||
internal static partial class Utility
|
||||
{
|
||||
/// <summary>
|
||||
/// Rents a buffer from the shared ArrayPool, reads data into it asynchronously, and executes a callback with the buffer.
|
||||
/// The buffer is automatically returned to the pool after use.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The return type of the callback</typeparam>
|
||||
/// <param name="stream">The stream to read from</param>
|
||||
/// <param name="size">The size of the buffer to rent and read</param>
|
||||
/// <param name="callback">The callback to execute with the rented buffer</param>
|
||||
/// <param name="cancellationToken">Cancellation token</param>
|
||||
/// <returns>The result of the callback</returns>
|
||||
public static async ValueTask<T> WithRentedBufferReadFullyAsync<T>(
|
||||
this Stream stream,
|
||||
int size,
|
||||
Func<byte[], T> callback,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
var buffer = ArrayPool<byte>.Shared.Rent(size);
|
||||
try
|
||||
{
|
||||
if (
|
||||
!await stream
|
||||
.ReadFullyAsync(buffer, 0, size, cancellationToken)
|
||||
.ConfigureAwait(false)
|
||||
)
|
||||
{
|
||||
throw new EndOfStreamException();
|
||||
}
|
||||
return callback(buffer);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rents a buffer from the shared ArrayPool, reads data into it asynchronously, and executes a callback with the buffer.
|
||||
/// The buffer is automatically returned to the pool after use. Returns false if end of stream is reached.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The return type of the callback</typeparam>
|
||||
/// <param name="stream">The stream to read from</param>
|
||||
/// <param name="size">The size of the buffer to rent and read</param>
|
||||
/// <param name="callback">The callback to execute with the rented buffer</param>
|
||||
/// <param name="cancellationToken">Cancellation token</param>
|
||||
/// <returns>A tuple containing success status and the result</returns>
|
||||
public static async ValueTask<(bool success, T result)> TryWithRentedBufferReadFullyAsync<T>(
|
||||
this Stream stream,
|
||||
int size,
|
||||
Func<byte[], T> callback,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
var buffer = ArrayPool<byte>.Shared.Rent(size);
|
||||
try
|
||||
{
|
||||
if (
|
||||
!await stream
|
||||
.ReadFullyAsync(buffer, 0, size, cancellationToken)
|
||||
.ConfigureAwait(false)
|
||||
)
|
||||
{
|
||||
return (false, default!);
|
||||
}
|
||||
return (true, callback(buffer));
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rents a buffer from the shared ArrayPool, reads exactly the specified amount of data asynchronously, and executes a callback with the buffer.
|
||||
/// The buffer is automatically returned to the pool after use. Throws EndOfStreamException if not enough data is available.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The return type of the callback</typeparam>
|
||||
/// <param name="stream">The stream to read from</param>
|
||||
/// <param name="size">The size of the buffer to rent and read</param>
|
||||
/// <param name="callback">The callback to execute with the rented buffer</param>
|
||||
/// <param name="cancellationToken">Cancellation token</param>
|
||||
/// <returns>The result of the callback</returns>
|
||||
public static async ValueTask<T> WithRentedBufferReadExactAsync<T>(
|
||||
this Stream stream,
|
||||
int size,
|
||||
Func<byte[], T> callback,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
var buffer = ArrayPool<byte>.Shared.Rent(size);
|
||||
try
|
||||
{
|
||||
await stream.ReadExactAsync(buffer, 0, size, cancellationToken).ConfigureAwait(false);
|
||||
return callback(buffer);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
extension(Stream source)
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -138,6 +138,97 @@ internal static partial class Utility
|
||||
return sTime.AddSeconds(unixtime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rents a buffer from the shared ArrayPool, reads data into it, and executes a callback with the buffer.
|
||||
/// The buffer is automatically returned to the pool after use.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The return type of the callback</typeparam>
|
||||
/// <param name="stream">The stream to read from</param>
|
||||
/// <param name="size">The size of the buffer to rent and read</param>
|
||||
/// <param name="callback">The callback to execute with the rented buffer</param>
|
||||
/// <returns>The result of the callback</returns>
|
||||
public static T WithRentedBufferReadFully<T>(
|
||||
this Stream stream,
|
||||
int size,
|
||||
Func<byte[], T> callback
|
||||
)
|
||||
{
|
||||
var buffer = ArrayPool<byte>.Shared.Rent(size);
|
||||
try
|
||||
{
|
||||
if (!stream.ReadFully(buffer.AsSpan(0, size)))
|
||||
{
|
||||
throw new EndOfStreamException();
|
||||
}
|
||||
return callback(buffer);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rents a buffer from the shared ArrayPool, reads data into it, and executes a callback with the buffer.
|
||||
/// The buffer is automatically returned to the pool after use. Returns false if end of stream is reached.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The return type of the callback</typeparam>
|
||||
/// <param name="stream">The stream to read from</param>
|
||||
/// <param name="size">The size of the buffer to rent and read</param>
|
||||
/// <param name="callback">The callback to execute with the rented buffer</param>
|
||||
/// <param name="result">The result of the callback, or default if read failed</param>
|
||||
/// <returns>True if the read was successful, false if end of stream was reached</returns>
|
||||
public static bool TryWithRentedBufferReadFully<T>(
|
||||
this Stream stream,
|
||||
int size,
|
||||
Func<byte[], T> callback,
|
||||
out T result
|
||||
)
|
||||
{
|
||||
var buffer = ArrayPool<byte>.Shared.Rent(size);
|
||||
try
|
||||
{
|
||||
if (!stream.ReadFully(buffer.AsSpan(0, size)))
|
||||
{
|
||||
result = default!;
|
||||
return false;
|
||||
}
|
||||
result = callback(buffer);
|
||||
return true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rents a buffer from the shared ArrayPool, reads exactly the specified amount of data, and executes a callback with the buffer.
|
||||
/// The buffer is automatically returned to the pool after use. Throws EndOfStreamException if not enough data is available.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The return type of the callback</typeparam>
|
||||
/// <param name="stream">The stream to read from</param>
|
||||
/// <param name="size">The size of the buffer to rent and read</param>
|
||||
/// <param name="callback">The callback to execute with the rented buffer</param>
|
||||
/// <returns>The result of the callback</returns>
|
||||
public static T WithRentedBufferReadExact<T>(
|
||||
this Stream stream,
|
||||
int size,
|
||||
Func<byte[], T> callback
|
||||
)
|
||||
{
|
||||
var buffer = ArrayPool<byte>.Shared.Rent(size);
|
||||
try
|
||||
{
|
||||
stream.ReadExact(buffer, 0, size);
|
||||
return callback(buffer);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
extension(Stream source)
|
||||
{
|
||||
public long TransferTo(Stream destination, long maxLength)
|
||||
|
||||
@@ -216,9 +216,9 @@
|
||||
"net10.0": {
|
||||
"Microsoft.NET.ILLink.Tasks": {
|
||||
"type": "Direct",
|
||||
"requested": "[10.0.0, )",
|
||||
"resolved": "10.0.0",
|
||||
"contentHash": "kICGrGYEzCNI3wPzfEXcwNHgTvlvVn9yJDhSdRK+oZQy4jvYH529u7O0xf5ocQKzOMjfS07+3z9PKRIjrFMJDA=="
|
||||
"requested": "[10.0.2, )",
|
||||
"resolved": "10.0.2",
|
||||
"contentHash": "sXdDtMf2qcnbygw9OdE535c2lxSxrZP8gO4UhDJ0xiJbl1wIqXS1OTcTDFTIJPOFd6Mhcm8gPEthqWGUxBsTqw=="
|
||||
},
|
||||
"Microsoft.NETFramework.ReferenceAssemblies": {
|
||||
"type": "Direct",
|
||||
@@ -264,9 +264,9 @@
|
||||
"net8.0": {
|
||||
"Microsoft.NET.ILLink.Tasks": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.0.22, )",
|
||||
"resolved": "8.0.22",
|
||||
"contentHash": "MhcMithKEiyyNkD2ZfbDZPmcOdi0GheGfg8saEIIEfD/fol3iHmcV8TsZkD4ZYz5gdUuoX4YtlVySUU7Sxl9SQ=="
|
||||
"requested": "[8.0.23, )",
|
||||
"resolved": "8.0.23",
|
||||
"contentHash": "GqHiB1HbbODWPbY/lc5xLQH8siEEhNA0ptpJCC6X6adtAYNEzu5ZlqV3YHA3Gh7fuEwgA8XqVwMtH2KNtuQM1Q=="
|
||||
},
|
||||
"Microsoft.NETFramework.ReferenceAssemblies": {
|
||||
"type": "Direct",
|
||||
|
||||
Reference in New Issue
Block a user