more basic LLM async and fixed CRC async

This commit is contained in:
Adam Hathcock 2026-01-19 16:08:46 +00:00
parent 884f0b702e
commit ecd9317ab3
9 changed files with 124 additions and 48 deletions

View file

@ -210,7 +210,7 @@ public abstract class AbstractArchive<TEntry, TVolume> : IArchive, IAsyncArchive
public async ValueTask<IAsyncReader> 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)."

View file

@ -112,7 +112,7 @@ public partial class RarArchive : AbstractArchive<RarArchiveEntry, RarVolume>, I
public override bool IsSolid => Volumes.First().IsSolidArchive;
public override async ValueTask<bool> IsSolidAsync() =>
(await VolumesAsync.CastAsync<RarVolume>().FirstAsync()).IsSolidArchive;
await (await VolumesAsync.CastAsync<RarVolume>().FirstAsync()).IsSolidArchiveAsync();
public override bool IsEncrypted => Entries.First(x => !x.IsDirectory).IsEncrypted;

View file

@ -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<ushort> ReadUInt16Async(
public async ValueTask<ushort> 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<uint> ReadUInt32Async(
public async ValueTask<uint> 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);
}

View file

@ -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;

View file

@ -10,22 +10,27 @@ namespace SharpCompress.Common.Rar;
internal sealed class AsyncRarCryptoBinaryReader : AsyncRarCrcBinaryReader
{
private BlockTransformer _rijndael;
private BlockTransformer _rijndael = default!;
private readonly Queue<byte> _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<AsyncRarCryptoBinaryReader> 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
{

View file

@ -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
{

View file

@ -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);
}
}

View file

@ -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<byte> _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

View file

@ -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<RarFilePart> GetVolumeFilePartsAsync()
internal async IAsyncEnumerable<RarFilePart> 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<bool> 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<int> 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<int> 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; }
}