Consolidate ReadExact and ReadFully methods into Utility.cs

Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-01-03 15:58:27 +00:00
parent 54640548ed
commit 1a71c01fd4
5 changed files with 117 additions and 129 deletions

View File

@@ -29,57 +29,35 @@ namespace SharpCompress.Common
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)

View File

@@ -53,39 +53,4 @@ internal static class Utils
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;
}
}
}

View File

@@ -11,23 +11,11 @@ public static class BinaryReaderExtensions
{
public async Task<byte> ReadByteAsync(CancellationToken cancellationToken = default)
{
var buffer = ArrayPool<byte>.Shared.Rent(1);
try
{
var bytesRead = await reader
.BaseStream.ReadAsync(buffer, 0, 1, cancellationToken)
.ConfigureAwait(false);
if (bytesRead != 1)
{
throw new EndOfStreamException();
}
return buffer[0];
}
finally
{
ArrayPool<byte>.Shared.Return(buffer);
}
var buffer = new byte[1];
await reader
.BaseStream.ReadExactAsync(buffer, 0, 1, cancellationToken)
.ConfigureAwait(false);
return buffer[0];
}
public async Task<byte[]> ReadBytesAsync(
@@ -35,24 +23,11 @@ public static class BinaryReaderExtensions
CancellationToken cancellationToken = default
)
{
var buffer = ArrayPool<byte>.Shared.Rent(count);
try
{
var bytesRead = await reader
.BaseStream.ReadAsync(buffer, 0, 1, cancellationToken)
.ConfigureAwait(false);
if (bytesRead != count)
{
throw new EndOfStreamException();
}
var bytes = new byte[count];
System.Buffer.BlockCopy(buffer, 0, bytes, 0, count);
return bytes;
}
finally
{
ArrayPool<byte>.Shared.Return(buffer);
}
var bytes = new byte[count];
await reader
.BaseStream.ReadExactAsync(bytes, 0, count, cancellationToken)
.ConfigureAwait(false);
return bytes;
}
}
}

View File

@@ -98,20 +98,9 @@ public static class StreamExtensions
int offset,
int count,
CancellationToken cancellationToken
)
{
var totalRead = 0;
while (totalRead < count)
{
var read = await stream
.ReadAsync(buffer, offset + totalRead, count - totalRead, cancellationToken)
.ConfigureAwait(false);
if (read == 0)
{
throw new EndOfStreamException();
}
totalRead += read;
}
}
) =>
await stream
.ReadExactAsync(buffer, offset, count, cancellationToken)
.ConfigureAwait(false);
}
}

View File

@@ -273,6 +273,33 @@ internal static class Utility
}
}
#if NET60_OR_GREATER
public bool ReadFully(byte[] buffer)
{
try
{
source.ReadExactly(buffer);
return true;
}
catch (EndOfStreamException)
{
return false;
}
}
public bool ReadFully(Span<byte> buffer)
{
try
{
source.ReadExactly(buffer);
return true;
}
catch (EndOfStreamException)
{
return false;
}
}
#else
public bool ReadFully(byte[] buffer)
{
var total = 0;
@@ -302,6 +329,7 @@ internal static class Utility
}
return (total >= buffer.Length);
}
#endif
public async Task<bool> ReadFullyAsync(
byte[] buffer,
@@ -354,36 +382,89 @@ internal static class Utility
}
}
#if NET60_OR_GREATER
public static bool ReadFully(this Stream stream, byte[] buffer)
/// <summary>
/// Read exactly the requested number of bytes from a stream. Throws EndOfStreamException if not enough data is available.
/// </summary>
public static void ReadExact(this Stream stream, byte[] buffer, int offset, int length)
{
try
if (stream is null)
{
stream.ReadExactly(buffer);
return true;
throw new ArgumentNullException(nameof(stream));
}
catch (EndOfStreamException)
if (buffer is null)
{
return false;
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;
}
}
public static bool ReadFully(this Stream stream, Span<byte> buffer)
/// <summary>
/// Read exactly the requested number of bytes from a stream asynchronously. Throws EndOfStreamException if not enough data is available.
/// </summary>
public static async Task ReadExactAsync(
this Stream stream,
byte[] buffer,
int offset,
int length,
CancellationToken cancellationToken = default
)
{
try
if (stream is null)
{
stream.ReadExactly(buffer);
return true;
throw new ArgumentNullException(nameof(stream));
}
catch (EndOfStreamException)
if (buffer is null)
{
return false;
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 = await stream
.ReadAsync(buffer, offset, length, cancellationToken)
.ConfigureAwait(false);
if (fetched <= 0)
{
throw new EndOfStreamException();
}
offset += fetched;
length -= fetched;
}
}
#else
#endif
public static string TrimNulls(this string source) => source.Replace('\0', ' ').Trim();