From ecd9317ab33f4e4cb034699844a39fa24f5fcf02 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Mon, 19 Jan 2026 16:08:46 +0000 Subject: [PATCH] more basic LLM async and fixed CRC async --- src/SharpCompress/Archives/AbstractArchive.cs | 2 +- src/SharpCompress/Archives/Rar/RarArchive.cs | 2 +- .../Common/Rar/AsyncMarkingBinaryReader.cs | 24 ++---- .../Common/Rar/AsyncRarCrcBinaryReader.cs | 5 +- .../Common/Rar/AsyncRarCryptoBinaryReader.cs | 23 ++++-- .../Common/Rar/Headers/ArchiveHeader.cs | 4 + .../Common/Rar/Headers/RarHeaderFactory.cs | 8 +- .../Common/Rar/RarCryptoBinaryReader.cs | 22 +++-- src/SharpCompress/Common/Rar/RarVolume.cs | 82 +++++++++++++++++-- 9 files changed, 124 insertions(+), 48 deletions(-) diff --git a/src/SharpCompress/Archives/AbstractArchive.cs b/src/SharpCompress/Archives/AbstractArchive.cs index b94620c6..c29cb3ae 100644 --- a/src/SharpCompress/Archives/AbstractArchive.cs +++ b/src/SharpCompress/Archives/AbstractArchive.cs @@ -210,7 +210,7 @@ public abstract class AbstractArchive : IArchive, IAsyncArchive public async ValueTask ExtractAllEntriesAsync() { - if (!IsSolid && Type != ArchiveType.SevenZip) + if (!await IsSolidAsync() && Type != ArchiveType.SevenZip) { throw new SharpCompressException( "ExtractAllEntries can only be used on solid archives or 7Zip archives (which require random access)." diff --git a/src/SharpCompress/Archives/Rar/RarArchive.cs b/src/SharpCompress/Archives/Rar/RarArchive.cs index 5d5f244a..25229d65 100644 --- a/src/SharpCompress/Archives/Rar/RarArchive.cs +++ b/src/SharpCompress/Archives/Rar/RarArchive.cs @@ -112,7 +112,7 @@ public partial class RarArchive : AbstractArchive, I public override bool IsSolid => Volumes.First().IsSolidArchive; public override async ValueTask IsSolidAsync() => - (await VolumesAsync.CastAsync().FirstAsync()).IsSolidArchive; + await (await VolumesAsync.CastAsync().FirstAsync()).IsSolidArchiveAsync(); public override bool IsEncrypted => Entries.First(x => !x.IsDirectory).IsEncrypted; diff --git a/src/SharpCompress/Common/Rar/AsyncMarkingBinaryReader.cs b/src/SharpCompress/Common/Rar/AsyncMarkingBinaryReader.cs index 74b0fa2e..c1963edb 100644 --- a/src/SharpCompress/Common/Rar/AsyncMarkingBinaryReader.cs +++ b/src/SharpCompress/Common/Rar/AsyncMarkingBinaryReader.cs @@ -41,27 +41,25 @@ internal class AsyncMarkingBinaryReader { CurrentReadByteCount += count; var bytes = new byte[count]; - await _reader.ReadBytesAsync(bytes, 0, count, cancellationToken).ConfigureAwait(false); + await _reader.ReadBytesAsync(bytes, 0, count, cancellationToken).ConfigureAwait(false); return bytes; } - public virtual async ValueTask ReadUInt16Async( + public async ValueTask ReadUInt16Async( CancellationToken cancellationToken = default ) { CurrentReadByteCount += 2; - var bytes = new byte[2]; - await _reader.ReadBytesAsync(bytes, 0, 2, cancellationToken).ConfigureAwait(false); + var bytes = await ReadBytesAsync( 2, cancellationToken).ConfigureAwait(false); return BinaryPrimitives.ReadUInt16LittleEndian(bytes); } - public virtual async ValueTask ReadUInt32Async( + public async ValueTask ReadUInt32Async( CancellationToken cancellationToken = default ) { CurrentReadByteCount += 4; - var bytes = new byte[4]; - await _reader.ReadBytesAsync(bytes, 0, 4, cancellationToken).ConfigureAwait(false); + var bytes = await ReadBytesAsync( 4, cancellationToken).ConfigureAwait(false); return BinaryPrimitives.ReadUInt32LittleEndian(bytes); } @@ -70,8 +68,7 @@ internal class AsyncMarkingBinaryReader ) { CurrentReadByteCount += 8; - var bytes = new byte[8]; - await _reader.ReadBytesAsync(bytes, 0, 8, cancellationToken).ConfigureAwait(false); + var bytes = await ReadBytesAsync( 8, cancellationToken).ConfigureAwait(false); return BinaryPrimitives.ReadUInt64LittleEndian(bytes); } @@ -80,8 +77,7 @@ internal class AsyncMarkingBinaryReader ) { CurrentReadByteCount += 2; - var bytes = new byte[2]; - await _reader.ReadBytesAsync(bytes, 0, 2, cancellationToken).ConfigureAwait(false); + var bytes = await ReadBytesAsync(2, cancellationToken).ConfigureAwait(false); return BinaryPrimitives.ReadInt16LittleEndian(bytes); } @@ -90,8 +86,7 @@ internal class AsyncMarkingBinaryReader ) { CurrentReadByteCount += 4; - var bytes = new byte[4]; - await _reader.ReadBytesAsync(bytes, 0, 4, cancellationToken).ConfigureAwait(false); + var bytes = await ReadBytesAsync( 4, cancellationToken).ConfigureAwait(false); return BinaryPrimitives.ReadInt32LittleEndian(bytes); } @@ -100,8 +95,7 @@ internal class AsyncMarkingBinaryReader ) { CurrentReadByteCount += 8; - var bytes = new byte[8]; - await _reader.ReadBytesAsync(bytes, 0, 8, cancellationToken).ConfigureAwait(false); + var bytes = await ReadBytesAsync(8, cancellationToken).ConfigureAwait(false); return BinaryPrimitives.ReadInt64LittleEndian(bytes); } diff --git a/src/SharpCompress/Common/Rar/AsyncRarCrcBinaryReader.cs b/src/SharpCompress/Common/Rar/AsyncRarCrcBinaryReader.cs index 8a8eff6b..c4c440a1 100644 --- a/src/SharpCompress/Common/Rar/AsyncRarCrcBinaryReader.cs +++ b/src/SharpCompress/Common/Rar/AsyncRarCrcBinaryReader.cs @@ -6,13 +6,10 @@ using SharpCompress.Compressors.Rar; namespace SharpCompress.Common.Rar; -internal class AsyncRarCrcBinaryReader : AsyncMarkingBinaryReader +internal class AsyncRarCrcBinaryReader(Stream stream) : AsyncMarkingBinaryReader(stream) { private uint _currentCrc; - public AsyncRarCrcBinaryReader(Stream stream) - : base(stream) { } - public uint GetCrc32() => ~_currentCrc; public void ResetCrc() => _currentCrc = 0xffffffff; diff --git a/src/SharpCompress/Common/Rar/AsyncRarCryptoBinaryReader.cs b/src/SharpCompress/Common/Rar/AsyncRarCryptoBinaryReader.cs index 038c0bb9..d938498d 100644 --- a/src/SharpCompress/Common/Rar/AsyncRarCryptoBinaryReader.cs +++ b/src/SharpCompress/Common/Rar/AsyncRarCryptoBinaryReader.cs @@ -10,22 +10,27 @@ namespace SharpCompress.Common.Rar; internal sealed class AsyncRarCryptoBinaryReader : AsyncRarCrcBinaryReader { - private BlockTransformer _rijndael; + private BlockTransformer _rijndael = default!; private readonly Queue _data = new(); private long _readCount; - public AsyncRarCryptoBinaryReader(Stream stream, ICryptKey cryptKey) + private AsyncRarCryptoBinaryReader(Stream stream) : base(stream) { - var salt = base.ReadBytesNoCrcAsync(EncryptionConstV5.SIZE_SALT30, CancellationToken.None) - .GetAwaiter() - .GetResult(); - _readCount += EncryptionConstV5.SIZE_SALT30; - _rijndael = new BlockTransformer(cryptKey.Transformer(salt)); } - public AsyncRarCryptoBinaryReader(Stream stream, ICryptKey cryptKey, byte[] salt) - : base(stream) => _rijndael = new BlockTransformer(cryptKey.Transformer(salt)); + public static async ValueTask Create(Stream stream, ICryptKey cryptKey, byte[]? salt = null) + { + var binary = new AsyncRarCryptoBinaryReader(stream); + if (salt == null) + { + salt = await binary.ReadBytesAsync(EncryptionConstV5.SIZE_SALT30); + binary._readCount += EncryptionConstV5.SIZE_SALT30; + } + binary._rijndael = new BlockTransformer(cryptKey.Transformer(salt)); + return binary; + } + public override long CurrentReadByteCount { diff --git a/src/SharpCompress/Common/Rar/Headers/ArchiveHeader.cs b/src/SharpCompress/Common/Rar/Headers/ArchiveHeader.cs index f8d27ab0..ea9660b3 100644 --- a/src/SharpCompress/Common/Rar/Headers/ArchiveHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/ArchiveHeader.cs @@ -57,6 +57,10 @@ internal sealed class ArchiveHeader : RarHeader VolumeNumber = (int) await reader.ReadRarVIntUInt32Async(cancellationToken).ConfigureAwait(false); } + // later: we may have a locator record if we need it + //if (ExtraSize != 0) { + // ReadLocator(reader); + //} } else { diff --git a/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs b/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs index c4b66fb9..8e320e9a 100644 --- a/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs +++ b/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs @@ -97,12 +97,12 @@ public class RarHeaderFactory _cryptInfo.ReadInitV(new MarkingBinaryReader(stream)); var _headerKey = new CryptKey5(Options.Password!, _cryptInfo); - reader = new RarCryptoBinaryReader(stream, _headerKey, _cryptInfo.Salt); + reader = RarCryptoBinaryReader.Create(stream, _headerKey, _cryptInfo.Salt); } else { var key = new CryptKey3(Options.Password); - reader = new RarCryptoBinaryReader(stream, key); + reader = RarCryptoBinaryReader.Create(stream, key); } } @@ -258,12 +258,12 @@ public class RarHeaderFactory _cryptInfo.ReadInitV(new AsyncMarkingBinaryReader(stream)); var _headerKey = new CryptKey5(Options.Password!, _cryptInfo); - reader = new AsyncRarCryptoBinaryReader(stream, _headerKey, _cryptInfo.Salt); + reader = await AsyncRarCryptoBinaryReader.Create(stream, _headerKey, _cryptInfo.Salt); } else { var key = new CryptKey3(Options.Password); - reader = new AsyncRarCryptoBinaryReader(stream, key); + reader = await AsyncRarCryptoBinaryReader.Create(stream, key); } } diff --git a/src/SharpCompress/Common/Rar/RarCryptoBinaryReader.cs b/src/SharpCompress/Common/Rar/RarCryptoBinaryReader.cs index 6e44286b..0b60938c 100644 --- a/src/SharpCompress/Common/Rar/RarCryptoBinaryReader.cs +++ b/src/SharpCompress/Common/Rar/RarCryptoBinaryReader.cs @@ -1,4 +1,4 @@ -#nullable disable + using System.Collections.Generic; using System.IO; @@ -9,20 +9,26 @@ namespace SharpCompress.Common.Rar; internal sealed class RarCryptoBinaryReader : RarCrcBinaryReader { - private BlockTransformer _rijndael; + private BlockTransformer _rijndael = default!; private readonly Queue _data = new(); private long _readCount; - public RarCryptoBinaryReader(Stream stream, ICryptKey cryptKey) + private RarCryptoBinaryReader(Stream stream) : base(stream) { - var salt = base.ReadBytes(EncryptionConstV5.SIZE_SALT30); - _readCount += EncryptionConstV5.SIZE_SALT30; - _rijndael = new BlockTransformer(cryptKey.Transformer(salt)); } - public RarCryptoBinaryReader(Stream stream, ICryptKey cryptKey, byte[] salt) - : base(stream) => _rijndael = new BlockTransformer(cryptKey.Transformer(salt)); + public static RarCryptoBinaryReader Create(Stream stream, ICryptKey cryptKey, byte[]? salt = null) + { + var binary = new RarCryptoBinaryReader(stream); + if (salt == null) + { + salt = binary.ReadBytes(EncryptionConstV5.SIZE_SALT30); + binary._readCount += EncryptionConstV5.SIZE_SALT30; + } + binary._rijndael = new BlockTransformer(cryptKey.Transformer(salt)); + return binary; + } // track read count ourselves rather than using the underlying stream since we buffer public override long CurrentReadByteCount diff --git a/src/SharpCompress/Common/Rar/RarVolume.cs b/src/SharpCompress/Common/Rar/RarVolume.cs index 2d463150..0440da4f 100644 --- a/src/SharpCompress/Common/Rar/RarVolume.cs +++ b/src/SharpCompress/Common/Rar/RarVolume.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Runtime.CompilerServices; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -75,10 +76,10 @@ public abstract class RarVolume : Volume } } - internal async IAsyncEnumerable GetVolumeFilePartsAsync() + internal async IAsyncEnumerable GetVolumeFilePartsAsync([EnumeratorCancellation] CancellationToken cancellationToken = default) { MarkHeader? lastMarkHeader = null; - await foreach (var header in _headerFactory.ReadHeadersAsync(Stream)) + await foreach (var header in _headerFactory.ReadHeadersAsync(Stream).WithCancellation(cancellationToken)) { switch (header.HeaderType) { @@ -123,11 +124,11 @@ public abstract class RarVolume : Volume if (ArchiveHeader is null) { if (Mode == StreamingMode.Streaming) - { - throw new InvalidOperationException( + { + throw new InvalidOperationException( "ArchiveHeader should never been null in a streaming read." - ); - } + ); + } // we only want to load the archive header to avoid overhead but have to do the nasty thing and reset the stream GetVolumeFileParts().First(); @@ -173,6 +174,12 @@ public abstract class RarVolume : Volume } } + public async ValueTask IsSolidArchiveAsync(CancellationToken cancellationToken = default) + { + await EnsureArchiveHeaderLoadedAsync(cancellationToken); + return ArchiveHeader?.IsSolid ?? false; + } + public int MinVersion { get @@ -221,5 +228,68 @@ public abstract class RarVolume : Volume } } + private async ValueTask EnsureArchiveHeaderLoadedAsync(CancellationToken cancellationToken) + { + if (ArchiveHeader is null) + { + if (Mode == StreamingMode.Streaming) + { + throw new InvalidOperationException( + "ArchiveHeader should never been null in a streaming read." + ); + } + + // we only want to load the archive header to avoid overhead but have to do the nasty thing and reset the stream + await GetVolumeFilePartsAsync(cancellationToken).FirstAsync(); + Stream.Position = 0; + } + } + + public virtual async ValueTask MinVersionAsync( + CancellationToken cancellationToken = default + ) + { + await EnsureArchiveHeaderLoadedAsync(cancellationToken).ConfigureAwait(false); + if (_maxCompressionAlgorithm >= 50) + { + return 5; //5-6 + } + else if (_maxCompressionAlgorithm >= 29) + { + return 3; //3-4 + } + else if (_maxCompressionAlgorithm >= 20) + { + return 2; //2 + } + else + { + return 1; + } + } + + public virtual async ValueTask MaxVersionAsync( + CancellationToken cancellationToken = default + ) + { + await EnsureArchiveHeaderLoadedAsync(cancellationToken).ConfigureAwait(false); + if (_maxCompressionAlgorithm >= 50) + { + return 6; //5-6 + } + else if (_maxCompressionAlgorithm >= 29) + { + return 4; //3-4 + } + else if (_maxCompressionAlgorithm >= 20) + { + return 2; //2 + } + else + { + return 1; + } + } + public string? Comment { get; internal set; } }