mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-09-22 15:04:43 +00:00
more async streams
This commit is contained in:
@@ -1,7 +0,0 @@
|
||||
enabled: true
|
||||
agent:
|
||||
name: copilot-coding-agent
|
||||
allow:
|
||||
- paths: ["src/**/*", "tests/**/*", "README.md", "AGENTS.md"]
|
||||
actions: ["create", "modify"]
|
||||
require_review_before_merge: true
|
||||
17
.github/agents/copilot-agent.yml
vendored
17
.github/agents/copilot-agent.yml
vendored
@@ -1,17 +0,0 @@
|
||||
enabled: true
|
||||
agent:
|
||||
name: copilot-coding-agent
|
||||
allow:
|
||||
- paths: ["src/**/*", "tests/**/*", "README.md", "AGENTS.md"]
|
||||
actions: ["create", "modify", "delete"]
|
||||
require_review_before_merge: true
|
||||
required_approvals: 1
|
||||
allowed_merge_strategies:
|
||||
- squash
|
||||
- merge
|
||||
auto_merge_on_green: false
|
||||
run_workflows: true
|
||||
notes: |
|
||||
- This manifest expresses the policy for the Copilot coding agent in this repository.
|
||||
- It does NOT install or authorize the agent; a repository admin must install the Copilot coding agent app and grant the repository the necessary permissions (contents: write, pull_requests: write, checks: write, actions: write/read, issues: write) to allow the agent to act.
|
||||
- Keep allow paths narrow and prefer require_review_before_merge during initial rollout.
|
||||
@@ -0,0 +1,132 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SharpCompress.Common.Zip;
|
||||
|
||||
internal partial class PkwareTraditionalCryptoStream
|
||||
{
|
||||
public override async Task<int> ReadAsync(
|
||||
byte[] buffer,
|
||||
int offset,
|
||||
int count,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
if (_mode == CryptoMode.Encrypt)
|
||||
{
|
||||
throw new NotSupportedException("This stream does not encrypt via Read()");
|
||||
}
|
||||
|
||||
if (buffer is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(buffer));
|
||||
}
|
||||
|
||||
var temp = new byte[count];
|
||||
var readBytes = await _stream
|
||||
.ReadAsync(temp, 0, count, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
var decrypted = _encryptor.Decrypt(temp, readBytes);
|
||||
Buffer.BlockCopy(decrypted, 0, buffer, offset, readBytes);
|
||||
return readBytes;
|
||||
}
|
||||
|
||||
#if !LEGACY_DOTNET
|
||||
public override async ValueTask<int> ReadAsync(
|
||||
Memory<byte> buffer,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
if (_mode == CryptoMode.Encrypt)
|
||||
{
|
||||
throw new NotSupportedException("This stream does not encrypt via Read()");
|
||||
}
|
||||
|
||||
byte[] temp = ArrayPool<byte>.Shared.Rent(buffer.Length);
|
||||
try
|
||||
{
|
||||
int readBytes = await _stream
|
||||
.ReadAsync(temp.AsMemory(0, buffer.Length), cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
var decrypted = _encryptor.Decrypt(temp, readBytes);
|
||||
decrypted.AsMemory(0, readBytes).CopyTo(buffer);
|
||||
return readBytes;
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(temp);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
public override async Task WriteAsync(
|
||||
byte[] buffer,
|
||||
int offset,
|
||||
int count,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
if (_mode == CryptoMode.Decrypt)
|
||||
{
|
||||
throw new NotSupportedException("This stream does not Decrypt via Write()");
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] plaintext;
|
||||
if (offset != 0)
|
||||
{
|
||||
plaintext = new byte[count];
|
||||
Buffer.BlockCopy(buffer, offset, plaintext, 0, count);
|
||||
}
|
||||
else
|
||||
{
|
||||
plaintext = buffer;
|
||||
}
|
||||
|
||||
var encrypted = _encryptor.Encrypt(plaintext, count);
|
||||
await _stream
|
||||
.WriteAsync(encrypted, 0, encrypted.Length, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
#if !LEGACY_DOTNET
|
||||
public override async ValueTask WriteAsync(
|
||||
ReadOnlyMemory<byte> buffer,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
if (_mode == CryptoMode.Decrypt)
|
||||
{
|
||||
throw new NotSupportedException("This stream does not Decrypt via Write()");
|
||||
}
|
||||
|
||||
if (buffer.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] plaintext;
|
||||
if (buffer.Span.Overlaps(buffer.Span))
|
||||
{
|
||||
plaintext = buffer.ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
plaintext = new byte[buffer.Length];
|
||||
buffer.CopyTo(plaintext);
|
||||
}
|
||||
|
||||
var encrypted = _encryptor.Encrypt(plaintext, buffer.Length);
|
||||
await _stream
|
||||
.WriteAsync(encrypted.AsMemory(0, encrypted.Length), cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -9,7 +9,7 @@ internal enum CryptoMode
|
||||
Decrypt,
|
||||
}
|
||||
|
||||
internal class PkwareTraditionalCryptoStream : Stream
|
||||
internal partial class PkwareTraditionalCryptoStream : Stream
|
||||
{
|
||||
private readonly PkwareTraditionalEncryptionData _encryptor;
|
||||
private readonly CryptoMode _mode;
|
||||
|
||||
142
src/SharpCompress/Common/Zip/WinzipAesCryptoStream.Async.cs
Normal file
142
src/SharpCompress/Common/Zip/WinzipAesCryptoStream.Async.cs
Normal file
@@ -0,0 +1,142 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SharpCompress.Common.Zip;
|
||||
|
||||
internal partial class WinzipAesCryptoStream
|
||||
{
|
||||
#if !LEGACY_DOTNET
|
||||
public override async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_isDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_isDisposed = true;
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(WinzipAesCryptoStream));
|
||||
#endif
|
||||
// Read out last 10 auth bytes asynchronously
|
||||
byte[] authBytes = ArrayPool<byte>.Shared.Rent(10);
|
||||
try
|
||||
{
|
||||
await _stream.ReadFullyAsync(authBytes, 0, 10).ConfigureAwait(false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(authBytes);
|
||||
await _stream.DisposeAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
public override async Task<int> ReadAsync(
|
||||
byte[] buffer,
|
||||
int offset,
|
||||
int count,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
if (_totalBytesLeftToRead == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
var bytesToRead = count;
|
||||
if (count > _totalBytesLeftToRead)
|
||||
{
|
||||
bytesToRead = (int)_totalBytesLeftToRead;
|
||||
}
|
||||
var read = await _stream
|
||||
.ReadAsync(buffer, offset, bytesToRead, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
_totalBytesLeftToRead -= read;
|
||||
|
||||
ReadTransformBlocks(buffer, offset, read);
|
||||
|
||||
return read;
|
||||
}
|
||||
|
||||
#if !LEGACY_DOTNET
|
||||
public override async ValueTask<int> ReadAsync(
|
||||
Memory<byte> buffer,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
if (_totalBytesLeftToRead == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
var bytesToRead = buffer.Length;
|
||||
if (buffer.Length > _totalBytesLeftToRead)
|
||||
{
|
||||
bytesToRead = (int)_totalBytesLeftToRead;
|
||||
}
|
||||
var read = await _stream
|
||||
.ReadAsync(buffer.Slice(0, bytesToRead), cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
_totalBytesLeftToRead -= read;
|
||||
|
||||
ReadTransformBlocks(buffer.Span, read);
|
||||
|
||||
return read;
|
||||
}
|
||||
|
||||
private void ReadTransformBlocks(Span<byte> buffer, int count)
|
||||
{
|
||||
var posn = 0;
|
||||
var last = count;
|
||||
|
||||
while (posn < buffer.Length && posn < last)
|
||||
{
|
||||
var n = ReadTransformOneBlock(buffer, posn, last);
|
||||
posn += n;
|
||||
}
|
||||
}
|
||||
|
||||
private int ReadTransformOneBlock(Span<byte> buffer, int offset, int last)
|
||||
{
|
||||
if (_isFinalBlock)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
var bytesRemaining = last - offset;
|
||||
var bytesToRead =
|
||||
(bytesRemaining > BLOCK_SIZE_IN_BYTES) ? BLOCK_SIZE_IN_BYTES : bytesRemaining;
|
||||
|
||||
// update the counter
|
||||
System.Buffers.Binary.BinaryPrimitives.WriteInt32LittleEndian(_counter, _nonce++);
|
||||
|
||||
// Determine if this is the final block
|
||||
if ((bytesToRead == bytesRemaining) && (_totalBytesLeftToRead == 0))
|
||||
{
|
||||
_counterOut = _transform.TransformFinalBlock(_counter, 0, BLOCK_SIZE_IN_BYTES);
|
||||
_isFinalBlock = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_transform.TransformBlock(
|
||||
_counter,
|
||||
0, // offset
|
||||
BLOCK_SIZE_IN_BYTES,
|
||||
_counterOut,
|
||||
0
|
||||
); // offset
|
||||
}
|
||||
|
||||
XorInPlace(buffer, offset, bytesToRead);
|
||||
return bytesToRead;
|
||||
}
|
||||
|
||||
private void XorInPlace(Span<byte> buffer, int offset, int count)
|
||||
{
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
buffer[offset + i] = (byte)(_counterOut[i] ^ buffer[offset + i]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -2,10 +2,11 @@ using System;
|
||||
using System.Buffers.Binary;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SharpCompress.Common.Zip;
|
||||
|
||||
internal class WinzipAesCryptoStream : Stream
|
||||
internal partial class WinzipAesCryptoStream : Stream
|
||||
{
|
||||
private const int BLOCK_SIZE_IN_BYTES = 16;
|
||||
private readonly SymmetricAlgorithm _cipher;
|
||||
@@ -73,9 +74,17 @@ internal class WinzipAesCryptoStream : Stream
|
||||
#endif
|
||||
if (disposing)
|
||||
{
|
||||
//read out last 10 auth bytes
|
||||
Span<byte> ten = stackalloc byte[10];
|
||||
_stream.ReadFully(ten);
|
||||
// Read out last 10 auth bytes - catch exceptions for async-only streams
|
||||
try
|
||||
{
|
||||
Span<byte> ten = stackalloc byte[10];
|
||||
_stream.ReadFully(ten);
|
||||
}
|
||||
catch (NotSupportedException)
|
||||
{
|
||||
// Stream may be async-only, auth bytes will be skipped
|
||||
// This is acceptable when the entire stream has been read
|
||||
}
|
||||
_stream.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user