Compare commits

..

6 Commits

Author SHA1 Message Date
Adam Hathcock
c4a28e7cfb Update tests/SharpCompress.Test/Rar/RarArchiveAsyncTests.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-09 09:11:19 +00:00
Adam Hathcock
29197f2142 Update tests/SharpCompress.Test/Rar/RarArchiveTests.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-09 09:10:59 +00:00
copilot-swe-agent[bot]
2a4081362e Complete fix for RAR extraction subdirectory issue
Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com>
2026-02-08 12:56:44 +00:00
copilot-swe-agent[bot]
d5cab8172b Address code review feedback: clarify file size comment
Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com>
2026-02-08 12:54:11 +00:00
copilot-swe-agent[bot]
4084b347d4 Fix RAR extraction to preserve subdirectory structure
- Set default ExtractFullPath=true in WriteToDirectoryInternal methods
- Add test case with sample RAR archive containing subdirectories
- Tests verify files are extracted to correct subdirectories, not root

Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com>
2026-02-08 12:52:25 +00:00
copilot-swe-agent[bot]
5437d9ff8c Initial plan 2026-02-08 12:38:49 +00:00
41 changed files with 305 additions and 225 deletions

View File

@@ -43,6 +43,9 @@ public static class IArchiveExtensions
var bytesRead = 0L;
var seenDirectories = new HashSet<string>();
// When extracting an entire archive, default to extracting with full paths
options ??= new ExtractionOptions { ExtractFullPath = true, Overwrite = true };
foreach (var entry in archive.Entries)
{
if (entry.IsDirectory)

View File

@@ -58,6 +58,9 @@ public static class IAsyncArchiveExtensions
var bytesRead = 0L;
var seenDirectories = new HashSet<string>();
// When extracting an entire archive, default to extracting with full paths
options ??= new ExtractionOptions { ExtractFullPath = true, Overwrite = true };
await foreach (var entry in archive.EntriesAsync.WithCancellation(cancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();

View File

@@ -1,3 +1,5 @@
#nullable disable
using System.Security.Cryptography;
using System.Text;
using SharpCompress.Common.Rar.Headers;
@@ -10,7 +12,7 @@ internal class CryptKey3 : ICryptKey
private string _password;
public CryptKey3(string? password) => _password = password ?? "";
public CryptKey3(string password) => _password = password ?? "";
public ICryptoTransform Transformer(byte[] salt)
{

View File

@@ -1,3 +1,5 @@
#nullable disable
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common.Rar;

View File

@@ -1,3 +1,5 @@
#nullable disable
using SharpCompress.Common.Rar;
using SharpCompress.IO;

View File

@@ -65,7 +65,7 @@ public partial class RarHeaderFactory
if (_isRar5 && _cryptInfo != null)
{
await _cryptInfo.ReadInitVAsync(new AsyncMarkingBinaryReader(stream));
var _headerKey = new CryptKey5(Options.Password.NotNull(), _cryptInfo);
var _headerKey = new CryptKey5(Options.Password!, _cryptInfo);
reader = await AsyncRarCryptoBinaryReader.Create(
stream,
@@ -189,7 +189,7 @@ public partial class RarHeaderFactory
Options.Password,
fh.Rar5CryptoInfo.NotNull()
)
: new CryptKey3(Options.Password.NotNull())
: new CryptKey3(Options.Password)
);
}
}

View File

@@ -62,7 +62,7 @@ public partial class RarHeaderFactory
if (_isRar5 && _cryptInfo != null)
{
_cryptInfo.ReadInitV(new MarkingBinaryReader(stream));
var _headerKey = new CryptKey5(Options.Password.NotNull(), _cryptInfo);
var _headerKey = new CryptKey5(Options.Password!, _cryptInfo);
reader = RarCryptoBinaryReader.Create(stream, _headerKey, _cryptInfo.Salt);
}

View File

@@ -1,3 +1,5 @@
#nullable disable
using System.IO;
using System.Threading;
using System.Threading.Tasks;

View File

@@ -1,3 +1,5 @@
#nullable disable
using System;
using System.Collections.Generic;
using System.IO;
@@ -16,7 +18,7 @@ internal partial class ArchiveDatabase
internal List<long> _packSizes = new();
internal List<uint?> _packCrCs = new();
internal List<CFolder> _folders = new();
internal List<int>? _numUnpackStreamsVector;
internal List<int> _numUnpackStreamsVector;
internal List<CFileItem> _files = new();
internal List<long> _packStreamStartPositions = new();
@@ -45,7 +47,7 @@ internal partial class ArchiveDatabase
_packSizes.Count == 0
&& _packCrCs.Count == 0
&& _folders.Count == 0
&& (_numUnpackStreamsVector?.Count ?? 0) == 0
&& _numUnpackStreamsVector.Count == 0
&& _files.Count == 0;
private void FillStartPos()
@@ -92,7 +94,7 @@ internal partial class ArchiveDatabase
_folderStartFileIndex.Add(i); // check it
if (_numUnpackStreamsVector.NotNull()[folderIndex] != 0)
if (_numUnpackStreamsVector![folderIndex] != 0)
{
break;
}

View File

@@ -1,9 +1,11 @@
#nullable disable
namespace SharpCompress.Common.SevenZip;
internal class CCoderInfo
{
internal CMethodId _methodId;
internal byte[]? _props;
internal byte[] _props;
internal int _numInStreams;
internal int _numOutStreams;
}

View File

@@ -1,3 +1,5 @@
#nullable disable
using System;
namespace SharpCompress.Common.SevenZip;
@@ -8,7 +10,7 @@ internal class CFileItem
public uint? Attrib { get; internal set; }
public uint? ExtendedAttrib { get; internal set; }
public uint? Crc { get; internal set; }
public string? Name { get; internal set; }
public string Name { get; internal set; }
public bool HasStream { get; internal set; }
public bool IsDir { get; internal set; }

View File

@@ -34,7 +34,7 @@ internal class SevenZipFilePart : FilePart
internal CFileItem Header { get; }
internal CFolder? Folder { get; }
internal override string? FilePartName => Header.Name;
internal override string FilePartName => Header.Name;
internal override Stream? GetRawStream() => null;

View File

@@ -24,6 +24,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#nullable disable
using System;
using System.IO;
using System.Threading;
@@ -76,12 +78,11 @@ public sealed partial class ADCStream
var toCopy = count;
var copied = 0;
var outBuf = _outBuffer.NotNull();
while (_outPosition + toCopy >= outBuf.Length)
while (_outPosition + toCopy >= _outBuffer.Length)
{
cancellationToken.ThrowIfCancellationRequested();
var piece = outBuf.Length - _outPosition;
Array.Copy(outBuf, _outPosition, buffer, inPosition, piece);
var piece = _outBuffer.Length - _outPosition;
Array.Copy(_outBuffer, _outPosition, buffer, inPosition, piece);
inPosition += piece;
copied += piece;
_position += piece;
@@ -96,10 +97,9 @@ public sealed partial class ADCStream
{
return copied;
}
outBuf = _outBuffer;
}
Array.Copy(outBuf, _outPosition, buffer, inPosition, toCopy);
Array.Copy(_outBuffer, _outPosition, buffer, inPosition, toCopy);
_outPosition += toCopy;
_position += toCopy;
copied += toCopy;

View File

@@ -24,6 +24,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#nullable disable
using System;
using System.IO;
using System.Threading;
@@ -54,7 +56,7 @@ public sealed partial class ADCStream : Stream
/// <summary>
/// Buffer with currently used chunk of decompressed data
/// </summary>
private byte[]? _outBuffer;
private byte[] _outBuffer;
/// <summary>
/// Position in buffer of decompressed data
@@ -137,11 +139,10 @@ public sealed partial class ADCStream : Stream
var toCopy = count;
var copied = 0;
while (_outPosition + toCopy >= _outBuffer.NotNull().Length)
while (_outPosition + toCopy >= _outBuffer.Length)
{
var outBuf = _outBuffer.NotNull();
var piece = outBuf.Length - _outPosition;
Array.Copy(outBuf, _outPosition, buffer, inPosition, piece);
var piece = _outBuffer.Length - _outPosition;
Array.Copy(_outBuffer, _outPosition, buffer, inPosition, piece);
inPosition += piece;
copied += piece;
_position += piece;
@@ -154,7 +155,7 @@ public sealed partial class ADCStream : Stream
}
}
Array.Copy(_outBuffer.NotNull(), _outPosition, buffer, inPosition, toCopy);
Array.Copy(_outBuffer, _outPosition, buffer, inPosition, toCopy);
_outPosition += toCopy;
_position += toCopy;
copied += toCopy;

View File

@@ -1,5 +1,3 @@
#nullable disable
using System;
using System.IO;
using System.Threading;

View File

@@ -1,3 +1,5 @@
#nullable disable
using System;
using System.IO;
@@ -9,8 +11,8 @@ internal sealed class BinTree : InWindow
private uint _cyclicBufferSize;
private uint _matchMaxLen;
private uint[]? _son;
private uint[]? _hash;
private uint[] _son;
private uint[] _hash;
private uint _cutValue = 0xFF;
private uint _hashMask;
@@ -54,10 +56,9 @@ internal sealed class BinTree : InWindow
public new void Init()
{
base.Init();
var hash = _hash.NotNull();
for (uint i = 0; i < _hashSizeSum; i++)
{
hash[i] = K_EMPTY_HASH_VALUE;
_hash[i] = K_EMPTY_HASH_VALUE;
}
_cyclicBufferPos = 0;
ReduceOffsets(-1);
@@ -140,8 +141,6 @@ internal sealed class BinTree : InWindow
public uint GetMatches(uint[] distances)
{
var son = _son.NotNull();
var hash = _hash.NotNull();
uint lenLimit;
if (_pos + _matchMaxLen <= _streamPos)
{
@@ -165,27 +164,26 @@ internal sealed class BinTree : InWindow
hash2Value = 0,
hash3Value = 0;
var bufferBase = _bufferBase.NotNull();
if (_hashArray)
{
var temp = Crc.TABLE[bufferBase[cur]] ^ bufferBase[cur + 1];
var temp = Crc.TABLE[_bufferBase[cur]] ^ _bufferBase[cur + 1];
hash2Value = temp & (K_HASH2_SIZE - 1);
temp ^= ((uint)(bufferBase[cur + 2]) << 8);
temp ^= ((uint)(_bufferBase[cur + 2]) << 8);
hash3Value = temp & (K_HASH3_SIZE - 1);
hashValue = (temp ^ (Crc.TABLE[bufferBase[cur + 3]] << 5)) & _hashMask;
hashValue = (temp ^ (Crc.TABLE[_bufferBase[cur + 3]] << 5)) & _hashMask;
}
else
{
hashValue = bufferBase[cur] ^ ((uint)(bufferBase[cur + 1]) << 8);
hashValue = _bufferBase[cur] ^ ((uint)(_bufferBase[cur + 1]) << 8);
}
var curMatch = hash[_kFixHashSize + hashValue];
var curMatch = _hash[_kFixHashSize + hashValue];
if (_hashArray)
{
var curMatch2 = hash[hash2Value];
var curMatch3 = hash[K_HASH3_OFFSET + hash3Value];
hash[hash2Value] = _pos;
hash[K_HASH3_OFFSET + hash3Value] = _pos;
var curMatch2 = _hash[hash2Value];
var curMatch3 = _hash[K_HASH3_OFFSET + hash3Value];
_hash[hash2Value] = _pos;
_hash[K_HASH3_OFFSET + hash3Value] = _pos;
if (curMatch2 > matchMinPos)
{
if (_bufferBase[_bufferOffset + curMatch2] == _bufferBase[cur])
@@ -214,7 +212,7 @@ internal sealed class BinTree : InWindow
}
}
hash[_kFixHashSize + hashValue] = _pos;
_hash[_kFixHashSize + hashValue] = _pos;
var ptr0 = (_cyclicBufferPos << 1) + 1;
var ptr1 = (_cyclicBufferPos << 1);
@@ -244,7 +242,7 @@ internal sealed class BinTree : InWindow
{
if (curMatch <= matchMinPos || count-- == 0)
{
son[ptr0] = son[ptr1] = K_EMPTY_HASH_VALUE;
_son[ptr0] = _son[ptr1] = K_EMPTY_HASH_VALUE;
break;
}
var delta = _pos - curMatch;
@@ -272,24 +270,24 @@ internal sealed class BinTree : InWindow
distances[offset++] = delta - 1;
if (len == lenLimit)
{
son[ptr1] = son[cyclicPos];
son[ptr0] = son[cyclicPos + 1];
_son[ptr1] = _son[cyclicPos];
_son[ptr0] = _son[cyclicPos + 1];
break;
}
}
}
if (_bufferBase[pby1 + len] < _bufferBase[cur + len])
{
son[ptr1] = curMatch;
_son[ptr1] = curMatch;
ptr1 = cyclicPos + 1;
curMatch = son[ptr1];
curMatch = _son[ptr1];
len1 = len;
}
else
{
son[ptr0] = curMatch;
_son[ptr0] = curMatch;
ptr0 = cyclicPos;
curMatch = son[ptr0];
curMatch = _son[ptr0];
len0 = len;
}
}
@@ -299,8 +297,6 @@ internal sealed class BinTree : InWindow
public void Skip(uint num)
{
var son = _son.NotNull();
var hash = _hash.NotNull();
do
{
uint lenLimit;
@@ -327,10 +323,10 @@ internal sealed class BinTree : InWindow
{
var temp = Crc.TABLE[_bufferBase[cur]] ^ _bufferBase[cur + 1];
var hash2Value = temp & (K_HASH2_SIZE - 1);
hash[hash2Value] = _pos;
_hash[hash2Value] = _pos;
temp ^= ((uint)(_bufferBase[cur + 2]) << 8);
var hash3Value = temp & (K_HASH3_SIZE - 1);
hash[K_HASH3_OFFSET + hash3Value] = _pos;
_hash[K_HASH3_OFFSET + hash3Value] = _pos;
hashValue = (temp ^ (Crc.TABLE[_bufferBase[cur + 3]] << 5)) & _hashMask;
}
else
@@ -338,8 +334,8 @@ internal sealed class BinTree : InWindow
hashValue = _bufferBase[cur] ^ ((uint)(_bufferBase[cur + 1]) << 8);
}
var curMatch = hash[_kFixHashSize + hashValue];
hash[_kFixHashSize + hashValue] = _pos;
var curMatch = _hash[_kFixHashSize + hashValue];
_hash[_kFixHashSize + hashValue] = _pos;
var ptr0 = (_cyclicBufferPos << 1) + 1;
var ptr1 = (_cyclicBufferPos << 1);
@@ -353,7 +349,7 @@ internal sealed class BinTree : InWindow
{
if (curMatch <= matchMinPos || count-- == 0)
{
son[ptr0] = son[ptr1] = K_EMPTY_HASH_VALUE;
_son[ptr0] = _son[ptr1] = K_EMPTY_HASH_VALUE;
break;
}
@@ -378,23 +374,23 @@ internal sealed class BinTree : InWindow
}
if (len == lenLimit)
{
son[ptr1] = son[cyclicPos];
son[ptr0] = son[cyclicPos + 1];
_son[ptr1] = _son[cyclicPos];
_son[ptr0] = _son[cyclicPos + 1];
break;
}
}
if (_bufferBase[pby1 + len] < _bufferBase[cur + len])
{
son[ptr1] = curMatch;
_son[ptr1] = curMatch;
ptr1 = cyclicPos + 1;
curMatch = son[ptr1];
curMatch = _son[ptr1];
len1 = len;
}
else
{
son[ptr0] = curMatch;
_son[ptr0] = curMatch;
ptr0 = cyclicPos;
curMatch = son[ptr0];
curMatch = _son[ptr0];
len0 = len;
}
}
@@ -422,8 +418,8 @@ internal sealed class BinTree : InWindow
private void Normalize()
{
var subValue = _pos - _cyclicBufferSize;
NormalizeLinks(_son.NotNull(), _cyclicBufferSize * 2, subValue);
NormalizeLinks(_hash.NotNull(), _hashSizeSum, subValue);
NormalizeLinks(_son, _cyclicBufferSize * 2, subValue);
NormalizeLinks(_hash, _hashSizeSum, subValue);
ReduceOffsets((int)subValue);
}

View File

@@ -1,3 +1,5 @@
#nullable disable
using System;
using System.IO;
using System.Threading;
@@ -23,7 +25,6 @@ public partial class Decoder : ICoder, ISetDecoderProperties
)
{
return await _lowCoder[posState]
.NotNull()
.DecodeAsync(rangeDecoder, cancellationToken)
.ConfigureAwait(false);
}
@@ -34,7 +35,6 @@ public partial class Decoder : ICoder, ISetDecoderProperties
)
{
symbol += await _midCoder[posState]
.NotNull()
.DecodeAsync(rangeDecoder, cancellationToken)
.ConfigureAwait(false);
}
@@ -108,8 +108,7 @@ public partial class Decoder : ICoder, ISetDecoderProperties
byte prevByte,
CancellationToken cancellationToken = default
) =>
await _coders
.NotNull()[GetState(pos, prevByte)]
await _coders[GetState(pos, prevByte)]
.DecodeNormalAsync(rangeDecoder, cancellationToken)
.ConfigureAwait(false);
@@ -120,8 +119,7 @@ public partial class Decoder : ICoder, ISetDecoderProperties
byte matchByte,
CancellationToken cancellationToken = default
) =>
await _coders
.NotNull()[GetState(pos, prevByte)]
await _coders[GetState(pos, prevByte)]
.DecodeWithMatchByteAsync(rangeDecoder, matchByte, cancellationToken)
.ConfigureAwait(false);
}
@@ -139,26 +137,26 @@ public partial class Decoder : ICoder, ISetDecoderProperties
{
CreateDictionary();
}
await _outWindow.NotNull().InitAsync(outStream);
await _outWindow.InitAsync(outStream);
if (outSize > 0)
{
_outWindow.NotNull().SetLimit(outSize);
_outWindow.SetLimit(outSize);
}
else
{
_outWindow.NotNull().SetLimit(long.MaxValue - _outWindow.NotNull().Total);
_outWindow.SetLimit(long.MaxValue - _outWindow.Total);
}
var rangeDecoder = new RangeCoder.Decoder();
await rangeDecoder.InitAsync(inStream, cancellationToken).ConfigureAwait(false);
await CodeAsync(_dictionarySize, _outWindow.NotNull(), rangeDecoder, cancellationToken)
await CodeAsync(_dictionarySize, _outWindow, rangeDecoder, cancellationToken)
.ConfigureAwait(false);
await _outWindow.NotNull().ReleaseStreamAsync(cancellationToken).ConfigureAwait(false);
await _outWindow.ReleaseStreamAsync(cancellationToken).ConfigureAwait(false);
rangeDecoder.ReleaseStream();
await _outWindow.NotNull().DisposeAsync().ConfigureAwait(false);
await _outWindow.DisposeAsync().ConfigureAwait(false);
_outWindow = null;
}
@@ -341,6 +339,6 @@ public partial class Decoder : ICoder, ISetDecoderProperties
{
CreateDictionary();
}
await _outWindow.NotNull().TrainAsync(stream);
await _outWindow.TrainAsync(stream);
}
}

View File

@@ -1,3 +1,5 @@
#nullable disable
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
@@ -13,12 +15,8 @@ public partial class Decoder : ICoder, ISetDecoderProperties // ,System.IO.Strea
{
private BitDecoder _choice = new();
private BitDecoder _choice2 = new();
private readonly BitTreeDecoder?[] _lowCoder = new BitTreeDecoder?[
Base.K_NUM_POS_STATES_MAX
];
private readonly BitTreeDecoder?[] _midCoder = new BitTreeDecoder?[
Base.K_NUM_POS_STATES_MAX
];
private readonly BitTreeDecoder[] _lowCoder = new BitTreeDecoder[Base.K_NUM_POS_STATES_MAX];
private readonly BitTreeDecoder[] _midCoder = new BitTreeDecoder[Base.K_NUM_POS_STATES_MAX];
private BitTreeDecoder _highCoder = new(Base.K_NUM_HIGH_LEN_BITS);
private uint _numPosStates;
@@ -37,8 +35,8 @@ public partial class Decoder : ICoder, ISetDecoderProperties // ,System.IO.Strea
_choice.Init();
for (uint posState = 0; posState < _numPosStates; posState++)
{
_lowCoder[posState].NotNull().Init();
_midCoder[posState].NotNull().Init();
_lowCoder[posState].Init();
_midCoder[posState].Init();
}
_choice2.Init();
_highCoder.Init();
@@ -48,12 +46,12 @@ public partial class Decoder : ICoder, ISetDecoderProperties // ,System.IO.Strea
{
if (_choice.Decode(rangeDecoder) == 0)
{
return _lowCoder[posState].NotNull().Decode(rangeDecoder);
return _lowCoder[posState].Decode(rangeDecoder);
}
var symbol = Base.K_NUM_LOW_LEN_SYMBOLS;
if (_choice2.Decode(rangeDecoder) == 0)
{
symbol += _midCoder[posState].NotNull().Decode(rangeDecoder);
symbol += _midCoder[posState].Decode(rangeDecoder);
}
else
{
@@ -112,7 +110,7 @@ public partial class Decoder : ICoder, ISetDecoderProperties // ,System.IO.Strea
}
}
private Decoder2[]? _coders;
private Decoder2[] _coders;
private int _numPrevBits;
private int _numPosBits;
private uint _posMask;
@@ -136,11 +134,10 @@ public partial class Decoder : ICoder, ISetDecoderProperties // ,System.IO.Strea
public void Init()
{
var coders = _coders.NotNull();
var numStates = (uint)1 << (_numPrevBits + _numPosBits);
for (uint i = 0; i < numStates; i++)
{
coders[i].Init();
_coders[i].Init();
}
}
@@ -148,18 +145,17 @@ public partial class Decoder : ICoder, ISetDecoderProperties // ,System.IO.Strea
((pos & _posMask) << _numPrevBits) + (uint)(prevByte >> (8 - _numPrevBits));
public byte DecodeNormal(RangeCoder.Decoder rangeDecoder, uint pos, byte prevByte) =>
_coders.NotNull()[GetState(pos, prevByte)].DecodeNormal(rangeDecoder);
_coders[GetState(pos, prevByte)].DecodeNormal(rangeDecoder);
public byte DecodeWithMatchByte(
RangeCoder.Decoder rangeDecoder,
uint pos,
byte prevByte,
byte matchByte
) =>
_coders.NotNull()[GetState(pos, prevByte)].DecodeWithMatchByte(rangeDecoder, matchByte);
) => _coders[GetState(pos, prevByte)].DecodeWithMatchByte(rangeDecoder, matchByte);
}
private OutWindow? _outWindow;
private OutWindow _outWindow;
private readonly BitDecoder[] _isMatchDecoders = new BitDecoder[
Base.K_NUM_STATES << Base.K_NUM_POS_STATES_BITS_MAX
@@ -289,32 +285,32 @@ public partial class Decoder : ICoder, ISetDecoderProperties // ,System.IO.Strea
Stream outStream,
long inSize,
long outSize,
ICodeProgress? progress
ICodeProgress progress
)
{
if (_outWindow is null)
{
CreateDictionary();
}
_outWindow.NotNull().Init(outStream);
_outWindow.Init(outStream);
if (outSize > 0)
{
_outWindow.NotNull().SetLimit(outSize);
_outWindow.SetLimit(outSize);
}
else
{
_outWindow.NotNull().SetLimit(long.MaxValue - _outWindow.NotNull().Total);
_outWindow.SetLimit(long.MaxValue - _outWindow.Total);
}
var rangeDecoder = new RangeCoder.Decoder();
rangeDecoder.Init(inStream);
Code(_dictionarySize, _outWindow.NotNull(), rangeDecoder);
Code(_dictionarySize, _outWindow, rangeDecoder);
_outWindow.NotNull().ReleaseStream();
_outWindow.ReleaseStream();
rangeDecoder.ReleaseStream();
_outWindow.NotNull().Dispose();
_outWindow.Dispose();
_outWindow = null;
}
@@ -477,6 +473,6 @@ public partial class Decoder : ICoder, ISetDecoderProperties // ,System.IO.Strea
{
CreateDictionary();
}
_outWindow.NotNull().Train(stream);
_outWindow.Train(stream);
}
}

View File

@@ -147,7 +147,7 @@ public partial class LzmaStream
_decoder.SetDecoderProperties(Properties);
}
await _rangeDecoder.InitAsync(_inputStream.NotNull(), cancellationToken);
await _rangeDecoder.InitAsync(_inputStream, cancellationToken);
}
else if (control > 0x02)
{

View File

@@ -1,3 +1,5 @@
#nullable disable
using System.IO;
using System.Threading;
using System.Threading.Tasks;
@@ -15,7 +17,7 @@ internal partial class Encoder
{
var b = (byte)(temp + (_low >> 32));
var buffer = new[] { b };
await Stream.WriteAsync(buffer, 0, 1, cancellationToken).ConfigureAwait(false);
await _stream.WriteAsync(buffer, 0, 1, cancellationToken).ConfigureAwait(false);
temp = 0xFF;
} while (--_cacheSize != 0);
_cache = (byte)(((uint)_low) >> 24);
@@ -70,7 +72,7 @@ internal partial class Encoder
}
public async ValueTask FlushStreamAsync(CancellationToken cancellationToken = default) =>
await Stream.FlushAsync(cancellationToken).ConfigureAwait(false);
await _stream.FlushAsync(cancellationToken).ConfigureAwait(false);
}
internal partial class Decoder
@@ -101,7 +103,7 @@ internal partial class Decoder
while (_range < K_TOP_VALUE)
{
var buffer = new byte[1];
var read = await Stream
var read = await _stream
.ReadAsync(buffer, 0, 1, cancellationToken)
.ConfigureAwait(false);
if (read == 0)
@@ -119,7 +121,7 @@ internal partial class Decoder
if (_range < K_TOP_VALUE)
{
var buffer = new byte[1];
var read = await Stream
var read = await _stream
.ReadAsync(buffer, 0, 1, cancellationToken)
.ConfigureAwait(false);
if (read == 0)
@@ -150,7 +152,7 @@ internal partial class Decoder
if (range < K_TOP_VALUE)
{
var read = await Stream
var read = await _stream
.ReadAsync(buffer, 0, 1, cancellationToken)
.ConfigureAwait(false);
if (read == 0)

View File

@@ -1,3 +1,5 @@
#nullable disable
using System.IO;
using System.Runtime.CompilerServices;
@@ -7,7 +9,7 @@ internal partial class Encoder
{
public const uint K_TOP_VALUE = (1 << 24);
private Stream? _stream;
private Stream _stream;
public ulong _low;
public uint _range;
@@ -20,8 +22,6 @@ internal partial class Encoder
public void ReleaseStream() => _stream = null;
private Stream Stream => _stream.NotNull();
public void Init()
{
//StartPosition = Stream.Position;
@@ -40,9 +40,9 @@ internal partial class Encoder
}
}
public void FlushStream() => Stream.Flush();
public void FlushStream() => _stream.Flush();
public void CloseStream() => Stream.Dispose();
public void CloseStream() => _stream.Dispose();
public void ShiftLow()
{
@@ -51,7 +51,7 @@ internal partial class Encoder
var temp = _cache;
do
{
Stream.WriteByte((byte)(temp + (_low >> 32)));
_stream.WriteByte((byte)(temp + (_low >> 32)));
temp = 0xFF;
} while (--_cacheSize != 0);
_cache = (byte)(((uint)_low) >> 24);
@@ -86,7 +86,7 @@ internal partial class Decoder
public uint _range;
public uint _code;
public Stream? _stream;
public Stream _stream;
public long _total;
public void Init(Stream stream)
@@ -97,7 +97,7 @@ internal partial class Decoder
_range = 0xFFFFFFFF;
for (var i = 0; i < 5; i++)
{
_code = (_code << 8) | (byte)stream.ReadByte();
_code = (_code << 8) | (byte)_stream.ReadByte();
}
_total = 5;
}
@@ -106,13 +106,11 @@ internal partial class Decoder
// Stream.ReleaseStream();
_stream = null;
private Stream Stream => _stream.NotNull();
public void Normalize()
{
while (_range < K_TOP_VALUE)
{
_code = (_code << 8) | (byte)Stream.ReadByte();
_code = (_code << 8) | (byte)_stream.ReadByte();
_range <<= 8;
_total++;
}
@@ -123,7 +121,7 @@ internal partial class Decoder
{
if (_range < K_TOP_VALUE)
{
_code = (_code << 8) | (byte)Stream.ReadByte();
_code = (_code << 8) | (byte)_stream.ReadByte();
_range <<= 8;
_total++;
}
@@ -152,7 +150,7 @@ internal partial class Decoder
if (range < K_TOP_VALUE)
{
code = (code << 8) | (byte)Stream.ReadByte();
code = (code << 8) | (byte)_stream.ReadByte();
range <<= 8;
_total++;
}

View File

@@ -1,3 +1,5 @@
#nullable disable
using System.Threading;
using System.Threading.Tasks;

View File

@@ -1,3 +1,5 @@
#nullable disable
using System.Threading;
using System.Threading.Tasks;

View File

@@ -1,3 +1,5 @@
#nullable disable
using System;
using System.IO;
using System.Threading;
@@ -13,9 +15,9 @@ public class PpmdStream : Stream
private readonly PpmdProperties _properties;
private readonly Stream _stream;
private readonly bool _compress;
private Model? _model;
private ModelPpm? _modelH;
private Decoder? _decoder;
private Model _model;
private ModelPpm _modelH;
private Decoder _decoder;
private long _position;
private bool _isDisposed;
@@ -176,7 +178,7 @@ public class PpmdStream : Stream
{
if (_compress)
{
_model.NotNull().EncodeBlock(_stream, new MemoryStream(), true);
_model.EncodeBlock(_stream, new MemoryStream(), true);
}
}
base.Dispose(isDisposing);
@@ -199,12 +201,12 @@ public class PpmdStream : Stream
var size = 0;
if (_properties.Version == PpmdVersion.I1)
{
size = _model.NotNull().DecodeBlock(_stream, buffer, offset, count);
size = _model.DecodeBlock(_stream, buffer, offset, count);
}
if (_properties.Version == PpmdVersion.H)
{
int c;
while (size < count && (c = _modelH.NotNull().DecodeChar()) >= 0)
while (size < count && (c = _modelH.DecodeChar()) >= 0)
{
buffer[offset++] = (byte)c;
size++;
@@ -213,7 +215,7 @@ public class PpmdStream : Stream
if (_properties.Version == PpmdVersion.H7Z)
{
int c;
while (size < count && (c = _modelH.NotNull().DecodeChar(_decoder.NotNull())) >= 0)
while (size < count && (c = _modelH.DecodeChar(_decoder)) >= 0)
{
buffer[offset++] = (byte)c;
size++;
@@ -245,7 +247,6 @@ public class PpmdStream : Stream
if (_properties.Version == PpmdVersion.I1)
{
size = await _model
.NotNull()
.DecodeBlockAsync(_stream, buffer, offset, count, cancellationToken)
.ConfigureAwait(false);
}
@@ -254,12 +255,7 @@ public class PpmdStream : Stream
int c;
while (
size < count
&& (
c = await _modelH
.NotNull()
.DecodeCharAsync(cancellationToken)
.ConfigureAwait(false)
) >= 0
&& (c = await _modelH.DecodeCharAsync(cancellationToken).ConfigureAwait(false)) >= 0
)
{
buffer[offset++] = (byte)c;
@@ -273,8 +269,7 @@ public class PpmdStream : Stream
size < count
&& (
c = await _modelH
.NotNull()
.DecodeCharAsync(_decoder.NotNull(), cancellationToken)
.DecodeCharAsync(_decoder, cancellationToken)
.ConfigureAwait(false)
) >= 0
)
@@ -309,7 +304,6 @@ public class PpmdStream : Stream
// Need to use a temporary buffer since DecodeBlockAsync works with byte[]
var tempBuffer = new byte[count];
size = await _model
.NotNull()
.DecodeBlockAsync(_stream, tempBuffer, 0, count, cancellationToken)
.ConfigureAwait(false);
tempBuffer.AsMemory(0, size).CopyTo(buffer);
@@ -319,12 +313,7 @@ public class PpmdStream : Stream
int c;
while (
size < count
&& (
c = await _modelH
.NotNull()
.DecodeCharAsync(cancellationToken)
.ConfigureAwait(false)
) >= 0
&& (c = await _modelH.DecodeCharAsync(cancellationToken).ConfigureAwait(false)) >= 0
)
{
buffer.Span[offset++] = (byte)c;
@@ -338,8 +327,7 @@ public class PpmdStream : Stream
size < count
&& (
c = await _modelH
.NotNull()
.DecodeCharAsync(_decoder.NotNull(), cancellationToken)
.DecodeCharAsync(_decoder, cancellationToken)
.ConfigureAwait(false)
) >= 0
)
@@ -357,7 +345,7 @@ public class PpmdStream : Stream
{
if (_compress)
{
_model.NotNull().EncodeBlock(_stream, new MemoryStream(buffer, offset, count), false);
_model.EncodeBlock(_stream, new MemoryStream(buffer, offset, count), false);
}
}
}

View File

@@ -1,3 +1,5 @@
#nullable disable
using System;
using System.Buffers;
using System.IO;
@@ -45,7 +47,7 @@ internal partial class RarStream
if (tmpCount > 0)
{
var toCopy = tmpCount < count ? tmpCount : count;
Buffer.BlockCopy(tmpBuffer.NotNull(), tmpOffset, buffer, offset, toCopy);
Buffer.BlockCopy(tmpBuffer, tmpOffset, buffer, offset, toCopy);
tmpOffset += toCopy;
tmpCount -= toCopy;
offset += toCopy;

View File

@@ -1,3 +1,5 @@
#nullable disable
using System;
using System.Buffers;
using System.IO;
@@ -13,11 +15,11 @@ internal partial class RarStream : Stream
private bool fetch;
private byte[]? tmpBuffer = ArrayPool<byte>.Shared.Rent(65536);
private byte[] tmpBuffer = ArrayPool<byte>.Shared.Rent(65536);
private int tmpOffset;
private int tmpCount;
private byte[]? outBuffer;
private byte[] outBuffer;
private int outOffset;
private int outCount;
private int outTotal;
@@ -45,11 +47,8 @@ internal partial class RarStream : Stream
{
if (disposing)
{
if (this.tmpBuffer is not null)
{
ArrayPool<byte>.Shared.Return(this.tmpBuffer);
this.tmpBuffer = null;
}
ArrayPool<byte>.Shared.Return(this.tmpBuffer);
this.tmpBuffer = null;
readStream.Dispose();
}
isDisposed = true;
@@ -80,7 +79,7 @@ internal partial class RarStream : Stream
if (tmpCount > 0)
{
var toCopy = tmpCount < count ? tmpCount : count;
Buffer.BlockCopy(tmpBuffer.NotNull(), tmpOffset, buffer, offset, toCopy);
Buffer.BlockCopy(tmpBuffer, tmpOffset, buffer, offset, toCopy);
tmpOffset += toCopy;
tmpCount -= toCopy;
offset += toCopy;
@@ -120,7 +119,7 @@ internal partial class RarStream : Stream
if (outCount > 0)
{
var toCopy = outCount < count ? outCount : count;
Buffer.BlockCopy(buffer, offset, outBuffer.NotNull(), outOffset, toCopy);
Buffer.BlockCopy(buffer, offset, outBuffer, outOffset, toCopy);
outOffset += toCopy;
outCount -= toCopy;
offset += toCopy;
@@ -130,7 +129,7 @@ internal partial class RarStream : Stream
if (count > 0)
{
EnsureBufferCapacity(count);
Buffer.BlockCopy(buffer, offset, tmpBuffer.NotNull(), tmpCount, count);
Buffer.BlockCopy(buffer, offset, tmpBuffer, tmpCount, count);
tmpCount += count;
tmpOffset = 0;
unpack.Suspended = true;
@@ -143,16 +142,15 @@ internal partial class RarStream : Stream
private void EnsureBufferCapacity(int count)
{
var buffer = this.tmpBuffer.NotNull();
if (buffer.Length < this.tmpCount + count)
if (this.tmpBuffer.Length < this.tmpCount + count)
{
var newLength =
buffer.Length * 2 > this.tmpCount + count
? buffer.Length * 2
this.tmpBuffer.Length * 2 > this.tmpCount + count
? this.tmpBuffer.Length * 2
: this.tmpCount + count;
var newBuffer = ArrayPool<byte>.Shared.Rent(newLength);
Buffer.BlockCopy(buffer, 0, newBuffer, 0, this.tmpCount);
var oldBuffer = buffer;
Buffer.BlockCopy(this.tmpBuffer, 0, newBuffer, 0, this.tmpCount);
var oldBuffer = this.tmpBuffer;
this.tmpBuffer = newBuffer;
ArrayPool<byte>.Shared.Return(oldBuffer);
}

View File

@@ -1,3 +1,5 @@
#nullable disable
using System;
namespace SharpCompress.Compressors.Xz;
@@ -8,7 +10,7 @@ public static class Crc32
public const uint DefaultPolynomial = 0xedb88320u;
public const uint DefaultSeed = 0xffffffffu;
private static uint[]? defaultTable;
private static uint[] defaultTable;
public static uint Compute(byte[] buffer) => Compute(DefaultSeed, buffer);

View File

@@ -1,3 +1,5 @@
#nullable disable
using System;
namespace SharpCompress.Compressors.Xz;
@@ -7,7 +9,7 @@ public static class Crc64
{
public const ulong DefaultSeed = 0x0;
internal static ulong[]? Table;
internal static ulong[] Table;
public const ulong Iso3309Polynomial = 0xD800000000000000;

View File

@@ -1,3 +1,5 @@
#nullable disable
using System;
using System.IO;
using System.Threading;
@@ -34,7 +36,7 @@ public sealed partial class XZStream : XZReadOnlyStream
private void AssertBlockCheckTypeIsSupported()
{
switch (Header.NotNull().BlockCheckType)
switch (Header.BlockCheckType)
{
case CheckType.NONE:
case CheckType.CRC32:
@@ -47,11 +49,11 @@ public sealed partial class XZStream : XZReadOnlyStream
}
private readonly Stream _baseStream;
public XZHeader? Header { get; private set; }
public XZIndex? Index { get; private set; }
public XZFooter? Footer { get; private set; }
public XZHeader Header { get; private set; }
public XZIndex Index { get; private set; }
public XZFooter Footer { get; private set; }
public bool HeaderIsRead { get; private set; }
private XZBlock? _currentBlock;
private XZBlock _currentBlock;
private bool _endOfStream;
@@ -111,7 +113,7 @@ public sealed partial class XZStream : XZReadOnlyStream
var remaining = count - bytesRead;
var newOffset = offset + bytesRead;
var justRead = _currentBlock.NotNull().Read(buffer, newOffset, remaining);
var justRead = _currentBlock.Read(buffer, newOffset, remaining);
if (justRead < remaining)
{
NextBlock();
@@ -128,9 +130,5 @@ public sealed partial class XZStream : XZReadOnlyStream
}
private void NextBlock() =>
_currentBlock = new XZBlock(
BaseStream,
Header.NotNull().BlockCheckType,
Header.NotNull().BlockCheckSize
);
_currentBlock = new XZBlock(BaseStream, Header.BlockCheckType, Header.BlockCheckSize);
}

View File

@@ -1,3 +1,5 @@
#nullable disable
using System;
using System.Security.Cryptography;

View File

@@ -1,3 +1,5 @@
#nullable disable
using System;
using System.IO;
@@ -13,7 +15,7 @@ public sealed class Crc32Stream : Stream
public const uint DEFAULT_POLYNOMIAL = 0xedb88320u;
public const uint DEFAULT_SEED = 0xffffffffu;
private static uint[]? _defaultTable;
private static uint[] _defaultTable;
public Crc32Stream(Stream stream)
: this(stream, DEFAULT_POLYNOMIAL, DEFAULT_SEED) { }

View File

@@ -1,3 +1,4 @@
#nullable disable
using System;
using System.Collections;
using System.Collections.Generic;

View File

@@ -1,3 +1,5 @@
#nullable disable
using System;
using System.Collections;
using System.Collections.Generic;
@@ -41,7 +43,7 @@ internal sealed class LazyReadOnlyCollection<T> : ICollection<T>
#region IEnumerator Members
object? IEnumerator.Current => Current;
object IEnumerator.Current => Current;
public bool MoveNext()
{

View File

@@ -1,3 +1,5 @@
#nullable disable
using System;
using System.Collections;
using System.Collections.Generic;
@@ -13,7 +15,7 @@ namespace SharpCompress.Readers.Ace;
internal class MultiVolumeAceReader : AceReader
{
private readonly IEnumerator<Stream> streams;
private Stream? tempStream;
private Stream tempStream;
internal MultiVolumeAceReader(IEnumerable<Stream> streams, ReaderOptions options)
: base(options) => this.streams = streams.GetEnumerator();
@@ -52,13 +54,13 @@ internal class MultiVolumeAceReader : AceReader
{
private readonly MultiVolumeAceReader reader;
private readonly IEnumerator<Stream> nextReadableStreams;
private Stream? tempStream;
private Stream tempStream;
private bool isFirst = true;
internal MultiVolumeStreamEnumerator(
MultiVolumeAceReader r,
IEnumerator<Stream> nextReadableStreams,
Stream? tempStream
Stream tempStream
)
{
reader = r;
@@ -70,12 +72,7 @@ internal class MultiVolumeAceReader : AceReader
IEnumerator IEnumerable.GetEnumerator() => this;
private FilePart? _current;
public FilePart Current
{
get => _current.NotNull();
private set => _current = value;
}
public FilePart Current { get; private set; }
public void Dispose() { }
@@ -85,7 +82,7 @@ internal class MultiVolumeAceReader : AceReader
{
if (isFirst)
{
_current = reader.Entry.Parts.First();
Current = reader.Entry.Parts.First();
isFirst = false; //first stream already to go
return true;
}
@@ -96,7 +93,7 @@ internal class MultiVolumeAceReader : AceReader
}
if (tempStream != null)
{
reader.LoadStreamForReading(tempStream.NotNull());
reader.LoadStreamForReading(tempStream);
tempStream = null;
}
else if (!nextReadableStreams.MoveNext())
@@ -110,7 +107,7 @@ internal class MultiVolumeAceReader : AceReader
reader.LoadStreamForReading(nextReadableStreams.Current);
}
_current = reader.Entry.Parts.First();
Current = reader.Entry.Parts.First();
return true;
}

View File

@@ -1,3 +1,5 @@
#nullable disable
using System;
using System.Collections;
using System.Collections.Generic;
@@ -14,7 +16,7 @@ namespace SharpCompress.Readers.Arj;
internal class MultiVolumeArjReader : ArjReader
{
private readonly IEnumerator<Stream> streams;
private Stream? tempStream;
private Stream tempStream;
internal MultiVolumeArjReader(IEnumerable<Stream> streams, ReaderOptions options)
: base(options) => this.streams = streams.GetEnumerator();
@@ -53,13 +55,13 @@ internal class MultiVolumeArjReader : ArjReader
{
private readonly MultiVolumeArjReader reader;
private readonly IEnumerator<Stream> nextReadableStreams;
private Stream? tempStream;
private Stream tempStream;
private bool isFirst = true;
internal MultiVolumeStreamEnumerator(
MultiVolumeArjReader r,
IEnumerator<Stream> nextReadableStreams,
Stream? tempStream
Stream tempStream
)
{
reader = r;
@@ -71,12 +73,7 @@ internal class MultiVolumeArjReader : ArjReader
IEnumerator IEnumerable.GetEnumerator() => this;
private FilePart? _current;
public FilePart Current
{
get => _current.NotNull();
private set => _current = value;
}
public FilePart Current { get; private set; }
public void Dispose() { }
@@ -97,7 +94,7 @@ internal class MultiVolumeArjReader : ArjReader
}
if (tempStream != null)
{
reader.LoadStreamForReading(tempStream.NotNull());
reader.LoadStreamForReading(tempStream);
tempStream = null;
}
else if (!nextReadableStreams.MoveNext())

View File

@@ -1,3 +1,5 @@
#nullable disable
using System.Collections;
using System.Collections.Generic;
using System.IO;
@@ -24,13 +26,13 @@ internal partial class MultiVolumeRarReader : RarReader
{
private readonly MultiVolumeRarReader reader;
private readonly IEnumerator<Stream> nextReadableStreams;
private Stream? tempStream;
private Stream tempStream;
private bool isFirst = true;
internal MultiVolumeStreamAsyncEnumerator(
MultiVolumeRarReader r,
IEnumerator<Stream> nextReadableStreams,
Stream? tempStream
Stream tempStream
)
{
reader = r;
@@ -38,12 +40,7 @@ internal partial class MultiVolumeRarReader : RarReader
this.tempStream = tempStream;
}
private FilePart? _current;
public FilePart Current
{
get => _current.NotNull();
private set => _current = value;
}
public FilePart Current { get; private set; }
public async ValueTask<bool> MoveNextAsync()
{
@@ -60,7 +57,7 @@ internal partial class MultiVolumeRarReader : RarReader
}
if (tempStream != null)
{
await reader.LoadStreamForReadingAsync(tempStream.NotNull());
await reader.LoadStreamForReadingAsync(tempStream);
tempStream = null;
}
else if (!nextReadableStreams.MoveNext())

View File

@@ -1,3 +1,5 @@
#nullable disable
using System.Collections;
using System.Collections.Generic;
using System.IO;
@@ -12,7 +14,7 @@ namespace SharpCompress.Readers.Rar;
internal partial class MultiVolumeRarReader : RarReader
{
private readonly IEnumerator<Stream> streams;
private Stream? tempStream;
private Stream tempStream;
internal MultiVolumeRarReader(IEnumerable<Stream> streams, ReaderOptions options)
: base(options) => this.streams = streams.GetEnumerator();
@@ -53,13 +55,13 @@ internal partial class MultiVolumeRarReader : RarReader
{
private readonly MultiVolumeRarReader reader;
private readonly IEnumerator<Stream> nextReadableStreams;
private Stream? tempStream;
private Stream tempStream;
private bool isFirst = true;
internal MultiVolumeStreamEnumerator(
MultiVolumeRarReader r,
IEnumerator<Stream> nextReadableStreams,
Stream? tempStream
Stream tempStream
)
{
reader = r;
@@ -71,12 +73,7 @@ internal partial class MultiVolumeRarReader : RarReader
IEnumerator IEnumerable.GetEnumerator() => this;
private FilePart? _current;
public FilePart Current
{
get => _current.NotNull();
private set => _current = value;
}
public FilePart Current { get; private set; }
public void Dispose() { }
@@ -97,7 +94,7 @@ internal partial class MultiVolumeRarReader : RarReader
}
if (tempStream != null)
{
reader.LoadStreamForReading(tempStream.NotNull());
reader.LoadStreamForReading(tempStream);
tempStream = null;
}
else if (!nextReadableStreams.MoveNext())

View File

@@ -216,9 +216,9 @@
"net10.0": {
"Microsoft.NET.ILLink.Tasks": {
"type": "Direct",
"requested": "[10.0.2, )",
"resolved": "10.0.2",
"contentHash": "sXdDtMf2qcnbygw9OdE535c2lxSxrZP8gO4UhDJ0xiJbl1wIqXS1OTcTDFTIJPOFd6Mhcm8gPEthqWGUxBsTqw=="
"requested": "[10.0.0, )",
"resolved": "10.0.0",
"contentHash": "kICGrGYEzCNI3wPzfEXcwNHgTvlvVn9yJDhSdRK+oZQy4jvYH529u7O0xf5ocQKzOMjfS07+3z9PKRIjrFMJDA=="
},
"Microsoft.NETFramework.ReferenceAssemblies": {
"type": "Direct",
@@ -264,9 +264,9 @@
"net8.0": {
"Microsoft.NET.ILLink.Tasks": {
"type": "Direct",
"requested": "[8.0.23, )",
"resolved": "8.0.23",
"contentHash": "GqHiB1HbbODWPbY/lc5xLQH8siEEhNA0ptpJCC6X6adtAYNEzu5ZlqV3YHA3Gh7fuEwgA8XqVwMtH2KNtuQM1Q=="
"requested": "[8.0.22, )",
"resolved": "8.0.22",
"contentHash": "MhcMithKEiyyNkD2ZfbDZPmcOdi0GheGfg8saEIIEfD/fol3iHmcV8TsZkD4ZYz5gdUuoX4YtlVySUU7Sxl9SQ=="
},
"Microsoft.NETFramework.ReferenceAssemblies": {
"type": "Direct",

View File

@@ -718,6 +718,47 @@ public class RarArchiveAsyncTests : ArchiveTests
VerifyFiles();
}
/// <summary>
/// Tests for Issue #1050 - RAR extraction with WriteToDirectoryAsync creates folders
/// but places all files at the top level instead of in their subdirectories.
/// </summary>
[Fact]
public async ValueTask Rar_Issue1050_WriteToDirectoryAsync_ExtractsToSubdirectories()
{
var testFile = "Rar.issue1050.rar";
using var fileStream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, testFile));
await using var archive = RarArchive.OpenAsyncArchive(fileStream);
// Extract using archive.WriteToDirectoryAsync without explicit options
await archive.WriteToDirectoryAsync(SCRATCH_FILES_PATH);
// Verify files are in their subdirectories, not at the root
Assert.True(
File.Exists(Path.Combine(SCRATCH_FILES_PATH, "PhysicsBraid", "263825.tr11dtp")),
"File should be in PhysicsBraid subdirectory"
);
Assert.True(
File.Exists(Path.Combine(SCRATCH_FILES_PATH, "Animations", "15441.tr11anim")),
"File should be in Animations subdirectory"
);
Assert.True(
File.Exists(Path.Combine(SCRATCH_FILES_PATH, "Braid", "766728.tr11dtp")),
"File should be in Braid subdirectory"
);
Assert.True(
File.Exists(Path.Combine(SCRATCH_FILES_PATH, "Braid", "766832.tr11dtp")),
"File should be in Braid subdirectory"
);
Assert.True(
File.Exists(Path.Combine(SCRATCH_FILES_PATH, "HeadBraid", "321353.tr11modeldata")),
"File should be in HeadBraid subdirectory"
);
// NOTE: The file size check is omitted because there's a separate pre-existing bug
// in the async RAR stream implementation that causes incorrect file sizes.
// This test only verifies the directory structure fix.
}
private async ValueTask ArchiveOpenStreamReadAsync(
ReaderOptions? readerOptions,
params string[] testArchives

View File

@@ -722,6 +722,47 @@ public class RarArchiveTests : ArchiveTests
Assert.Contains("unpacked file size does not match header", exception.Message);
}
/// <summary>
/// Tests for Issue #1050 - RAR extraction with WriteToDirectory creates folders
/// but places all files at the top level instead of in their subdirectories.
/// </summary>
[Fact]
public void Rar_Issue1050_WriteToDirectory_ExtractsToSubdirectories()
{
var testFile = "Rar.issue1050.rar";
using var fileStream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, testFile));
using var archive = RarArchive.OpenArchive(fileStream);
// Extract using archive.WriteToDirectory without explicit options
archive.WriteToDirectory(SCRATCH_FILES_PATH);
// Verify files are in their subdirectories, not at the root
Assert.True(
File.Exists(Path.Combine(SCRATCH_FILES_PATH, "PhysicsBraid", "263825.tr11dtp")),
"File should be in PhysicsBraid subdirectory"
);
Assert.True(
File.Exists(Path.Combine(SCRATCH_FILES_PATH, "Animations", "15441.tr11anim")),
"File should be in Animations subdirectory"
);
Assert.True(
File.Exists(Path.Combine(SCRATCH_FILES_PATH, "Braid", "766728.tr11dtp")),
"File should be in Braid subdirectory"
);
Assert.True(
File.Exists(Path.Combine(SCRATCH_FILES_PATH, "Braid", "766832.tr11dtp")),
"File should be in Braid subdirectory"
);
Assert.True(
File.Exists(Path.Combine(SCRATCH_FILES_PATH, "HeadBraid", "321353.tr11modeldata")),
"File should be in HeadBraid subdirectory"
);
// Verify the exact file size of 766832.tr11dtp matches the archive entry size
var fileInfo = new FileInfo(Path.Combine(SCRATCH_FILES_PATH, "Braid", "766832.tr11dtp"));
Assert.Equal(4867620, fileInfo.Length); // Expected: 4,867,620 bytes
}
/// <summary>
/// Test case for malformed RAR archives that previously caused infinite loops.
/// This test verifies that attempting to read entries from a potentially malformed

Binary file not shown.