diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index ef84a272..00b1a064 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "csharpier": { - "version": "0.25.0", + "version": "0.28.1", "commands": [ "dotnet-csharpier" ] diff --git a/.editorconfig b/.editorconfig index 1903a97c..eab3d428 100644 --- a/.editorconfig +++ b/.editorconfig @@ -70,7 +70,7 @@ indent_style = tab [*.{cs,csx,cake,vb,vbx}] # Default Severity for all .NET Code Style rules below -dotnet_analyzer_diagnostic.severity = warning +dotnet_analyzer_diagnostic.severity = silent ########################################## # File Header (Uncomment to support file headers) @@ -269,6 +269,8 @@ dotnet_diagnostic.CA1305.severity = suggestion dotnet_diagnostic.CA1307.severity = suggestion dotnet_diagnostic.CA1309.severity = suggestion dotnet_diagnostic.CA1310.severity = error +dotnet_diagnostic.CA1507.severity = suggestion +dotnet_diagnostic.CA1513.severity = suggestion dotnet_diagnostic.CA1707.severity = suggestion dotnet_diagnostic.CA1708.severity = suggestion dotnet_diagnostic.CA1711.severity = suggestion @@ -286,6 +288,7 @@ dotnet_diagnostic.CA1834.severity = error dotnet_diagnostic.CA1845.severity = suggestion dotnet_diagnostic.CA1848.severity = suggestion dotnet_diagnostic.CA1852.severity = suggestion +dotnet_diagnostic.CA1860.severity = silent dotnet_diagnostic.CA2016.severity = suggestion dotnet_diagnostic.CA2201.severity = error dotnet_diagnostic.CA2206.severity = error @@ -303,13 +306,12 @@ dotnet_diagnostic.CS1998.severity = error dotnet_diagnostic.CS8602.severity = error dotnet_diagnostic.CS8604.severity = error dotnet_diagnostic.CS8618.severity = error -dotnet_diagnostic.CS0618.severity = error +dotnet_diagnostic.CS0618.severity = suggestion dotnet_diagnostic.CS1998.severity = error dotnet_diagnostic.CS4014.severity = error dotnet_diagnostic.CS8600.severity = error dotnet_diagnostic.CS8603.severity = error dotnet_diagnostic.CS8625.severity = error -dotnet_diagnostic.CS8981.severity = suggestion dotnet_diagnostic.BL0005.severity = suggestion @@ -318,7 +320,7 @@ dotnet_diagnostic.MVC1000.severity = suggestion dotnet_diagnostic.RZ10012.severity = error dotnet_diagnostic.IDE0004.severity = error # redundant cast -dotnet_diagnostic.IDE0005.severity = error +dotnet_diagnostic.IDE0005.severity = suggestion dotnet_diagnostic.IDE0007.severity = error # Use var dotnet_diagnostic.IDE0011.severity = error # Use braces on if statements dotnet_diagnostic.IDE0010.severity = silent # populate switch @@ -329,7 +331,7 @@ dotnet_diagnostic.IDE0023.severity = suggestion # use expression body for operat dotnet_diagnostic.IDE0024.severity = silent # expression body for operators dotnet_diagnostic.IDE0025.severity = suggestion # use expression body for properties dotnet_diagnostic.IDE0027.severity = suggestion # Use expression body for accessors -dotnet_diagnostic.IDE0028.severity = silent +dotnet_diagnostic.IDE0028.severity = silent # expression body for accessors dotnet_diagnostic.IDE0032.severity = suggestion # Use auto property dotnet_diagnostic.IDE0033.severity = error # prefer tuple name dotnet_diagnostic.IDE0037.severity = suggestion # simplify anonymous type @@ -337,7 +339,7 @@ dotnet_diagnostic.IDE0040.severity = error # modifiers required dotnet_diagnostic.IDE0041.severity = error # simplify null dotnet_diagnostic.IDE0042.severity = error # deconstruct variable dotnet_diagnostic.IDE0044.severity = suggestion # make field only when possible -dotnet_diagnostic.IDE0047.severity = suggestion # paratemeter name +dotnet_diagnostic.IDE0047.severity = suggestion # parameter name dotnet_diagnostic.IDE0051.severity = error # unused field dotnet_diagnostic.IDE0052.severity = error # unused member dotnet_diagnostic.IDE0053.severity = suggestion # lambda not needed @@ -351,11 +353,20 @@ dotnet_diagnostic.IDE0066.severity = suggestion # switch expression dotnet_diagnostic.IDE0072.severity = suggestion # Populate switch - forces population of all cases even when default specified dotnet_diagnostic.IDE0078.severity = suggestion # use pattern matching dotnet_diagnostic.IDE0090.severity = suggestion # new can be simplified -dotnet_diagnostic.IDE0130.severity = error # namespace folder structure +dotnet_diagnostic.IDE0130.severity = suggestion # namespace folder structure dotnet_diagnostic.IDE0160.severity = silent # Use block namespaces ARE NOT required dotnet_diagnostic.IDE0161.severity = error # Please use file namespaces dotnet_diagnostic.IDE0200.severity = suggestion # lambda not needed dotnet_diagnostic.IDE1006.severity = suggestion # Naming rule violation: These words cannot contain lower case characters +dotnet_diagnostic.IDE0260.severity = suggestion # Use pattern matching +dotnet_diagnostic.IDE0270.severity = suggestion # Null check simplifcation +dotnet_diagnostic.IDE0290.severity = error # Primary Constructor +dotnet_diagnostic.IDE0300.severity = suggestion # Collection +dotnet_diagnostic.IDE0305.severity = suggestion # Collection ToList + +dotnet_diagnostic.NX0001.severity = error +dotnet_diagnostic.NX0002.severity = silent +dotnet_diagnostic.NX0003.severity = silent ########################################## # Styles diff --git a/.github/workflows/dotnetcore.yml b/.github/workflows/dotnetcore.yml index 4486b23f..27d73e23 100644 --- a/.github/workflows/dotnetcore.yml +++ b/.github/workflows/dotnetcore.yml @@ -15,18 +15,11 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: actions/setup-dotnet@v3 + - uses: actions/setup-dotnet@v4 with: - dotnet-version: 7.0.x - - name: NuGet Caching - uses: actions/cache@v3 - with: - path: ~/.nuget/packages - key: ${{ runner.os }}-nuget-${{ hashFiles('packages.lock.json', '*/packages.lock.json') }} - restore-keys: | - ${{ runner.os }}-nuget- + dotnet-version: 8.0.x - run: dotnet run --project build/build.csproj - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: ${{ matrix.os }}-sharpcompress.nupkg path: artifacts/* diff --git a/Directory.Build.props b/Directory.Build.props index 4b343bf8..b3d0b595 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -10,5 +10,7 @@ true False False + true + true diff --git a/Directory.Packages.props b/Directory.Packages.props new file mode 100644 index 00000000..e128f52f --- /dev/null +++ b/Directory.Packages.props @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/NuGet.config b/NuGet.config new file mode 100644 index 00000000..48259210 --- /dev/null +++ b/NuGet.config @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/README.md b/README.md index 20681105..2ab41c09 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ # SharpCompress -SharpCompress is a compression library in pure C# for .NET Standard 2.0, 2.1, .NET Core 3.1 and .NET 5.0 that can unrar, un7zip, unzip, untar unbzip2, ungzip, unlzip with forward-only reading and file random access APIs. Write support for zip/tar/bzip2/gzip/lzip are implemented. +SharpCompress is a compression library in pure C# for .NET Framework 4.62, .NET Standard 2.1, .NET 6.0 and NET 8.0 that can unrar, un7zip, unzip, untar unbzip2, ungzip, unlzip with forward-only reading and file random access APIs. Write support for zip/tar/bzip2/gzip/lzip are implemented. The major feature is support for non-seekable streams so large files can be processed on the fly (i.e. download stream). GitHub Actions Build - [![SharpCompress](https://github.com/adamhathcock/sharpcompress/actions/workflows/dotnetcore.yml/badge.svg)](https://github.com/adamhathcock/sharpcompress/actions/workflows/dotnetcore.yml) -[![Static Badge](https://img.shields.io/badge/API%20Documentation-RobiniaDocs-43bc00?logo=readme&logoColor=white)](https://www.robiniadocs.com/d/sharpcompress/api/SharpCompress.html) +[![Static Badge](https://img.shields.io/badge/API%20Docs-DNDocs-190088?logo=readme&logoColor=white)](https://dndocs.com/d/sharpcompress/api/index.html) ## Need Help? @@ -36,7 +36,7 @@ I'm always looking for help or ideas. Please submit code or email with ideas. Un ## TODOs (always lots) -* RAR 5 decryption support +* RAR 5 decryption crc check support * 7Zip writing * Zip64 (Need writing and extend Reading) * Multi-volume Zip support. diff --git a/SharpCompress.sln b/SharpCompress.sln index 71ec294e..ab4995b1 100644 --- a/SharpCompress.sln +++ b/SharpCompress.sln @@ -17,6 +17,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Config", "Config", "{CDB425 ProjectSection(SolutionItems) = preProject Directory.Build.props = Directory.Build.props global.json = global.json + .editorconfig = .editorconfig + Directory.Packages.props = Directory.Packages.props + NuGet.config = NuGet.config EndProjectSection EndProject Global diff --git a/SharpCompress.sln.DotSettings b/SharpCompress.sln.DotSettings index 248b4c35..4ff50910 100644 --- a/SharpCompress.sln.DotSettings +++ b/SharpCompress.sln.DotSettings @@ -79,6 +79,10 @@ <Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /> <Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /> <Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /> + <Policy><Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static readonly fields (private)"><ElementKinds><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /></Policy> + <Policy><Descriptor Staticness="Instance" AccessRightKinds="Private" Description="Instance fields (private)"><ElementKinds><Kind Name="FIELD" /><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /></Policy> + <Policy><Descriptor Staticness="Static" AccessRightKinds="Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="Static readonly fields (not private)"><ElementKinds><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /></Policy> + <Policy><Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static fields (private)"><ElementKinds><Kind Name="FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /></Policy> <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> @@ -127,6 +131,7 @@ True True True + True <SessionState ContinuousTestingIsOn="False" ContinuousTestingMode="0" FrameworkVersion="{x:Null}" IsLocked="False" Name="All tests from Solution" PlatformMonoPreference="{x:Null}" PlatformType="{x:Null}" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <Solution /> </SessionState> diff --git a/build/Program.cs b/build/Program.cs index 9475a66b..acafec41 100644 --- a/build/Program.cs +++ b/build/Program.cs @@ -61,7 +61,7 @@ Target( Target( Test, DependsOn(Build), - ForEach("net7.0", "net462"), + ForEach("net8.0", "net462"), framework => { IEnumerable GetFiles(string d) diff --git a/build/build.csproj b/build/build.csproj index 7b478cf3..e0dffc7f 100644 --- a/build/build.csproj +++ b/build/build.csproj @@ -2,13 +2,13 @@ Exe - net7.0 + net8.0 - - - + + + diff --git a/build/packages.lock.json b/build/packages.lock.json new file mode 100644 index 00000000..8a8fa2c0 --- /dev/null +++ b/build/packages.lock.json @@ -0,0 +1,25 @@ +{ + "version": 2, + "dependencies": { + "net8.0": { + "Bullseye": { + "type": "Direct", + "requested": "[5.0.0, )", + "resolved": "5.0.0", + "contentHash": "bqyt+m17ym+5aN45C5oZRAjuLDt8jKiCm/ys1XfymIXSkrTFwvI/QsbY3ucPSHDz7SF7uON7B57kXFv5H2k1ew==" + }, + "Glob": { + "type": "Direct", + "requested": "[1.1.9, )", + "resolved": "1.1.9", + "contentHash": "AfK5+ECWYTP7G3AAdnU8IfVj+QpGjrh9GC2mpdcJzCvtQ4pnerAGwHsxJ9D4/RnhDUz2DSzd951O/lQjQby2Sw==" + }, + "SimpleExec": { + "type": "Direct", + "requested": "[12.0.0, )", + "resolved": "12.0.0", + "contentHash": "ptxlWtxC8vM6Y6e3h9ZTxBBkOWnWrm/Sa1HT+2i1xcXY3Hx2hmKDZP5RShPf8Xr9D+ivlrXNy57ktzyH8kyt+Q==" + } + } + } +} \ No newline at end of file diff --git a/global.json b/global.json index e6e67e4e..391ba3c2 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "7.0.101", + "version": "8.0.100", "rollForward": "latestFeature" } } diff --git a/src/SharpCompress/Algorithms/Adler32.cs b/src/SharpCompress/Algorithms/Adler32.cs index 1f094c13..10dbfc4d 100644 --- a/src/SharpCompress/Algorithms/Adler32.cs +++ b/src/SharpCompress/Algorithms/Adler32.cs @@ -62,7 +62,7 @@ internal static class Adler32 // From https://github.com/SixLabors/ImageSharp/bl public static int ReduceSum(Vector256 accumulator) { // Add upper lane to lower lane. - Vector128 vsum = Sse2.Add(accumulator.GetLower(), accumulator.GetUpper()); + var vsum = Sse2.Add(accumulator.GetLower(), accumulator.GetUpper()); // Add odd to even. vsum = Sse2.Add(vsum, Sse2.Shuffle(vsum, 0b_11_11_01_01)); @@ -81,7 +81,7 @@ internal static class Adler32 // From https://github.com/SixLabors/ImageSharp/bl [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int EvenReduceSum(Vector256 accumulator) { - Vector128 vsum = Sse2.Add(accumulator.GetLower(), accumulator.GetUpper()); // add upper lane to lower lane + var vsum = Sse2.Add(accumulator.GetLower(), accumulator.GetUpper()); // add upper lane to lower lane vsum = Sse2.Add(vsum, Sse2.Shuffle(vsum, 0b_11_10_11_10)); // add high to low // Vector128.ToScalar() isn't optimized pre-net5.0 https://github.com/dotnet/runtime/pull/37882 @@ -189,29 +189,29 @@ internal static class Adler32 // From https://github.com/SixLabors/ImageSharp/bl [MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)] private static unsafe uint CalculateSse(uint adler, ReadOnlySpan buffer) { - uint s1 = adler & 0xFFFF; - uint s2 = (adler >> 16) & 0xFFFF; + var s1 = adler & 0xFFFF; + var s2 = (adler >> 16) & 0xFFFF; // Process the data in blocks. - uint length = (uint)buffer.Length; - uint blocks = length / BlockSize; + var length = (uint)buffer.Length; + var blocks = length / BlockSize; length -= blocks * BlockSize; fixed (byte* bufferPtr = &MemoryMarshal.GetReference(buffer)) { fixed (byte* tapPtr = &MemoryMarshal.GetReference(Tap1Tap2)) { - byte* localBufferPtr = bufferPtr; + var localBufferPtr = bufferPtr; // _mm_setr_epi8 on x86 - Vector128 tap1 = Sse2.LoadVector128((sbyte*)tapPtr); - Vector128 tap2 = Sse2.LoadVector128((sbyte*)(tapPtr + 0x10)); - Vector128 zero = Vector128.Zero; + var tap1 = Sse2.LoadVector128((sbyte*)tapPtr); + var tap2 = Sse2.LoadVector128((sbyte*)(tapPtr + 0x10)); + var zero = Vector128.Zero; var ones = Vector128.Create((short)1); while (blocks > 0) { - uint n = NMAX / BlockSize; /* The NMAX constraint. */ + var n = NMAX / BlockSize; /* The NMAX constraint. */ if (n > blocks) { n = blocks; @@ -221,15 +221,15 @@ internal static class Adler32 // From https://github.com/SixLabors/ImageSharp/bl // Process n blocks of data. At most NMAX data bytes can be // processed before s2 must be reduced modulo BASE. - Vector128 v_ps = Vector128.CreateScalar(s1 * n); - Vector128 v_s2 = Vector128.CreateScalar(s2); - Vector128 v_s1 = Vector128.Zero; + var v_ps = Vector128.CreateScalar(s1 * n); + var v_s2 = Vector128.CreateScalar(s2); + var v_s1 = Vector128.Zero; do { // Load 32 input bytes. - Vector128 bytes1 = Sse3.LoadDquVector128(localBufferPtr); - Vector128 bytes2 = Sse3.LoadDquVector128(localBufferPtr + 0x10); + var bytes1 = Sse3.LoadDquVector128(localBufferPtr); + var bytes2 = Sse3.LoadDquVector128(localBufferPtr + 0x10); // Add previous block byte sum to v_ps. v_ps = Sse2.Add(v_ps, v_s1); @@ -237,11 +237,11 @@ internal static class Adler32 // From https://github.com/SixLabors/ImageSharp/bl // Horizontally add the bytes for s1, multiply-adds the // bytes by [ 32, 31, 30, ... ] for s2. v_s1 = Sse2.Add(v_s1, Sse2.SumAbsoluteDifferences(bytes1, zero).AsUInt32()); - Vector128 mad1 = Ssse3.MultiplyAddAdjacent(bytes1, tap1); + var mad1 = Ssse3.MultiplyAddAdjacent(bytes1, tap1); v_s2 = Sse2.Add(v_s2, Sse2.MultiplyAddAdjacent(mad1, ones).AsUInt32()); v_s1 = Sse2.Add(v_s1, Sse2.SumAbsoluteDifferences(bytes2, zero).AsUInt32()); - Vector128 mad2 = Ssse3.MultiplyAddAdjacent(bytes2, tap2); + var mad2 = Ssse3.MultiplyAddAdjacent(bytes2, tap2); v_s2 = Sse2.Add(v_s2, Sse2.MultiplyAddAdjacent(mad2, ones).AsUInt32()); localBufferPtr += BlockSize; @@ -281,15 +281,15 @@ internal static class Adler32 // From https://github.com/SixLabors/ImageSharp/bl [MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)] public static unsafe uint CalculateAvx2(uint adler, ReadOnlySpan buffer) { - uint s1 = adler & 0xFFFF; - uint s2 = (adler >> 16) & 0xFFFF; - uint length = (uint)buffer.Length; + var s1 = adler & 0xFFFF; + var s2 = (adler >> 16) & 0xFFFF; + var length = (uint)buffer.Length; fixed (byte* bufferPtr = &MemoryMarshal.GetReference(buffer)) { - byte* localBufferPtr = bufferPtr; + var localBufferPtr = bufferPtr; - Vector256 zero = Vector256.Zero; + var zero = Vector256.Zero; var dot3v = Vector256.Create((short)1); var dot2v = Vector256.Create( 32, @@ -333,29 +333,29 @@ internal static class Adler32 // From https://github.com/SixLabors/ImageSharp/bl while (length >= 32) { - int k = length < NMAX ? (int)length : (int)NMAX; + var k = length < NMAX ? (int)length : (int)NMAX; k -= k % 32; length -= (uint)k; - Vector256 vs10 = vs1; - Vector256 vs3 = Vector256.Zero; + var vs10 = vs1; + var vs3 = Vector256.Zero; while (k >= 32) { // Load 32 input bytes. - Vector256 block = Avx.LoadVector256(localBufferPtr); + var block = Avx.LoadVector256(localBufferPtr); // Sum of abs diff, resulting in 2 x int32's - Vector256 vs1sad = Avx2.SumAbsoluteDifferences(block, zero); + var vs1sad = Avx2.SumAbsoluteDifferences(block, zero); vs1 = Avx2.Add(vs1, vs1sad.AsUInt32()); vs3 = Avx2.Add(vs3, vs10); // sum 32 uint8s to 16 shorts. - Vector256 vshortsum2 = Avx2.MultiplyAddAdjacent(block, dot2v); + var vshortsum2 = Avx2.MultiplyAddAdjacent(block, dot2v); // sum 16 shorts to 8 uint32s. - Vector256 vsum2 = Avx2.MultiplyAddAdjacent(vshortsum2, dot3v); + var vsum2 = Avx2.MultiplyAddAdjacent(vshortsum2, dot3v); vs2 = Avx2.Add(vsum2.AsUInt32(), vs2); vs10 = vs1; @@ -434,14 +434,14 @@ internal static class Adler32 // From https://github.com/SixLabors/ImageSharp/bl [MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)] private static unsafe uint CalculateScalar(uint adler, ReadOnlySpan buffer) { - uint s1 = adler & 0xFFFF; - uint s2 = (adler >> 16) & 0xFFFF; + var s1 = adler & 0xFFFF; + var s2 = (adler >> 16) & 0xFFFF; uint k; fixed (byte* bufferPtr = buffer) { var localBufferPtr = bufferPtr; - uint length = (uint)buffer.Length; + var length = (uint)buffer.Length; while (length > 0) { diff --git a/src/SharpCompress/Archives/AbstractArchive.cs b/src/SharpCompress/Archives/AbstractArchive.cs index cfac55f2..df54d781 100644 --- a/src/SharpCompress/Archives/AbstractArchive.cs +++ b/src/SharpCompress/Archives/AbstractArchive.cs @@ -12,39 +12,35 @@ public abstract class AbstractArchive : IArchive, IArchiveExtra where TEntry : IArchiveEntry where TVolume : IVolume { - private readonly LazyReadOnlyCollection lazyVolumes; - private readonly LazyReadOnlyCollection lazyEntries; + private readonly LazyReadOnlyCollection _lazyVolumes; + private readonly LazyReadOnlyCollection _lazyEntries; + private bool _disposed; + private readonly SourceStream? _sourceStream; public event EventHandler>? EntryExtractionBegin; public event EventHandler>? EntryExtractionEnd; public event EventHandler? CompressedBytesRead; public event EventHandler? FilePartExtractionBegin; - protected ReaderOptions ReaderOptions { get; } - private bool disposed; - protected SourceStream SrcStream; - - internal AbstractArchive(ArchiveType type, SourceStream srcStream) + internal AbstractArchive(ArchiveType type, SourceStream sourceStream) { Type = type; - ReaderOptions = srcStream.ReaderOptions; - SrcStream = srcStream; - lazyVolumes = new LazyReadOnlyCollection(LoadVolumes(SrcStream)); - lazyEntries = new LazyReadOnlyCollection(LoadEntries(Volumes)); + ReaderOptions = sourceStream.ReaderOptions; + _sourceStream = sourceStream; + _lazyVolumes = new LazyReadOnlyCollection(LoadVolumes(_sourceStream)); + _lazyEntries = new LazyReadOnlyCollection(LoadEntries(Volumes)); } -#nullable disable internal AbstractArchive(ArchiveType type) { Type = type; - lazyVolumes = new LazyReadOnlyCollection(Enumerable.Empty()); - lazyEntries = new LazyReadOnlyCollection(Enumerable.Empty()); + ReaderOptions = new(); + _lazyVolumes = new LazyReadOnlyCollection(Enumerable.Empty()); + _lazyEntries = new LazyReadOnlyCollection(Enumerable.Empty()); } -#nullable enable - public ArchiveType Type { get; } void IArchiveExtractionListener.FireEntryExtractionBegin(IArchiveEntry entry) => @@ -65,12 +61,12 @@ public abstract class AbstractArchive : IArchive, IArchiveExtra /// /// Returns an ReadOnlyCollection of all the RarArchiveEntries across the one or many parts of the RarArchive. /// - public virtual ICollection Entries => lazyEntries; + public virtual ICollection Entries => _lazyEntries; /// /// Returns an ReadOnlyCollection of all the RarArchiveVolumes across the one or many parts of the RarArchive. /// - public ICollection Volumes => lazyVolumes; + public ICollection Volumes => _lazyVolumes; /// /// The total size of the files compressed in the archive. @@ -84,29 +80,29 @@ public abstract class AbstractArchive : IArchive, IArchiveExtra public virtual long TotalUncompressSize => Entries.Aggregate(0L, (total, cf) => total + cf.Size); - protected abstract IEnumerable LoadVolumes(SourceStream srcStream); + protected abstract IEnumerable LoadVolumes(SourceStream sourceStream); protected abstract IEnumerable LoadEntries(IEnumerable volumes); IEnumerable IArchive.Entries => Entries.Cast(); - IEnumerable IArchive.Volumes => lazyVolumes.Cast(); + IEnumerable IArchive.Volumes => _lazyVolumes.Cast(); public virtual void Dispose() { - if (!disposed) + if (!_disposed) { - lazyVolumes.ForEach(v => v.Dispose()); - lazyEntries.GetLoaded().Cast().ForEach(x => x.Close()); - SrcStream?.Dispose(); + _lazyVolumes.ForEach(v => v.Dispose()); + _lazyEntries.GetLoaded().Cast().ForEach(x => x.Close()); + _sourceStream?.Dispose(); - disposed = true; + _disposed = true; } } void IArchiveExtractionListener.EnsureEntriesLoaded() { - lazyEntries.EnsureFullyLoaded(); - lazyVolumes.EnsureFullyLoaded(); + _lazyEntries.EnsureFullyLoaded(); + _lazyVolumes.EnsureFullyLoaded(); } void IExtractionListener.FireCompressedBytesRead( diff --git a/src/SharpCompress/Archives/AbstractWritableArchive.cs b/src/SharpCompress/Archives/AbstractWritableArchive.cs index 30083ec7..614489fe 100644 --- a/src/SharpCompress/Archives/AbstractWritableArchive.cs +++ b/src/SharpCompress/Archives/AbstractWritableArchive.cs @@ -31,18 +31,18 @@ public abstract class AbstractWritableArchive } } - private readonly List newEntries = new List(); - private readonly List removedEntries = new List(); + private readonly List newEntries = new(); + private readonly List removedEntries = new(); - private readonly List modifiedEntries = new List(); + private readonly List modifiedEntries = new(); private bool hasModifications; private bool pauseRebuilding; internal AbstractWritableArchive(ArchiveType type) : base(type) { } - internal AbstractWritableArchive(ArchiveType type, SourceStream srcStream) - : base(type, srcStream) { } + internal AbstractWritableArchive(ArchiveType type, SourceStream sourceStream) + : base(type, sourceStream) { } public override ICollection Entries { @@ -120,6 +120,10 @@ public abstract class AbstractWritableArchive { foreach (var path in Entries.Select(x => x.Key)) { + if (path is null) + { + continue; + } var p = path.Replace('/', '\\'); if (p.Length > 0 && p[0] == '\\') { diff --git a/src/SharpCompress/Archives/ArchiveFactory.cs b/src/SharpCompress/Archives/ArchiveFactory.cs index cd0952f1..3f479327 100644 --- a/src/SharpCompress/Archives/ArchiveFactory.cs +++ b/src/SharpCompress/Archives/ArchiveFactory.cs @@ -25,8 +25,8 @@ public static class ArchiveFactory public static IWritableArchive Create(ArchiveType type) { - var factory = Factory.Factories - .OfType() + var factory = Factory + .Factories.OfType() .FirstOrDefault(item => item.KnownArchiveType == type); if (factory != null) diff --git a/src/SharpCompress/Archives/ArchiveVolumeFactory.cs b/src/SharpCompress/Archives/ArchiveVolumeFactory.cs index d8f5535f..81a5d4fd 100644 --- a/src/SharpCompress/Archives/ArchiveVolumeFactory.cs +++ b/src/SharpCompress/Archives/ArchiveVolumeFactory.cs @@ -11,7 +11,7 @@ internal abstract class ArchiveVolumeFactory FileInfo? item = null; //split 001, 002 ... - Match m = Regex.Match(part1.Name, @"^(.*\.)([0-9]+)$", RegexOptions.IgnoreCase); + var m = Regex.Match(part1.Name, @"^(.*\.)([0-9]+)$", RegexOptions.IgnoreCase); if (m.Success) item = new FileInfo( Path.Combine( diff --git a/src/SharpCompress/Archives/GZip/GZipArchive.cs b/src/SharpCompress/Archives/GZip/GZipArchive.cs index fb2f7691..ea46e8d0 100644 --- a/src/SharpCompress/Archives/GZip/GZipArchive.cs +++ b/src/SharpCompress/Archives/GZip/GZipArchive.cs @@ -90,25 +90,23 @@ public class GZipArchive : AbstractWritableArchive { stream.CheckNotNull(nameof(stream)); return new GZipArchive( - new SourceStream(stream, i => null, readerOptions ?? new ReaderOptions()) + new SourceStream(stream, _ => null, readerOptions ?? new ReaderOptions()) ); } - public static GZipArchive Create() => new GZipArchive(); + public static GZipArchive Create() => new(); /// /// Constructor with a SourceStream able to handle FileInfo and Streams. /// - /// - /// - internal GZipArchive(SourceStream srcStream) - : base(ArchiveType.Tar, srcStream) { } + /// + private GZipArchive(SourceStream sourceStream) + : base(ArchiveType.GZip, sourceStream) { } - protected override IEnumerable LoadVolumes(SourceStream srcStream) + protected override IEnumerable LoadVolumes(SourceStream sourceStream) { - srcStream.LoadAllParts(); - var idx = 0; - return srcStream.Streams.Select(a => new GZipVolume(a, ReaderOptions, idx++)); + sourceStream.LoadAllParts(); + return sourceStream.Streams.Select(a => new GZipVolume(a, ReaderOptions, 0)); } public static bool IsGZipFile(string filePath) => IsGZipFile(new FileInfo(filePath)); @@ -184,7 +182,11 @@ public class GZipArchive : AbstractWritableArchive foreach (var entry in oldEntries.Concat(newEntries).Where(x => !x.IsDirectory)) { using var entryStream = entry.OpenEntryStream(); - writer.Write(entry.Key, entryStream, entry.LastModifiedTime); + writer.Write( + entry.Key.NotNull("Entry Key is null"), + entryStream, + entry.LastModifiedTime + ); } } diff --git a/src/SharpCompress/Archives/GZip/GZipArchiveEntry.cs b/src/SharpCompress/Archives/GZip/GZipArchiveEntry.cs index be872e80..459d042d 100644 --- a/src/SharpCompress/Archives/GZip/GZipArchiveEntry.cs +++ b/src/SharpCompress/Archives/GZip/GZipArchiveEntry.cs @@ -6,7 +6,7 @@ namespace SharpCompress.Archives.GZip; public class GZipArchiveEntry : GZipEntry, IArchiveEntry { - internal GZipArchiveEntry(GZipArchive archive, GZipFilePart part) + internal GZipArchiveEntry(GZipArchive archive, GZipFilePart? part) : base(part) => Archive = archive; public virtual Stream OpenEntryStream() diff --git a/src/SharpCompress/Archives/GZip/GZipWritableArchiveEntry.cs b/src/SharpCompress/Archives/GZip/GZipWritableArchiveEntry.cs index 27dfc2bf..90f6f16f 100644 --- a/src/SharpCompress/Archives/GZip/GZipWritableArchiveEntry.cs +++ b/src/SharpCompress/Archives/GZip/GZipWritableArchiveEntry.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using System.Collections.Generic; using System.IO; @@ -32,7 +30,7 @@ internal sealed class GZipWritableArchiveEntry : GZipArchiveEntry, IWritableArch public override long Crc => 0; - public override string Key { get; } + public override string? Key { get; } public override long CompressedSize => 0; diff --git a/src/SharpCompress/Archives/IArchiveEntryExtensions.cs b/src/SharpCompress/Archives/IArchiveEntryExtensions.cs index 0992f152..3d1daa1a 100644 --- a/src/SharpCompress/Archives/IArchiveEntryExtensions.cs +++ b/src/SharpCompress/Archives/IArchiveEntryExtensions.cs @@ -17,15 +17,11 @@ public static class IArchiveEntryExtensions streamListener.EnsureEntriesLoaded(); streamListener.FireEntryExtractionBegin(archiveEntry); streamListener.FireFilePartExtractionBegin( - archiveEntry.Key, + archiveEntry.Key ?? "Key", archiveEntry.Size, archiveEntry.CompressedSize ); var entryStream = archiveEntry.OpenEntryStream(); - if (entryStream is null) - { - return; - } using (entryStream) { using Stream s = new ListeningStream(streamListener, entryStream); diff --git a/src/SharpCompress/Archives/IArchiveExtensions.cs b/src/SharpCompress/Archives/IArchiveExtensions.cs index 56ea0d9f..382c3ddf 100644 --- a/src/SharpCompress/Archives/IArchiveExtensions.cs +++ b/src/SharpCompress/Archives/IArchiveExtensions.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; -using System.Threading.Tasks; using SharpCompress.Common; namespace SharpCompress.Archives; @@ -59,7 +58,7 @@ public static class IArchiveExtensions } // Create each directory - var path = Path.Combine(destination, entry.Key); + var path = Path.Combine(destination, entry.Key.NotNull("Entry Key is null")); if (Path.GetDirectoryName(path) is { } directory && seenDirectories.Add(path)) { Directory.CreateDirectory(directory); diff --git a/src/SharpCompress/Archives/Rar/FileInfoRarArchiveVolume.cs b/src/SharpCompress/Archives/Rar/FileInfoRarArchiveVolume.cs index 3e576db3..6d9b44fd 100644 --- a/src/SharpCompress/Archives/Rar/FileInfoRarArchiveVolume.cs +++ b/src/SharpCompress/Archives/Rar/FileInfoRarArchiveVolume.cs @@ -13,7 +13,7 @@ namespace SharpCompress.Archives.Rar; /// internal class FileInfoRarArchiveVolume : RarVolume { - internal FileInfoRarArchiveVolume(FileInfo fileInfo, ReaderOptions options, int index = 0) + internal FileInfoRarArchiveVolume(FileInfo fileInfo, ReaderOptions options, int index) : base(StreamingMode.Seekable, fileInfo.OpenRead(), FixOptions(options), index) { FileInfo = fileInfo; diff --git a/src/SharpCompress/Archives/Rar/RarArchive.cs b/src/SharpCompress/Archives/Rar/RarArchive.cs index 29d733f6..a05ad417 100644 --- a/src/SharpCompress/Archives/Rar/RarArchive.cs +++ b/src/SharpCompress/Archives/Rar/RarArchive.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -14,40 +15,39 @@ namespace SharpCompress.Archives.Rar; public class RarArchive : AbstractArchive { internal Lazy UnpackV2017 { get; } = - new Lazy(() => new Compressors.Rar.UnpackV2017.Unpack()); - internal Lazy UnpackV1 { get; } = - new Lazy(() => new Compressors.Rar.UnpackV1.Unpack()); + new(() => new Compressors.Rar.UnpackV2017.Unpack()); + internal Lazy UnpackV1 { get; } = new(() => new Compressors.Rar.UnpackV1.Unpack()); /// /// Constructor with a SourceStream able to handle FileInfo and Streams. /// - /// - /// - internal RarArchive(SourceStream srcStream) - : base(ArchiveType.Rar, srcStream) { } + /// + private RarArchive(SourceStream sourceStream) + : base(ArchiveType.Rar, sourceStream) { } protected override IEnumerable LoadEntries(IEnumerable volumes) => RarArchiveEntryFactory.GetEntries(this, volumes, ReaderOptions); - protected override IEnumerable LoadVolumes(SourceStream srcStream) + protected override IEnumerable LoadVolumes(SourceStream sourceStream) { - SrcStream.LoadAllParts(); //request all streams - var streams = SrcStream.Streams.ToArray(); - var idx = 0; + sourceStream.LoadAllParts(); //request all streams + var streams = sourceStream.Streams.ToArray(); + var i = 0; if (streams.Length > 1 && IsRarFile(streams[1], ReaderOptions)) //test part 2 - true = multipart not split { - SrcStream.IsVolumes = true; + sourceStream.IsVolumes = true; streams[1].Position = 0; - SrcStream.Position = 0; + sourceStream.Position = 0; - return srcStream.Streams.Select( - a => new StreamRarArchiveVolume(a, ReaderOptions, idx++) - ); - } - else //split mode or single file - { - return new StreamRarArchiveVolume(SrcStream, ReaderOptions, idx++).AsEnumerable(); + return sourceStream.Streams.Select(a => new StreamRarArchiveVolume( + a, + ReaderOptions, + i++ + )); } + + //split mode or single file + return new StreamRarArchiveVolume(sourceStream, ReaderOptions, i++).AsEnumerable(); } protected override IReader CreateReaderForSolidExtraction() @@ -106,7 +106,7 @@ public class RarArchive : AbstractArchive public static RarArchive Open(Stream stream, ReaderOptions? options = null) { stream.CheckNotNull(nameof(stream)); - return new RarArchive(new SourceStream(stream, i => null, options ?? new ReaderOptions())); + return new RarArchive(new SourceStream(stream, _ => null, options ?? new ReaderOptions())); } /// diff --git a/src/SharpCompress/Archives/Rar/RarArchiveEntry.cs b/src/SharpCompress/Archives/Rar/RarArchiveEntry.cs index 5885210a..fa59b295 100644 --- a/src/SharpCompress/Archives/Rar/RarArchiveEntry.cs +++ b/src/SharpCompress/Archives/Rar/RarArchiveEntry.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -40,7 +41,10 @@ public class RarArchiveEntry : RarEntry, IArchiveEntry get { CheckIncomplete(); - return parts.Select(fp => fp.FileHeader).Single(fh => !fh.IsSplitAfter).FileCrc; + return BitConverter.ToUInt32( + parts.Select(fp => fp.FileHeader).Single(fh => !fh.IsSplitAfter).FileCrc, + 0 + ); } } diff --git a/src/SharpCompress/Archives/Rar/RarArchiveVolumeFactory.cs b/src/SharpCompress/Archives/Rar/RarArchiveVolumeFactory.cs index 72ccf60f..f5ce89b8 100644 --- a/src/SharpCompress/Archives/Rar/RarArchiveVolumeFactory.cs +++ b/src/SharpCompress/Archives/Rar/RarArchiveVolumeFactory.cs @@ -11,7 +11,7 @@ internal static class RarArchiveVolumeFactory FileInfo? item = null; //new style rar - ..part1 | /part01 | part001 .... - Match m = Regex.Match(part1.Name, @"^(.*\.part)([0-9]+)(\.rar)$", RegexOptions.IgnoreCase); + var m = Regex.Match(part1.Name, @"^(.*\.part)([0-9]+)(\.rar)$", RegexOptions.IgnoreCase); if (m.Success) item = new FileInfo( Path.Combine( diff --git a/src/SharpCompress/Archives/Rar/SeekableFilePart.cs b/src/SharpCompress/Archives/Rar/SeekableFilePart.cs index 08aceb13..97822d90 100644 --- a/src/SharpCompress/Archives/Rar/SeekableFilePart.cs +++ b/src/SharpCompress/Archives/Rar/SeekableFilePart.cs @@ -6,8 +6,8 @@ namespace SharpCompress.Archives.Rar; internal class SeekableFilePart : RarFilePart { - private readonly Stream stream; - private readonly string? password; + private readonly Stream _stream; + private readonly string? _password; internal SeekableFilePart( MarkHeader mh, @@ -18,18 +18,27 @@ internal class SeekableFilePart : RarFilePart ) : base(mh, fh, index) { - this.stream = stream; - this.password = password; + _stream = stream; + _password = password; } internal override Stream GetCompressedStream() { - stream.Position = FileHeader.DataStartPosition; + _stream.Position = FileHeader.DataStartPosition; + if (FileHeader.R4Salt != null) { - return new RarCryptoWrapper(stream, password!, FileHeader.R4Salt); + var cryptKey = new CryptKey3(_password!); + return new RarCryptoWrapper(_stream, FileHeader.R4Salt, cryptKey); } - return stream; + + if (FileHeader.Rar5CryptoInfo != null) + { + var cryptKey = new CryptKey5(_password!, FileHeader.Rar5CryptoInfo); + return new RarCryptoWrapper(_stream, FileHeader.Rar5CryptoInfo.Salt, cryptKey); + } + + return _stream; } internal override string FilePartName => "Unknown Stream - File Entry: " + FileHeader.FileName; diff --git a/src/SharpCompress/Archives/Rar/StreamRarArchiveVolume.cs b/src/SharpCompress/Archives/Rar/StreamRarArchiveVolume.cs index 3eb5095c..60cbab9a 100644 --- a/src/SharpCompress/Archives/Rar/StreamRarArchiveVolume.cs +++ b/src/SharpCompress/Archives/Rar/StreamRarArchiveVolume.cs @@ -9,7 +9,7 @@ namespace SharpCompress.Archives.Rar; internal class StreamRarArchiveVolume : RarVolume { - internal StreamRarArchiveVolume(Stream stream, ReaderOptions options, int index = 0) + internal StreamRarArchiveVolume(Stream stream, ReaderOptions options, int index) : base(StreamingMode.Seekable, stream, options, index) { } internal override IEnumerable ReadFileParts() => GetVolumeFileParts(); diff --git a/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs b/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs index 7577b43d..ee165982 100644 --- a/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs +++ b/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using System.Collections.Generic; using System.IO; @@ -14,14 +12,14 @@ namespace SharpCompress.Archives.SevenZip; public class SevenZipArchive : AbstractArchive { - private ArchiveDatabase database; + private ArchiveDatabase? _database; /// /// Constructor expects a filepath to an existing file. /// /// /// - public static SevenZipArchive Open(string filePath, ReaderOptions readerOptions = null) + public static SevenZipArchive Open(string filePath, ReaderOptions? readerOptions = null) { filePath.CheckNotNullOrEmpty("filePath"); return Open(new FileInfo(filePath), readerOptions ?? new ReaderOptions()); @@ -32,7 +30,7 @@ public class SevenZipArchive : AbstractArchive /// /// - public static SevenZipArchive Open(FileInfo fileInfo, ReaderOptions readerOptions = null) + public static SevenZipArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) { fileInfo.CheckNotNull("fileInfo"); return new SevenZipArchive( @@ -51,7 +49,7 @@ public class SevenZipArchive : AbstractArchive public static SevenZipArchive Open( IEnumerable fileInfos, - ReaderOptions readerOptions = null + ReaderOptions? readerOptions = null ) { fileInfos.CheckNotNull(nameof(fileInfos)); @@ -72,7 +70,7 @@ public class SevenZipArchive : AbstractArchive public static SevenZipArchive Open( IEnumerable streams, - ReaderOptions readerOptions = null + ReaderOptions? readerOptions = null ) { streams.CheckNotNull(nameof(streams)); @@ -91,27 +89,25 @@ public class SevenZipArchive : AbstractArchive /// /// - public static SevenZipArchive Open(Stream stream, ReaderOptions readerOptions = null) + public static SevenZipArchive Open(Stream stream, ReaderOptions? readerOptions = null) { stream.CheckNotNull("stream"); return new SevenZipArchive( - new SourceStream(stream, i => null, readerOptions ?? new ReaderOptions()) + new SourceStream(stream, _ => null, readerOptions ?? new ReaderOptions()) ); } /// /// Constructor with a SourceStream able to handle FileInfo and Streams. /// - /// - /// - internal SevenZipArchive(SourceStream srcStream) - : base(ArchiveType.SevenZip, srcStream) { } + /// + private SevenZipArchive(SourceStream sourceStream) + : base(ArchiveType.SevenZip, sourceStream) { } - protected override IEnumerable LoadVolumes(SourceStream srcStream) + protected override IEnumerable LoadVolumes(SourceStream sourceStream) { - SrcStream.LoadAllParts(); //request all streams - var idx = 0; - return new SevenZipVolume(srcStream, ReaderOptions, idx++).AsEnumerable(); //simple single volume or split, multivolume not supported + sourceStream.NotNull("SourceStream is null").LoadAllParts(); //request all streams + return new SevenZipVolume(sourceStream, ReaderOptions, 0).AsEnumerable(); //simple single volume or split, multivolume not supported } public static bool IsSevenZipFile(string filePath) => IsSevenZipFile(new FileInfo(filePath)); @@ -135,13 +131,17 @@ public class SevenZipArchive : AbstractArchive(); + } + var entries = new SevenZipArchiveEntry[_database._files.Count]; + for (var i = 0; i < _database._files.Count; i++) + { + var file = _database._files[i]; entries[i] = new SevenZipArchiveEntry( this, - new SevenZipFilePart(stream, database, i, file, ReaderOptions.ArchiveEncoding) + new SevenZipFilePart(stream, _database, i, file, ReaderOptions.ArchiveEncoding) ); } foreach (var group in entries.Where(x => !x.IsDirectory).GroupBy(x => x.FilePart.Folder)) @@ -159,12 +159,12 @@ public class SevenZipArchive : AbstractArchive SIGNATURE => + private static ReadOnlySpan Signature => new byte[] { (byte)'7', (byte)'z', 0xBC, 0xAF, 0x27, 0x1C }; private static bool SignatureMatch(Stream stream) { var reader = new BinaryReader(stream); ReadOnlySpan signatureBytes = reader.ReadBytes(6); - return signatureBytes.SequenceEqual(SIGNATURE); + return signatureBytes.SequenceEqual(Signature); } protected override IReader CreateReaderForSolidExtraction() => @@ -196,30 +196,24 @@ public class SevenZipArchive : AbstractArchive Entries.Where(x => !x.IsDirectory).GroupBy(x => x.FilePart.Folder).Count() > 1; - public override long TotalSize - { - get - { - var i = Entries.Count; - return database._packSizes.Aggregate(0L, (total, packSize) => total + packSize); - } - } + public override long TotalSize => + _database?._packSizes.Aggregate(0L, (total, packSize) => total + packSize) ?? 0; private sealed class SevenZipReader : AbstractReader { - private readonly SevenZipArchive archive; - private CFolder currentFolder; - private Stream currentStream; - private CFileItem currentItem; + private readonly SevenZipArchive _archive; + private CFolder? _currentFolder; + private Stream? _currentStream; + private CFileItem? _currentItem; internal SevenZipReader(ReaderOptions readerOptions, SevenZipArchive archive) - : base(readerOptions, ArchiveType.SevenZip) => this.archive = archive; + : base(readerOptions, ArchiveType.SevenZip) => this._archive = archive; - public override SevenZipVolume Volume => archive.Volumes.Single(); + public override SevenZipVolume Volume => _archive.Volumes.Single(); protected override IEnumerable GetEntries(Stream stream) { - var entries = archive.Entries.ToList(); + var entries = _archive.Entries.ToList(); stream.Position = 0; foreach (var dir in entries.Where(x => x.IsDirectory)) { @@ -229,37 +223,42 @@ public class SevenZipArchive : AbstractArchive !x.IsDirectory).GroupBy(x => x.FilePart.Folder) ) { - currentFolder = group.Key; + _currentFolder = group.Key; if (group.Key is null) { - currentStream = Stream.Null; + _currentStream = Stream.Null; } else { - currentStream = archive.database.GetFolderStream( + _currentStream = _archive._database?.GetFolderStream( stream, - currentFolder, + _currentFolder, new PasswordProvider(Options.Password) ); } foreach (var entry in group) { - currentItem = entry.FilePart.Header; + _currentItem = entry.FilePart.Header; yield return entry; } } } protected override EntryStream GetEntryStream() => - CreateEntryStream(new ReadOnlySubStream(currentStream, currentItem.Size)); + CreateEntryStream( + new ReadOnlySubStream( + _currentStream.NotNull("currentStream is not null"), + _currentItem?.Size ?? 0 + ) + ); } private class PasswordProvider : IPasswordProvider { - private readonly string _password; + private readonly string? _password; - public PasswordProvider(string password) => _password = password; + public PasswordProvider(string? password) => _password = password; - public string CryptoGetTextPassword() => _password; + public string? CryptoGetTextPassword() => _password; } } diff --git a/src/SharpCompress/Archives/Tar/TarArchive.cs b/src/SharpCompress/Archives/Tar/TarArchive.cs index 43184b66..fdf8cd42 100644 --- a/src/SharpCompress/Archives/Tar/TarArchive.cs +++ b/src/SharpCompress/Archives/Tar/TarArchive.cs @@ -114,7 +114,7 @@ public class TarArchive : AbstractWritableArchive var tarHeader = new TarHeader(new ArchiveEncoding()); var readSucceeded = tarHeader.Read(new BinaryReader(stream)); var isEmptyArchive = - tarHeader.Name.Length == 0 + tarHeader.Name?.Length == 0 && tarHeader.Size == 0 && Enum.IsDefined(typeof(EntryType), tarHeader.EntryType); return readSucceeded || isEmptyArchive; @@ -123,22 +123,20 @@ public class TarArchive : AbstractWritableArchive return false; } - protected override IEnumerable LoadVolumes(SourceStream srcStream) + protected override IEnumerable LoadVolumes(SourceStream sourceStream) { - SrcStream.LoadAllParts(); //request all streams - var idx = 0; - return new TarVolume(srcStream, ReaderOptions, idx++).AsEnumerable(); //simple single volume or split, multivolume not supported + sourceStream.NotNull("SourceStream is null").LoadAllParts(); //request all streams + return new TarVolume(sourceStream, ReaderOptions, 1).AsEnumerable(); //simple single volume or split, multivolume not supported } /// /// Constructor with a SourceStream able to handle FileInfo and Streams. /// - /// - /// - internal TarArchive(SourceStream srcStream) - : base(ArchiveType.Tar, srcStream) { } + /// + private TarArchive(SourceStream sourceStream) + : base(ArchiveType.Tar, sourceStream) { } - internal TarArchive() + private TarArchive() : base(ArchiveType.Tar) { } protected override IEnumerable LoadEntries(IEnumerable volumes) @@ -192,10 +190,14 @@ public class TarArchive : AbstractWritableArchive ); } } + else + { + throw new IncompleteArchiveException("Failed to read TAR header"); + } } } - public static TarArchive Create() => new TarArchive(); + public static TarArchive Create() => new(); protected override TarArchiveEntry CreateEntryInternal( string filePath, @@ -225,7 +227,12 @@ public class TarArchive : AbstractWritableArchive foreach (var entry in oldEntries.Concat(newEntries).Where(x => !x.IsDirectory)) { using var entryStream = entry.OpenEntryStream(); - writer.Write(entry.Key, entryStream, entry.LastModifiedTime, entry.Size); + writer.Write( + entry.Key.NotNull("Entry Key is null"), + entryStream, + entry.LastModifiedTime, + entry.Size + ); } } diff --git a/src/SharpCompress/Archives/Tar/TarArchiveEntry.cs b/src/SharpCompress/Archives/Tar/TarArchiveEntry.cs index 2da84d71..d04c4ef8 100644 --- a/src/SharpCompress/Archives/Tar/TarArchiveEntry.cs +++ b/src/SharpCompress/Archives/Tar/TarArchiveEntry.cs @@ -7,7 +7,7 @@ namespace SharpCompress.Archives.Tar; public class TarArchiveEntry : TarEntry, IArchiveEntry { - internal TarArchiveEntry(TarArchive archive, TarFilePart part, CompressionType compressionType) + internal TarArchiveEntry(TarArchive archive, TarFilePart? part, CompressionType compressionType) : base(part, compressionType) => Archive = archive; public virtual Stream OpenEntryStream() => Parts.Single().GetCompressedStream(); diff --git a/src/SharpCompress/Archives/Tar/TarWritableArchiveEntry.cs b/src/SharpCompress/Archives/Tar/TarWritableArchiveEntry.cs index 0bbea709..32be7d9b 100644 --- a/src/SharpCompress/Archives/Tar/TarWritableArchiveEntry.cs +++ b/src/SharpCompress/Archives/Tar/TarWritableArchiveEntry.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using System.Collections.Generic; using System.IO; diff --git a/src/SharpCompress/Archives/Zip/ZipArchive.cs b/src/SharpCompress/Archives/Zip/ZipArchive.cs index 14e4a93e..85130f70 100644 --- a/src/SharpCompress/Archives/Zip/ZipArchive.cs +++ b/src/SharpCompress/Archives/Zip/ZipArchive.cs @@ -16,10 +16,7 @@ namespace SharpCompress.Archives.Zip; public class ZipArchive : AbstractWritableArchive { -#nullable disable - private readonly SeekableZipHeaderFactory headerFactory; - -#nullable enable + private readonly SeekableZipHeaderFactory? headerFactory; /// /// Gets or sets the compression level applied to files added to the archive, @@ -30,13 +27,13 @@ public class ZipArchive : AbstractWritableArchive /// /// Constructor with a SourceStream able to handle FileInfo and Streams. /// - /// + /// /// - internal ZipArchive(SourceStream srcStream) - : base(ArchiveType.Zip, srcStream) => + internal ZipArchive(SourceStream sourceStream) + : base(ArchiveType.Zip, sourceStream) => headerFactory = new SeekableZipHeaderFactory( - srcStream.ReaderOptions.Password, - srcStream.ReaderOptions.ArchiveEncoding + sourceStream.ReaderOptions.Password, + sourceStream.ReaderOptions.ArchiveEncoding ); /// @@ -189,21 +186,21 @@ public class ZipArchive : AbstractWritableArchive } } - protected override IEnumerable LoadVolumes(SourceStream srcStream) + protected override IEnumerable LoadVolumes(SourceStream stream) { - SrcStream.LoadAllParts(); //request all streams - SrcStream.Position = 0; + stream.LoadAllParts(); //request all streams + stream.Position = 0; - var streams = SrcStream.Streams.ToList(); + var streams = stream.Streams.ToList(); var idx = 0; - if (streams.Count > 1) //test part 2 - true = multipart not split + if (streams.Count() > 1) //test part 2 - true = multipart not split { streams[1].Position += 4; //skip the POST_DATA_DESCRIPTOR to prevent an exception var isZip = IsZipFile(streams[1], ReaderOptions.Password); streams[1].Position -= 4; if (isZip) { - SrcStream.IsVolumes = true; + stream.IsVolumes = true; var tmp = streams[0]; //arcs as zip, z01 ... swap the zip the end streams.RemoveAt(0); @@ -215,7 +212,7 @@ public class ZipArchive : AbstractWritableArchive } //split mode or single file - return new ZipVolume(SrcStream, ReaderOptions, idx++).AsEnumerable(); + return new ZipVolume(stream, ReaderOptions, idx++).AsEnumerable(); } internal ZipArchive() @@ -224,14 +221,13 @@ public class ZipArchive : AbstractWritableArchive protected override IEnumerable LoadEntries(IEnumerable volumes) { var vols = volumes.ToArray(); - foreach (var h in headerFactory.ReadSeekableHeader(vols.Last().Stream)) + foreach (var h in headerFactory.NotNull().ReadSeekableHeader(vols.Last().Stream)) { if (h != null) { switch (h.ZipHeaderType) { case ZipHeaderType.DirectoryEntry: - { var deh = (DirectoryEntryHeader)h; Stream s; @@ -254,14 +250,14 @@ public class ZipArchive : AbstractWritableArchive yield return new ZipArchiveEntry( this, - new SeekableZipFilePart(headerFactory, deh, s) + new SeekableZipFilePart(headerFactory.NotNull(), deh, s) ); } break; case ZipHeaderType.DirectoryEnd: { var bytes = ((DirectoryEndHeader)h).Comment ?? Array.Empty(); - volumes.Last().Comment = ReaderOptions.ArchiveEncoding.Decode(bytes); + vols.Last().Comment = ReaderOptions.ArchiveEncoding.Decode(bytes); yield break; } } @@ -282,7 +278,11 @@ public class ZipArchive : AbstractWritableArchive foreach (var entry in oldEntries.Concat(newEntries).Where(x => !x.IsDirectory)) { using var entryStream = entry.OpenEntryStream(); - writer.Write(entry.Key, entryStream, entry.LastModifiedTime); + writer.Write( + entry.Key.NotNull("Entry Key is null"), + entryStream, + entry.LastModifiedTime + ); } } @@ -294,7 +294,7 @@ public class ZipArchive : AbstractWritableArchive bool closeStream ) => new ZipWritableArchiveEntry(this, source, filePath, size, modified, closeStream); - public static ZipArchive Create() => new ZipArchive(); + public static ZipArchive Create() => new(); protected override IReader CreateReaderForSolidExtraction() { diff --git a/src/SharpCompress/Archives/Zip/ZipArchiveVolumeFactory.cs b/src/SharpCompress/Archives/Zip/ZipArchiveVolumeFactory.cs index fdaa85b9..1b2f093d 100644 --- a/src/SharpCompress/Archives/Zip/ZipArchiveVolumeFactory.cs +++ b/src/SharpCompress/Archives/Zip/ZipArchiveVolumeFactory.cs @@ -12,7 +12,7 @@ internal static class ZipArchiveVolumeFactory //load files with zip/zipx first. Swapped to end once loaded in ZipArchive //new style .zip, z01.. | .zipx, zx01 - if the numbers go beyond 99 then they use 100 ...1000 etc - Match m = Regex.Match(part1.Name, @"^(.*\.)(zipx?|zx?[0-9]+)$", RegexOptions.IgnoreCase); + var m = Regex.Match(part1.Name, @"^(.*\.)(zipx?|zx?[0-9]+)$", RegexOptions.IgnoreCase); if (m.Success) item = new FileInfo( Path.Combine( diff --git a/src/SharpCompress/BufferPool.cs b/src/SharpCompress/BufferPool.cs new file mode 100644 index 00000000..b11dbe6f --- /dev/null +++ b/src/SharpCompress/BufferPool.cs @@ -0,0 +1,33 @@ +using System.Buffers; + +namespace SharpCompress; + +internal static class BufferPool +{ + /// + /// gets a buffer from the pool + /// + /// size of the buffer + /// the buffer + public static byte[] Rent(int bufferSize) + { +#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER + return ArrayPool.Shared.Rent(bufferSize); +#else + return new byte[bufferSize]; +#endif + } + + /// + /// returns a buffer to the pool + /// + /// the buffer to return + public static void Return(byte[] buffer) + { +#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER + ArrayPool.Shared.Return(buffer); +#else + // no-op +#endif + } +} diff --git a/src/SharpCompress/Common/ArchiveEncoding.cs b/src/SharpCompress/Common/ArchiveEncoding.cs index f66044d3..3701a93b 100644 --- a/src/SharpCompress/Common/ArchiveEncoding.cs +++ b/src/SharpCompress/Common/ArchiveEncoding.cs @@ -8,12 +8,12 @@ public class ArchiveEncoding /// /// Default encoding to use when archive format doesn't specify one. /// - public Encoding Default { get; set; } + public Encoding? Default { get; set; } /// /// ArchiveEncoding used by encryption schemes which don't comply with RFC 2898. /// - public Encoding Password { get; set; } + public Encoding? Password { get; set; } /// /// Set this encoding when you want to force it for all encoding operations. @@ -50,6 +50,8 @@ public class ArchiveEncoding public Encoding GetEncoding() => Forced ?? Default ?? Encoding.UTF8; + public Encoding GetPasswordEncoding() => Password ?? Encoding.UTF8; + public Func GetDecoder() => CustomDecoder ?? ((bytes, index, count) => GetEncoding().GetString(bytes, index, count)); } diff --git a/src/SharpCompress/Common/CompressionType.cs b/src/SharpCompress/Common/CompressionType.cs index 11263245..20f166c4 100644 --- a/src/SharpCompress/Common/CompressionType.cs +++ b/src/SharpCompress/Common/CompressionType.cs @@ -14,5 +14,7 @@ public enum CompressionType LZip, Xz, Unknown, - Deflate64 + Deflate64, + Shrink, + Lzw } diff --git a/src/SharpCompress/Common/Entry.cs b/src/SharpCompress/Common/Entry.cs index 85219a43..6209b3de 100644 --- a/src/SharpCompress/Common/Entry.cs +++ b/src/SharpCompress/Common/Entry.cs @@ -14,7 +14,7 @@ public abstract class Entry : IEntry /// /// The string key of the file internal to the Archive. /// - public abstract string Key { get; } + public abstract string? Key { get; } /// /// The target of a symlink entry internal to the Archive. Will be null if not a symlink. @@ -71,11 +71,11 @@ public abstract class Entry : IEntry /// public abstract bool IsSplitAfter { get; } - public int VolumeIndexFirst => Parts?.FirstOrDefault()?.Index ?? 0; - public int VolumeIndexLast => Parts?.LastOrDefault()?.Index ?? 0; + public int VolumeIndexFirst => Parts.FirstOrDefault()?.Index ?? 0; + public int VolumeIndexLast => Parts.LastOrDefault()?.Index ?? 0; /// - public override string ToString() => Key; + public override string ToString() => Key ?? "Entry"; internal abstract IEnumerable Parts { get; } diff --git a/src/SharpCompress/Common/ExtractionMethods.cs b/src/SharpCompress/Common/ExtractionMethods.cs index c8e5896e..27d41648 100644 --- a/src/SharpCompress/Common/ExtractionMethods.cs +++ b/src/SharpCompress/Common/ExtractionMethods.cs @@ -16,7 +16,7 @@ internal static class ExtractionMethods ) { string destinationFileName; - string fullDestinationDirectoryPath = Path.GetFullPath(destinationDirectory); + var fullDestinationDirectoryPath = Path.GetFullPath(destinationDirectory); //check for trailing slash. if ( @@ -36,11 +36,12 @@ internal static class ExtractionMethods options ??= new ExtractionOptions() { Overwrite = true }; - string file = Path.GetFileName(entry.Key); + var file = Path.GetFileName(entry.Key.NotNull("Entry Key is null")).NotNull("File is null"); if (options.ExtractFullPath) { - string folder = Path.GetDirectoryName(entry.Key)!; - string destdir = Path.GetFullPath(Path.Combine(fullDestinationDirectoryPath, folder)); + var folder = Path.GetDirectoryName(entry.Key.NotNull("Entry Key is null")) + .NotNull("Directory is null"); + var destdir = Path.GetFullPath(Path.Combine(fullDestinationDirectoryPath, folder)); if (!Directory.Exists(destdir)) { @@ -102,7 +103,7 @@ internal static class ExtractionMethods } else { - FileMode fm = FileMode.Create; + var fm = FileMode.Create; options ??= new ExtractionOptions() { Overwrite = true }; if (!options.Overwrite) diff --git a/src/SharpCompress/Common/FilePart.cs b/src/SharpCompress/Common/FilePart.cs index 3c286d54..23b8b400 100644 --- a/src/SharpCompress/Common/FilePart.cs +++ b/src/SharpCompress/Common/FilePart.cs @@ -8,7 +8,7 @@ public abstract class FilePart internal ArchiveEncoding ArchiveEncoding { get; } - internal abstract string FilePartName { get; } + internal abstract string? FilePartName { get; } public int Index { get; set; } internal abstract Stream GetCompressedStream(); diff --git a/src/SharpCompress/Common/GZip/GZipEntry.cs b/src/SharpCompress/Common/GZip/GZipEntry.cs index bb9a22da..9a551d07 100644 --- a/src/SharpCompress/Common/GZip/GZipEntry.cs +++ b/src/SharpCompress/Common/GZip/GZipEntry.cs @@ -6,23 +6,23 @@ namespace SharpCompress.Common.GZip; public class GZipEntry : Entry { - private readonly GZipFilePart _filePart; + private readonly GZipFilePart? _filePart; - internal GZipEntry(GZipFilePart filePart) => _filePart = filePart; + internal GZipEntry(GZipFilePart? filePart) => _filePart = filePart; public override CompressionType CompressionType => CompressionType.GZip; - public override long Crc => _filePart.Crc ?? 0; + public override long Crc => _filePart?.Crc ?? 0; - public override string Key => _filePart.FilePartName; + public override string? Key => _filePart?.FilePartName; public override string? LinkTarget => null; public override long CompressedSize => 0; - public override long Size => _filePart.UncompressedSize ?? 0; + public override long Size => _filePart?.UncompressedSize ?? 0; - public override DateTime? LastModifiedTime => _filePart.DateModified; + public override DateTime? LastModifiedTime => _filePart?.DateModified; public override DateTime? CreatedTime => null; @@ -36,7 +36,7 @@ public class GZipEntry : Entry public override bool IsSplitAfter => false; - internal override IEnumerable Parts => _filePart.AsEnumerable(); + internal override IEnumerable Parts => _filePart.Empty(); internal static IEnumerable GetEntries(Stream stream, OptionsBase options) { diff --git a/src/SharpCompress/Common/GZip/GZipFilePart.cs b/src/SharpCompress/Common/GZip/GZipFilePart.cs index fbf4ee45..4a1c9515 100644 --- a/src/SharpCompress/Common/GZip/GZipFilePart.cs +++ b/src/SharpCompress/Common/GZip/GZipFilePart.cs @@ -34,7 +34,7 @@ internal sealed class GZipFilePart : FilePart internal uint? Crc { get; private set; } internal uint? UncompressedSize { get; private set; } - internal override string FilePartName => _name!; + internal override string? FilePartName => _name; internal override Stream GetCompressedStream() => new DeflateStream(_stream, CompressionMode.Decompress, CompressionLevel.Default); diff --git a/src/SharpCompress/Common/GZip/GZipVolume.cs b/src/SharpCompress/Common/GZip/GZipVolume.cs index 0dd9b8d9..600ba8e3 100644 --- a/src/SharpCompress/Common/GZip/GZipVolume.cs +++ b/src/SharpCompress/Common/GZip/GZipVolume.cs @@ -5,7 +5,7 @@ namespace SharpCompress.Common.GZip; public class GZipVolume : Volume { - public GZipVolume(Stream stream, ReaderOptions options, int index = 0) + public GZipVolume(Stream stream, ReaderOptions? options, int index) : base(stream, options, index) { } public GZipVolume(FileInfo fileInfo, ReaderOptions options) diff --git a/src/SharpCompress/Common/IEntry.cs b/src/SharpCompress/Common/IEntry.cs index df1fa603..56e1db81 100644 --- a/src/SharpCompress/Common/IEntry.cs +++ b/src/SharpCompress/Common/IEntry.cs @@ -9,7 +9,7 @@ public interface IEntry long CompressedSize { get; } long Crc { get; } DateTime? CreatedTime { get; } - string Key { get; } + string? Key { get; } string? LinkTarget { get; } bool IsDirectory { get; } bool IsEncrypted { get; } diff --git a/src/SharpCompress/Common/IVolume.cs b/src/SharpCompress/Common/IVolume.cs index abbc0406..aed35fd0 100644 --- a/src/SharpCompress/Common/IVolume.cs +++ b/src/SharpCompress/Common/IVolume.cs @@ -6,5 +6,5 @@ public interface IVolume : IDisposable { int Index { get; } - string FileName { get; } + string? FileName { get; } } diff --git a/src/SharpCompress/Common/OptionsBase.cs b/src/SharpCompress/Common/OptionsBase.cs index 61a6cb55..8d1cbd1c 100644 --- a/src/SharpCompress/Common/OptionsBase.cs +++ b/src/SharpCompress/Common/OptionsBase.cs @@ -7,5 +7,5 @@ public class OptionsBase /// public bool LeaveStreamOpen { get; set; } = true; - public ArchiveEncoding ArchiveEncoding { get; set; } = new ArchiveEncoding(); + public ArchiveEncoding ArchiveEncoding { get; set; } = new(); } diff --git a/src/SharpCompress/Common/PasswordProtectedException.cs b/src/SharpCompress/Common/PasswordProtectedException.cs deleted file mode 100644 index 9ebe3d65..00000000 --- a/src/SharpCompress/Common/PasswordProtectedException.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace SharpCompress.Common; - -public class PasswordProtectedException : ExtractionException -{ - public PasswordProtectedException(string message) - : base(message) { } - - public PasswordProtectedException(string message, Exception inner) - : base(message, inner) { } -} diff --git a/src/SharpCompress/Common/Rar/CryptKey3.cs b/src/SharpCompress/Common/Rar/CryptKey3.cs new file mode 100644 index 00000000..93e451b1 --- /dev/null +++ b/src/SharpCompress/Common/Rar/CryptKey3.cs @@ -0,0 +1,84 @@ +#nullable disable + +using System.Security.Cryptography; +using System.Text; +using SharpCompress.Common.Rar.Headers; + +namespace SharpCompress.Common.Rar; + +internal class CryptKey3 : ICryptKey +{ + const int AES_128 = 128; + + private string _password; + + public CryptKey3(string password) => _password = password ?? ""; + + public ICryptoTransform Transformer(byte[] salt) + { + var aesIV = new byte[EncryptionConstV5.SIZE_INITV]; + + var rawLength = 2 * _password.Length; + var rawPassword = new byte[rawLength + EncryptionConstV5.SIZE_SALT30]; + var passwordBytes = Encoding.UTF8.GetBytes(_password); + for (var i = 0; i < _password.Length; i++) + { + rawPassword[i * 2] = passwordBytes[i]; + rawPassword[(i * 2) + 1] = 0; + } + + for (var i = 0; i < salt.Length; i++) + { + rawPassword[i + rawLength] = salt[i]; + } + + var msgDigest = SHA1.Create(); + const int noOfRounds = (1 << 18); + const int iblock = 3; + + byte[] digest; + var data = new byte[(rawPassword.Length + iblock) * noOfRounds]; + + //TODO slow code below, find ways to optimize + for (var i = 0; i < noOfRounds; i++) + { + rawPassword.CopyTo(data, i * (rawPassword.Length + iblock)); + + data[(i * (rawPassword.Length + iblock)) + rawPassword.Length + 0] = (byte)i; + data[(i * (rawPassword.Length + iblock)) + rawPassword.Length + 1] = (byte)(i >> 8); + data[(i * (rawPassword.Length + iblock)) + rawPassword.Length + 2] = (byte)(i >> 16); + + if (i % (noOfRounds / EncryptionConstV5.SIZE_INITV) == 0) + { + digest = msgDigest.ComputeHash(data, 0, (i + 1) * (rawPassword.Length + iblock)); + aesIV[i / (noOfRounds / EncryptionConstV5.SIZE_INITV)] = digest[19]; + } + } + digest = msgDigest.ComputeHash(data); + //slow code ends + + var aesKey = new byte[EncryptionConstV5.SIZE_INITV]; + for (var i = 0; i < 4; i++) + { + for (var j = 0; j < 4; j++) + { + aesKey[(i * 4) + j] = (byte)( + ( + ((digest[i * 4] * 0x1000000) & 0xff000000) + | (uint)((digest[(i * 4) + 1] * 0x10000) & 0xff0000) + | (uint)((digest[(i * 4) + 2] * 0x100) & 0xff00) + | (uint)(digest[(i * 4) + 3] & 0xff) + ) >> (j * 8) + ); + } + } + + var aes = Aes.Create(); + aes.KeySize = AES_128; + aes.Mode = CipherMode.CBC; + aes.Padding = PaddingMode.None; + aes.Key = aesKey; + aes.IV = aesIV; + return aes.CreateDecryptor(); + } +} diff --git a/src/SharpCompress/Common/Rar/CryptKey5.cs b/src/SharpCompress/Common/Rar/CryptKey5.cs new file mode 100644 index 00000000..0b802691 --- /dev/null +++ b/src/SharpCompress/Common/Rar/CryptKey5.cs @@ -0,0 +1,95 @@ +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using SharpCompress.Common.Rar.Headers; + +namespace SharpCompress.Common.Rar; + +internal class CryptKey5 : ICryptKey +{ + const int AES_256 = 256; + const int DERIVED_KEY_LENGTH = 0x10; + const int SHA256_DIGEST_SIZE = 32; + + private string _password; + private Rar5CryptoInfo _cryptoInfo; + private byte[] _pswCheck = { }; + private byte[] _hashKey = { }; + + public CryptKey5(string password, Rar5CryptoInfo rar5CryptoInfo) + { + _password = password ?? ""; + _cryptoInfo = rar5CryptoInfo; + } + + public byte[] PswCheck => _pswCheck; + + public byte[] HashKey => _hashKey; + + private static List GenerateRarPBKDF2Key( + string password, + byte[] salt, + int iterations, + int keyLength + ) + { + using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(password)); + var block = hmac.ComputeHash(salt); + var finalHash = (byte[])block.Clone(); + + var loop = new int[] { iterations, 17, 17 }; + var res = new List { }; + + for (var x = 0; x < 3; x++) + { + for (var i = 1; i < loop[x]; i++) + { + block = hmac.ComputeHash(block); + for (var j = 0; j < finalHash.Length; j++) + { + finalHash[j] ^= block[j]; + } + } + + res.Add((byte[])finalHash.Clone()); + } + + return res; + } + + public ICryptoTransform Transformer(byte[] salt) + { + var iterations = (1 << _cryptoInfo.LG2Count); // Adjust the number of iterations as needed + + var salt_rar5 = salt.Concat(new byte[] { 0, 0, 0, 1 }); + var derivedKey = GenerateRarPBKDF2Key( + _password, + salt_rar5.ToArray(), + iterations, + DERIVED_KEY_LENGTH + ); + + _hashKey = derivedKey[1]; + + _pswCheck = new byte[EncryptionConstV5.SIZE_PSWCHECK]; + + for (var i = 0; i < SHA256_DIGEST_SIZE; i++) + { + _pswCheck[i % EncryptionConstV5.SIZE_PSWCHECK] ^= derivedKey[2][i]; + } + + if (_cryptoInfo.UsePswCheck && !_cryptoInfo.PswCheck.SequenceEqual(_pswCheck)) + { + throw new CryptographicException("The password did not match."); + } + + var aes = Aes.Create(); + aes.KeySize = AES_256; + aes.Mode = CipherMode.CBC; + aes.Padding = PaddingMode.None; + aes.Key = derivedKey[0]; + aes.IV = _cryptoInfo.InitV; + return aes.CreateDecryptor(); + } +} diff --git a/src/SharpCompress/Common/Rar/Headers/ArchiveCryptHeader.cs b/src/SharpCompress/Common/Rar/Headers/ArchiveCryptHeader.cs index 619e8fc9..5aa29d49 100644 --- a/src/SharpCompress/Common/Rar/Headers/ArchiveCryptHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/ArchiveCryptHeader.cs @@ -1,50 +1,19 @@ #nullable disable +using System; +using System.Security.Cryptography; +using SharpCompress.Common.Rar.Headers; using SharpCompress.IO; namespace SharpCompress.Common.Rar.Headers; internal class ArchiveCryptHeader : RarHeader { - private const int CRYPT_VERSION = 0; // Supported encryption version. - private const int SIZE_SALT50 = 16; - private const int SIZE_PSWCHECK = 8; - private const int SIZE_PSWCHECK_CSUM = 4; - private const int CRYPT5_KDF_LG2_COUNT_MAX = 24; // LOG2 of maximum accepted iteration count. - - private bool _usePswCheck; - private uint _lg2Count; // Log2 of PBKDF2 repetition count. - private byte[] _salt; - private byte[] _pswCheck; - private byte[] _pswCheckCsm; - public ArchiveCryptHeader(RarHeader header, RarCrcBinaryReader reader) : base(header, reader, HeaderType.Crypt) { } - protected override void ReadFinish(MarkingBinaryReader reader) - { - var cryptVersion = reader.ReadRarVIntUInt32(); - if (cryptVersion > CRYPT_VERSION) - { - //error? - return; - } - var encryptionFlags = reader.ReadRarVIntUInt32(); - _usePswCheck = FlagUtility.HasFlag(encryptionFlags, EncryptionFlagsV5.CHFL_CRYPT_PSWCHECK); - _lg2Count = reader.ReadRarVIntByte(1); + public Rar5CryptoInfo CryptInfo = new(); - //UsePswCheck = HasHeaderFlag(EncryptionFlagsV5.CHFL_CRYPT_PSWCHECK); - if (_lg2Count > CRYPT5_KDF_LG2_COUNT_MAX) - { - //error? - return; - } - - _salt = reader.ReadBytes(SIZE_SALT50); - if (_usePswCheck) - { - _pswCheck = reader.ReadBytes(SIZE_PSWCHECK); - _pswCheckCsm = reader.ReadBytes(SIZE_PSWCHECK_CSUM); - } - } + protected override void ReadFinish(MarkingBinaryReader reader) => + CryptInfo = new Rar5CryptoInfo(reader, false); } diff --git a/src/SharpCompress/Common/Rar/Headers/FileHeader.cs b/src/SharpCompress/Common/Rar/Headers/FileHeader.cs index e95052ef..eea8293d 100644 --- a/src/SharpCompress/Common/Rar/Headers/FileHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/FileHeader.cs @@ -1,5 +1,11 @@ #nullable disable +using System; +using System.IO; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using SharpCompress.IO; #if !Rar2017_64bit using size_t = System.UInt32; #else @@ -8,16 +14,11 @@ using nuint = System.UInt64; using size_t = System.UInt64; #endif -using SharpCompress.IO; -using System; -using System.IO; -using System.Text; - namespace SharpCompress.Common.Rar.Headers; internal class FileHeader : RarHeader { - private uint _fileCrc; + private byte[] _hash; public FileHeader(RarHeader header, RarCrcBinaryReader reader, HeaderType headerType) : base(header, reader, headerType) { } @@ -52,7 +53,7 @@ internal class FileHeader : RarHeader if (HasFlag(FileFlagsV5.HAS_CRC32)) { - FileCrc = reader.ReadUInt32(); + FileCrc = reader.ReadBytes(4); } var compressionInfo = reader.ReadRarVIntUInt16(); @@ -104,7 +105,13 @@ internal class FileHeader : RarHeader throw new InvalidFormatException("rar5 header size / extra size inconsistency"); } - isEncryptedRar5 = false; + const ushort FHEXTRA_CRYPT = 0x01; + const ushort FHEXTRA_HASH = 0x02; + const ushort FHEXTRA_HTIME = 0x03; + // const ushort FHEXTRA_VERSION = 0x04; + const ushort FHEXTRA_REDIR = 0x05; + // const ushort FHEXTRA_UOWNER = 0x06; + // const ushort FHEXTRA_SUBDATA = 0x07; while (RemainingHeaderBytes(reader) > 0) { @@ -113,23 +120,30 @@ internal class FileHeader : RarHeader var type = reader.ReadRarVIntUInt16(); switch (type) { - //TODO - case 1: // file encryption - + case FHEXTRA_CRYPT: // file encryption { - isEncryptedRar5 = true; + Rar5CryptoInfo = new Rar5CryptoInfo(reader, true); - //var version = reader.ReadRarVIntByte(); - //if (version != 0) throw new InvalidFormatException("unknown encryption algorithm " + version); + if (Rar5CryptoInfo.PswCheck.All(singleByte => singleByte == 0)) + { + Rar5CryptoInfo = null; + } } break; - // case 2: // file hash - // { - // - // } - // break; - case 3: // file time - + case FHEXTRA_HASH: + { + const uint FHEXTRA_HASH_BLAKE2 = 0x0; + // const uint HASH_BLAKE2 = 0x03; + const int BLAKE2_DIGEST_SIZE = 0x20; + if ((uint)reader.ReadRarVInt() == FHEXTRA_HASH_BLAKE2) + { + // var hash = HASH_BLAKE2; + _hash = reader.ReadBytes(BLAKE2_DIGEST_SIZE); + } + // enum HASH_TYPE {HASH_NONE,HASH_RAR14,HASH_CRC32,HASH_BLAKE2}; + } + break; + case FHEXTRA_HTIME: // file time { var flags = reader.ReadRarVIntUInt16(); var isWindowsTime = (flags & 1) == 0; @@ -148,22 +162,27 @@ internal class FileHeader : RarHeader } break; //TODO - // case 4: // file version + // case FHEXTRA_VERSION: // file version // { // // } // break; - // case 5: // file system redirection + case FHEXTRA_REDIR: // file system redirection + { + RedirType = reader.ReadRarVIntByte(); + RedirFlags = reader.ReadRarVIntByte(); + var nn = reader.ReadRarVIntUInt16(); + var bb = reader.ReadBytes(nn); + RedirTargetName = ConvertPathV5(Encoding.UTF8.GetString(bb, 0, bb.Length)); + } + break; + //TODO + // case FHEXTRA_UOWNER: // unix owner // { // // } // break; - // case 6: // unix owner - // { - // - // } - // break; - // case 7: // service data + // case FHEXTRA_SUBDATA: // service data // { // // } @@ -171,6 +190,7 @@ internal class FileHeader : RarHeader default: // skip unknown record types to allow new record types to be added in the future + //Console.WriteLine($"unhandled rar header field type {type}"); break; } // drain any trailing bytes of extra record @@ -222,7 +242,7 @@ internal class FileHeader : RarHeader HostOs = reader.ReadByte(); - FileCrc = reader.ReadUInt32(); + FileCrc = reader.ReadBytes(4); FileLastModifiedTime = Utility.DosDateToDateTime(reader.ReadUInt32()); @@ -255,13 +275,11 @@ internal class FileHeader : RarHeader var fileNameBytes = reader.ReadBytes(nameSize); - const int saltSize = 8; const int newLhdSize = 32; switch (HeaderCode) { case HeaderCodeV.RAR4_FILE_HEADER: - { if (HasFlag(FileFlagsV4.UNICODE)) { @@ -288,12 +306,11 @@ internal class FileHeader : RarHeader } break; case HeaderCodeV.RAR4_NEW_SUB_HEADER: - { var datasize = HeaderSize - newLhdSize - nameSize; if (HasFlag(FileFlagsV4.SALT)) { - datasize -= saltSize; + datasize -= EncryptionConstV5.SIZE_SALT30; } if (datasize > 0) { @@ -314,7 +331,7 @@ internal class FileHeader : RarHeader if (HasFlag(FileFlagsV4.SALT)) { - R4Salt = reader.ReadBytes(saltSize); + R4Salt = reader.ReadBytes(EncryptionConstV5.SIZE_SALT30); } if (HasFlag(FileFlagsV4.EXT_TIME)) { @@ -395,18 +412,10 @@ internal class FileHeader : RarHeader private bool HasFlag(ushort flag) => (Flags & flag) == flag; - internal uint FileCrc + internal byte[] FileCrc { - get - { - if (IsRar5 && !HasFlag(FileFlagsV5.HAS_CRC32)) - { - //!!! rar5: - throw new InvalidOperationException("TODO rar5"); - } - return _fileCrc; - } - private set => _fileCrc = value; + get => _hash; + private set => _hash = value; } // 0 - storing @@ -428,11 +437,17 @@ internal class FileHeader : RarHeader public bool IsSolid { get; private set; } + public byte RedirType { get; private set; } + public bool IsRedir => RedirType != 0; + public byte RedirFlags { get; private set; } + public bool IsRedirDirectory => (RedirFlags & RedirFlagV5.DIRECTORY) != 0; + public string RedirTargetName { get; private set; } + // unused for UnpackV1 implementation (limitation) internal size_t WindowSize { get; private set; } internal byte[] R4Salt { get; private set; } - + internal Rar5CryptoInfo Rar5CryptoInfo { get; private set; } private byte HostOs { get; set; } internal uint FileAttributes { get; private set; } internal long CompressedSize { get; private set; } @@ -450,8 +465,7 @@ internal class FileHeader : RarHeader public bool IsDirectory => HasFlag(IsRar5 ? FileFlagsV5.DIRECTORY : FileFlagsV4.DIRECTORY); - private bool isEncryptedRar5 = false; - public bool IsEncrypted => IsRar5 ? isEncryptedRar5 : HasFlag(FileFlagsV4.PASSWORD); + public bool IsEncrypted => IsRar5 ? Rar5CryptoInfo != null : HasFlag(FileFlagsV4.PASSWORD); internal DateTime? FileLastModifiedTime { get; private set; } diff --git a/src/SharpCompress/Common/Rar/Headers/Flags.cs b/src/SharpCompress/Common/Rar/Headers/Flags.cs index 5a52f001..990dbc99 100644 --- a/src/SharpCompress/Common/Rar/Headers/Flags.cs +++ b/src/SharpCompress/Common/Rar/Headers/Flags.cs @@ -50,6 +50,17 @@ internal static class EncryptionFlagsV5 public const uint FHEXTRA_CRYPT_HASHMAC = 0x02; } +internal static class EncryptionConstV5 +{ + public const int VERSION = 0; + public const uint CRYPT5_KDF_LG2_COUNT_MAX = 0x24; + public const int SIZE_SALT30 = 0x08; + public const int SIZE_SALT50 = 0x10; + public const int SIZE_INITV = 0x10; + public const int SIZE_PSWCHECK = 0x08; + public const int SIZE_PSWCHECK_CSUM = 0x04; +} + internal static class HeaderFlagsV5 { public const ushort HAS_EXTRA = 0x0001; @@ -146,3 +157,17 @@ internal static class EndArchiveFlagsV5 { public const ushort HAS_NEXT_VOLUME = 0x0001; } + +internal static class RedirTypeV5 +{ + public const byte UNIX_SYMLINK = 0x0001; + public const byte WIN_SYMLINK = 0x0002; + public const byte WIN_JUNCTION = 0x0003; + public const byte HARD_LINK = 0x0004; + public const byte FILE_COPY = 0x0005; +} + +internal static class RedirFlagV5 +{ + public const byte DIRECTORY = 0x0001; +} diff --git a/src/SharpCompress/Common/Rar/Headers/RarHeader.cs b/src/SharpCompress/Common/Rar/Headers/RarHeader.cs index 81002fc4..0d8648e8 100644 --- a/src/SharpCompress/Common/Rar/Headers/RarHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/RarHeader.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using SharpCompress.IO; diff --git a/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs b/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs index d371166d..74d68fc7 100644 --- a/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs +++ b/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs @@ -1,5 +1,7 @@ using System.Collections.Generic; using System.IO; +using System.Linq; +using SharpCompress.Common.Rar; using SharpCompress.IO; using SharpCompress.Readers; @@ -9,6 +11,8 @@ public class RarHeaderFactory { private bool _isRar5; + private Rar5CryptoInfo? _cryptInfo; + public RarHeaderFactory(StreamingMode mode, ReaderOptions options) { StreamingMode = mode; @@ -53,7 +57,19 @@ public class RarHeaderFactory "Encrypted Rar archive has no password specified." ); } - reader = new RarCryptoBinaryReader(stream, Options.Password); + + if (_isRar5 && _cryptInfo != null) + { + _cryptInfo.ReadInitV(new MarkingBinaryReader(stream)); + var _headerKey = new CryptKey5(Options.Password!, _cryptInfo); + + reader = new RarCryptoBinaryReader(stream, _headerKey, _cryptInfo.Salt); + } + else + { + var key = new CryptKey3(Options.Password); + reader = new RarCryptoBinaryReader(stream, key); + } } var header = RarHeader.TryReadBase(reader, _isRar5, Options.ArchiveEncoding); @@ -82,13 +98,11 @@ public class RarHeaderFactory switch (StreamingMode) { case StreamingMode.Seekable: - { reader.BaseStream.Position += ph.DataSize; } break; case StreamingMode.Streaming: - { reader.BaseStream.Skip(ph.DataSize); } @@ -105,7 +119,14 @@ public class RarHeaderFactory case HeaderCodeV.RAR5_SERVICE_HEADER: { var fh = new FileHeader(header, reader, HeaderType.Service); - SkipData(fh, reader); + if (fh.FileName == "CMT") + { + fh.PackedStream = new ReadOnlySubStream(reader.BaseStream, fh.CompressedSize); + } + else + { + SkipData(fh, reader); + } return fh; } @@ -123,17 +144,15 @@ public class RarHeaderFactory switch (StreamingMode) { case StreamingMode.Seekable: - { fh.DataStartPosition = reader.BaseStream.Position; reader.BaseStream.Position += fh.CompressedSize; } break; case StreamingMode.Streaming: - { var ms = new ReadOnlySubStream(reader.BaseStream, fh.CompressedSize); - if (fh.R4Salt is null) + if (fh.R4Salt is null && fh.Rar5CryptoInfo is null) { fh.PackedStream = ms; } @@ -141,8 +160,10 @@ public class RarHeaderFactory { fh.PackedStream = new RarCryptoWrapper( ms, - Options.Password!, - fh.R4Salt + fh.R4Salt is null ? fh.Rar5CryptoInfo.Salt : fh.R4Salt, + fh.R4Salt is null + ? new CryptKey5(Options.Password!, fh.Rar5CryptoInfo) + : new CryptKey3(Options.Password!) ); } } @@ -161,9 +182,11 @@ public class RarHeaderFactory } case HeaderCodeV.RAR5_ARCHIVE_ENCRYPTION_HEADER: { - var ch = new ArchiveCryptHeader(header, reader); + var cryptoHeader = new ArchiveCryptHeader(header, reader); IsEncrypted = true; - return ch; + _cryptInfo = cryptoHeader.CryptInfo; + + return cryptoHeader; } default: { @@ -177,14 +200,12 @@ public class RarHeaderFactory switch (StreamingMode) { case StreamingMode.Seekable: - { fh.DataStartPosition = reader.BaseStream.Position; reader.BaseStream.Position += fh.CompressedSize; } break; case StreamingMode.Streaming: - { //skip the data because it's useless? reader.BaseStream.Skip(fh.CompressedSize); diff --git a/src/SharpCompress/Common/Rar/ICryptKey.cs b/src/SharpCompress/Common/Rar/ICryptKey.cs new file mode 100644 index 00000000..94f068f5 --- /dev/null +++ b/src/SharpCompress/Common/Rar/ICryptKey.cs @@ -0,0 +1,8 @@ +using System.Security.Cryptography; + +namespace SharpCompress.Common.Rar; + +internal interface ICryptKey +{ + ICryptoTransform Transformer(byte[] salt); +} diff --git a/src/SharpCompress/Common/Rar/Rar5CryptoInfo.cs b/src/SharpCompress/Common/Rar/Rar5CryptoInfo.cs new file mode 100644 index 00000000..7b8edff9 --- /dev/null +++ b/src/SharpCompress/Common/Rar/Rar5CryptoInfo.cs @@ -0,0 +1,57 @@ +using System; +using System.Security.Cryptography; +using SharpCompress.Common.Rar.Headers; +using SharpCompress.IO; + +namespace SharpCompress.Common.Rar; + +internal class Rar5CryptoInfo +{ + public Rar5CryptoInfo() { } + + public Rar5CryptoInfo(MarkingBinaryReader reader, bool readInitV) + { + var cryptVersion = reader.ReadRarVIntUInt32(); + if (cryptVersion > EncryptionConstV5.VERSION) + { + throw new CryptographicException($"Unsupported crypto version of {cryptVersion}"); + } + var encryptionFlags = reader.ReadRarVIntUInt32(); + UsePswCheck = FlagUtility.HasFlag(encryptionFlags, EncryptionFlagsV5.CHFL_CRYPT_PSWCHECK); + LG2Count = reader.ReadRarVIntByte(1); + + if (LG2Count > EncryptionConstV5.CRYPT5_KDF_LG2_COUNT_MAX) + { + throw new CryptographicException($"Unsupported LG2 count of {LG2Count}."); + } + + Salt = reader.ReadBytes(EncryptionConstV5.SIZE_SALT50); + + if (readInitV) // File header needs to read IV here + { + ReadInitV(reader); + } + + if (UsePswCheck) + { + PswCheck = reader.ReadBytes(EncryptionConstV5.SIZE_PSWCHECK); + var _pswCheckCsm = reader.ReadBytes(EncryptionConstV5.SIZE_PSWCHECK_CSUM); + + var sha = SHA256.Create(); + UsePswCheck = sha.ComputeHash(PswCheck).AsSpan().StartsWith(_pswCheckCsm.AsSpan()); + } + } + + public void ReadInitV(MarkingBinaryReader reader) => + InitV = reader.ReadBytes(EncryptionConstV5.SIZE_INITV); + + public bool UsePswCheck = false; + + public int LG2Count = 0; + + public byte[] InitV = { }; + + public byte[] Salt = { }; + + public byte[] PswCheck = { }; +} diff --git a/src/SharpCompress/Common/Rar/RarCryptoBinaryReader.cs b/src/SharpCompress/Common/Rar/RarCryptoBinaryReader.cs index 0be6e74e..6e44286b 100644 --- a/src/SharpCompress/Common/Rar/RarCryptoBinaryReader.cs +++ b/src/SharpCompress/Common/Rar/RarCryptoBinaryReader.cs @@ -1,29 +1,29 @@ -using System.Collections.Generic; +#nullable disable + +using System.Collections.Generic; using System.IO; +using SharpCompress.Common.Rar.Headers; +using SharpCompress.Crypto; namespace SharpCompress.Common.Rar; internal sealed class RarCryptoBinaryReader : RarCrcBinaryReader { - private RarRijndael _rijndael; - private byte[] _salt; - private readonly string _password; - private readonly Queue _data = new Queue(); + private BlockTransformer _rijndael; + private readonly Queue _data = new(); private long _readCount; - public RarCryptoBinaryReader(Stream stream, string password) + public RarCryptoBinaryReader(Stream stream, ICryptKey cryptKey) : base(stream) { - _password = password; - - // coderb: not sure why this was being done at this logical point - //SkipQueue(); - var salt = ReadBytes(8); - - _salt = salt; - _rijndael = RarRijndael.InitializeFrom(_password, salt); + 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)); + // track read count ourselves rather than using the underlying stream since we buffer public override long CurrentReadByteCount { @@ -36,29 +36,9 @@ internal sealed class RarCryptoBinaryReader : RarCrcBinaryReader public override void Mark() => _readCount = 0; - private bool UseEncryption => _salt != null; + public override byte ReadByte() => ReadAndDecryptBytes(1)[0]; - public override byte ReadByte() - { - if (UseEncryption) - { - return ReadAndDecryptBytes(1)[0]; - } - - _readCount++; - return base.ReadByte(); - } - - public override byte[] ReadBytes(int count) - { - if (UseEncryption) - { - return ReadAndDecryptBytes(count); - } - - _readCount += count; - return base.ReadBytes(count); - } + public override byte[] ReadBytes(int count) => ReadAndDecryptBytes(count); private byte[] ReadAndDecryptBytes(int count) { diff --git a/src/SharpCompress/Common/Rar/RarCryptoWrapper.cs b/src/SharpCompress/Common/Rar/RarCryptoWrapper.cs index a1cee046..5500af8d 100644 --- a/src/SharpCompress/Common/Rar/RarCryptoWrapper.cs +++ b/src/SharpCompress/Common/Rar/RarCryptoWrapper.cs @@ -1,37 +1,30 @@ using System; using System.Collections.Generic; using System.IO; +using SharpCompress.Crypto; namespace SharpCompress.Common.Rar; internal sealed class RarCryptoWrapper : Stream { private readonly Stream _actualStream; - private readonly byte[] _salt; - private RarRijndael _rijndael; - private readonly Queue _data = new Queue(); + private BlockTransformer _rijndael; + private readonly Queue _data = new(); - public RarCryptoWrapper(Stream actualStream, string password, byte[] salt) + public RarCryptoWrapper(Stream actualStream, byte[] salt, ICryptKey key) { _actualStream = actualStream; - _salt = salt; - _rijndael = RarRijndael.InitializeFrom(password ?? "", salt); + _rijndael = new BlockTransformer(key.Transformer(salt)); } - public override void Flush() => throw new NotSupportedException(); + public override void Flush() { } public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException(); public override void SetLength(long value) => throw new NotSupportedException(); - public override int Read(byte[] buffer, int offset, int count) - { - if (_salt is null) - { - return _actualStream.Read(buffer, offset, count); - } - return ReadAndDecrypt(buffer, offset, count); - } + public override int Read(byte[] buffer, int offset, int count) => + ReadAndDecrypt(buffer, offset, count); public int ReadAndDecrypt(byte[] buffer, int offset, int count) { @@ -41,7 +34,7 @@ internal sealed class RarCryptoWrapper : Stream if (sizeToRead > 0) { var alignedSize = sizeToRead + ((~sizeToRead + 1) & 0xf); - Span cipherText = stackalloc byte[RarRijndael.CRYPTO_BLOCK_SIZE]; + Span cipherText = stackalloc byte[16]; for (var i = 0; i < alignedSize / 16; i++) { //long ax = System.currentTimeMillis(); @@ -77,11 +70,11 @@ internal sealed class RarCryptoWrapper : Stream protected override void Dispose(bool disposing) { - if (_rijndael != null) + if (disposing) { _rijndael.Dispose(); - _rijndael = null!; } + base.Dispose(disposing); } } diff --git a/src/SharpCompress/Common/Rar/RarEntry.cs b/src/SharpCompress/Common/Rar/RarEntry.cs index 7f280e4a..a064c2f8 100644 --- a/src/SharpCompress/Common/Rar/RarEntry.cs +++ b/src/SharpCompress/Common/Rar/RarEntry.cs @@ -20,12 +20,12 @@ public abstract class RarEntry : Entry /// /// The File's 32 bit CRC Hash /// - public override long Crc => FileHeader.FileCrc; + public override long Crc => BitConverter.ToUInt32(FileHeader.FileCrc, 0); /// /// The path of the file internal to the Rar Archive. /// - public override string Key => FileHeader.FileName; + public override string? Key => FileHeader.FileName; public override string? LinkTarget => null; @@ -55,12 +55,21 @@ public abstract class RarEntry : Entry public override bool IsEncrypted => FileHeader.IsEncrypted; /// - /// Entry is password protected and encrypted and cannot be extracted. + /// Entry Windows file attributes + /// + public override int? Attrib => (int)FileHeader.FileAttributes; + + /// + /// Entry is a directory /// public override bool IsDirectory => FileHeader.IsDirectory; public override bool IsSplitAfter => FileHeader.IsSplitAfter; + public bool IsRedir => FileHeader.IsRedir; + + public string RedirTargetName => FileHeader.RedirTargetName; + public override string ToString() => string.Format( "Entry Path: {0} Compressed Size: {1} Uncompressed Size: {2} CRC: {3}", diff --git a/src/SharpCompress/Common/Rar/RarRijndael.cs b/src/SharpCompress/Common/Rar/RarRijndael.cs deleted file mode 100644 index 94592639..00000000 --- a/src/SharpCompress/Common/Rar/RarRijndael.cs +++ /dev/null @@ -1,114 +0,0 @@ -#nullable disable - -using System; -using System.Security.Cryptography; -using System.Text; -using SharpCompress.Crypto; - -namespace SharpCompress.Common.Rar; - -internal class RarRijndael : IDisposable -{ - internal const int CRYPTO_BLOCK_SIZE = 16; - - private readonly string _password; - private readonly byte[] _salt; - private byte[] _aesInitializationVector; - private RijndaelEngine _rijndael; - - private RarRijndael(string password, byte[] salt) - { - _password = password; - _salt = salt; - } - - private void Initialize() - { - _rijndael = new RijndaelEngine(); - _aesInitializationVector = new byte[CRYPTO_BLOCK_SIZE]; - var rawLength = 2 * _password.Length; - var rawPassword = new byte[rawLength + 8]; - var passwordBytes = Encoding.UTF8.GetBytes(_password); - for (var i = 0; i < _password.Length; i++) - { - rawPassword[i * 2] = passwordBytes[i]; - rawPassword[(i * 2) + 1] = 0; - } - for (var i = 0; i < _salt.Length; i++) - { - rawPassword[i + rawLength] = _salt[i]; - } - - const int noOfRounds = (1 << 18); - const int iblock = 3; - byte[] digest; - var data = new byte[(rawPassword.Length + iblock) * noOfRounds]; - - //TODO slow code below, find ways to optimize - for (var i = 0; i < noOfRounds; i++) - { - rawPassword.CopyTo(data, i * (rawPassword.Length + iblock)); - - data[(i * (rawPassword.Length + iblock)) + rawPassword.Length + 0] = (byte)i; - data[(i * (rawPassword.Length + iblock)) + rawPassword.Length + 1] = (byte)(i >> 8); - data[(i * (rawPassword.Length + iblock)) + rawPassword.Length + 2] = (byte)( - i >> CRYPTO_BLOCK_SIZE - ); - - if (i % (noOfRounds / CRYPTO_BLOCK_SIZE) == 0) - { - digest = SHA1.Create() - .ComputeHash(data, 0, (i + 1) * (rawPassword.Length + iblock)); - _aesInitializationVector[i / (noOfRounds / CRYPTO_BLOCK_SIZE)] = digest[19]; - } - } - digest = SHA1.Create().ComputeHash(data); - //slow code ends - - var aesKey = new byte[CRYPTO_BLOCK_SIZE]; - for (var i = 0; i < 4; i++) - { - for (var j = 0; j < 4; j++) - { - aesKey[(i * 4) + j] = (byte)( - ( - ((digest[i * 4] * 0x1000000) & 0xff000000) - | (uint)((digest[(i * 4) + 1] * 0x10000) & 0xff0000) - | (uint)((digest[(i * 4) + 2] * 0x100) & 0xff00) - | (uint)(digest[(i * 4) + 3] & 0xff) - ) >> (j * 8) - ); - } - } - - _rijndael.Init(false, new KeyParameter(aesKey)); - } - - public static RarRijndael InitializeFrom(string password, byte[] salt) - { - var rijndael = new RarRijndael(password, salt); - rijndael.Initialize(); - return rijndael; - } - - public byte[] ProcessBlock(ReadOnlySpan cipherText) - { - Span plainText = stackalloc byte[CRYPTO_BLOCK_SIZE]; // 16 bytes - var decryptedBytes = new byte[CRYPTO_BLOCK_SIZE]; - _rijndael.ProcessBlock(cipherText, plainText); - - for (var j = 0; j < CRYPTO_BLOCK_SIZE; j++) - { - decryptedBytes[j] = (byte)(plainText[j] ^ _aesInitializationVector[j % 16]); //32:114, 33:101 - } - - for (var j = 0; j < _aesInitializationVector.Length; j++) - { - _aesInitializationVector[j] = cipherText[j]; - } - - return decryptedBytes; - } - - public void Dispose() { } -} diff --git a/src/SharpCompress/Common/Rar/RarVolume.cs b/src/SharpCompress/Common/Rar/RarVolume.cs index 7be719ec..d05e16c2 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.Text; using SharpCompress.Common.Rar.Headers; using SharpCompress.IO; using SharpCompress.Readers; @@ -14,17 +15,14 @@ namespace SharpCompress.Common.Rar; public abstract class RarVolume : Volume { private readonly RarHeaderFactory _headerFactory; - internal int _maxCompressionAlgorithm; + private int _maxCompressionAlgorithm; - internal RarVolume(StreamingMode mode, Stream stream, ReaderOptions options, int index = 0) + internal RarVolume(StreamingMode mode, Stream stream, ReaderOptions options, int index) : base(stream, options, index) => _headerFactory = new RarHeaderFactory(mode, options); -#nullable disable - internal ArchiveHeader ArchiveHeader { get; private set; } + private ArchiveHeader? ArchiveHeader { get; set; } -#nullable enable - - internal StreamingMode Mode => _headerFactory.StreamingMode; + private StreamingMode Mode => _headerFactory.StreamingMode; internal abstract IEnumerable ReadFileParts(); @@ -38,19 +36,16 @@ public abstract class RarVolume : Volume switch (header.HeaderType) { case HeaderType.Mark: - { lastMarkHeader = (MarkHeader)header; } break; case HeaderType.Archive: - { ArchiveHeader = (ArchiveHeader)header; } break; case HeaderType.File: - { var fh = (FileHeader)header; if (_maxCompressionAlgorithm < fh.CompressionAlgorithm) @@ -62,19 +57,13 @@ public abstract class RarVolume : Volume } break; case HeaderType.Service: - { var fh = (FileHeader)header; if (fh.FileName == "CMT") { - var part = CreateFilePart(lastMarkHeader!, fh); var buffer = new byte[fh.CompressedSize]; - part.GetCompressedStream().Read(buffer, 0, buffer.Length); - Comment = System.Text.Encoding.UTF8.GetString( - buffer, - 0, - buffer.Length - 1 - ); + fh.PackedStream.Read(buffer, 0, buffer.Length); + Comment = Encoding.UTF8.GetString(buffer, 0, buffer.Length - 1); } } break; @@ -108,7 +97,7 @@ public abstract class RarVolume : Volume get { EnsureArchiveHeaderLoaded(); - return ArchiveHeader.IsFirstVolume; + return ArchiveHeader?.IsFirstVolume ?? false; } } @@ -120,7 +109,7 @@ public abstract class RarVolume : Volume get { EnsureArchiveHeaderLoaded(); - return ArchiveHeader.IsVolume; + return ArchiveHeader?.IsVolume ?? false; } } @@ -133,7 +122,7 @@ public abstract class RarVolume : Volume get { EnsureArchiveHeaderLoaded(); - return ArchiveHeader.IsSolid; + return ArchiveHeader?.IsSolid ?? false; } } diff --git a/src/SharpCompress/Common/ReaderCancelledException.cs b/src/SharpCompress/Common/ReaderCancelledException.cs new file mode 100644 index 00000000..918e5abb --- /dev/null +++ b/src/SharpCompress/Common/ReaderCancelledException.cs @@ -0,0 +1,12 @@ +using System; + +namespace SharpCompress.Common; + +public class ReaderCancelledException : Exception +{ + public ReaderCancelledException(string message) + : base(message) { } + + public ReaderCancelledException(string message, Exception inner) + : base(message, inner) { } +} diff --git a/src/SharpCompress/Common/SevenZip/ArchiveDatabase.cs b/src/SharpCompress/Common/SevenZip/ArchiveDatabase.cs index 5e42494a..4bac08ad 100644 --- a/src/SharpCompress/Common/SevenZip/ArchiveDatabase.cs +++ b/src/SharpCompress/Common/SevenZip/ArchiveDatabase.cs @@ -15,15 +15,15 @@ internal class ArchiveDatabase internal long _startPositionAfterHeader; internal long _dataStartPosition; - internal List _packSizes = new List(); - internal List _packCrCs = new List(); - internal List _folders = new List(); + internal List _packSizes = new(); + internal List _packCrCs = new(); + internal List _folders = new(); internal List _numUnpackStreamsVector; - internal List _files = new List(); + internal List _files = new(); - internal List _packStreamStartPositions = new List(); - internal List _folderStartFileIndex = new List(); - internal List _fileIndexToFolderIndexMap = new List(); + internal List _packStreamStartPositions = new(); + internal List _folderStartFileIndex = new(); + internal List _fileIndexToFolderIndexMap = new(); internal IPasswordProvider PasswordProvider { get; } @@ -35,7 +35,7 @@ internal class ArchiveDatabase _packSizes.Clear(); _packCrCs.Clear(); _folders.Clear(); - _numUnpackStreamsVector = null!; + _numUnpackStreamsVector = null; _files.Clear(); _packStreamStartPositions.Clear(); diff --git a/src/SharpCompress/Common/SevenZip/ArchiveReader.cs b/src/SharpCompress/Common/SevenZip/ArchiveReader.cs index 42b5b6b2..3e506e0e 100644 --- a/src/SharpCompress/Common/SevenZip/ArchiveReader.cs +++ b/src/SharpCompress/Common/SevenZip/ArchiveReader.cs @@ -14,13 +14,13 @@ namespace SharpCompress.Common.SevenZip; internal class ArchiveReader { internal Stream _stream; - internal Stack _readerStack = new Stack(); + internal Stack _readerStack = new(); internal DataReader _currentReader; internal long _streamOrigin; internal long _streamEnding; internal byte[] _header; - private readonly Dictionary _cachedStreams = new Dictionary(); + private readonly Dictionary _cachedStreams = new(); internal void AddByteStream(byte[] buffer, int offset, int length) { @@ -1359,7 +1359,7 @@ internal class ArchiveReader { internal int _fileIndex; internal int _folderIndex; - internal List _extractStatuses = new List(); + internal List _extractStatuses = new(); internal CExtractFolderInfo(int fileIndex, int folderIndex) { @@ -1393,7 +1393,7 @@ internal class ArchiveReader public override bool CanWrite => false; - public override void Flush() => throw new NotSupportedException(); + public override void Flush() { } public override long Length => throw new NotSupportedException(); diff --git a/src/SharpCompress/Common/SevenZip/CFolder.cs b/src/SharpCompress/Common/SevenZip/CFolder.cs index 9d3516b1..8b0123da 100644 --- a/src/SharpCompress/Common/SevenZip/CFolder.cs +++ b/src/SharpCompress/Common/SevenZip/CFolder.cs @@ -6,11 +6,11 @@ namespace SharpCompress.Common.SevenZip; internal class CFolder { - internal List _coders = new List(); - internal List _bindPairs = new List(); - internal List _packStreams = new List(); + internal List _coders = new(); + internal List _bindPairs = new(); + internal List _packStreams = new(); internal int _firstPackStreamId; - internal List _unpackSizes = new List(); + internal List _unpackSizes = new(); internal uint? _unpackCrc; internal bool UnpackCrcDefined => _unpackCrc != null; diff --git a/src/SharpCompress/Common/SevenZip/CMethodId.cs b/src/SharpCompress/Common/SevenZip/CMethodId.cs index 6dce5773..8494aad5 100644 --- a/src/SharpCompress/Common/SevenZip/CMethodId.cs +++ b/src/SharpCompress/Common/SevenZip/CMethodId.cs @@ -7,10 +7,10 @@ internal readonly struct CMethodId public const ulong K_LZMA2_ID = 0x21; public const ulong K_AES_ID = 0x06F10701; - public static readonly CMethodId K_COPY = new CMethodId(K_COPY_ID); - public static readonly CMethodId K_LZMA = new CMethodId(K_LZMA_ID); - public static readonly CMethodId K_LZMA2 = new CMethodId(K_LZMA2_ID); - public static readonly CMethodId K_AES = new CMethodId(K_AES_ID); + public static readonly CMethodId K_COPY = new(K_COPY_ID); + public static readonly CMethodId K_LZMA = new(K_LZMA_ID); + public static readonly CMethodId K_LZMA2 = new(K_LZMA2_ID); + public static readonly CMethodId K_AES = new(K_AES_ID); public readonly ulong _id; diff --git a/src/SharpCompress/Common/SevenZip/SevenZipEntry.cs b/src/SharpCompress/Common/SevenZip/SevenZipEntry.cs index 66170ba8..fbcb3d00 100644 --- a/src/SharpCompress/Common/SevenZip/SevenZipEntry.cs +++ b/src/SharpCompress/Common/SevenZip/SevenZipEntry.cs @@ -13,7 +13,7 @@ public class SevenZipEntry : Entry public override long Crc => FilePart.Header.Crc ?? 0; - public override string Key => FilePart.Header.Name; + public override string? Key => FilePart.Header.Name; public override string? LinkTarget => null; diff --git a/src/SharpCompress/Common/SevenZip/SevenZipFilePart.cs b/src/SharpCompress/Common/SevenZip/SevenZipFilePart.cs index aad191c9..fe221b53 100644 --- a/src/SharpCompress/Common/SevenZip/SevenZipFilePart.cs +++ b/src/SharpCompress/Common/SevenZip/SevenZipFilePart.cs @@ -41,7 +41,7 @@ internal class SevenZipFilePart : FilePart { if (!Header.HasStream) { - return null!; + throw new InvalidOperationException("File does not have a stream."); } var folderStream = _database.GetFolderStream(_stream, Folder!, _database.PasswordProvider); @@ -73,34 +73,24 @@ internal class SevenZipFilePart : FilePart private const uint K_PPMD = 0x030401; private const uint K_B_ZIP2 = 0x040202; - internal CompressionType GetCompression() + private CompressionType GetCompression() { if (Header.IsDir) - return CompressionType.None; - - var coder = Folder!._coders.First(); - switch (coder._methodId._id) { - case K_LZMA: - case K_LZMA2: - { - return CompressionType.LZMA; - } - case K_PPMD: - { - return CompressionType.PPMd; - } - case K_B_ZIP2: - { - return CompressionType.BZip2; - } - default: - throw new NotImplementedException(); + return CompressionType.None; } + + var coder = Folder.NotNull()._coders.First(); + return coder._methodId._id switch + { + K_LZMA or K_LZMA2 => CompressionType.LZMA, + K_PPMD => CompressionType.PPMd, + K_B_ZIP2 => CompressionType.BZip2, + _ => throw new NotImplementedException() + }; } internal bool IsEncrypted => - Header.IsDir - ? false - : Folder!._coders.FindIndex(c => c._methodId._id == CMethodId.K_AES_ID) != -1; + !Header.IsDir + && Folder?._coders.FindIndex(c => c._methodId._id == CMethodId.K_AES_ID) != -1; } diff --git a/src/SharpCompress/Common/Tar/Headers/TarHeader.cs b/src/SharpCompress/Common/Tar/Headers/TarHeader.cs index ef86ab00..e9aa343c 100644 --- a/src/SharpCompress/Common/Tar/Headers/TarHeader.cs +++ b/src/SharpCompress/Common/Tar/Headers/TarHeader.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using System.Buffers.Binary; using System.IO; @@ -9,12 +7,12 @@ namespace SharpCompress.Common.Tar.Headers; internal sealed class TarHeader { - internal static readonly DateTime EPOCH = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + internal static readonly DateTime EPOCH = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); public TarHeader(ArchiveEncoding archiveEncoding) => ArchiveEncoding = archiveEncoding; - internal string Name { get; set; } - internal string LinkName { get; set; } + internal string? Name { get; set; } + internal string? LinkName { get; set; } internal long Mode { get; set; } internal long UserId { get; set; } @@ -22,7 +20,7 @@ internal sealed class TarHeader internal long Size { get; set; } internal DateTime LastModifiedTime { get; set; } internal EntryType EntryType { get; set; } - internal Stream PackedStream { get; set; } + internal Stream? PackedStream { get; set; } internal ArchiveEncoding ArchiveEncoding { get; } internal const int BLOCK_SIZE = 512; @@ -36,7 +34,9 @@ internal sealed class TarHeader WriteOctalBytes(0, buffer, 116, 8); // group ID //ArchiveEncoding.UTF8.GetBytes("magic").CopyTo(buffer, 257); - var nameByteCount = ArchiveEncoding.GetEncoding().GetByteCount(Name); + var nameByteCount = ArchiveEncoding + .GetEncoding() + .GetByteCount(Name.NotNull("Name is null")); if (nameByteCount > 100) { // Set mock filename and filetype to indicate the next block is the actual name of the file @@ -46,7 +46,7 @@ internal sealed class TarHeader } else { - WriteStringBytes(ArchiveEncoding.Encode(Name), buffer, 100); + WriteStringBytes(ArchiveEncoding.Encode(Name.NotNull("Name is null")), buffer, 100); WriteOctalBytes(Size, buffer, 124, 12); var time = (long)(LastModifiedTime.ToUniversalTime() - EPOCH).TotalSeconds; WriteOctalBytes(time, buffer, 136, 12); @@ -77,7 +77,7 @@ internal sealed class TarHeader // // and then infinite recursion is occured in WriteLongFilenameHeader because truncated.Length is 102. Name = ArchiveEncoding.Decode( - ArchiveEncoding.Encode(Name), + ArchiveEncoding.Encode(Name.NotNull("Name is null")), 0, 100 - ArchiveEncoding.GetEncoding().GetMaxByteCount(1) ); @@ -87,7 +87,7 @@ internal sealed class TarHeader private void WriteLongFilenameHeader(Stream output) { - var nameBytes = ArchiveEncoding.Encode(Name); + var nameBytes = ArchiveEncoding.Encode(Name.NotNull("Name is null")); output.Write(nameBytes, 0, nameBytes.Length); // pad to multiple of BlockSize bytes, and make sure a terminating null is added @@ -101,57 +101,79 @@ internal sealed class TarHeader internal bool Read(BinaryReader reader) { - var buffer = ReadBlock(reader); - if (buffer.Length == 0) - { - return false; - } + string? longName = null; + string? longLinkName = null; + var hasLongValue = true; + byte[] buffer; + EntryType entryType; - // for symlinks, additionally read the linkname - if (ReadEntryType(buffer) == EntryType.SymLink) + do { - LinkName = ArchiveEncoding.Decode(buffer, 157, 100).TrimNulls(); - } - - if (ReadEntryType(buffer) == EntryType.LongName) - { - Name = ReadLongName(reader, buffer); buffer = ReadBlock(reader); - } - else - { - Name = ArchiveEncoding.Decode(buffer, 0, 100).TrimNulls(); - } - EntryType = ReadEntryType(buffer); + if (buffer.Length == 0) + { + return false; + } + + entryType = ReadEntryType(buffer); + + // LongName and LongLink headers can follow each other and need + // to apply to the header that follows them. + if (entryType == EntryType.LongName) + { + longName = ReadLongName(reader, buffer); + continue; + } + else if (entryType == EntryType.LongLink) + { + longLinkName = ReadLongName(reader, buffer); + continue; + } + + hasLongValue = false; + } while (hasLongValue); + + Name = longName ?? ArchiveEncoding.Decode(buffer, 0, 100).TrimNulls(); + EntryType = entryType; Size = ReadSize(buffer); + // for symlinks, additionally read the linkname + if (entryType == EntryType.SymLink || entryType == EntryType.HardLink) + { + LinkName = longLinkName ?? ArchiveEncoding.Decode(buffer, 157, 100).TrimNulls(); + } + Mode = ReadAsciiInt64Base8(buffer, 100, 7); - if (EntryType == EntryType.Directory) + + if (entryType == EntryType.Directory) { Mode |= 0b1_000_000_000; } - UserId = ReadAsciiInt64Base8(buffer, 108, 7); - GroupId = ReadAsciiInt64Base8(buffer, 116, 7); - var unixTimeStamp = ReadAsciiInt64Base8(buffer, 136, 11); - LastModifiedTime = EPOCH.AddSeconds(unixTimeStamp).ToLocalTime(); + UserId = ReadAsciiInt64Base8oldGnu(buffer, 108, 7); + GroupId = ReadAsciiInt64Base8oldGnu(buffer, 116, 7); + var unixTimeStamp = ReadAsciiInt64Base8(buffer, 136, 11); + + LastModifiedTime = EPOCH.AddSeconds(unixTimeStamp).ToLocalTime(); Magic = ArchiveEncoding.Decode(buffer, 257, 6).TrimNulls(); if (!string.IsNullOrEmpty(Magic) && "ustar".Equals(Magic)) { - var namePrefix = ArchiveEncoding.Decode(buffer, 345, 157); - namePrefix = namePrefix.TrimNulls(); + var namePrefix = ArchiveEncoding.Decode(buffer, 345, 157).TrimNulls(); + if (!string.IsNullOrEmpty(namePrefix)) { Name = namePrefix + "/" + Name; } } - if (EntryType != EntryType.LongName && Name.Length == 0) + + if (entryType != EntryType.LongName && Name.Length == 0) { return false; } + return true; } @@ -249,6 +271,24 @@ internal sealed class TarHeader return Convert.ToInt64(s, 8); } + private static long ReadAsciiInt64Base8oldGnu(byte[] buffer, int offset, int count) + { + if (buffer[offset] == 0x80 && buffer[offset + 1] == 0x00) + { + return buffer[offset + 4] << 24 + | buffer[offset + 5] << 16 + | buffer[offset + 6] << 8 + | buffer[offset + 7]; + } + var s = Encoding.UTF8.GetString(buffer, offset, count).TrimNulls(); + + if (string.IsNullOrEmpty(s)) + { + return 0; + } + return Convert.ToInt64(s, 8); + } + private static long ReadAsciiInt64(byte[] buffer, int offset, int count) { var s = Encoding.UTF8.GetString(buffer, offset, count).TrimNulls(); @@ -305,5 +345,5 @@ internal sealed class TarHeader public long? DataStartPosition { get; set; } - public string Magic { get; set; } + public string? Magic { get; set; } } diff --git a/src/SharpCompress/Common/Tar/TarEntry.cs b/src/SharpCompress/Common/Tar/TarEntry.cs index 59743070..2597b837 100644 --- a/src/SharpCompress/Common/Tar/TarEntry.cs +++ b/src/SharpCompress/Common/Tar/TarEntry.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using System.Collections.Generic; using System.IO; @@ -10,9 +8,9 @@ namespace SharpCompress.Common.Tar; public class TarEntry : Entry { - private readonly TarFilePart _filePart; + private readonly TarFilePart? _filePart; - internal TarEntry(TarFilePart filePart, CompressionType type) + internal TarEntry(TarFilePart? filePart, CompressionType type) { _filePart = filePart; CompressionType = type; @@ -22,15 +20,15 @@ public class TarEntry : Entry public override long Crc => 0; - public override string Key => _filePart.Header.Name; + public override string? Key => _filePart?.Header.Name; - public override string LinkTarget => _filePart.Header.LinkName; + public override string? LinkTarget => _filePart?.Header.LinkName; - public override long CompressedSize => _filePart.Header.Size; + public override long CompressedSize => _filePart?.Header.Size ?? 0; - public override long Size => _filePart.Header.Size; + public override long Size => _filePart?.Header.Size ?? 0; - public override DateTime? LastModifiedTime => _filePart.Header.LastModifiedTime; + public override DateTime? LastModifiedTime => _filePart?.Header.LastModifiedTime; public override DateTime? CreatedTime => null; @@ -40,17 +38,17 @@ public class TarEntry : Entry public override bool IsEncrypted => false; - public override bool IsDirectory => _filePart.Header.EntryType == EntryType.Directory; + public override bool IsDirectory => _filePart?.Header.EntryType == EntryType.Directory; public override bool IsSplitAfter => false; - public long Mode => _filePart.Header.Mode; + public long Mode => _filePart?.Header.Mode ?? 0; - public long UserID => _filePart.Header.UserId; + public long UserID => _filePart?.Header.UserId ?? 0; - public long GroupId => _filePart.Header.GroupId; + public long GroupId => _filePart?.Header.GroupId ?? 0; - internal override IEnumerable Parts => _filePart.AsEnumerable(); + internal override IEnumerable Parts => _filePart.Empty(); internal static IEnumerable GetEntries( StreamingMode mode, @@ -59,17 +57,17 @@ public class TarEntry : Entry ArchiveEncoding archiveEncoding ) { - foreach (var h in TarHeaderFactory.ReadHeader(mode, stream, archiveEncoding)) + foreach (var header in TarHeaderFactory.ReadHeader(mode, stream, archiveEncoding)) { - if (h != null) + if (header != null) { if (mode == StreamingMode.Seekable) { - yield return new TarEntry(new TarFilePart(h, stream), compressionType); + yield return new TarEntry(new TarFilePart(header, stream), compressionType); } else { - yield return new TarEntry(new TarFilePart(h, null), compressionType); + yield return new TarEntry(new TarFilePart(header, null), compressionType); } } else diff --git a/src/SharpCompress/Common/Tar/TarFilePart.cs b/src/SharpCompress/Common/Tar/TarFilePart.cs index bfd2fe29..06516114 100644 --- a/src/SharpCompress/Common/Tar/TarFilePart.cs +++ b/src/SharpCompress/Common/Tar/TarFilePart.cs @@ -5,9 +5,9 @@ namespace SharpCompress.Common.Tar; internal sealed class TarFilePart : FilePart { - private readonly Stream _seekableStream; + private readonly Stream? _seekableStream; - internal TarFilePart(TarHeader header, Stream seekableStream) + internal TarFilePart(TarHeader header, Stream? seekableStream) : base(header.ArchiveEncoding) { _seekableStream = seekableStream; @@ -16,16 +16,16 @@ internal sealed class TarFilePart : FilePart internal TarHeader Header { get; } - internal override string FilePartName => Header.Name; + internal override string? FilePartName => Header?.Name; internal override Stream GetCompressedStream() { if (_seekableStream != null) { - _seekableStream.Position = Header.DataStartPosition!.Value; + _seekableStream.Position = Header.DataStartPosition ?? 0; return new TarReadOnlySubStream(_seekableStream, Header.Size); } - return Header.PackedStream; + return Header.PackedStream.NotNull(); } internal override Stream? GetRawStream() => null; diff --git a/src/SharpCompress/Common/Tar/TarHeaderFactory.cs b/src/SharpCompress/Common/Tar/TarHeaderFactory.cs index 85eec5b4..13813353 100644 --- a/src/SharpCompress/Common/Tar/TarHeaderFactory.cs +++ b/src/SharpCompress/Common/Tar/TarHeaderFactory.cs @@ -28,7 +28,6 @@ internal static class TarHeaderFactory switch (mode) { case StreamingMode.Seekable: - { header.DataStartPosition = reader.BaseStream.Position; @@ -37,7 +36,6 @@ internal static class TarHeaderFactory } break; case StreamingMode.Streaming: - { header.PackedStream = new TarReadOnlySubStream(stream, header.Size); } diff --git a/src/SharpCompress/Common/Tar/TarReadOnlySubStream.cs b/src/SharpCompress/Common/Tar/TarReadOnlySubStream.cs index 7ccfd238..09a2fdb8 100644 --- a/src/SharpCompress/Common/Tar/TarReadOnlySubStream.cs +++ b/src/SharpCompress/Common/Tar/TarReadOnlySubStream.cs @@ -1,6 +1,6 @@ -using SharpCompress.IO; using System; using System.IO; +using SharpCompress.IO; namespace SharpCompress.Common.Tar; @@ -47,7 +47,7 @@ internal class TarReadOnlySubStream : NonDisposingStream public override bool CanWrite => false; - public override void Flush() => throw new NotSupportedException(); + public override void Flush() { } public override long Length => throw new NotSupportedException(); @@ -63,7 +63,7 @@ internal class TarReadOnlySubStream : NonDisposingStream { count = (int)BytesLeftToRead; } - int read = Stream.Read(buffer, offset, count); + var read = Stream.Read(buffer, offset, count); if (read > 0) { BytesLeftToRead -= read; @@ -78,7 +78,7 @@ internal class TarReadOnlySubStream : NonDisposingStream { return -1; } - int value = Stream.ReadByte(); + var value = Stream.ReadByte(); if (value != -1) { --BytesLeftToRead; diff --git a/src/SharpCompress/Common/Volume.cs b/src/SharpCompress/Common/Volume.cs index 1f259257..7be161ec 100644 --- a/src/SharpCompress/Common/Volume.cs +++ b/src/SharpCompress/Common/Volume.cs @@ -9,11 +9,11 @@ public abstract class Volume : IVolume { private readonly Stream _actualStream; - internal Volume(Stream stream, ReaderOptions readerOptions, int index = 0) + internal Volume(Stream stream, ReaderOptions? readerOptions, int index = 0) { Index = index; - ReaderOptions = readerOptions; - if (readerOptions.LeaveStreamOpen) + ReaderOptions = readerOptions ?? new ReaderOptions(); + if (ReaderOptions.LeaveStreamOpen) { stream = NonDisposingStream.Create(stream); } @@ -32,7 +32,7 @@ public abstract class Volume : IVolume public virtual int Index { get; internal set; } - public string FileName => (_actualStream as FileStream)?.Name!; + public string? FileName => (_actualStream as FileStream)?.Name; /// /// RarArchive is part of a multi-part archive. diff --git a/src/SharpCompress/Common/Zip/Headers/DirectoryEntryHeader.cs b/src/SharpCompress/Common/Zip/Headers/DirectoryEntryHeader.cs index fc2cb262..2b26d298 100644 --- a/src/SharpCompress/Common/Zip/Headers/DirectoryEntryHeader.cs +++ b/src/SharpCompress/Common/Zip/Headers/DirectoryEntryHeader.cs @@ -14,8 +14,8 @@ internal class DirectoryEntryHeader : ZipFileEntry VersionNeededToExtract = reader.ReadUInt16(); Flags = (HeaderFlags)reader.ReadUInt16(); CompressionMethod = (ZipCompressionMethod)reader.ReadUInt16(); - LastModifiedTime = reader.ReadUInt16(); - LastModifiedDate = reader.ReadUInt16(); + OriginalLastModifiedTime = LastModifiedTime = reader.ReadUInt16(); + OriginalLastModifiedDate = LastModifiedDate = reader.ReadUInt16(); Crc = reader.ReadUInt32(); CompressedSize = reader.ReadUInt32(); UncompressedSize = reader.ReadUInt32(); @@ -52,8 +52,8 @@ internal class DirectoryEntryHeader : ZipFileEntry LoadExtra(extra); - var unicodePathExtra = Extra.FirstOrDefault( - u => u.Type == ExtraDataType.UnicodePathExtraField + var unicodePathExtra = Extra.FirstOrDefault(u => + u.Type == ExtraDataType.UnicodePathExtraField ); if (unicodePathExtra != null && ArchiveEncoding.Forced == null) { @@ -85,6 +85,36 @@ internal class DirectoryEntryHeader : ZipFileEntry RelativeOffsetOfEntryHeader = zip64ExtraData.RelativeOffsetOfEntryHeader; } } + + var unixTimeExtra = Extra.FirstOrDefault(u => u.Type == ExtraDataType.UnixTimeExtraField); + + if (unixTimeExtra is not null) + { + // Tuple order is last modified time, last access time, and creation time. + var unixTimeTuple = ((UnixTimeExtraField)unixTimeExtra).UnicodeTimes; + + if (unixTimeTuple.Item1.HasValue) + { + var dosTime = Utility.DateTimeToDosTime(unixTimeTuple.Item1.Value); + + LastModifiedDate = (ushort)(dosTime >> 16); + LastModifiedTime = (ushort)(dosTime & 0x0FFFF); + } + else if (unixTimeTuple.Item2.HasValue) + { + var dosTime = Utility.DateTimeToDosTime(unixTimeTuple.Item2.Value); + + LastModifiedDate = (ushort)(dosTime >> 16); + LastModifiedTime = (ushort)(dosTime & 0x0FFFF); + } + else if (unixTimeTuple.Item3.HasValue) + { + var dosTime = Utility.DateTimeToDosTime(unixTimeTuple.Item3.Value); + + LastModifiedDate = (ushort)(dosTime >> 16); + LastModifiedTime = (ushort)(dosTime & 0x0FFFF); + } + } } internal ushort Version { get; private set; } diff --git a/src/SharpCompress/Common/Zip/Headers/LocalEntryHeader.cs b/src/SharpCompress/Common/Zip/Headers/LocalEntryHeader.cs index 93cc55ac..1e3dc62d 100644 --- a/src/SharpCompress/Common/Zip/Headers/LocalEntryHeader.cs +++ b/src/SharpCompress/Common/Zip/Headers/LocalEntryHeader.cs @@ -13,8 +13,8 @@ internal class LocalEntryHeader : ZipFileEntry Version = reader.ReadUInt16(); Flags = (HeaderFlags)reader.ReadUInt16(); CompressionMethod = (ZipCompressionMethod)reader.ReadUInt16(); - LastModifiedTime = reader.ReadUInt16(); - LastModifiedDate = reader.ReadUInt16(); + OriginalLastModifiedTime = LastModifiedTime = reader.ReadUInt16(); + OriginalLastModifiedDate = LastModifiedDate = reader.ReadUInt16(); Crc = reader.ReadUInt32(); CompressedSize = reader.ReadUInt32(); UncompressedSize = reader.ReadUInt32(); @@ -42,8 +42,8 @@ internal class LocalEntryHeader : ZipFileEntry LoadExtra(extra); - var unicodePathExtra = Extra.FirstOrDefault( - u => u.Type == ExtraDataType.UnicodePathExtraField + var unicodePathExtra = Extra.FirstOrDefault(u => + u.Type == ExtraDataType.UnicodePathExtraField ); if (unicodePathExtra != null && ArchiveEncoding.Forced == null) { @@ -64,6 +64,36 @@ internal class LocalEntryHeader : ZipFileEntry UncompressedSize = zip64ExtraData.UncompressedSize; } } + + var unixTimeExtra = Extra.FirstOrDefault(u => u.Type == ExtraDataType.UnixTimeExtraField); + + if (unixTimeExtra is not null) + { + // Tuple order is last modified time, last access time, and creation time. + var unixTimeTuple = ((UnixTimeExtraField)unixTimeExtra).UnicodeTimes; + + if (unixTimeTuple.Item1.HasValue) + { + var dosTime = Utility.DateTimeToDosTime(unixTimeTuple.Item1.Value); + + LastModifiedDate = (ushort)(dosTime >> 16); + LastModifiedTime = (ushort)(dosTime & 0x0FFFF); + } + else if (unixTimeTuple.Item2.HasValue) + { + var dosTime = Utility.DateTimeToDosTime(unixTimeTuple.Item2.Value); + + LastModifiedDate = (ushort)(dosTime >> 16); + LastModifiedTime = (ushort)(dosTime & 0x0FFFF); + } + else if (unixTimeTuple.Item3.HasValue) + { + var dosTime = Utility.DateTimeToDosTime(unixTimeTuple.Item3.Value); + + LastModifiedDate = (ushort)(dosTime >> 16); + LastModifiedTime = (ushort)(dosTime & 0x0FFFF); + } + } } internal ushort Version { get; private set; } diff --git a/src/SharpCompress/Common/Zip/Headers/LocalEntryHeaderExtraFactory.cs b/src/SharpCompress/Common/Zip/Headers/LocalEntryHeaderExtraFactory.cs index 80ec9b8f..88e4a187 100644 --- a/src/SharpCompress/Common/Zip/Headers/LocalEntryHeaderExtraFactory.cs +++ b/src/SharpCompress/Common/Zip/Headers/LocalEntryHeaderExtraFactory.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Buffers.Binary; using System.Text; @@ -13,7 +13,8 @@ internal enum ExtraDataType : ushort // Third Party Mappings // -Info-ZIP Unicode Path Extra Field UnicodePathExtraField = 0x7075, - Zip64ExtendedInformationExtraField = 0x0001 + Zip64ExtendedInformationExtraField = 0x0001, + UnixTimeExtraField = 0x5455 } internal class ExtraData @@ -145,6 +146,84 @@ internal sealed class Zip64ExtendedInformationExtraField : ExtraData public uint VolumeNumber { get; private set; } } +internal sealed class UnixTimeExtraField : ExtraData +{ + public UnixTimeExtraField(ExtraDataType type, ushort length, byte[] dataBytes) + : base(type, length, dataBytes) { } + + /// + /// The unix modified time, last access time, and creation time, if set. + /// + /// Must return Tuple explicitly due to net462 support. + internal Tuple UnicodeTimes + { + get + { + // There has to be at least 5 byte for there to be a timestamp. + // 1 byte for flags and 4 bytes for a timestamp. + if (DataBytes is null || DataBytes.Length < 5) + { + return Tuple.Create(null, null, null); + } + + var flags = DataBytes[0]; + var isModifiedTimeSpecified = (flags & 0x01) == 1; + var isLastAccessTimeSpecified = (flags & 0x02) == 1; + var isCreationTimeSpecified = (flags & 0x04) == 1; + var currentIndex = 1; + DateTime? modifiedTime = null; + DateTime? lastAccessTime = null; + DateTime? creationTime = null; + + if (isModifiedTimeSpecified) + { + var modifiedEpochTime = BinaryPrimitives.ReadInt32LittleEndian( + DataBytes.AsSpan(currentIndex, 4) + ); + + currentIndex += 4; + modifiedTime = DateTimeOffset.FromUnixTimeSeconds(modifiedEpochTime).UtcDateTime; + } + + if (isLastAccessTimeSpecified) + { + if (currentIndex + 4 > DataBytes.Length) + { + throw new ArchiveException("Invalid UnicodeExtraTime field"); + } + + var lastAccessEpochTime = BinaryPrimitives.ReadInt32LittleEndian( + DataBytes.AsSpan(currentIndex, 4) + ); + + currentIndex += 4; + lastAccessTime = DateTimeOffset + .FromUnixTimeSeconds(lastAccessEpochTime) + .UtcDateTime; + } + + if (isCreationTimeSpecified) + { + if (currentIndex + 4 > DataBytes.Length) + { + throw new ArchiveException("Invalid UnicodeExtraTime field"); + } + + var creationTimeEpochTime = BinaryPrimitives.ReadInt32LittleEndian( + DataBytes.AsSpan(currentIndex, 4) + ); + + currentIndex += 4; + creationTime = DateTimeOffset + .FromUnixTimeSeconds(creationTimeEpochTime) + .UtcDateTime; + } + + return Tuple.Create(modifiedTime, lastAccessTime, creationTime); + } + } +} + internal static class LocalEntryHeaderExtraFactory { internal static ExtraData Create(ExtraDataType type, ushort length, byte[] extraData) => @@ -154,6 +233,7 @@ internal static class LocalEntryHeaderExtraFactory => new ExtraUnicodePathExtraField(type, length, extraData), ExtraDataType.Zip64ExtendedInformationExtraField => new Zip64ExtendedInformationExtraField(type, length, extraData), + ExtraDataType.UnixTimeExtraField => new UnixTimeExtraField(type, length, extraData), _ => new ExtraData(type, length, extraData) }; } diff --git a/src/SharpCompress/Common/Zip/Headers/ZipFileEntry.cs b/src/SharpCompress/Common/Zip/Headers/ZipFileEntry.cs index 98d230f5..0570bf6d 100644 --- a/src/SharpCompress/Common/Zip/Headers/ZipFileEntry.cs +++ b/src/SharpCompress/Common/Zip/Headers/ZipFileEntry.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using System.Buffers.Binary; using System.Collections.Generic; @@ -20,21 +18,21 @@ internal abstract class ZipFileEntry : ZipHeader { get { - if (Name.EndsWith('/')) + if (Name?.EndsWith('/') ?? false) { return true; } //.NET Framework 4.5 : System.IO.Compression::CreateFromDirectory() probably writes backslashes to headers - return CompressedSize == 0 && UncompressedSize == 0 && Name.EndsWith('\\'); + return CompressedSize == 0 && UncompressedSize == 0 && (Name?.EndsWith('\\') ?? false); } } - internal Stream PackedStream { get; set; } + internal Stream? PackedStream { get; set; } internal ArchiveEncoding ArchiveEncoding { get; } - internal string Name { get; set; } + internal string? Name { get; set; } internal HeaderFlags Flags { get; set; } @@ -48,7 +46,7 @@ internal abstract class ZipFileEntry : ZipHeader internal List Extra { get; set; } - public string Password { get; set; } + public string? Password { get; set; } internal PkwareTraditionalEncryptionData ComposeEncryptionData(Stream archiveStream) { @@ -65,10 +63,28 @@ internal abstract class ZipFileEntry : ZipHeader return encryptionData; } - internal WinzipAesEncryptionData WinzipAesEncryptionData { get; set; } + internal WinzipAesEncryptionData? WinzipAesEncryptionData { get; set; } + /// + /// The last modified date as read from the Local or Central Directory header. + /// + internal ushort OriginalLastModifiedDate { get; set; } + + /// + /// The last modified date from the UnixTimeExtraField, if present, or the + /// Local or Cental Directory header, if not. + /// internal ushort LastModifiedDate { get; set; } + /// + /// The last modified time as read from the Local or Central Directory header. + /// + internal ushort OriginalLastModifiedTime { get; set; } + + /// + /// The last modified time from the UnixTimeExtraField, if present, or the + /// Local or Cental Directory header, if not. + /// internal ushort LastModifiedTime { get; set; } internal uint Crc { get; set; } @@ -101,7 +117,7 @@ internal abstract class ZipFileEntry : ZipHeader } } - internal ZipFilePart Part { get; set; } + internal ZipFilePart? Part { get; set; } internal bool IsZip64 => CompressedSize >= uint.MaxValue; } diff --git a/src/SharpCompress/Common/Zip/PkwareTraditionalCryptoStream.cs b/src/SharpCompress/Common/Zip/PkwareTraditionalCryptoStream.cs index 273a7a3c..6ac48c6f 100644 --- a/src/SharpCompress/Common/Zip/PkwareTraditionalCryptoStream.cs +++ b/src/SharpCompress/Common/Zip/PkwareTraditionalCryptoStream.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; namespace SharpCompress.Common.Zip; @@ -87,10 +87,7 @@ internal class PkwareTraditionalCryptoStream : Stream _stream.Write(encrypted, 0, encrypted.Length); } - public override void Flush() - { - //throw new NotSupportedException(); - } + public override void Flush() { } public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException(); diff --git a/src/SharpCompress/Common/Zip/PkwareTraditionalEncryptionData.cs b/src/SharpCompress/Common/Zip/PkwareTraditionalEncryptionData.cs index 353000d8..7b517857 100644 --- a/src/SharpCompress/Common/Zip/PkwareTraditionalEncryptionData.cs +++ b/src/SharpCompress/Common/Zip/PkwareTraditionalEncryptionData.cs @@ -6,7 +6,7 @@ namespace SharpCompress.Common.Zip; internal class PkwareTraditionalEncryptionData { - private static readonly CRC32 CRC32 = new CRC32(); + private static readonly CRC32 CRC32 = new(); private readonly uint[] _keys = { 0x12345678, 0x23456789, 0x34567890 }; private readonly ArchiveEncoding _archiveEncoding; @@ -39,7 +39,7 @@ internal class PkwareTraditionalEncryptionData { throw new CryptographicException("The password did not match."); } - if (plainTextHeader[11] != (byte)((header.LastModifiedTime >> 8) & 0xff)) + if (plainTextHeader[11] != (byte)((header.OriginalLastModifiedTime >> 8) & 0xff)) { throw new CryptographicException("The password did not match."); } @@ -103,7 +103,7 @@ internal class PkwareTraditionalEncryptionData internal byte[] StringToByteArray(string value) { - var a = _archiveEncoding.Password.GetBytes(value); + var a = _archiveEncoding.GetPasswordEncoding().GetBytes(value); return a; } diff --git a/src/SharpCompress/Common/Zip/SeekableZipFilePart.cs b/src/SharpCompress/Common/Zip/SeekableZipFilePart.cs index 63fdc933..e37008e5 100644 --- a/src/SharpCompress/Common/Zip/SeekableZipFilePart.cs +++ b/src/SharpCompress/Common/Zip/SeekableZipFilePart.cs @@ -42,16 +42,16 @@ internal class SeekableZipFilePart : ZipFilePart protected override Stream CreateBaseStream() { - BaseStream.Position = Header.DataStartPosition!.Value; + BaseStream.Position = Header.DataStartPosition.NotNull(); if ( (Header.CompressedSize == 0) && FlagUtility.HasFlag(Header.Flags, HeaderFlags.UsePostDataDescriptor) - && (_directoryEntryHeader?.HasData == true) - && (_directoryEntryHeader?.CompressedSize != 0) + && _directoryEntryHeader.HasData + && (_directoryEntryHeader.CompressedSize != 0) ) { - return new ReadOnlySubStream(BaseStream, _directoryEntryHeader!.CompressedSize); + return new ReadOnlySubStream(BaseStream, _directoryEntryHeader.CompressedSize); } return BaseStream; diff --git a/src/SharpCompress/Common/Zip/StreamingZipFilePart.cs b/src/SharpCompress/Common/Zip/StreamingZipFilePart.cs index 1cd1f61f..97e44b6b 100644 --- a/src/SharpCompress/Common/Zip/StreamingZipFilePart.cs +++ b/src/SharpCompress/Common/Zip/StreamingZipFilePart.cs @@ -13,7 +13,7 @@ internal sealed class StreamingZipFilePart : ZipFilePart internal StreamingZipFilePart(ZipFileEntry header, Stream stream) : base(header, stream) { } - protected override Stream CreateBaseStream() => Header.PackedStream; + protected override Stream CreateBaseStream() => Header.PackedStream.NotNull(); internal override Stream GetCompressedStream() { diff --git a/src/SharpCompress/Common/Zip/StreamingZipHeaderFactory.cs b/src/SharpCompress/Common/Zip/StreamingZipHeaderFactory.cs index e76a2856..508dfd1a 100644 --- a/src/SharpCompress/Common/Zip/StreamingZipHeaderFactory.cs +++ b/src/SharpCompress/Common/Zip/StreamingZipHeaderFactory.cs @@ -15,10 +15,7 @@ internal class StreamingZipHeaderFactory : ZipHeaderFactory ArchiveEncoding archiveEncoding, IEnumerable? entries ) - : base(StreamingMode.Streaming, password, archiveEncoding) - { - _entries = entries; - } + : base(StreamingMode.Streaming, password, archiveEncoding) => _entries = entries; internal IEnumerable ReadStreamHeader(Stream stream) { @@ -39,16 +36,19 @@ internal class StreamingZipHeaderFactory : ZipHeaderFactory uint headerBytes = 0; if ( _lastEntryHeader != null - && ( - FlagUtility.HasFlag(_lastEntryHeader.Flags, HeaderFlags.UsePostDataDescriptor) - || _lastEntryHeader.IsZip64 - ) + && FlagUtility.HasFlag(_lastEntryHeader.Flags, HeaderFlags.UsePostDataDescriptor) ) { + if (_lastEntryHeader.Part is null) + { + continue; + } reader = ((StreamingZipFilePart)_lastEntryHeader.Part).FixStreamedFileLocation( ref rewindableStream ); + var pos = rewindableStream.CanSeek ? (long?)rewindableStream.Position : null; + var crc = reader.ReadUInt32(); if (crc == POST_DATA_DESCRIPTOR) { @@ -81,6 +81,60 @@ internal class StreamingZipHeaderFactory : ZipHeaderFactory _lastEntryHeader.DataStartPosition = pos - _lastEntryHeader.CompressedSize; } } + else if (_lastEntryHeader != null && _lastEntryHeader.IsZip64) + { + if (_lastEntryHeader.Part is null) + continue; + + reader = ((StreamingZipFilePart)_lastEntryHeader.Part).FixStreamedFileLocation( + ref rewindableStream + ); + + var pos = rewindableStream.CanSeek ? (long?)rewindableStream.Position : null; + + headerBytes = reader.ReadUInt32(); + + var version = reader.ReadUInt16(); + var flags = (HeaderFlags)reader.ReadUInt16(); + var compressionMethod = (ZipCompressionMethod)reader.ReadUInt16(); + var lastModifiedDate = reader.ReadUInt16(); + var lastModifiedTime = reader.ReadUInt16(); + + var crc = reader.ReadUInt32(); + + if (crc == POST_DATA_DESCRIPTOR) + { + crc = reader.ReadUInt32(); + } + _lastEntryHeader.Crc = crc; + + // The DataDescriptor can be either 64bit or 32bit + var compressed_size = reader.ReadUInt32(); + var uncompressed_size = reader.ReadUInt32(); + + // Check if we have header or 64bit DataDescriptor + var test_header = !(headerBytes == 0x04034b50 || headerBytes == 0x02014b50); + + var test_64bit = ((long)uncompressed_size << 32) | compressed_size; + if (test_64bit == _lastEntryHeader.CompressedSize && test_header) + { + _lastEntryHeader.UncompressedSize = + ((long)reader.ReadUInt32() << 32) | headerBytes; + headerBytes = reader.ReadUInt32(); + } + else + { + _lastEntryHeader.UncompressedSize = uncompressed_size; + } + + if (pos.HasValue) + { + _lastEntryHeader.DataStartPosition = pos - _lastEntryHeader.CompressedSize; + + // 4 = First 4 bytes of the entry header (i.e. 50 4B 03 04) + rewindableStream.Position = pos.Value + 4; + } + } else { headerBytes = reader.ReadUInt32(); @@ -97,13 +151,12 @@ internal class StreamingZipHeaderFactory : ZipHeaderFactory if (header.ZipHeaderType == ZipHeaderType.LocalEntry) { var local_header = ((LocalEntryHeader)header); - var dir_header = _entries?.FirstOrDefault( - entry => - entry.Key == local_header.Name - && local_header.CompressedSize == 0 - && local_header.UncompressedSize == 0 - && local_header.Crc == 0 - && local_header.IsDirectory == false + var dir_header = _entries?.FirstOrDefault(entry => + entry.Key == local_header.Name + && local_header.CompressedSize == 0 + && local_header.UncompressedSize == 0 + && local_header.Crc == 0 + && local_header.IsDirectory == false ); if (dir_header != null) diff --git a/src/SharpCompress/Common/Zip/WinzipAesCryptoStream.cs b/src/SharpCompress/Common/Zip/WinzipAesCryptoStream.cs index 093ac034..5742458a 100644 --- a/src/SharpCompress/Common/Zip/WinzipAesCryptoStream.cs +++ b/src/SharpCompress/Common/Zip/WinzipAesCryptoStream.cs @@ -73,7 +73,7 @@ internal class WinzipAesCryptoStream : Stream } } - public override void Flush() => throw new NotSupportedException(); + public override void Flush() { } public override int Read(byte[] buffer, int offset, int count) { diff --git a/src/SharpCompress/Common/Zip/WinzipAesEncryptionData.cs b/src/SharpCompress/Common/Zip/WinzipAesEncryptionData.cs index 251b919e..31322019 100644 --- a/src/SharpCompress/Common/Zip/WinzipAesEncryptionData.cs +++ b/src/SharpCompress/Common/Zip/WinzipAesEncryptionData.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using System.Buffers.Binary; using System.Security.Cryptography; @@ -10,12 +8,7 @@ internal class WinzipAesEncryptionData { private const int RFC2898_ITERATIONS = 1000; - private readonly byte[] _salt; private readonly WinzipAesKeySize _keySize; - private readonly byte[] _passwordVerifyValue; - private readonly string _password; - - private byte[] _generatedVerifyValue; internal WinzipAesEncryptionData( WinzipAesKeySize keySize, @@ -25,10 +18,28 @@ internal class WinzipAesEncryptionData ) { _keySize = keySize; - _salt = salt; - _passwordVerifyValue = passwordVerifyValue; - _password = password; - Initialize(); + +#if NETFRAMEWORK || NETSTANDARD2_0 + var rfc2898 = new Rfc2898DeriveBytes(password, salt, RFC2898_ITERATIONS); +#else + var rfc2898 = new Rfc2898DeriveBytes( + password, + salt, + RFC2898_ITERATIONS, + HashAlgorithmName.SHA1 + ); +#endif + + KeyBytes = rfc2898.GetBytes(KeySizeInBytes); // 16 or 24 or 32 ??? + IvBytes = rfc2898.GetBytes(KeySizeInBytes); + var generatedVerifyValue = rfc2898.GetBytes(2); + + var verify = BinaryPrimitives.ReadInt16LittleEndian(passwordVerifyValue); + var generated = BinaryPrimitives.ReadInt16LittleEndian(generatedVerifyValue); + if (verify != generated) + { + throw new InvalidFormatException("bad password"); + } } internal byte[] IvBytes { get; set; } @@ -45,32 +56,4 @@ internal class WinzipAesEncryptionData WinzipAesKeySize.KeySize256 => 32, _ => throw new InvalidOperationException(), }; - - private void Initialize() - { -#if NET7_0 - var rfc2898 = new Rfc2898DeriveBytes( - _password, - _salt, - RFC2898_ITERATIONS, - HashAlgorithmName.SHA1 - ); -#else - var rfc2898 = new Rfc2898DeriveBytes(_password, _salt, RFC2898_ITERATIONS); -#endif - - KeyBytes = rfc2898.GetBytes(KeySizeInBytes); // 16 or 24 or 32 ??? - IvBytes = rfc2898.GetBytes(KeySizeInBytes); - _generatedVerifyValue = rfc2898.GetBytes(2); - - var verify = BinaryPrimitives.ReadInt16LittleEndian(_passwordVerifyValue); - if (_password != null) - { - var generated = BinaryPrimitives.ReadInt16LittleEndian(_generatedVerifyValue); - if (verify != generated) - { - throw new InvalidFormatException("bad password"); - } - } - } } diff --git a/src/SharpCompress/Common/Zip/ZipCompressionMethod.cs b/src/SharpCompress/Common/Zip/ZipCompressionMethod.cs index 13c2dbe7..a98ed667 100644 --- a/src/SharpCompress/Common/Zip/ZipCompressionMethod.cs +++ b/src/SharpCompress/Common/Zip/ZipCompressionMethod.cs @@ -3,6 +3,7 @@ namespace SharpCompress.Common.Zip; internal enum ZipCompressionMethod { None = 0, + Shrink = 1, Deflate = 8, Deflate64 = 9, BZip2 = 12, diff --git a/src/SharpCompress/Common/Zip/ZipEntry.cs b/src/SharpCompress/Common/Zip/ZipEntry.cs index 2c544b94..c2cecf39 100644 --- a/src/SharpCompress/Common/Zip/ZipEntry.cs +++ b/src/SharpCompress/Common/Zip/ZipEntry.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using System.Collections.Generic; using SharpCompress.Common.Zip.Headers; @@ -8,67 +6,43 @@ namespace SharpCompress.Common.Zip; public class ZipEntry : Entry { - private readonly ZipFilePart _filePart; + private readonly ZipFilePart? _filePart; - internal ZipEntry(ZipFilePart filePart) + internal ZipEntry(ZipFilePart? filePart) { - if (filePart != null) + if (filePart == null) { - _filePart = filePart; - LastModifiedTime = Utility.DosDateToDateTime( - filePart.Header.LastModifiedDate, - filePart.Header.LastModifiedTime - ); + return; } + _filePart = filePart; + LastModifiedTime = Utility.DosDateToDateTime( + filePart.Header.LastModifiedDate, + filePart.Header.LastModifiedTime + ); } - public override CompressionType CompressionType - { - get + public override CompressionType CompressionType => + _filePart?.Header.CompressionMethod switch { - switch (_filePart.Header.CompressionMethod) - { - case ZipCompressionMethod.BZip2: - { - return CompressionType.BZip2; - } - case ZipCompressionMethod.Deflate: - { - return CompressionType.Deflate; - } - case ZipCompressionMethod.Deflate64: - { - return CompressionType.Deflate64; - } - case ZipCompressionMethod.LZMA: - { - return CompressionType.LZMA; - } - case ZipCompressionMethod.PPMd: - { - return CompressionType.PPMd; - } - case ZipCompressionMethod.None: - { - return CompressionType.None; - } - default: - { - return CompressionType.Unknown; - } - } - } - } + ZipCompressionMethod.BZip2 => CompressionType.BZip2, + ZipCompressionMethod.Deflate => CompressionType.Deflate, + ZipCompressionMethod.Deflate64 => CompressionType.Deflate64, + ZipCompressionMethod.LZMA => CompressionType.LZMA, + ZipCompressionMethod.PPMd => CompressionType.PPMd, + ZipCompressionMethod.None => CompressionType.None, + ZipCompressionMethod.Shrink => CompressionType.Shrink, + _ => CompressionType.Unknown + }; - public override long Crc => _filePart.Header.Crc; + public override long Crc => _filePart?.Header.Crc ?? 0; - public override string Key => _filePart.Header.Name; + public override string? Key => _filePart?.Header.Name; - public override string LinkTarget => null; + public override string? LinkTarget => null; - public override long CompressedSize => _filePart.Header.CompressedSize; + public override long CompressedSize => _filePart?.Header.CompressedSize ?? 0; - public override long Size => _filePart.Header.UncompressedSize; + public override long Size => _filePart?.Header.UncompressedSize ?? 0; public override DateTime? LastModifiedTime { get; } @@ -79,11 +53,11 @@ public class ZipEntry : Entry public override DateTime? ArchivedTime => null; public override bool IsEncrypted => - FlagUtility.HasFlag(_filePart.Header.Flags, HeaderFlags.Encrypted); + FlagUtility.HasFlag(_filePart?.Header.Flags ?? HeaderFlags.None, HeaderFlags.Encrypted); - public override bool IsDirectory => _filePart.Header.IsDirectory; + public override bool IsDirectory => _filePart?.Header.IsDirectory ?? false; public override bool IsSplitAfter => false; - internal override IEnumerable Parts => _filePart.AsEnumerable(); + internal override IEnumerable Parts => _filePart.Empty(); } diff --git a/src/SharpCompress/Common/Zip/ZipFilePart.cs b/src/SharpCompress/Common/Zip/ZipFilePart.cs index faefdf15..e8516150 100644 --- a/src/SharpCompress/Common/Zip/ZipFilePart.cs +++ b/src/SharpCompress/Common/Zip/ZipFilePart.cs @@ -9,6 +9,7 @@ using SharpCompress.Compressors.Deflate; using SharpCompress.Compressors.Deflate64; using SharpCompress.Compressors.LZMA; using SharpCompress.Compressors.PPMd; +using SharpCompress.Compressors.Shrink; using SharpCompress.Compressors.Xz; using SharpCompress.IO; using ZstdSharp; @@ -28,7 +29,7 @@ internal abstract class ZipFilePart : FilePart internal Stream BaseStream { get; } internal ZipFileEntry Header { get; set; } - internal override string FilePartName => Header.Name; + internal override string? FilePartName => Header.Name; internal override Stream GetCompressedStream() { @@ -79,6 +80,15 @@ internal abstract class ZipFilePart : FilePart return new DataDescriptorStream(stream); } + case ZipCompressionMethod.Shrink: + { + return new ShrinkStream( + stream, + CompressionMode.Decompress, + Header.CompressedSize, + Header.UncompressedSize + ); + } case ZipCompressionMethod.Deflate: { return new DeflateStream(stream, CompressionMode.Decompress); @@ -192,6 +202,7 @@ internal abstract class ZipFilePart : FilePart switch (Header.CompressionMethod) { case ZipCompressionMethod.None: + case ZipCompressionMethod.Shrink: case ZipCompressionMethod.Deflate: case ZipCompressionMethod.Deflate64: case ZipCompressionMethod.BZip2: diff --git a/src/SharpCompress/Common/Zip/ZipHeaderFactory.cs b/src/SharpCompress/Common/Zip/ZipHeaderFactory.cs index 8a58e220..45869ff6 100644 --- a/src/SharpCompress/Common/Zip/ZipHeaderFactory.cs +++ b/src/SharpCompress/Common/Zip/ZipHeaderFactory.cs @@ -55,7 +55,13 @@ internal class ZipHeaderFactory } case POST_DATA_DESCRIPTOR: { - if (FlagUtility.HasFlag(_lastEntryHeader!.Flags, HeaderFlags.UsePostDataDescriptor)) + if ( + _lastEntryHeader != null + && FlagUtility.HasFlag( + _lastEntryHeader.NotNull().Flags, + HeaderFlags.UsePostDataDescriptor + ) + ) { _lastEntryHeader.Crc = reader.ReadUInt32(); _lastEntryHeader.CompressedSize = zip64 @@ -142,8 +148,8 @@ internal class ZipHeaderFactory if (entryHeader.CompressionMethod == ZipCompressionMethod.WinzipAes) { - var data = entryHeader.Extra.SingleOrDefault( - x => x.Type == ExtraDataType.WinZipAes + var data = entryHeader.Extra.SingleOrDefault(x => + x.Type == ExtraDataType.WinZipAes ); if (data != null) { diff --git a/src/SharpCompress/Compressors/BZip2/BZip2Stream.cs b/src/SharpCompress/Compressors/BZip2/BZip2Stream.cs index feea12d1..2c00ea71 100644 --- a/src/SharpCompress/Compressors/BZip2/BZip2Stream.cs +++ b/src/SharpCompress/Compressors/BZip2/BZip2Stream.cs @@ -69,7 +69,7 @@ public sealed class BZip2Stream : Stream public override void SetLength(long value) => stream.SetLength(value); -#if !NETFRAMEWORK && !NETSTANDARD2_0 +#if !NETFRAMEWORK&& !NETSTANDARD2_0 public override int Read(Span buffer) => stream.Read(buffer); diff --git a/src/SharpCompress/Compressors/BZip2/CBZip2InputStream.cs b/src/SharpCompress/Compressors/BZip2/CBZip2InputStream.cs index e03a5096..a467942f 100644 --- a/src/SharpCompress/Compressors/BZip2/CBZip2InputStream.cs +++ b/src/SharpCompress/Compressors/BZip2/CBZip2InputStream.cs @@ -1,4 +1,4 @@ -#nullable disable +#nullable disable using System; using System.IO; @@ -42,14 +42,17 @@ internal class CBZip2InputStream : Stream private static void Cadvise() { //System.out.Println("CRC Error"); - //throw new CCoruptionError(); + throw new InvalidOperationException("BZip2 error"); } private static void BadBGLengths() => Cadvise(); private static void BitStreamEOF() => Cadvise(); - private static void CompressedStreamEOF() => Cadvise(); + private static void CompressedStreamEOF() + { + throw new InvalidOperationException("BZip2 compressed file ends unexpectedly"); + } private void MakeMaps() { @@ -87,7 +90,7 @@ internal class CBZip2InputStream : Stream private int bsBuff; private int bsLive; - private readonly CRC mCrc = new CRC(); + private readonly CRC mCrc = new(); private readonly bool[] inUse = new bool[256]; private int nInUse; diff --git a/src/SharpCompress/Compressors/BZip2/CBZip2OutputStream.cs b/src/SharpCompress/Compressors/BZip2/CBZip2OutputStream.cs index a975ffbd..bf01c2c3 100644 --- a/src/SharpCompress/Compressors/BZip2/CBZip2OutputStream.cs +++ b/src/SharpCompress/Compressors/BZip2/CBZip2OutputStream.cs @@ -284,7 +284,7 @@ internal sealed class CBZip2OutputStream : Stream private int bytesOut; private int bsBuff; private int bsLive; - private readonly CRC mCrc = new CRC(); + private readonly CRC mCrc = new(); private readonly bool[] inUse = new bool[256]; private int nInUse; diff --git a/src/SharpCompress/Compressors/Deflate/DeflateManager.cs b/src/SharpCompress/Compressors/Deflate/DeflateManager.cs index 27ad4018..cccd0237 100644 --- a/src/SharpCompress/Compressors/Deflate/DeflateManager.cs +++ b/src/SharpCompress/Compressors/Deflate/DeflateManager.cs @@ -69,7 +69,6 @@ // ----------------------------------------------------------------------- using System; - using SharpCompress.Algorithms; namespace SharpCompress.Compressors.Deflate; @@ -343,9 +342,9 @@ internal sealed partial class DeflateManager private readonly short[] dyn_dtree; // distance tree private readonly short[] bl_tree; // Huffman tree for bit lengths - private readonly Tree treeLiterals = new Tree(); // desc for literal tree - private readonly Tree treeDistances = new Tree(); // desc for distance tree - private readonly Tree treeBitLengths = new Tree(); // desc for bit length tree + private readonly Tree treeLiterals = new(); // desc for literal tree + private readonly Tree treeDistances = new(); // desc for distance tree + private readonly Tree treeBitLengths = new(); // desc for bit length tree // number of codes at each bit length for an optimal tree private readonly short[] bl_count = new short[InternalConstants.MAX_BITS + 1]; @@ -1788,21 +1787,14 @@ internal sealed partial class DeflateManager return status == BUSY_STATE ? ZlibConstants.Z_DATA_ERROR : ZlibConstants.Z_OK; } - private void SetDeflater() - { - switch (config.Flavor) + private void SetDeflater() => + DeflateFunction = config.Flavor switch { - case DeflateFlavor.Store: - DeflateFunction = DeflateNone; - break; - case DeflateFlavor.Fast: - DeflateFunction = DeflateFast; - break; - case DeflateFlavor.Slow: - DeflateFunction = DeflateSlow; - break; - } - } + DeflateFlavor.Store => DeflateNone, + DeflateFlavor.Fast => DeflateFast, + DeflateFlavor.Slow => DeflateSlow, + _ => DeflateFunction + }; internal int SetParams(CompressionLevel level, CompressionStrategy strategy) { @@ -1959,7 +1951,9 @@ internal sealed partial class DeflateManager // returning Z_STREAM_END instead of Z_BUFF_ERROR. } else if ( - _codec.AvailableBytesIn == 0 && (int)flush <= old_flush && flush != FlushType.Finish + _codec.AvailableBytesIn == 0 + && (int)flush <= old_flush + && flush != FlushType.Finish ) { // workitem 8557 diff --git a/src/SharpCompress/Compressors/Deflate/DeflateStream.cs b/src/SharpCompress/Compressors/Deflate/DeflateStream.cs index 05003d7f..5566e817 100644 --- a/src/SharpCompress/Compressors/Deflate/DeflateStream.cs +++ b/src/SharpCompress/Compressors/Deflate/DeflateStream.cs @@ -366,9 +366,5 @@ public class DeflateStream : Stream #endregion public MemoryStream InputBuffer => - new MemoryStream( - _baseStream._z.InputBuffer, - _baseStream._z.NextIn, - _baseStream._z.AvailableBytesIn - ); + new(_baseStream._z.InputBuffer, _baseStream._z.NextIn, _baseStream._z.AvailableBytesIn); } diff --git a/src/SharpCompress/Compressors/Deflate/GZipStream.cs b/src/SharpCompress/Compressors/Deflate/GZipStream.cs index c547df14..0f7beca8 100644 --- a/src/SharpCompress/Compressors/Deflate/GZipStream.cs +++ b/src/SharpCompress/Compressors/Deflate/GZipStream.cs @@ -35,15 +35,7 @@ namespace SharpCompress.Compressors.Deflate; public class GZipStream : Stream { - internal static readonly DateTime UNIX_EPOCH = new DateTime( - 1970, - 1, - 1, - 0, - 0, - 0, - DateTimeKind.Utc - ); + internal static readonly DateTime UNIX_EPOCH = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); private string? _comment; private string? _fileName; diff --git a/src/SharpCompress/Compressors/Deflate/Inflate.cs b/src/SharpCompress/Compressors/Deflate/Inflate.cs index ebea2805..d768548b 100644 --- a/src/SharpCompress/Compressors/Deflate/Inflate.cs +++ b/src/SharpCompress/Compressors/Deflate/Inflate.cs @@ -64,7 +64,6 @@ // ----------------------------------------------------------------------- using System; - using SharpCompress.Algorithms; namespace SharpCompress.Compressors.Deflate; @@ -106,11 +105,11 @@ internal sealed class InflateBlocks internal int[] blens; // bit lengths of codes internal uint check; // check on output internal object checkfn; // check function - internal InflateCodes codes = new InflateCodes(); // if CODES, current state + internal InflateCodes codes = new(); // if CODES, current state internal int end; // one byte after sliding window internal int[] hufts; // single malloc for tree space internal int index; // index into blens (or border) - internal InfTree inftree = new InfTree(); + internal InfTree inftree = new(); internal int last; // true if this block is the last block internal int left; // if STORED, bytes left to copy private InflateBlockMode mode; // current inflate_block mode diff --git a/src/SharpCompress/Compressors/Deflate/ZlibBaseStream.cs b/src/SharpCompress/Compressors/Deflate/ZlibBaseStream.cs index 19a3c60a..94b1491d 100644 --- a/src/SharpCompress/Compressors/Deflate/ZlibBaseStream.cs +++ b/src/SharpCompress/Compressors/Deflate/ZlibBaseStream.cs @@ -30,8 +30,8 @@ using System; using System.Buffers.Binary; using System.Collections.Generic; using System.IO; -using SharpCompress.Common.Tar.Headers; using System.Text; +using SharpCompress.Common.Tar.Headers; namespace SharpCompress.Compressors.Deflate; @@ -102,7 +102,7 @@ internal class ZlibBaseStream : Stream { if (_z is null) { - bool wantRfc1950Header = (_flavor == ZlibStreamFlavor.ZLIB); + var wantRfc1950Header = (_flavor == ZlibStreamFlavor.ZLIB); _z = new ZlibCodec(); if (_compressionMode == CompressionMode.Decompress) { @@ -147,13 +147,13 @@ internal class ZlibBaseStream : Stream z.InputBuffer = buffer; _z.NextIn = offset; _z.AvailableBytesIn = count; - bool done = false; + var done = false; do { _z.OutputBuffer = workingBuffer; _z.NextOut = 0; _z.AvailableBytesOut = _workingBuffer.Length; - int rc = (_wantCompress) ? _z.Deflate(_flushMode) : _z.Inflate(_flushMode); + var rc = (_wantCompress) ? _z.Deflate(_flushMode) : _z.Inflate(_flushMode); if (rc != ZlibConstants.Z_OK && rc != ZlibConstants.Z_STREAM_END) { throw new ZlibException((_wantCompress ? "de" : "in") + "flating: " + _z.Message); @@ -181,18 +181,18 @@ internal class ZlibBaseStream : Stream if (_streamMode == StreamMode.Writer) { - bool done = false; + var done = false; do { _z.OutputBuffer = workingBuffer; _z.NextOut = 0; _z.AvailableBytesOut = _workingBuffer.Length; - int rc = + var rc = (_wantCompress) ? _z.Deflate(FlushType.Finish) : _z.Inflate(FlushType.Finish); if (rc != ZlibConstants.Z_STREAM_END && rc != ZlibConstants.Z_OK) { - string verb = (_wantCompress ? "de" : "in") + "flating"; + var verb = (_wantCompress ? "de" : "in") + "flating"; if (_z.Message is null) { throw new ZlibException(String.Format("{0}: (rc = {1})", verb, rc)); @@ -225,7 +225,7 @@ internal class ZlibBaseStream : Stream Span intBuf = stackalloc byte[4]; BinaryPrimitives.WriteInt32LittleEndian(intBuf, crc.Crc32Result); _stream.Write(intBuf); - int c2 = (int)(crc.TotalBytesRead & 0x00000000FFFFFFFF); + var c2 = (int)(crc.TotalBytesRead & 0x00000000FFFFFFFF); BinaryPrimitives.WriteInt32LittleEndian(intBuf, c2); _stream.Write(intBuf); } @@ -256,8 +256,8 @@ internal class ZlibBaseStream : Stream { // Make sure we have read to the end of the stream _z.InputBuffer.AsSpan(_z.NextIn, _z.AvailableBytesIn).CopyTo(trailer); - int bytesNeeded = 8 - _z.AvailableBytesIn; - int bytesRead = _stream.Read( + var bytesNeeded = 8 - _z.AvailableBytesIn; + var bytesRead = _stream.Read( trailer.Slice(_z.AvailableBytesIn, bytesNeeded) ); if (bytesNeeded != bytesRead) @@ -275,10 +275,10 @@ internal class ZlibBaseStream : Stream _z.InputBuffer.AsSpan(_z.NextIn, trailer.Length).CopyTo(trailer); } - Int32 crc32_expected = BinaryPrimitives.ReadInt32LittleEndian(trailer); - Int32 crc32_actual = crc.Crc32Result; - Int32 isize_expected = BinaryPrimitives.ReadInt32LittleEndian(trailer.Slice(4)); - Int32 isize_actual = (Int32)(_z.TotalBytesOut & 0x00000000FFFFFFFF); + var crc32_expected = BinaryPrimitives.ReadInt32LittleEndian(trailer); + var crc32_actual = crc.Crc32Result; + var isize_expected = BinaryPrimitives.ReadInt32LittleEndian(trailer.Slice(4)); + var isize_actual = (Int32)(_z.TotalBytesOut & 0x00000000FFFFFFFF); if (crc32_actual != crc32_expected) { @@ -380,11 +380,11 @@ internal class ZlibBaseStream : Stream private string ReadZeroTerminatedString() { var list = new List(); - bool done = false; + var done = false; do { // workitem 7740 - int n = _stream.Read(_buf1, 0, 1); + var n = _stream.Read(_buf1, 0, 1); if (n != 1) { throw new ZlibException("Unexpected EOF reading GZIP header."); @@ -398,17 +398,17 @@ internal class ZlibBaseStream : Stream list.Add(_buf1[0]); } } while (!done); - byte[] buffer = list.ToArray(); + var buffer = list.ToArray(); return _encoding.GetString(buffer, 0, buffer.Length); } private int _ReadAndValidateGzipHeader() { - int totalBytesRead = 0; + var totalBytesRead = 0; // read the header on the first read Span header = stackalloc byte[10]; - int n = _stream.Read(header); + var n = _stream.Read(header); // workitem 8501: handle edge case (decompress empty stream) if (n == 0) @@ -426,7 +426,7 @@ internal class ZlibBaseStream : Stream throw new ZlibException("Bad GZIP header."); } - int timet = BinaryPrimitives.ReadInt32LittleEndian(header.Slice(4)); + var timet = BinaryPrimitives.ReadInt32LittleEndian(header.Slice(4)); _GzipMtime = TarHeader.EPOCH.AddSeconds(timet); totalBytesRead += n; if ((header[3] & 0x04) == 0x04) @@ -435,8 +435,8 @@ internal class ZlibBaseStream : Stream n = _stream.Read(header.Slice(0, 2)); // 2-byte length field totalBytesRead += n; - short extraLength = (short)(header[0] + header[1] * 256); - byte[] extra = new byte[extraLength]; + var extraLength = (short)(header[0] + header[1] * 256); + var extra = new byte[extraLength]; n = _stream.Read(extra, 0, extra.Length); if (n != extraLength) { @@ -498,7 +498,7 @@ internal class ZlibBaseStream : Stream throw new ZlibException("Cannot Read after Writing."); } - int rc = 0; + var rc = 0; // set up the output of the deflate/inflate codec: _z.OutputBuffer = buffer; diff --git a/src/SharpCompress/Compressors/Deflate64/Deflate64Stream.cs b/src/SharpCompress/Compressors/Deflate64/Deflate64Stream.cs index 149f7c92..da4117b9 100644 --- a/src/SharpCompress/Compressors/Deflate64/Deflate64Stream.cs +++ b/src/SharpCompress/Compressors/Deflate64/Deflate64Stream.cs @@ -4,11 +4,11 @@ #nullable disable -using SharpCompress.Common.Zip; using System; using System.Diagnostics; using System.IO; using System.Runtime.CompilerServices; +using SharpCompress.Common.Zip; namespace SharpCompress.Compressors.Deflate64; @@ -118,8 +118,8 @@ public sealed class Deflate64Stream : Stream EnsureNotDisposed(); int bytesRead; - int currentOffset = offset; - int remainingCount = count; + var currentOffset = offset; + var remainingCount = count; while (true) { @@ -142,7 +142,7 @@ public sealed class Deflate64Stream : Stream break; } - int bytes = _stream.Read(_buffer, 0, _buffer.Length); + var bytes = _stream.Read(_buffer, 0, _buffer.Length); if (bytes <= 0) { break; diff --git a/src/SharpCompress/Compressors/Deflate64/DeflateInput.cs b/src/SharpCompress/Compressors/Deflate64/DeflateInput.cs index 6a12df3b..304a2a78 100644 --- a/src/SharpCompress/Compressors/Deflate64/DeflateInput.cs +++ b/src/SharpCompress/Compressors/Deflate64/DeflateInput.cs @@ -22,7 +22,7 @@ internal sealed class DeflateInput Debug.Assert(StartIndex + Count <= Buffer.Length, "Input buffer is in invalid state!"); } - internal InputState DumpState() => new InputState(Count, StartIndex); + internal InputState DumpState() => new(Count, StartIndex); internal void RestoreState(InputState state) { diff --git a/src/SharpCompress/Compressors/Deflate64/HuffmanTree.cs b/src/SharpCompress/Compressors/Deflate64/HuffmanTree.cs index 051b613e..e37802bb 100644 --- a/src/SharpCompress/Compressors/Deflate64/HuffmanTree.cs +++ b/src/SharpCompress/Compressors/Deflate64/HuffmanTree.cs @@ -42,11 +42,9 @@ internal sealed class HuffmanTree private readonly int _tableMask; // huffman tree for static block - public static HuffmanTree StaticLiteralLengthTree { get; } = - new HuffmanTree(GetStaticLiteralTreeLength()); + public static HuffmanTree StaticLiteralLengthTree { get; } = new(GetStaticLiteralTreeLength()); - public static HuffmanTree StaticDistanceTree { get; } = - new HuffmanTree(GetStaticDistanceTreeLength()); + public static HuffmanTree StaticDistanceTree { get; } = new(GetStaticDistanceTreeLength()); public HuffmanTree(byte[] codeLengths) { diff --git a/src/SharpCompress/Compressors/Deflate64/InflaterManaged.cs b/src/SharpCompress/Compressors/Deflate64/InflaterManaged.cs index 6a3c226b..4e11854f 100644 --- a/src/SharpCompress/Compressors/Deflate64/InflaterManaged.cs +++ b/src/SharpCompress/Compressors/Deflate64/InflaterManaged.cs @@ -243,8 +243,8 @@ internal sealed class InflaterManaged private void Reset() => _state = //_hasFormatReader ? - //InflaterState.ReadingHeader : // start by reading Header info - InflaterState.ReadingBFinal; // start by reading BFinal bit + //InflaterState.ReadingHeader : // start by reading Header info + InflaterState.ReadingBFinal; // start by reading BFinal bit public void SetInput(byte[] inputBytes, int offset, int length) => _input.SetInput(inputBytes, offset, length); // append the bytes diff --git a/src/SharpCompress/Compressors/Filters/BCJ2Filter.cs b/src/SharpCompress/Compressors/Filters/BCJ2Filter.cs index f658b385..1ed531cc 100644 --- a/src/SharpCompress/Compressors/Filters/BCJ2Filter.cs +++ b/src/SharpCompress/Compressors/Filters/BCJ2Filter.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; namespace SharpCompress.Compressors.Filters; @@ -79,7 +79,7 @@ internal class BCJ2Filter : Stream public override bool CanWrite => false; - public override void Flush() => throw new NotSupportedException(); + public override void Flush() { } public override long Length => _baseStream.Length + _data1.Length + _data2.Length; diff --git a/src/SharpCompress/Compressors/Filters/BCJFilterARM.cs b/src/SharpCompress/Compressors/Filters/BCJFilterARM.cs index 86e90b10..5d861621 100644 --- a/src/SharpCompress/Compressors/Filters/BCJFilterARM.cs +++ b/src/SharpCompress/Compressors/Filters/BCJFilterARM.cs @@ -18,7 +18,7 @@ internal class BCJFilterARM : Filter { if ((buffer[i + 3] & 0xFF) == 0xEB) { - int src = + var src = ((buffer[i + 2] & 0xFF) << 16) | ((buffer[i + 1] & 0xFF) << 8) | (buffer[i] & 0xFF); diff --git a/src/SharpCompress/Compressors/Filters/BCJFilterARM64.cs b/src/SharpCompress/Compressors/Filters/BCJFilterARM64.cs new file mode 100644 index 00000000..24f5ab17 --- /dev/null +++ b/src/SharpCompress/Compressors/Filters/BCJFilterARM64.cs @@ -0,0 +1,63 @@ +using System; +using System.Buffers.Binary; +using System.IO; + +namespace SharpCompress.Compressors.Filters; + +internal class BCJFilterARM64 : Filter +{ + private int _pos; + + public BCJFilterARM64(bool isEncoder, Stream baseStream) + : base(isEncoder, baseStream, 8) => _pos = 0; + + protected override int Transform(byte[] buffer, int offset, int count) + { + var end = offset + count - 4; + int i; + + for (i = offset; i <= end; i += 4) + { + uint pc = (uint)(_pos + i - offset); + uint instr = BinaryPrimitives.ReadUInt32LittleEndian( + new ReadOnlySpan(buffer, i, 4) + ); + + if ((instr >> 26) == 0x25) + { + uint src = instr; + instr = 0x94000000; + + pc >>= 2; + if (!_isEncoder) + pc = 0U - pc; + + instr |= (src + pc) & 0x03FFFFFF; + BinaryPrimitives.WriteUInt32LittleEndian(new Span(buffer, i, 4), instr); + } + else if ((instr & 0x9F000000) == 0x90000000) + { + uint src = ((instr >> 29) & 3) | ((instr >> 3) & 0x001FFFFC); + + if (((src + 0x00020000) & 0x001C0000) != 0) + continue; + + instr &= 0x9000001F; + + pc >>= 12; + if (!_isEncoder) + pc = 0U - pc; + + uint dest = src + pc; + instr |= (dest & 3) << 29; + instr |= (dest & 0x0003FFFC) << 3; + instr |= (0U - (dest & 0x00020000)) & 0x00E00000; + BinaryPrimitives.WriteUInt32LittleEndian(new Span(buffer, i, 4), instr); + } + } + + i -= offset; + _pos += i; + return i; + } +} diff --git a/src/SharpCompress/Compressors/Filters/BCJFilterARMT.cs b/src/SharpCompress/Compressors/Filters/BCJFilterARMT.cs index d2ad1bbf..db5b36f2 100644 --- a/src/SharpCompress/Compressors/Filters/BCJFilterARMT.cs +++ b/src/SharpCompress/Compressors/Filters/BCJFilterARMT.cs @@ -18,7 +18,7 @@ internal class BCJFilterARMT : Filter { if ((buffer[i + 1] & 0xF8) == 0xF0 && (buffer[i + 3] & 0xF8) == 0xF8) { - int src = + var src = ((buffer[i + 1] & 0x07) << 19) | ((buffer[i] & 0xFF) << 11) | ((buffer[i + 3] & 0x07) << 8) diff --git a/src/SharpCompress/Compressors/Filters/BCJFilterIA64.cs b/src/SharpCompress/Compressors/Filters/BCJFilterIA64.cs index 4d89ba39..07eaf319 100644 --- a/src/SharpCompress/Compressors/Filters/BCJFilterIA64.cs +++ b/src/SharpCompress/Compressors/Filters/BCJFilterIA64.cs @@ -52,29 +52,29 @@ internal class BCJFilterIA64 : Filter for (i = offset; i <= end; i += 16) { - int instrTemplate = buffer[i] & 0x1F; - int mask = BRANCH_TABLE[instrTemplate]; + var instrTemplate = buffer[i] & 0x1F; + var mask = BRANCH_TABLE[instrTemplate]; for (int slot = 0, bitPos = 5; slot < 3; ++slot, bitPos += 41) { if (((mask >>> slot) & 1) == 0) continue; - int bytePos = bitPos >>> 3; - int bitRes = bitPos & 7; + var bytePos = bitPos >>> 3; + var bitRes = bitPos & 7; long instr = 0; - for (int j = 0; j < 6; ++j) + for (var j = 0; j < 6; ++j) { instr |= (buffer[i + bytePos + j] & 0xFFL) << (8 * j); } - long instrNorm = instr >>> bitRes; + var instrNorm = instr >>> bitRes; if (((instrNorm >>> 37) & 0x0F) != 0x05 || ((instrNorm >>> 9) & 0x07) != 0x00) continue; - int src = (int)((instrNorm >>> 13) & 0x0FFFFF); + var src = (int)((instrNorm >>> 13) & 0x0FFFFF); src |= ((int)(instrNorm >>> 36) & 1) << 20; src <<= 4; @@ -93,7 +93,7 @@ internal class BCJFilterIA64 : Filter instr &= (1 << bitRes) - 1; instr |= instrNorm << bitRes; - for (int j = 0; j < 6; ++j) + for (var j = 0; j < 6; ++j) { buffer[i + bytePos + j] = (byte)(instr >>> (8 * j)); } diff --git a/src/SharpCompress/Compressors/Filters/BCJFilterPPC.cs b/src/SharpCompress/Compressors/Filters/BCJFilterPPC.cs index 11ed61a1..ccfa7480 100644 --- a/src/SharpCompress/Compressors/Filters/BCJFilterPPC.cs +++ b/src/SharpCompress/Compressors/Filters/BCJFilterPPC.cs @@ -18,7 +18,7 @@ internal class BCJFilterPPC : Filter { if ((buffer[i] & 0xFC) == 0x48 && (buffer[i + 3] & 0x03) == 0x01) { - int src = + var src = ((buffer[i] & 0x03) << 24) | ((buffer[i + 1] & 0xFF) << 16) | ((buffer[i + 2] & 0xFF) << 8) diff --git a/src/SharpCompress/Compressors/Filters/BCJFilterRISCV.cs b/src/SharpCompress/Compressors/Filters/BCJFilterRISCV.cs new file mode 100644 index 00000000..67d9cb52 --- /dev/null +++ b/src/SharpCompress/Compressors/Filters/BCJFilterRISCV.cs @@ -0,0 +1,210 @@ +using System; +using System.Buffers.Binary; +using System.IO; + +namespace SharpCompress.Compressors.Filters; + +internal class BCJFilterRISCV : Filter +{ + private int _pos; + + public BCJFilterRISCV(bool isEncoder, Stream baseStream) + : base(isEncoder, baseStream, 8) => _pos = 0; + + private int Decode(byte[] buffer, int offset, int count) + { + if (count < 8) + { + return 0; + } + + var end = offset + count - 8; + int i; + for (i = offset; i <= end; i += 2) + { + uint inst = buffer[i]; + if (inst == 0xEF) + { + uint b1 = buffer[i + 1]; + if ((b1 & 0x0D) != 0) + continue; + + uint b2 = buffer[i + 2]; + uint b3 = buffer[i + 3]; + uint pc = (uint)(_pos + i); + + uint addr = ((b1 & 0xF0) << 13) | (b2 << 9) | (b3 << 1); + + addr -= pc; + + buffer[i + 1] = (byte)((b1 & 0x0F) | ((addr >> 8) & 0xF0)); + + buffer[i + 2] = (byte)( + ((addr >> 16) & 0x0F) | ((addr >> 7) & 0x10) | ((addr << 4) & 0xE0) + ); + + buffer[i + 3] = (byte)(((addr >> 4) & 0x7F) | ((addr >> 13) & 0x80)); + + i += 4 - 2; + } + else if ((inst & 0x7F) == 0x17) + { + uint inst2 = 0; + inst |= (uint)buffer[i + 1] << 8; + inst |= (uint)buffer[i + 2] << 16; + inst |= (uint)buffer[i + 3] << 24; + + if ((inst & 0xE80) != 0) + { + inst2 = BinaryPrimitives.ReadUInt32LittleEndian( + new ReadOnlySpan(buffer, i + 4, 4) + ); + if (((((inst) << 8) ^ (inst2)) & 0xF8003) != 3) + { + i += 6 - 2; + continue; + } + uint addr = inst & 0xFFFFF000; + addr += inst2 >> 20; + + inst = 0x17 | (2 << 7) | (inst2 << 12); + inst2 = addr; + } + else + { + uint inst2_rs1 = inst >> 27; + if ((uint)(((inst) - 0x3117) << 18) >= ((inst2_rs1) & 0x1D)) + { + i += 4 - 2; + continue; + } + + uint addr = BinaryPrimitives.ReadUInt32BigEndian( + new ReadOnlySpan(buffer, i + 4, 4) + ); + + addr -= (uint)(_pos + i); + + inst2 = (inst >> 12) | (addr << 20); + + inst = 0x17 | (inst2_rs1 << 7) | ((addr + 0x800) & 0xFFFFF000); + } + BinaryPrimitives.WriteUInt32LittleEndian(new Span(buffer, i, 4), inst); + BinaryPrimitives.WriteUInt32LittleEndian(new Span(buffer, i + 4, 4), inst2); + + i += 8 - 2; + } + } + i -= offset; + _pos += i; + return i; + } + + private int Encode(byte[] buffer, int offset, int count) + { + if (count < 8) + { + return 0; + } + + var end = offset + count - 8; + int i; + for (i = offset; i <= end; i += 2) + { + uint inst = buffer[i]; + if (inst == 0xEF) + { + uint b1 = buffer[i + 1]; + if ((b1 & 0x0D) != 0) + continue; + + uint b2 = buffer[i + 2]; + uint b3 = buffer[i + 3]; + uint pc = (uint)(_pos + i); + + uint addr = + ((b1 & 0xF0) << 8) + | ((b2 & 0x0F) << 16) + | ((b2 & 0x10) << 7) + | ((b2 & 0xE0) >> 4) + | ((b3 & 0x7F) << 4) + | ((b3 & 0x80) << 13); + + addr += pc; + + buffer[i + 1] = (byte)((b1 & 0x0F) | ((addr >> 13) & 0xF0)); + + buffer[i + 2] = (byte)(addr >> 9); + + buffer[i + 3] = (byte)(addr >> 1); + + i += 4 - 2; + } + else if ((inst & 0x7F) == 0x17) + { + inst |= (uint)buffer[i + 1] << 8; + inst |= (uint)buffer[i + 2] << 16; + inst |= (uint)buffer[i + 3] << 24; + + if ((inst & 0xE80) != 0) + { + uint inst2 = BinaryPrimitives.ReadUInt32LittleEndian( + new ReadOnlySpan(buffer, i + 4, 4) + ); + if (((((inst) << 8) ^ (inst2)) & 0xF8003) != 3) + { + i += 6 - 2; + continue; + } + uint addr = inst & 0xFFFFF000; + addr += (inst2 >> 20) - ((inst2 >> 19) & 0x1000); + + addr += (uint)(_pos + i); + inst = 0x17 | (2 << 7) | (inst2 << 12); + + BinaryPrimitives.WriteUInt32LittleEndian(new Span(buffer, i, 4), inst); + BinaryPrimitives.WriteUInt32BigEndian(new Span(buffer, i + 4, 4), addr); + } + else + { + uint fake_rs1 = inst >> 27; + if ((uint)(((inst) - 0x3117) << 18) >= ((fake_rs1) & 0x1D)) + { + i += 4 - 2; + continue; + } + + uint fake_addr = BinaryPrimitives.ReadUInt32LittleEndian( + new ReadOnlySpan(buffer, i + 4, 4) + ); + + uint fake_inst2 = (inst >> 12) | (fake_addr << 20); + + inst = 0x17 | (fake_rs1 << 7) | (fake_addr & 0xFFFFF000); + + BinaryPrimitives.WriteUInt32LittleEndian(new Span(buffer, i, 4), inst); + BinaryPrimitives.WriteUInt32LittleEndian( + new Span(buffer, i + 4, 4), + fake_inst2 + ); + } + i += 8 - 2; + } + } + i -= offset; + _pos += i; + return i; + } + + protected override int Transform(byte[] buffer, int offset, int count) + { + if (_isEncoder) + { + return Encode(buffer, offset, count); + } + else + { + return Decode(buffer, offset, count); + } + } +} diff --git a/src/SharpCompress/Compressors/Filters/BCJFilterSPARC.cs b/src/SharpCompress/Compressors/Filters/BCJFilterSPARC.cs index 67756d34..db7c75be 100644 --- a/src/SharpCompress/Compressors/Filters/BCJFilterSPARC.cs +++ b/src/SharpCompress/Compressors/Filters/BCJFilterSPARC.cs @@ -21,7 +21,7 @@ internal class BCJFilterSPARC : Filter || (buffer[i] == 0x7F && (buffer[i + 1] & 0xC0) == 0xC0) ) { - int src = + var src = ((buffer[i] & 0xFF) << 24) | ((buffer[i + 1] & 0xFF) << 16) | ((buffer[i + 2] & 0xFF) << 8) diff --git a/src/SharpCompress/Compressors/Filters/BranchExecFilter.cs b/src/SharpCompress/Compressors/Filters/BranchExecFilter.cs index 9fc439d4..d198cf8f 100644 --- a/src/SharpCompress/Compressors/Filters/BranchExecFilter.cs +++ b/src/SharpCompress/Compressors/Filters/BranchExecFilter.cs @@ -24,40 +24,27 @@ public sealed class BranchExecFilter } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool X86TestByte(byte b) - { - return b == 0x00 || b == 0xFF; - } + private static bool X86TestByte(byte b) => b == 0x00 || b == 0xFF; //Replaced X86Converter with bcj_x86() - https://github.com/torvalds/linux/blob/master/lib/xz/xz_dec_bcj.c //This was to fix an issue decoding a Test zip made with WinZip (that 7zip was also able to read). //The previous version of the code would corrupt 2 bytes in the Test.exe at 0x6CF9 (3D6D - should be 4000) - Test zip: WinZip27.Xz.zipx public static void X86Converter(byte[] buf, uint ip, ref uint state) { - bool[] mask_to_allowed_status = new[] - { - true, - true, - true, - false, - true, - false, - false, - false - }; + var mask_to_allowed_status = new[] { true, true, true, false, true, false, false, false }; - byte[] mask_to_bit_num = new byte[] { 0, 1, 2, 2, 3, 3, 3, 3 }; + var mask_to_bit_num = new byte[] { 0, 1, 2, 2, 3, 3, 3, 3 }; int i; - int prev_pos = -1; - uint prev_mask = state & 7; + var prev_pos = -1; + var prev_mask = state & 7; uint src; uint dest; uint j; byte b; - uint pos = ip; + var pos = ip; - uint size = (uint)buf.Length; + var size = (uint)buf.Length; if (size <= 4) return; diff --git a/src/SharpCompress/Compressors/Filters/DeltaFilter.cs b/src/SharpCompress/Compressors/Filters/DeltaFilter.cs index c5fbeb41..85ec9b15 100644 --- a/src/SharpCompress/Compressors/Filters/DeltaFilter.cs +++ b/src/SharpCompress/Compressors/Filters/DeltaFilter.cs @@ -23,9 +23,9 @@ namespace SharpCompress.Compressors.Filters protected override int Transform(byte[] buffer, int offset, int count) { - int end = offset + count; + var end = offset + count; - for (int i = offset; i < end; i++) + for (var i = offset; i < end; i++) { buffer[i] += _history[(_distance + _position--) & DISTANCE_MASK]; _history[_position & DISTANCE_MASK] = buffer[i]; diff --git a/src/SharpCompress/Compressors/Filters/Filter.cs b/src/SharpCompress/Compressors/Filters/Filter.cs index 93d95195..aa5e620d 100644 --- a/src/SharpCompress/Compressors/Filters/Filter.cs +++ b/src/SharpCompress/Compressors/Filters/Filter.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; namespace SharpCompress.Compressors.Filters; @@ -40,7 +40,7 @@ internal abstract class Filter : Stream public override bool CanWrite => _isEncoder; - public override void Flush() => throw new NotSupportedException(); + public override void Flush() { } public override long Length => _baseStream.Length; diff --git a/src/SharpCompress/Compressors/LZMA/AesDecoderStream.cs b/src/SharpCompress/Compressors/LZMA/AesDecoderStream.cs index 24823c73..e89d705a 100644 --- a/src/SharpCompress/Compressors/LZMA/AesDecoderStream.cs +++ b/src/SharpCompress/Compressors/LZMA/AesDecoderStream.cs @@ -20,7 +20,8 @@ internal sealed class AesDecoderStream : DecoderStream2 public AesDecoderStream(Stream input, byte[] info, IPasswordProvider pass, long limit) { - if (pass.CryptoGetTextPassword() == null) + var password = pass.CryptoGetTextPassword(); + if (password == null) { throw new SharpCompress.Common.CryptographicException( "Encrypted 7Zip archive has no password specified." @@ -35,10 +36,10 @@ internal sealed class AesDecoderStream : DecoderStream2 throw new NotSupportedException("AES decoder does not support padding."); } - Init(info, out int numCyclesPower, out byte[] salt, out byte[] seed); + Init(info, out var numCyclesPower, out var salt, out var seed); - byte[] password = Encoding.Unicode.GetBytes(pass.CryptoGetTextPassword()); - byte[]? key = InitKey(numCyclesPower, salt, password); + var passwordBytes = Encoding.Unicode.GetBytes(password); + var key = InitKey(numCyclesPower, salt, passwordBytes); if (key == null) { throw new InvalidOperationException("Initialized with null key"); @@ -100,7 +101,7 @@ internal sealed class AesDecoderStream : DecoderStream2 do { - int read = mStream.Read(mBuffer, mEnding, mBuffer.Length - mEnding); + var read = mStream.Read(mBuffer, mEnding, mBuffer.Length - mEnding); if (read == 0) { // We are not done decoding and have less than 16 bytes. @@ -133,7 +134,7 @@ internal sealed class AesDecoderStream : DecoderStream2 } // Otherwise we transform directly into the target buffer. - int processed = mDecoder.TransformBlock(mBuffer, mOffset, count & ~15, buffer, offset); + var processed = mDecoder.TransformBlock(mBuffer, mOffset, count & ~15, buffer, offset); mOffset += processed; mWritten += processed; return processed; @@ -143,7 +144,7 @@ internal sealed class AesDecoderStream : DecoderStream2 private void Init(byte[] info, out int numCyclesPower, out byte[] salt, out byte[] iv) { - byte bt = info[0]; + var bt = info[0]; numCyclesPower = bt & 0x3F; if ((bt & 0xC0) == 0) @@ -153,14 +154,14 @@ internal sealed class AesDecoderStream : DecoderStream2 return; } - int saltSize = (bt >> 7) & 1; - int ivSize = (bt >> 6) & 1; + var saltSize = (bt >> 7) & 1; + var ivSize = (bt >> 6) & 1; if (info.Length == 1) { throw new InvalidOperationException(); } - byte bt2 = info[1]; + var bt2 = info[1]; saltSize += (bt2 >> 4); ivSize += (bt2 & 15); if (info.Length < 2 + saltSize + ivSize) @@ -169,13 +170,13 @@ internal sealed class AesDecoderStream : DecoderStream2 } salt = new byte[saltSize]; - for (int i = 0; i < saltSize; i++) + for (var i = 0; i < saltSize; i++) { salt[i] = info[i + 2]; } iv = new byte[16]; - for (int i = 0; i < ivSize; i++) + for (var i = 0; i < ivSize; i++) { iv[i] = info[i + saltSize + 2]; } @@ -198,7 +199,7 @@ internal sealed class AesDecoderStream : DecoderStream2 key[pos] = salt[pos]; } - for (int i = 0; i < pass.Length && pos < 32; i++) + for (var i = 0; i < pass.Length && pos < 32; i++) { key[pos++] = pass[i]; } @@ -207,31 +208,9 @@ internal sealed class AesDecoderStream : DecoderStream2 } else { -#if NETSTANDARD2_0 - using IncrementalHash sha = IncrementalHash.CreateHash(HashAlgorithmName.SHA256); - byte[] counter = new byte[8]; - long numRounds = 1L << mNumCyclesPower; - for (long round = 0; round < numRounds; round++) - { - sha.AppendData(salt, 0, salt.Length); - sha.AppendData(pass, 0, pass.Length); - sha.AppendData(counter, 0, 8); - - // This mirrors the counter so we don't have to convert long to byte[] each round. - // (It also ensures the counter is little endian, which BitConverter does not.) - for (int i = 0; i < 8; i++) - { - if (++counter[i] != 0) - { - break; - } - } - } - return sha.GetHashAndReset(); -#else using var sha = SHA256.Create(); - byte[] counter = new byte[8]; - long numRounds = 1L << mNumCyclesPower; + var counter = new byte[8]; + var numRounds = 1L << mNumCyclesPower; for (long round = 0; round < numRounds; round++) { sha.TransformBlock(salt, 0, salt.Length, null, 0); @@ -240,7 +219,7 @@ internal sealed class AesDecoderStream : DecoderStream2 // This mirrors the counter so we don't have to convert long to byte[] each round. // (It also ensures the counter is little endian, which BitConverter does not.) - for (int i = 0; i < 8; i++) + for (var i = 0; i < 8; i++) { if (++counter[i] != 0) { @@ -251,7 +230,6 @@ internal sealed class AesDecoderStream : DecoderStream2 sha.TransformFinalBlock(counter, 0, 0); return sha.Hash; -#endif } } @@ -261,7 +239,7 @@ internal sealed class AesDecoderStream : DecoderStream2 // Just transform as much as possible so we can feed from it as long as possible. if (mUnderflow == 0) { - int blockSize = (mEnding - mOffset) & ~15; + var blockSize = (mEnding - mOffset) & ~15; mUnderflow = mDecoder.TransformBlock(mBuffer, mOffset, blockSize, mBuffer, mOffset); } diff --git a/src/SharpCompress/Compressors/LZMA/DecoderStream.cs b/src/SharpCompress/Compressors/LZMA/DecoderStream.cs index 16865351..a3dbf37f 100644 --- a/src/SharpCompress/Compressors/LZMA/DecoderStream.cs +++ b/src/SharpCompress/Compressors/LZMA/DecoderStream.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using SharpCompress.Common.SevenZip; using SharpCompress.Compressors.LZMA.Utilites; @@ -14,7 +14,7 @@ internal abstract class DecoderStream2 : Stream public override bool CanWrite => false; - public override void Flush() => throw new NotSupportedException(); + public override void Flush() { } public override long Length => throw new NotSupportedException(); diff --git a/src/SharpCompress/Compressors/LZMA/LZipStream.cs b/src/SharpCompress/Compressors/LZMA/LZipStream.cs index 4b63a621..5e987214 100644 --- a/src/SharpCompress/Compressors/LZMA/LZipStream.cs +++ b/src/SharpCompress/Compressors/LZMA/LZipStream.cs @@ -63,18 +63,18 @@ public sealed class LZipStream : Stream var crc32Stream = (Crc32Stream)_stream; crc32Stream.WrappedStream.Dispose(); crc32Stream.Dispose(); - var compressedCount = _countingWritableSubStream!.Count; + var compressedCount = _countingWritableSubStream.NotNull().Count; Span intBuf = stackalloc byte[8]; BinaryPrimitives.WriteUInt32LittleEndian(intBuf, crc32Stream.Crc); - _countingWritableSubStream.Write(intBuf.Slice(0, 4)); + _countingWritableSubStream?.Write(intBuf.Slice(0, 4)); BinaryPrimitives.WriteInt64LittleEndian(intBuf, _writeCount); - _countingWritableSubStream.Write(intBuf); + _countingWritableSubStream?.Write(intBuf); //total with headers BinaryPrimitives.WriteUInt64LittleEndian(intBuf, compressedCount + 6 + 20); - _countingWritableSubStream.Write(intBuf); + _countingWritableSubStream?.Write(intBuf); } _finished = true; } diff --git a/src/SharpCompress/Compressors/LZMA/Log.cs b/src/SharpCompress/Compressors/LZMA/Log.cs index e954e259..1431966c 100644 --- a/src/SharpCompress/Compressors/LZMA/Log.cs +++ b/src/SharpCompress/Compressors/LZMA/Log.cs @@ -6,7 +6,7 @@ namespace SharpCompress.Compressors.LZMA; internal static class Log { - private static readonly Stack INDENT = new Stack(); + private static readonly Stack INDENT = new(); private static bool NEEDS_INDENT = true; static Log() => INDENT.Push(""); diff --git a/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs b/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs index 31039761..1b2fdc81 100644 --- a/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs +++ b/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs @@ -11,11 +11,11 @@ public class Decoder : ICoder, ISetDecoderProperties // ,System.IO.Stream { private class LenDecoder { - private BitDecoder _choice = new BitDecoder(); - private BitDecoder _choice2 = new BitDecoder(); + 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 BitTreeDecoder _highCoder = new BitTreeDecoder(Base.K_NUM_HIGH_LEN_BITS); + private BitTreeDecoder _highCoder = new(Base.K_NUM_HIGH_LEN_BITS); private uint _numPosStates; public void Create(uint numPosStates) @@ -173,18 +173,18 @@ public class Decoder : ICoder, ISetDecoderProperties // ,System.IO.Stream Base.K_NUM_FULL_DISTANCES - Base.K_END_POS_MODEL_INDEX ]; - private BitTreeDecoder _posAlignDecoder = new BitTreeDecoder(Base.K_NUM_ALIGN_BITS); + private BitTreeDecoder _posAlignDecoder = new(Base.K_NUM_ALIGN_BITS); - private readonly LenDecoder _lenDecoder = new LenDecoder(); - private readonly LenDecoder _repLenDecoder = new LenDecoder(); + private readonly LenDecoder _lenDecoder = new(); + private readonly LenDecoder _repLenDecoder = new(); - private readonly LiteralDecoder _literalDecoder = new LiteralDecoder(); + private readonly LiteralDecoder _literalDecoder = new(); private int _dictionarySize; private uint _posStateMask; - private Base.State _state = new Base.State(); + private Base.State _state = new(); private uint _rep0, _rep1, _rep2, @@ -318,9 +318,8 @@ public class Decoder : ICoder, ISetDecoderProperties // ,System.IO.Stream { var posState = (uint)outWindow._total & _posStateMask; if ( - _isMatchDecoders[ - (_state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState - ].Decode(rangeDecoder) == 0 + _isMatchDecoders[(_state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState] + .Decode(rangeDecoder) == 0 ) { byte b; @@ -355,7 +354,8 @@ public class Decoder : ICoder, ISetDecoderProperties // ,System.IO.Stream if ( _isRep0LongDecoders[ (_state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState - ].Decode(rangeDecoder) == 0 + ] + .Decode(rangeDecoder) == 0 ) { _state.UpdateShortRep(); diff --git a/src/SharpCompress/Compressors/LZMA/LzmaEncoder.cs b/src/SharpCompress/Compressors/LZMA/LzmaEncoder.cs index 558fe37b..509e54c8 100644 --- a/src/SharpCompress/Compressors/LZMA/LzmaEncoder.cs +++ b/src/SharpCompress/Compressors/LZMA/LzmaEncoder.cs @@ -61,7 +61,7 @@ internal class Encoder : ICoder, ISetCoderProperties, IWriteCoderProperties return (uint)(G_FAST_POS[pos >> 26] + 52); } - private Base.State _state = new Base.State(); + private Base.State _state = new(); private byte _previousByte; private readonly uint[] _repDistances = new uint[Base.K_NUM_REP_DISTANCES]; @@ -191,15 +191,15 @@ internal class Encoder : ICoder, ISetCoderProperties, IWriteCoderProperties private class LenEncoder { - private BitEncoder _choice = new BitEncoder(); - private BitEncoder _choice2 = new BitEncoder(); + private BitEncoder _choice = new(); + private BitEncoder _choice2 = new(); private readonly BitTreeEncoder[] _lowCoder = new BitTreeEncoder[ Base.K_NUM_POS_STATES_ENCODING_MAX ]; private readonly BitTreeEncoder[] _midCoder = new BitTreeEncoder[ Base.K_NUM_POS_STATES_ENCODING_MAX ]; - private BitTreeEncoder _highCoder = new BitTreeEncoder(Base.K_NUM_HIGH_LEN_BITS); + private BitTreeEncoder _highCoder = new(Base.K_NUM_HIGH_LEN_BITS); public LenEncoder() { @@ -359,7 +359,7 @@ internal class Encoder : ICoder, ISetCoderProperties, IWriteCoderProperties private readonly Optimal[] _optimum = new Optimal[K_NUM_OPTS]; private BinTree _matchFinder; - private readonly RangeCoder.Encoder _rangeEncoder = new RangeCoder.Encoder(); + private readonly RangeCoder.Encoder _rangeEncoder = new(); private readonly BitEncoder[] _isMatch = new BitEncoder[ Base.K_NUM_STATES << Base.K_NUM_POS_STATES_BITS_MAX @@ -382,12 +382,12 @@ internal class Encoder : ICoder, ISetCoderProperties, IWriteCoderProperties Base.K_NUM_FULL_DISTANCES - Base.K_END_POS_MODEL_INDEX ]; - private BitTreeEncoder _posAlignEncoder = new BitTreeEncoder(Base.K_NUM_ALIGN_BITS); + private BitTreeEncoder _posAlignEncoder = new(Base.K_NUM_ALIGN_BITS); - private readonly LenPriceTableEncoder _lenEncoder = new LenPriceTableEncoder(); - private readonly LenPriceTableEncoder _repMatchLenEncoder = new LenPriceTableEncoder(); + private readonly LenPriceTableEncoder _lenEncoder = new(); + private readonly LenPriceTableEncoder _repMatchLenEncoder = new(); - private readonly LiteralEncoder _literalEncoder = new LiteralEncoder(); + private readonly LiteralEncoder _literalEncoder = new(); private readonly uint[] _matchDistances = new uint[(Base.K_MATCH_MAX_LEN * 2) + 2]; @@ -553,9 +553,8 @@ internal class Encoder : ICoder, ISetCoderProperties, IWriteCoderProperties if (repIndex == 0) { price = _isRepG0[state._index].GetPrice0(); - price += _isRep0Long[ - (state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState - ].GetPrice1(); + price += _isRep0Long[(state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState] + .GetPrice1(); } else { @@ -713,9 +712,8 @@ internal class Encoder : ICoder, ISetCoderProperties, IWriteCoderProperties .GetPrice(!_state.IsCharState(), matchByte, currentByte); _optimum[1].MakeAsChar(); - var matchPrice = _isMatch[ - (_state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState - ].GetPrice1(); + var matchPrice = _isMatch[(_state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState] + .GetPrice1(); var repMatchPrice = matchPrice + _isRep[_state._index].GetPrice1(); if (matchByte == currentByte) @@ -995,9 +993,8 @@ internal class Encoder : ICoder, ISetCoderProperties, IWriteCoderProperties var posStateNext = (position + 1) & _posStateMask; var nextRepMatchPrice = curAnd1Price - + _isMatch[ - (state2._index << Base.K_NUM_POS_STATES_BITS_MAX) + posStateNext - ].GetPrice1() + + _isMatch[(state2._index << Base.K_NUM_POS_STATES_BITS_MAX) + posStateNext] + .GetPrice1() + _isRep[state2._index].GetPrice1(); { var offset = cur + 1 + lenTest2; @@ -1069,7 +1066,8 @@ internal class Encoder : ICoder, ISetCoderProperties, IWriteCoderProperties + GetRepPrice(repIndex, lenTest, state, posState) + _isMatch[ (state2._index << Base.K_NUM_POS_STATES_BITS_MAX) + posStateNext - ].GetPrice0() + ] + .GetPrice0() + _literalEncoder .GetSubCoder( position + lenTest, @@ -1088,7 +1086,8 @@ internal class Encoder : ICoder, ISetCoderProperties, IWriteCoderProperties curAndLenCharPrice + _isMatch[ (state2._index << Base.K_NUM_POS_STATES_BITS_MAX) + posStateNext - ].GetPrice1(); + ] + .GetPrice1(); var nextRepMatchPrice = nextMatchPrice + _isRep[state2._index].GetPrice1(); // for(; lenTest2 >= 2; lenTest2--) @@ -1174,7 +1173,8 @@ internal class Encoder : ICoder, ISetCoderProperties, IWriteCoderProperties + _isMatch[ (state2._index << Base.K_NUM_POS_STATES_BITS_MAX) + posStateNext - ].GetPrice0() + ] + .GetPrice0() + _literalEncoder .GetSubCoder( position + lenTest, @@ -1194,7 +1194,8 @@ internal class Encoder : ICoder, ISetCoderProperties, IWriteCoderProperties + _isMatch[ (state2._index << Base.K_NUM_POS_STATES_BITS_MAX) + posStateNext - ].GetPrice1(); + ] + .GetPrice1(); var nextRepMatchPrice = nextMatchPrice + _isRep[state2._index].GetPrice1(); @@ -1243,10 +1244,8 @@ internal class Encoder : ICoder, ISetCoderProperties, IWriteCoderProperties return; } - _isMatch[(_state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState].Encode( - _rangeEncoder, - 1 - ); + _isMatch[(_state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState] + .Encode(_rangeEncoder, 1); _isRep[_state._index].Encode(_rangeEncoder, 0); _state.UpdateMatch(); var len = Base.K_MATCH_MIN_LEN; @@ -1321,10 +1320,8 @@ internal class Encoder : ICoder, ISetCoderProperties, IWriteCoderProperties // it's not used ReadMatchDistances(out var len, out var numDistancePairs); var posState = (uint)(_nowPos64) & _posStateMask; - _isMatch[(_state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState].Encode( - _rangeEncoder, - 0 - ); + _isMatch[(_state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState] + .Encode(_rangeEncoder, 0); _state.UpdateChar(); var curByte = _matchFinder.GetIndexByte((int)(0 - _additionalOffset)); _literalEncoder @@ -1730,7 +1727,7 @@ internal class Encoder : ICoder, ISetCoderProperties, IWriteCoderProperties ReadOnlySpan properties ) { - for (int i = 0; i < properties.Length; i++) + for (var i = 0; i < properties.Length; i++) { var prop = properties[i]; switch (propIDs[i]) diff --git a/src/SharpCompress/Compressors/LZMA/LzmaStream.cs b/src/SharpCompress/Compressors/LZMA/LzmaStream.cs index 701b8e64..043cbbad 100644 --- a/src/SharpCompress/Compressors/LZMA/LzmaStream.cs +++ b/src/SharpCompress/Compressors/LZMA/LzmaStream.cs @@ -14,8 +14,8 @@ public class LzmaStream : Stream private readonly long _outputSize; private readonly int _dictionarySize; - private readonly OutWindow _outWindow = new OutWindow(); - private readonly RangeCoder.Decoder _rangeDecoder = new RangeCoder.Decoder(); + private readonly OutWindow _outWindow = new(); + private readonly RangeCoder.Decoder _rangeDecoder = new(); private Decoder _decoder; private long _position; @@ -207,14 +207,23 @@ public class LzmaStream : Stream if (_availableBytes == 0 && !_uncompressedChunk) { - _rangeDecoder.ReleaseStream(); + // Check range corruption scenario if ( !_rangeDecoder.IsFinished || (_rangeDecoderLimit >= 0 && _rangeDecoder._total != _rangeDecoderLimit) ) { - throw new DataErrorException(); + // Stream might have End Of Stream marker + _outWindow.SetLimit(toProcess + 1); + if (!_decoder.Code(_dictionarySize, _outWindow, _rangeDecoder)) + { + _rangeDecoder.ReleaseStream(); + throw new DataErrorException(); + } } + + _rangeDecoder.ReleaseStream(); + _inputPosition += _rangeDecoder._total; if (_outWindow.HasPending) { diff --git a/src/SharpCompress/Compressors/LZMA/Registry.cs b/src/SharpCompress/Compressors/LZMA/Registry.cs index f4cfde4b..eb3e3bdd 100644 --- a/src/SharpCompress/Compressors/LZMA/Registry.cs +++ b/src/SharpCompress/Compressors/LZMA/Registry.cs @@ -25,6 +25,8 @@ internal static class DecoderRegistry private const uint K_ARM = 0x03030501; private const uint K_ARMT = 0x03030701; private const uint K_SPARC = 0x03030805; + private const uint K_ARM64 = 0x0A; + private const uint K_RISCV = 0x0B; private const uint K_DEFLATE = 0x040108; private const uint K_B_ZIP2 = 0x040202; private const uint K_ZSTD = 0x4F71101; @@ -66,6 +68,10 @@ internal static class DecoderRegistry return new BCJFilterARMT(false, inStreams.Single()); case K_SPARC: return new BCJFilterSPARC(false, inStreams.Single()); + case K_ARM64: + return new BCJFilterARM64(false, inStreams.Single()); + case K_RISCV: + return new BCJFilterRISCV(false, inStreams.Single()); case K_B_ZIP2: return new BZip2Stream(inStreams.Single(), CompressionMode.Decompress, true); case K_PPMD: diff --git a/src/SharpCompress/Compressors/LZMA/Utilites/IPasswordProvider.cs b/src/SharpCompress/Compressors/LZMA/Utilites/IPasswordProvider.cs index 2f0eb904..d7d787d2 100644 --- a/src/SharpCompress/Compressors/LZMA/Utilites/IPasswordProvider.cs +++ b/src/SharpCompress/Compressors/LZMA/Utilites/IPasswordProvider.cs @@ -2,5 +2,5 @@ namespace SharpCompress.Compressors.LZMA.Utilites; internal interface IPasswordProvider { - string CryptoGetTextPassword(); + string? CryptoGetTextPassword(); } diff --git a/src/SharpCompress/Compressors/Lzw/LzwConstants.cs b/src/SharpCompress/Compressors/Lzw/LzwConstants.cs new file mode 100644 index 00000000..0325adbb --- /dev/null +++ b/src/SharpCompress/Compressors/Lzw/LzwConstants.cs @@ -0,0 +1,65 @@ +namespace SharpCompress.Compressors.Lzw +{ + /// + /// This class contains constants used for LZW + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage( + "Naming", + "CA1707:Identifiers should not contain underscores", + Justification = "kept for backwards compatibility" + )] + public sealed class LzwConstants + { + /// + /// Magic number found at start of LZW header: 0x1f 0x9d + /// + public const int MAGIC = 0x1f9d; + + /// + /// Maximum number of bits per code + /// + public const int MAX_BITS = 16; + + /* 3rd header byte: + * bit 0..4 Number of compression bits + * bit 5 Extended header + * bit 6 Free + * bit 7 Block mode + */ + + /// + /// Mask for 'number of compression bits' + /// + public const int BIT_MASK = 0x1f; + + /// + /// Indicates the presence of a fourth header byte + /// + public const int EXTENDED_MASK = 0x20; + + //public const int FREE_MASK = 0x40; + + /// + /// Reserved bits + /// + public const int RESERVED_MASK = 0x60; + + /// + /// Block compression: if table is full and compression rate is dropping, + /// clear the dictionary. + /// + public const int BLOCK_MODE_MASK = 0x80; + + /// + /// LZW file header size (in bytes) + /// + public const int HDR_SIZE = 3; + + /// + /// Initial number of bits per code + /// + public const int INIT_BITS = 9; + + private LzwConstants() { } + } +} diff --git a/src/SharpCompress/Compressors/Lzw/LzwStream.cs b/src/SharpCompress/Compressors/Lzw/LzwStream.cs new file mode 100644 index 00000000..488ba1aa --- /dev/null +++ b/src/SharpCompress/Compressors/Lzw/LzwStream.cs @@ -0,0 +1,597 @@ +using System; +using System.IO; +using SharpCompress.Common; + +namespace SharpCompress.Compressors.Lzw +{ + /// + /// This filter stream is used to decompress a LZW format stream. + /// Specifically, a stream that uses the LZC compression method. + /// This file format is usually associated with the .Z file extension. + /// + /// See http://en.wikipedia.org/wiki/Compress + /// See http://wiki.wxwidgets.org/Development:_Z_File_Format + /// + /// The file header consists of 3 (or optionally 4) bytes. The first two bytes + /// contain the magic marker "0x1f 0x9d", followed by a byte of flags. + /// + /// Based on Java code by Ronald Tschalar, which in turn was based on the unlzw.c + /// code in the gzip package. + /// + /// This sample shows how to unzip a compressed file + /// + /// using System; + /// using System.IO; + /// + /// using ICSharpCode.SharpZipLib.Core; + /// using ICSharpCode.SharpZipLib.LZW; + /// + /// class MainClass + /// { + /// public static void Main(string[] args) + /// { + /// using (Stream inStream = new LzwInputStream(File.OpenRead(args[0]))) + /// using (FileStream outStream = File.Create(Path.GetFileNameWithoutExtension(args[0]))) { + /// byte[] buffer = new byte[4096]; + /// StreamUtils.Copy(inStream, outStream, buffer); + /// // OR + /// inStream.Read(buffer, 0, buffer.Length); + /// // now do something with the buffer + /// } + /// } + /// } + /// + /// + public class LzwStream : Stream + { + public static bool IsLzwStream(Stream stream) + { + try + { + byte[] hdr = new byte[LzwConstants.HDR_SIZE]; + + int result = stream.Read(hdr, 0, hdr.Length); + + // Check the magic marker + if (result < 0) + throw new IncompleteArchiveException("Failed to read LZW header"); + + if (hdr[0] != (LzwConstants.MAGIC >> 8) || hdr[1] != (LzwConstants.MAGIC & 0xff)) + { + throw new IncompleteArchiveException( + String.Format( + "Wrong LZW header. Magic bytes don't match. 0x{0:x2} 0x{1:x2}", + hdr[0], + hdr[1] + ) + ); + } + } + catch (Exception) + { + return false; + } + return true; + } + + /// + /// Gets or sets a flag indicating ownership of underlying stream. + /// When the flag is true will close the underlying stream also. + /// + /// The default value is true. + public bool IsStreamOwner { get; set; } = false; + + /// + /// Creates a LzwInputStream + /// + /// + /// The stream to read compressed data from (baseInputStream LZW format) + /// + public LzwStream(Stream baseInputStream) + { + this.baseInputStream = baseInputStream; + } + + /// + /// See + /// + /// + public override int ReadByte() + { + int b = Read(one, 0, 1); + if (b == 1) + return (one[0] & 0xff); + return -1; + } + + /// + /// Reads decompressed data into the provided buffer byte array + /// + /// + /// The array to read and decompress data into + /// + /// + /// The offset indicating where the data should be placed + /// + /// + /// The number of bytes to decompress + /// + /// The number of bytes read. Zero signals the end of stream + public override int Read(byte[] buffer, int offset, int count) + { + if (!headerParsed) + ParseHeader(); + + if (eof) + return 0; + + int start = offset; + + /* Using local copies of various variables speeds things up by as + * much as 30% in Java! Performance not tested in C#. + */ + int[] lTabPrefix = tabPrefix; + byte[] lTabSuffix = tabSuffix; + byte[] lStack = stack; + int lNBits = nBits; + int lMaxCode = maxCode; + int lMaxMaxCode = maxMaxCode; + int lBitMask = bitMask; + int lOldCode = oldCode; + byte lFinChar = finChar; + int lStackP = stackP; + int lFreeEnt = freeEnt; + byte[] lData = data; + int lBitPos = bitPos; + + // empty stack if stuff still left + int sSize = lStack.Length - lStackP; + if (sSize > 0) + { + int num = (sSize >= count) ? count : sSize; + Array.Copy(lStack, lStackP, buffer, offset, num); + offset += num; + count -= num; + lStackP += num; + } + + if (count == 0) + { + stackP = lStackP; + return offset - start; + } + + // loop, filling local buffer until enough data has been decompressed + MainLoop: + do + { + if (end < EXTRA) + { + Fill(); + } + + int bitIn = (got > 0) ? (end - end % lNBits) << 3 : (end << 3) - (lNBits - 1); + + while (lBitPos < bitIn) + { + #region A + + // handle 1-byte reads correctly + if (count == 0) + { + nBits = lNBits; + maxCode = lMaxCode; + maxMaxCode = lMaxMaxCode; + bitMask = lBitMask; + oldCode = lOldCode; + finChar = lFinChar; + stackP = lStackP; + freeEnt = lFreeEnt; + bitPos = lBitPos; + + return offset - start; + } + + // check for code-width expansion + if (lFreeEnt > lMaxCode) + { + int nBytes = lNBits << 3; + lBitPos = (lBitPos - 1) + nBytes - (lBitPos - 1 + nBytes) % nBytes; + + lNBits++; + lMaxCode = (lNBits == maxBits) ? lMaxMaxCode : (1 << lNBits) - 1; + + lBitMask = (1 << lNBits) - 1; + lBitPos = ResetBuf(lBitPos); + goto MainLoop; + } + + #endregion A + + #region B + + // read next code + int pos = lBitPos >> 3; + int code = + ( + ( + (lData[pos] & 0xFF) + | ((lData[pos + 1] & 0xFF) << 8) + | ((lData[pos + 2] & 0xFF) << 16) + ) >> (lBitPos & 0x7) + ) & lBitMask; + + lBitPos += lNBits; + + // handle first iteration + if (lOldCode == -1) + { + if (code >= 256) + throw new IncompleteArchiveException( + "corrupt input: " + code + " > 255" + ); + + lFinChar = (byte)(lOldCode = code); + buffer[offset++] = lFinChar; + count--; + continue; + } + + // handle CLEAR code + if (code == TBL_CLEAR && blockMode) + { + Array.Copy(zeros, 0, lTabPrefix, 0, zeros.Length); + lFreeEnt = TBL_FIRST - 1; + + int nBytes = lNBits << 3; + lBitPos = (lBitPos - 1) + nBytes - (lBitPos - 1 + nBytes) % nBytes; + lNBits = LzwConstants.INIT_BITS; + lMaxCode = (1 << lNBits) - 1; + lBitMask = lMaxCode; + + // Code tables reset + + lBitPos = ResetBuf(lBitPos); + goto MainLoop; + } + + #endregion B + + #region C + + // setup + int inCode = code; + lStackP = lStack.Length; + + // Handle KwK case + if (code >= lFreeEnt) + { + if (code > lFreeEnt) + { + throw new IncompleteArchiveException( + "corrupt input: code=" + code + ", freeEnt=" + lFreeEnt + ); + } + + lStack[--lStackP] = lFinChar; + code = lOldCode; + } + + // Generate output characters in reverse order + while (code >= 256) + { + lStack[--lStackP] = lTabSuffix[code]; + code = lTabPrefix[code]; + } + + lFinChar = lTabSuffix[code]; + buffer[offset++] = lFinChar; + count--; + + // And put them out in forward order + sSize = lStack.Length - lStackP; + int num = (sSize >= count) ? count : sSize; + Array.Copy(lStack, lStackP, buffer, offset, num); + offset += num; + count -= num; + lStackP += num; + + #endregion C + + #region D + + // generate new entry in table + if (lFreeEnt < lMaxMaxCode) + { + lTabPrefix[lFreeEnt] = lOldCode; + lTabSuffix[lFreeEnt] = lFinChar; + lFreeEnt++; + } + + // Remember previous code + lOldCode = inCode; + + // if output buffer full, then return + if (count == 0) + { + nBits = lNBits; + maxCode = lMaxCode; + bitMask = lBitMask; + oldCode = lOldCode; + finChar = lFinChar; + stackP = lStackP; + freeEnt = lFreeEnt; + bitPos = lBitPos; + + return offset - start; + } + + #endregion D + } // while + + lBitPos = ResetBuf(lBitPos); + } while (got > 0); // do..while + + nBits = lNBits; + maxCode = lMaxCode; + bitMask = lBitMask; + oldCode = lOldCode; + finChar = lFinChar; + stackP = lStackP; + freeEnt = lFreeEnt; + bitPos = lBitPos; + + eof = true; + return offset - start; + } + + /// + /// Moves the unread data in the buffer to the beginning and resets + /// the pointers. + /// + /// + /// + private int ResetBuf(int bitPosition) + { + int pos = bitPosition >> 3; + Array.Copy(data, pos, data, 0, end - pos); + end -= pos; + return 0; + } + + private void Fill() + { + got = baseInputStream.Read(data, end, data.Length - 1 - end); + if (got > 0) + { + end += got; + } + } + + private void ParseHeader() + { + headerParsed = true; + + byte[] hdr = new byte[LzwConstants.HDR_SIZE]; + + int result = baseInputStream.Read(hdr, 0, hdr.Length); + + // Check the magic marker + if (result < 0) + throw new IncompleteArchiveException("Failed to read LZW header"); + + if (hdr[0] != (LzwConstants.MAGIC >> 8) || hdr[1] != (LzwConstants.MAGIC & 0xff)) + { + throw new IncompleteArchiveException( + String.Format( + "Wrong LZW header. Magic bytes don't match. 0x{0:x2} 0x{1:x2}", + hdr[0], + hdr[1] + ) + ); + } + + // Check the 3rd header byte + blockMode = (hdr[2] & LzwConstants.BLOCK_MODE_MASK) > 0; + maxBits = hdr[2] & LzwConstants.BIT_MASK; + + if (maxBits > LzwConstants.MAX_BITS) + { + throw new ArchiveException( + "Stream compressed with " + + maxBits + + " bits, but decompression can only handle " + + LzwConstants.MAX_BITS + + " bits." + ); + } + + if ((hdr[2] & LzwConstants.RESERVED_MASK) > 0) + { + throw new ArchiveException("Unsupported bits set in the header."); + } + + // Initialize variables + maxMaxCode = 1 << maxBits; + nBits = LzwConstants.INIT_BITS; + maxCode = (1 << nBits) - 1; + bitMask = maxCode; + oldCode = -1; + finChar = 0; + freeEnt = blockMode ? TBL_FIRST : 256; + + tabPrefix = new int[1 << maxBits]; + tabSuffix = new byte[1 << maxBits]; + stack = new byte[1 << maxBits]; + stackP = stack.Length; + + for (int idx = 255; idx >= 0; idx--) + tabSuffix[idx] = (byte)idx; + } + + #region Stream Overrides + + /// + /// Gets a value indicating whether the current stream supports reading + /// + public override bool CanRead + { + get { return baseInputStream.CanRead; } + } + + /// + /// Gets a value of false indicating seeking is not supported for this stream. + /// + public override bool CanSeek + { + get { return false; } + } + + /// + /// Gets a value of false indicating that this stream is not writeable. + /// + public override bool CanWrite + { + get { return false; } + } + + /// + /// A value representing the length of the stream in bytes. + /// + public override long Length + { + get { return got; } + } + + /// + /// The current position within the stream. + /// Throws a NotSupportedException when attempting to set the position + /// + /// Attempting to set the position + public override long Position + { + get { return baseInputStream.Position; } + set { throw new NotSupportedException("InflaterInputStream Position not supported"); } + } + + /// + /// Flushes the baseInputStream + /// + public override void Flush() + { + baseInputStream.Flush(); + } + + /// + /// Sets the position within the current stream + /// Always throws a NotSupportedException + /// + /// The relative offset to seek to. + /// The defining where to seek from. + /// The new position in the stream. + /// Any access + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException("Seek not supported"); + } + + /// + /// Set the length of the current stream + /// Always throws a NotSupportedException + /// + /// The new length value for the stream. + /// Any access + public override void SetLength(long value) + { + throw new NotSupportedException("InflaterInputStream SetLength not supported"); + } + + /// + /// Writes a sequence of bytes to stream and advances the current position + /// This method always throws a NotSupportedException + /// + /// The buffer containing data to write. + /// The offset of the first byte to write. + /// The number of bytes to write. + /// Any access + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotSupportedException("InflaterInputStream Write not supported"); + } + + /// + /// Writes one byte to the current stream and advances the current position + /// Always throws a NotSupportedException + /// + /// The byte to write. + /// Any access + public override void WriteByte(byte value) + { + throw new NotSupportedException("InflaterInputStream WriteByte not supported"); + } + + /// + /// Closes the input stream. When + /// is true the underlying stream is also closed. + /// + protected override void Dispose(bool disposing) + { + if (!isClosed) + { + isClosed = true; + if (IsStreamOwner) + { + baseInputStream.Dispose(); + } + } + } + + #endregion Stream Overrides + + #region Instance Fields + + private Stream baseInputStream; + + /// + /// Flag indicating wether this instance has been closed or not. + /// + private bool isClosed; + + private readonly byte[] one = new byte[1]; + private bool headerParsed; + + // string table stuff + private const int TBL_CLEAR = 0x100; + + private const int TBL_FIRST = TBL_CLEAR + 1; + + private int[] tabPrefix = new int[0]; // + private byte[] tabSuffix = new byte[0]; // + private readonly int[] zeros = new int[256]; + private byte[] stack = new byte[0]; // + + // various state + private bool blockMode; + + private int nBits; + private int maxBits; + private int maxMaxCode; + private int maxCode; + private int bitMask; + private int oldCode; + private byte finChar; + private int stackP; + private int freeEnt; + + // input buffer + private readonly byte[] data = new byte[1024 * 8]; + + private int bitPos; + private int end; + private int got; + private bool eof; + private const int EXTRA = 64; + + #endregion Instance Fields + } +} diff --git a/src/SharpCompress/Compressors/PPMd/H/ModelPPM.cs b/src/SharpCompress/Compressors/PPMd/H/ModelPPM.cs index 3b2d22ba..d4e91921 100644 --- a/src/SharpCompress/Compressors/PPMd/H/ModelPPM.cs +++ b/src/SharpCompress/Compressors/PPMd/H/ModelPPM.cs @@ -22,7 +22,7 @@ internal class ModelPpm } } - public SubAllocator SubAlloc { get; } = new SubAllocator(); + public SubAllocator SubAlloc { get; } = new(); public virtual See2Context DummySee2Cont => _dummySee2Cont; @@ -137,34 +137,34 @@ internal class ModelPpm // Temp fields //UPGRADE_NOTE: Final was removed from the declaration of 'tempState1 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly State _tempState1 = new State(null); + private readonly State _tempState1 = new(null); //UPGRADE_NOTE: Final was removed from the declaration of 'tempState2 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly State _tempState2 = new State(null); + private readonly State _tempState2 = new(null); //UPGRADE_NOTE: Final was removed from the declaration of 'tempState3 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly State _tempState3 = new State(null); + private readonly State _tempState3 = new(null); //UPGRADE_NOTE: Final was removed from the declaration of 'tempState4 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly State _tempState4 = new State(null); + private readonly State _tempState4 = new(null); //UPGRADE_NOTE: Final was removed from the declaration of 'tempStateRef1 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly StateRef _tempStateRef1 = new StateRef(); + private readonly StateRef _tempStateRef1 = new(); //UPGRADE_NOTE: Final was removed from the declaration of 'tempStateRef2 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly StateRef _tempStateRef2 = new StateRef(); + private readonly StateRef _tempStateRef2 = new(); //UPGRADE_NOTE: Final was removed from the declaration of 'tempPPMContext1 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly PpmContext _tempPpmContext1 = new PpmContext(null); + private readonly PpmContext _tempPpmContext1 = new(null); //UPGRADE_NOTE: Final was removed from the declaration of 'tempPPMContext2 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly PpmContext _tempPpmContext2 = new PpmContext(null); + private readonly PpmContext _tempPpmContext2 = new(null); //UPGRADE_NOTE: Final was removed from the declaration of 'tempPPMContext3 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly PpmContext _tempPpmContext3 = new PpmContext(null); + private readonly PpmContext _tempPpmContext3 = new(null); //UPGRADE_NOTE: Final was removed from the declaration of 'tempPPMContext4 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly PpmContext _tempPpmContext4 = new PpmContext(null); + private readonly PpmContext _tempPpmContext4 = new(null); //UPGRADE_NOTE: Final was removed from the declaration of 'ps '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" private readonly int[] _ps = new int[MAX_O]; diff --git a/src/SharpCompress/Compressors/PPMd/H/PPMContext.cs b/src/SharpCompress/Compressors/PPMd/H/PPMContext.cs index ab00615d..e4f98c71 100644 --- a/src/SharpCompress/Compressors/PPMd/H/PPMContext.cs +++ b/src/SharpCompress/Compressors/PPMd/H/PPMContext.cs @@ -64,19 +64,19 @@ internal class PpmContext : Pointer // Temp fields //UPGRADE_NOTE: Final was removed from the declaration of 'tempState1 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly State _tempState1 = new State(null); + private readonly State _tempState1 = new(null); //UPGRADE_NOTE: Final was removed from the declaration of 'tempState2 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly State _tempState2 = new State(null); + private readonly State _tempState2 = new(null); //UPGRADE_NOTE: Final was removed from the declaration of 'tempState3 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly State _tempState3 = new State(null); + private readonly State _tempState3 = new(null); //UPGRADE_NOTE: Final was removed from the declaration of 'tempState4 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly State _tempState4 = new State(null); + private readonly State _tempState4 = new(null); //UPGRADE_NOTE: Final was removed from the declaration of 'tempState5 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly State _tempState5 = new State(null); + private readonly State _tempState5 = new(null); private PpmContext _tempPpmContext; //UPGRADE_NOTE: Final was removed from the declaration of 'ps '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" diff --git a/src/SharpCompress/Compressors/PPMd/I1/Allocator.cs b/src/SharpCompress/Compressors/PPMd/I1/Allocator.cs index 7b677a93..c4d8aa4b 100644 --- a/src/SharpCompress/Compressors/PPMd/I1/Allocator.cs +++ b/src/SharpCompress/Compressors/PPMd/I1/Allocator.cs @@ -395,10 +395,8 @@ internal class Allocator unitCountDifference -= unitCount; } - _memoryNodes[UNITS_TO_INDEX[unitCountDifference - 1]].Insert( - newPointer, - unitCountDifference - ); + _memoryNodes[UNITS_TO_INDEX[unitCountDifference - 1]] + .Insert(newPointer, unitCountDifference); } private void GlueFreeBlocks() @@ -457,10 +455,11 @@ internal class Allocator if (INDEX_TO_UNITS[index] != unitCount) { var unitCountDifference = unitCount - INDEX_TO_UNITS[--index]; - _memoryNodes[unitCountDifference - 1].Insert( - memoryNode0 + (unitCount - unitCountDifference), - unitCountDifference - ); + _memoryNodes[unitCountDifference - 1] + .Insert( + memoryNode0 + (unitCount - unitCountDifference), + unitCountDifference + ); } _memoryNodes[index].Insert(memoryNode0, INDEX_TO_UNITS[index]); diff --git a/src/SharpCompress/Compressors/PPMd/I1/MemoryNode.cs b/src/SharpCompress/Compressors/PPMd/I1/MemoryNode.cs index 5e978c5c..c8b411d8 100644 --- a/src/SharpCompress/Compressors/PPMd/I1/MemoryNode.cs +++ b/src/SharpCompress/Compressors/PPMd/I1/MemoryNode.cs @@ -27,7 +27,7 @@ internal struct MemoryNode { public uint _address; public byte[] _memory; - public static readonly MemoryNode ZERO = new MemoryNode(0, null); + public static readonly MemoryNode ZERO = new(0, null); public const int SIZE = 12; /// @@ -64,7 +64,7 @@ internal struct MemoryNode public MemoryNode Next { get => - new MemoryNode( + new( _memory[_address + 4] | (((uint)_memory[_address + 5]) << 8) | (((uint)_memory[_address + 6]) << 16) @@ -150,7 +150,7 @@ internal struct MemoryNode /// /// public static implicit operator MemoryNode(Pointer pointer) => - new MemoryNode(pointer._address, pointer._memory); + new(pointer._address, pointer._memory); /// /// Allow pointer-like addition on a memory node. diff --git a/src/SharpCompress/Compressors/PPMd/I1/Model.cs b/src/SharpCompress/Compressors/PPMd/I1/Model.cs index 786cb0e7..9a1d090f 100644 --- a/src/SharpCompress/Compressors/PPMd/I1/Model.cs +++ b/src/SharpCompress/Compressors/PPMd/I1/Model.cs @@ -866,7 +866,8 @@ internal partial class Model ); } else if ( - (currentContext.SummaryFrequency += 4) > 128 + (4 * currentContext.NumberStatistics) + (currentContext.SummaryFrequency += 4) + > 128 + (4 * currentContext.NumberStatistics) ) { Refresh((uint)((currentContext.NumberStatistics + 2) >> 1), true, currentContext); diff --git a/src/SharpCompress/Compressors/PPMd/I1/Pointer.cs b/src/SharpCompress/Compressors/PPMd/I1/Pointer.cs index 72c557ba..d1a89545 100644 --- a/src/SharpCompress/Compressors/PPMd/I1/Pointer.cs +++ b/src/SharpCompress/Compressors/PPMd/I1/Pointer.cs @@ -22,7 +22,7 @@ internal struct Pointer { public uint _address; public byte[] _memory; - public static readonly Pointer ZERO = new Pointer(0, null); + public static readonly Pointer ZERO = new(0, null); public const int SIZE = 1; /// @@ -69,7 +69,7 @@ internal struct Pointer /// /// public static implicit operator Pointer(MemoryNode memoryNode) => - new Pointer(memoryNode._address, memoryNode._memory); + new(memoryNode._address, memoryNode._memory); /// /// Allow a to be implicitly converted to a . @@ -77,15 +77,14 @@ internal struct Pointer /// /// public static implicit operator Pointer(Model.PpmContext context) => - new Pointer(context._address, context._memory); + new(context._address, context._memory); /// /// Allow a to be implicitly converted to a . /// /// /// - public static implicit operator Pointer(PpmState state) => - new Pointer(state._address, state._memory); + public static implicit operator Pointer(PpmState state) => new(state._address, state._memory); /// /// Increase the address of a pointer by the given number of bytes. diff --git a/src/SharpCompress/Compressors/PPMd/I1/PpmContext.cs b/src/SharpCompress/Compressors/PPMd/I1/PpmContext.cs index a37d278d..701039c2 100644 --- a/src/SharpCompress/Compressors/PPMd/I1/PpmContext.cs +++ b/src/SharpCompress/Compressors/PPMd/I1/PpmContext.cs @@ -21,7 +21,7 @@ internal partial class Model { public uint _address; public byte[] _memory; - public static readonly PpmContext ZERO = new PpmContext(0, null); + public static readonly PpmContext ZERO = new(0, null); public const int SIZE = 12; /// @@ -70,7 +70,7 @@ internal partial class Model public PpmState Statistics { get => - new PpmState( + new( _memory[_address + 4] | (((uint)_memory[_address + 5]) << 8) | (((uint)_memory[_address + 6]) << 16) @@ -92,7 +92,7 @@ internal partial class Model public PpmContext Suffix { get => - new PpmContext( + new( _memory[_address + 8] | (((uint)_memory[_address + 9]) << 8) | (((uint)_memory[_address + 10]) << 16) @@ -133,7 +133,7 @@ internal partial class Model /// /// /// - public PpmState FirstState => new PpmState(_address + 2, _memory); + public PpmState FirstState => new(_address + 2, _memory); /// /// Gets or sets the symbol of the first PPM state. This is provided for convenience. The same @@ -164,7 +164,7 @@ internal partial class Model public PpmContext FirstStateSuccessor { get => - new PpmContext( + new( _memory[_address + 4] | (((uint)_memory[_address + 5]) << 8) | (((uint)_memory[_address + 6]) << 16) @@ -186,7 +186,7 @@ internal partial class Model /// /// public static implicit operator PpmContext(Pointer pointer) => - new PpmContext(pointer._address, pointer._memory); + new(pointer._address, pointer._memory); /// /// Allow pointer-like addition on a PPM context. diff --git a/src/SharpCompress/Compressors/PPMd/I1/PpmState.cs b/src/SharpCompress/Compressors/PPMd/I1/PpmState.cs index 757afd00..02e79a34 100644 --- a/src/SharpCompress/Compressors/PPMd/I1/PpmState.cs +++ b/src/SharpCompress/Compressors/PPMd/I1/PpmState.cs @@ -19,7 +19,7 @@ internal struct PpmState { public uint _address; public byte[] _memory; - public static readonly PpmState ZERO = new PpmState(0, null); + public static readonly PpmState ZERO = new(0, null); public const int SIZE = 6; /// @@ -55,7 +55,7 @@ internal struct PpmState public Model.PpmContext Successor { get => - new Model.PpmContext( + new( _memory[_address + 2] | (((uint)_memory[_address + 3]) << 8) | (((uint)_memory[_address + 4]) << 16) @@ -77,7 +77,7 @@ internal struct PpmState /// /// /// - public PpmState this[int offset] => new PpmState((uint)(_address + (offset * SIZE)), _memory); + public PpmState this[int offset] => new((uint)(_address + (offset * SIZE)), _memory); /// /// Allow a pointer to be implicitly converted to a PPM state. @@ -85,7 +85,7 @@ internal struct PpmState /// /// public static implicit operator PpmState(Pointer pointer) => - new PpmState(pointer._address, pointer._memory); + new(pointer._address, pointer._memory); /// /// Allow pointer-like addition on a PPM state. diff --git a/src/SharpCompress/Compressors/Rar/MultiVolumeReadOnlyStream.cs b/src/SharpCompress/Compressors/Rar/MultiVolumeReadOnlyStream.cs index 1e4adfde..eda9c765 100644 --- a/src/SharpCompress/Compressors/Rar/MultiVolumeReadOnlyStream.cs +++ b/src/SharpCompress/Compressors/Rar/MultiVolumeReadOnlyStream.cs @@ -128,9 +128,9 @@ internal sealed class MultiVolumeReadOnlyStream : Stream public override bool CanWrite => false; - public uint CurrentCrc { get; private set; } + public byte[] CurrentCrc { get; private set; } - public override void Flush() => throw new NotSupportedException(); + public override void Flush() { } public override long Length => throw new NotSupportedException(); diff --git a/src/SharpCompress/Compressors/Rar/RarBLAKE2spStream.cs b/src/SharpCompress/Compressors/Rar/RarBLAKE2spStream.cs new file mode 100644 index 00000000..7c897176 --- /dev/null +++ b/src/SharpCompress/Compressors/Rar/RarBLAKE2spStream.cs @@ -0,0 +1,305 @@ +using System; +using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; +using SharpCompress.Common; +using SharpCompress.Common.Rar.Headers; + +namespace SharpCompress.Compressors.Rar; + +internal class RarBLAKE2spStream : RarStream +{ + private readonly MultiVolumeReadOnlyStream readStream; + private readonly bool disableCRCCheck; + + const uint BLAKE2S_NUM_ROUNDS = 10; + const uint BLAKE2S_FINAL_FLAG = (~(uint)0); + const int BLAKE2S_BLOCK_SIZE = 64; + const int BLAKE2S_DIGEST_SIZE = 32; + const int BLAKE2SP_PARALLEL_DEGREE = 8; + const uint BLAKE2S_INIT_IV_SIZE = 8; + + static readonly UInt32[] k_BLAKE2S_IV = + { + 0x6A09E667U, + 0xBB67AE85U, + 0x3C6EF372U, + 0xA54FF53AU, + 0x510E527FU, + 0x9B05688CU, + 0x1F83D9ABU, + 0x5BE0CD19U + }; + + static readonly byte[][] k_BLAKE2S_Sigma = + { + new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, + new byte[] { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }, + new byte[] { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 }, + new byte[] { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 }, + new byte[] { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 }, + new byte[] { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 }, + new byte[] { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 }, + new byte[] { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 }, + new byte[] { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 }, + new byte[] { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 }, + }; + + internal class BLAKE2S + { + internal UInt32[] h; + internal UInt32[] t; + internal UInt32[] f; + internal byte[] b; + internal int bufferPosition; + internal UInt32 lastNodeFlag; + UInt32[] dummy; + + public BLAKE2S() + { + h = new uint[BLAKE2S_INIT_IV_SIZE]; + t = new uint[2]; + f = new uint[2]; + b = new byte[BLAKE2S_BLOCK_SIZE]; + dummy = new uint[2]; + } + }; + + internal class BLAKE2SP + { + internal BLAKE2S[] S; + internal int bufferPosition; + + public BLAKE2SP() + { + S = new BLAKE2S[BLAKE2SP_PARALLEL_DEGREE]; + for (var i = 0; i < S.Length; i++) + { + S[i] = new BLAKE2S(); + } + } + }; + + BLAKE2SP _blake2sp; + + byte[] _hash = { }; + + public RarBLAKE2spStream( + IRarUnpack unpack, + FileHeader fileHeader, + MultiVolumeReadOnlyStream readStream + ) + : base(unpack, fileHeader, readStream) + { + this.readStream = readStream; + disableCRCCheck = fileHeader.IsEncrypted; + _hash = fileHeader.FileCrc; + _blake2sp = new BLAKE2SP(); + ResetCrc(); + } + + public byte[] GetCrc() => _hash; + + internal void ResetCrc(BLAKE2S hash) + { + for (UInt32 j = 0; j < BLAKE2S_INIT_IV_SIZE; j++) + { + hash.h[j] = k_BLAKE2S_IV[j]; + } + hash.t[0] = 0; + hash.t[1] = 0; + hash.f[0] = 0; + hash.f[1] = 0; + hash.bufferPosition = 0; + hash.lastNodeFlag = 0; + } + + internal void G( + ref UInt32[] m, + ref byte[] sigma, + int i, + ref UInt32 a, + ref UInt32 b, + ref UInt32 c, + ref UInt32 d + ) + { + a += b + m[sigma[2 * i]]; + d ^= a; + d = (d >> 16) | (d << 16); + c += d; + b ^= c; + b = (b >> 12) | (b << 20); + + a += b + m[sigma[2 * i + 1]]; + d ^= a; + d = (d >> 8) | (d << 24); + c += d; + b ^= c; + b = (b >> 7) | (b << 25); + } + + internal void Compress(BLAKE2S hash) + { + var m = new UInt32[16]; + var v = new UInt32[16]; + + for (var i = 0; i < 16; i++) + { + m[i] = BitConverter.ToUInt32(hash.b, i * 4); + } + + for (var i = 0; i < 8; i++) + { + v[i] = hash.h[i]; + } + + v[8] = k_BLAKE2S_IV[0]; + v[9] = k_BLAKE2S_IV[1]; + v[10] = k_BLAKE2S_IV[2]; + v[11] = k_BLAKE2S_IV[3]; + + v[12] = hash.t[0] ^ k_BLAKE2S_IV[4]; + v[13] = hash.t[1] ^ k_BLAKE2S_IV[5]; + v[14] = hash.f[0] ^ k_BLAKE2S_IV[6]; + v[15] = hash.f[1] ^ k_BLAKE2S_IV[7]; + + for (var r = 0; r < BLAKE2S_NUM_ROUNDS; r++) + { + ref byte[] sigma = ref k_BLAKE2S_Sigma[r]; + + G(ref m, ref sigma, 0, ref v[0], ref v[4], ref v[8], ref v[12]); + G(ref m, ref sigma, 1, ref v[1], ref v[5], ref v[9], ref v[13]); + G(ref m, ref sigma, 2, ref v[2], ref v[6], ref v[10], ref v[14]); + G(ref m, ref sigma, 3, ref v[3], ref v[7], ref v[11], ref v[15]); + G(ref m, ref sigma, 4, ref v[0], ref v[5], ref v[10], ref v[15]); + G(ref m, ref sigma, 5, ref v[1], ref v[6], ref v[11], ref v[12]); + G(ref m, ref sigma, 6, ref v[2], ref v[7], ref v[8], ref v[13]); + G(ref m, ref sigma, 7, ref v[3], ref v[4], ref v[9], ref v[14]); + } + + for (var i = 0; i < 8; i++) + { + hash.h[i] ^= v[i] ^ v[i + 8]; + } + } + + internal void Update(BLAKE2S hash, ReadOnlySpan data, int size) + { + var i = 0; + while (size != 0) + { + var pos = hash.bufferPosition; + var reminder = BLAKE2S_BLOCK_SIZE - pos; + + if (size <= reminder) + { + data.Slice(i, size).CopyTo(new Span(hash.b, pos, size)); + hash.bufferPosition += size; + return; + } + data.Slice(i, reminder).CopyTo(new Span(hash.b, pos, reminder)); + hash.t[0] += BLAKE2S_BLOCK_SIZE; + hash.t[1] += hash.t[0] < BLAKE2S_BLOCK_SIZE ? 1U : 0U; + Compress(hash); + hash.bufferPosition = 0; + i += reminder; + size -= reminder; + } + } + + internal byte[] Final(BLAKE2S hash) + { + hash.t[0] += (uint)hash.bufferPosition; + hash.t[1] += hash.t[0] < hash.bufferPosition ? 1U : 0U; + hash.f[0] = BLAKE2S_FINAL_FLAG; + hash.f[1] = hash.lastNodeFlag; + Array.Clear(hash.b, hash.bufferPosition, BLAKE2S_BLOCK_SIZE - hash.bufferPosition); + Compress(hash); + + var mem = new MemoryStream(); + + for (var i = 0; i < 8; i++) + { + mem.Write(BitConverter.GetBytes(hash.h[i]), 0, 4); + } + + return mem.ToArray(); + } + + public void ResetCrc() + { + _blake2sp.bufferPosition = 0; + + for (UInt32 i = 0; i < BLAKE2SP_PARALLEL_DEGREE; i++) + { + _blake2sp.S[i].bufferPosition = 0; + ResetCrc(_blake2sp.S[i]); + _blake2sp.S[i].h[0] ^= (BLAKE2S_DIGEST_SIZE | BLAKE2SP_PARALLEL_DEGREE << 16 | 2 << 24); + _blake2sp.S[i].h[2] ^= i; + _blake2sp.S[i].h[3] ^= (BLAKE2S_DIGEST_SIZE << 24); + } + + _blake2sp.S[BLAKE2SP_PARALLEL_DEGREE - 1].lastNodeFlag = BLAKE2S_FINAL_FLAG; + } + + internal void Update(BLAKE2SP hash, ReadOnlySpan data, int size) + { + var i = 0; + var pos = hash.bufferPosition; + while (size != 0) + { + var index = pos / BLAKE2S_BLOCK_SIZE; + var reminder = BLAKE2S_BLOCK_SIZE - (pos & (BLAKE2S_BLOCK_SIZE - 1)); + if (reminder > size) + { + reminder = size; + } + // Update(hash.S[index], data, size); + Update(hash.S[index], data.Slice(i, reminder), reminder); + size -= reminder; + i += reminder; + pos += reminder; + pos &= (BLAKE2S_BLOCK_SIZE * (BLAKE2SP_PARALLEL_DEGREE - 1)); + } + hash.bufferPosition = pos; + } + + internal byte[] Final(BLAKE2SP hash) + { + var h = new BLAKE2S(); + + ResetCrc(h); + h.h[0] ^= (BLAKE2S_DIGEST_SIZE | BLAKE2SP_PARALLEL_DEGREE << 16 | 2 << 24); + h.h[3] ^= (1 << 16 | BLAKE2S_DIGEST_SIZE << 24); + h.lastNodeFlag = BLAKE2S_FINAL_FLAG; + + for (var i = 0; i < BLAKE2SP_PARALLEL_DEGREE; i++) + { + var digest = Final(_blake2sp.S[i]); + Update(h, digest, BLAKE2S_DIGEST_SIZE); + } + + return Final(h); + } + + public override int Read(byte[] buffer, int offset, int count) + { + var result = base.Read(buffer, offset, count); + if (result != 0) + { + Update(_blake2sp, new ReadOnlySpan(buffer, offset, result), result); + } + else + { + _hash = Final(_blake2sp); + if (!disableCRCCheck && !(GetCrc().SequenceEqual(readStream.CurrentCrc)) && count != 0) + { + // NOTE: we use the last FileHeader in a multipart volume to check CRC + throw new InvalidFormatException("file crc mismatch"); + } + } + + return result; + } +} diff --git a/src/SharpCompress/Compressors/Rar/RarCrcStream.cs b/src/SharpCompress/Compressors/Rar/RarCrcStream.cs index 98d50b00..ce42a4bd 100644 --- a/src/SharpCompress/Compressors/Rar/RarCrcStream.cs +++ b/src/SharpCompress/Compressors/Rar/RarCrcStream.cs @@ -1,3 +1,4 @@ +using System; using SharpCompress.Common; using SharpCompress.Common.Rar.Headers; @@ -7,6 +8,7 @@ internal class RarCrcStream : RarStream { private readonly MultiVolumeReadOnlyStream readStream; private uint currentCrc; + private readonly bool disableCRC; public RarCrcStream( IRarUnpack unpack, @@ -16,6 +18,7 @@ internal class RarCrcStream : RarStream : base(unpack, fileHeader, readStream) { this.readStream = readStream; + disableCRC = fileHeader.IsEncrypted; ResetCrc(); } @@ -30,7 +33,11 @@ internal class RarCrcStream : RarStream { currentCrc = RarCRC.CheckCrc(currentCrc, buffer, offset, result); } - else if (GetCrc() != readStream.CurrentCrc && count != 0) + else if ( + !disableCRC + && GetCrc() != BitConverter.ToUInt32(readStream.CurrentCrc, 0) + && count != 0 + ) { // NOTE: we use the last FileHeader in a multipart volume to check CRC throw new InvalidFormatException("file crc mismatch"); diff --git a/src/SharpCompress/Compressors/Rar/RarStream.cs b/src/SharpCompress/Compressors/Rar/RarStream.cs index 0bc2e054..bf5d3209 100644 --- a/src/SharpCompress/Compressors/Rar/RarStream.cs +++ b/src/SharpCompress/Compressors/Rar/RarStream.cs @@ -14,7 +14,7 @@ internal class RarStream : Stream private bool fetch; - private byte[] tmpBuffer = new byte[65536]; + private byte[] tmpBuffer = BufferPool.Rent(65536); private int tmpOffset; private int tmpCount; @@ -40,6 +40,11 @@ internal class RarStream : Stream { if (!isDisposed) { + if (disposing) + { + BufferPool.Return(this.tmpBuffer); + this.tmpBuffer = null; + } isDisposed = true; base.Dispose(disposing); readStream.Dispose(); @@ -86,6 +91,13 @@ internal class RarStream : Stream fetch = false; } _position += outTotal; + if (count > 0 && outTotal == 0 && _position != Length) + { + // sanity check, eg if we try to decompress a redir entry + throw new InvalidOperationException( + $"unpacked file size does not match header: expected {Length} found {_position}" + ); + } return outTotal; } @@ -111,16 +123,7 @@ internal class RarStream : Stream } if (count > 0) { - if (tmpBuffer.Length < tmpCount + count) - { - var newBuffer = new byte[ - tmpBuffer.Length * 2 > tmpCount + count - ? tmpBuffer.Length * 2 - : tmpCount + count - ]; - Buffer.BlockCopy(tmpBuffer, 0, newBuffer, 0, tmpCount); - tmpBuffer = newBuffer; - } + EnsureBufferCapacity(count); Buffer.BlockCopy(buffer, offset, tmpBuffer, tmpCount, count); tmpCount += count; tmpOffset = 0; @@ -131,4 +134,20 @@ internal class RarStream : Stream unpack.Suspended = false; } } + + private void EnsureBufferCapacity(int count) + { + if (this.tmpBuffer.Length < this.tmpCount + count) + { + var newLength = + this.tmpBuffer.Length * 2 > this.tmpCount + count + ? this.tmpBuffer.Length * 2 + : this.tmpCount + count; + var newBuffer = BufferPool.Rent(newLength); + Buffer.BlockCopy(this.tmpBuffer, 0, newBuffer, 0, this.tmpCount); + var oldBuffer = this.tmpBuffer; + this.tmpBuffer = newBuffer; + BufferPool.Return(oldBuffer); + } + } } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack.cs index 2ca62d6b..e6d4153d 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack.cs @@ -52,19 +52,19 @@ internal sealed partial class Unpack : BitInput, IRarUnpack public int PpmEscChar { get; set; } - private readonly ModelPpm ppm = new ModelPpm(); + private readonly ModelPpm ppm = new(); - private readonly RarVM rarVM = new RarVM(); + private readonly RarVM rarVM = new(); // Filters code, one entry per filter - private readonly List filters = new List(); + private readonly List filters = new(); // Filters stack, several entrances of same filter are possible - private readonly List prgStack = new List(); + private readonly List prgStack = new(); // lengths of preceding blocks, one length per filter. Used to reduce size // required to write block length if lengths are repeating - private readonly List oldFilterLengths = new List(); + private readonly List oldFilterLengths = new(); private int lastFilter; diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack20.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack20.cs index 15b980da..3a5d008d 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack20.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack20.cs @@ -25,15 +25,15 @@ internal partial class Unpack private readonly AudioVariables[] AudV = new AudioVariables[4]; - private readonly LitDecode LD = new LitDecode(); + private readonly LitDecode LD = new(); - private readonly DistDecode DD = new DistDecode(); + private readonly DistDecode DD = new(); - private readonly LowDistDecode LDD = new LowDistDecode(); + private readonly LowDistDecode LDD = new(); - private readonly RepDecode RD = new RepDecode(); + private readonly RepDecode RD = new(); - private readonly BitDecode BD = new BitDecode(); + private readonly BitDecode BD = new(); private static readonly int[] LDecode = { diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/UnpackUtility.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/UnpackUtility.cs index 09f5ee8f..b501337b 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV1/UnpackUtility.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/UnpackUtility.cs @@ -1,5 +1,4 @@ using System; - using SharpCompress.Compressors.Rar.VM; namespace SharpCompress.Compressors.Rar.UnpackV1; diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/FragmentedWindow.unpack50frag_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/FragmentedWindow.unpack50frag_cpp.cs index bfa69829..a9a6f280 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/FragmentedWindow.unpack50frag_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/FragmentedWindow.unpack50frag_cpp.cs @@ -1,5 +1,6 @@ #nullable disable +using System; #if !Rar2017_64bit using size_t = System.UInt32; #else @@ -7,7 +8,6 @@ using nint = System.Int64; using nuint = System.UInt64; using size_t = System.UInt64; #endif -using System; namespace SharpCompress.Compressors.Rar.UnpackV2017; diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs index 53db46d2..22fdc746 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs @@ -1,13 +1,13 @@ -#if !Rar2017_64bit +using System; +using System.IO; +using SharpCompress.Common.Rar.Headers; +#if !Rar2017_64bit using size_t = System.UInt32; #else using nint = System.Int64; using nuint = System.UInt64; using size_t = System.UInt64; #endif -using System; -using System.IO; -using SharpCompress.Common.Rar.Headers; namespace SharpCompress.Compressors.Rar.UnpackV2017; @@ -67,15 +67,15 @@ internal partial class Unpack : IRarUnpack private void UnstoreFile() { - var b = new byte[0x10000]; + Span b = stackalloc byte[(int)Math.Min(0x10000, DestUnpSize)]; do { - var n = readStream.Read(b, 0, (int)Math.Min(b.Length, DestUnpSize)); + var n = readStream.Read(b); if (n == 0) { break; } - writeStream.Write(b, 0, n); + writeStream.Write(b.Slice(0, n)); DestUnpSize -= n; } while (!Suspended); } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.rawint_hpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.rawint_hpp.cs index 03614dde..62fb3c9d 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.rawint_hpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.rawint_hpp.cs @@ -1,10 +1,12 @@ -#if !Rar2017_64bit +using uint32 = System.UInt32; + +/*#if !Rar2017_64bit #else using nint = System.Int64; using nuint = System.UInt64; using size_t = System.UInt64; -#endif -using uint32 = System.UInt32; +#endif*/ + namespace SharpCompress.Compressors.Rar.UnpackV2017; diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack20_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack20_cpp.cs index b92af290..51dbff0b 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack20_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack20_cpp.cs @@ -1,12 +1,14 @@ -#if !Rar2017_64bit +using System; +using static SharpCompress.Compressors.Rar.UnpackV2017.PackDef; +using static SharpCompress.Compressors.Rar.UnpackV2017.Unpack.Unpack20Local; + +/*#if !Rar2017_64bit #else using nint = System.Int64; using nuint = System.UInt64; using size_t = System.UInt64; -#endif -using System; -using static SharpCompress.Compressors.Rar.UnpackV2017.PackDef; -using static SharpCompress.Compressors.Rar.UnpackV2017.Unpack.Unpack20Local; +#endif*/ + namespace SharpCompress.Compressors.Rar.UnpackV2017; @@ -371,8 +373,8 @@ internal partial class Unpack private bool ReadTables20() { - var BitLength = new byte[BC20]; - var Table = new byte[MC20 * 4]; + Span BitLength = stackalloc byte[checked((int)BC20)]; + Span Table = stackalloc byte[checked((int)MC20 * 4)]; if (Inp.InAddr > ReadTop - 25) { if (!UnpReadBuf()) @@ -408,13 +410,13 @@ internal partial class Unpack TableSize = NC20 + DC20 + RC20; } - for (uint I = 0; I < BC20; I++) + for (int I = 0; I < checked((int)BC20); I++) { BitLength[I] = (byte)(Inp.getbits() >> 12); Inp.addbits(4); } MakeDecodeTables(BitLength, 0, BlockTables.BD, BC20); - for (uint I = 0; I < TableSize; ) + for (int I = 0; I < checked((int)TableSize); ) { if (Inp.InAddr > ReadTop - 5) { @@ -485,8 +487,7 @@ internal partial class Unpack MakeDecodeTables(Table, (int)NC20, BlockTables.DD, DC20); MakeDecodeTables(Table, (int)(NC20 + DC20), BlockTables.RD, RC20); } - //x memcpy(UnpOldTable20,Table,sizeof(UnpOldTable20)); - Array.Copy(Table, UnpOldTable20, UnpOldTable20.Length); + Table.CopyTo(this.UnpOldTable20); return true; } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs index f85921bd..143e3d31 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs @@ -1,5 +1,9 @@ -#nullable disable +#nullable disable +using System; +using static SharpCompress.Compressors.Rar.UnpackV2017.PackDef; +using static SharpCompress.Compressors.Rar.UnpackV2017.UnpackGlobal; +using int64 = System.Int64; #if !Rar2017_64bit using size_t = System.UInt32; #else @@ -7,11 +11,6 @@ using nint = System.Int64; using nuint = System.UInt64; using size_t = System.UInt64; #endif -using int64 = System.Int64; - -using System; -using static SharpCompress.Compressors.Rar.UnpackV2017.PackDef; -using static SharpCompress.Compressors.Rar.UnpackV2017.UnpackGlobal; namespace SharpCompress.Compressors.Rar.UnpackV2017; @@ -531,7 +530,6 @@ internal partial class Unpack { case FILTER_E8: case FILTER_E8E9: - { var FileOffset = (uint)WrittenFileSize; @@ -570,7 +568,6 @@ internal partial class Unpack } return SrcData; case FILTER_ARM: - { var FileOffset = (uint)WrittenFileSize; // DataSize is unsigned, so we use "CurPos+3" and not "DataSize-3" @@ -753,8 +750,8 @@ internal partial class Unpack } } - var BitLength = new byte[BC]; - for (uint I = 0; I < BC; I++) + Span BitLength = stackalloc byte[checked((int)BC)]; + for (int I = 0; I < BC; I++) { uint Length = (byte)(Inp.fgetbits() >> 12); Inp.faddbits(4); @@ -785,9 +782,9 @@ internal partial class Unpack MakeDecodeTables(BitLength, 0, Tables.BD, BC); - var Table = new byte[HUFF_TABLE_SIZE]; - const uint TableSize = HUFF_TABLE_SIZE; - for (uint I = 0; I < TableSize; ) + Span Table = stackalloc byte[checked((int)HUFF_TABLE_SIZE)]; + const int TableSize = checked((int)HUFF_TABLE_SIZE); + for (int I = 0; I < TableSize; ) { if (!Inp.ExternalBuffer && Inp.InAddr > ReadTop - 5) { diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs index f9a74440..9c6cbeb0 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs @@ -1,5 +1,9 @@ #nullable disable +using System; +using SharpCompress.Common; +using static SharpCompress.Compressors.Rar.UnpackV2017.PackDef; +using static SharpCompress.Compressors.Rar.UnpackV2017.UnpackGlobal; #if !Rar2017_64bit using size_t = System.UInt32; #else @@ -8,11 +12,6 @@ using nuint = System.UInt64; using size_t = System.UInt64; #endif -using System; -using SharpCompress.Common; -using static SharpCompress.Compressors.Rar.UnpackV2017.UnpackGlobal; -using static SharpCompress.Compressors.Rar.UnpackV2017.PackDef; - namespace SharpCompress.Compressors.Rar.UnpackV2017; internal sealed partial class Unpack : BitInput @@ -30,12 +29,12 @@ internal sealed partial class Unpack : BitInput Suspended = false; UnpAllBuf = false; UnpSomeRead = false; -#if RarV2017_RAR_SMP - MaxUserThreads = 1; - UnpThreadPool = CreateThreadPool(); - ReadBufMT = null; - UnpThreadData = null; -#endif + /*#if RarV2017_RAR_SMP + MaxUserThreads = 1; + UnpThreadPool = CreateThreadPool(); + ReadBufMT = null; + UnpThreadData = null; + #endif*/ MaxWinSize = 0; MaxWinMask = 0; @@ -199,21 +198,21 @@ internal sealed partial class Unpack : BitInput break; #endif case 50: // RAR 5.0 compression algorithm. -#if RarV2017_RAR_SMP - if (MaxUserThreads > 1) - { - // We do not use the multithreaded unpack routine to repack RAR archives - // in 'suspended' mode, because unlike the single threaded code it can - // write more than one dictionary for same loop pass. So we would need - // larger buffers of unknown size. Also we do not support multithreading - // in fragmented window mode. - if (!Fragmented) - { - Unpack5MT(Solid); - break; - } - } -#endif + /*#if RarV2017_RAR_SMP + if (MaxUserThreads > 1) + { + // We do not use the multithreaded unpack routine to repack RAR archives + // in 'suspended' mode, because unlike the single threaded code it can + // write more than one dictionary for same loop pass. So we would need + // larger buffers of unknown size. Also we do not support multithreading + // in fragmented window mode. + if (!Fragmented) + { + Unpack5MT(Solid); + break; + } + } + #endif*/ Unpack5(Solid); break; #if !Rar2017_NOSTRICT @@ -260,7 +259,7 @@ internal sealed partial class Unpack : BitInput // LengthTable contains the length in bits for every element of alphabet. // Dec is the structure to decode Huffman code/ // Size is size of length table and DecodeNum field in Dec structure, - private void MakeDecodeTables(byte[] LengthTable, int offset, DecodeTable Dec, uint Size) + private void MakeDecodeTables(Span LengthTable, int offset, DecodeTable Dec, uint Size) { // Size of alphabet and DecodePos array. Dec.MaxNum = Size; @@ -270,7 +269,7 @@ internal sealed partial class Unpack : BitInput //memset(LengthCount,0,sizeof(LengthCount)); for (size_t I = 0; I < Size; I++) { - LengthCount[LengthTable[offset + I] & 0xf]++; + LengthCount[LengthTable[checked((int)(offset + I))] & 0xf]++; } // We must not calculate the number of zero length codes. @@ -319,7 +318,7 @@ internal sealed partial class Unpack : BitInput for (uint I = 0; I < Size; I++) { // Get the current bit length. - var _CurBitLength = (byte)(LengthTable[offset + I] & 0xf); + var _CurBitLength = (byte)(LengthTable[checked((int)(offset + I))] & 0xf); if (_CurBitLength != 0) { diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpackinline_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpackinline_cpp.cs index 584d0b51..b81d75c4 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpackinline_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpackinline_cpp.cs @@ -1,10 +1,12 @@ -#if !Rar2017_64bit +using static SharpCompress.Compressors.Rar.UnpackV2017.PackDef; + +/*#if !Rar2017_64bit #else using nint = System.Int64; using nuint = System.UInt64; using size_t = System.UInt64; -#endif -using static SharpCompress.Compressors.Rar.UnpackV2017.PackDef; +#endif*/ + namespace SharpCompress.Compressors.Rar.UnpackV2017; diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs index 18cd9697..3bba4c94 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs @@ -1,3 +1,8 @@ +using System; +using System.Collections.Generic; +using static SharpCompress.Compressors.Rar.UnpackV2017.PackDef; +using static SharpCompress.Compressors.Rar.UnpackV2017.UnpackGlobal; +using int64 = System.Int64; #if !Rar2017_64bit using size_t = System.UInt32; #else @@ -5,12 +10,6 @@ using nint = System.Int64; using nuint = System.UInt64; using size_t = System.UInt64; #endif -using int64 = System.Int64; - -using System.Collections.Generic; -using static SharpCompress.Compressors.Rar.UnpackV2017.PackDef; -using static SharpCompress.Compressors.Rar.UnpackV2017.UnpackGlobal; -using System; // TODO: REMOVE THIS... WIP #pragma warning disable 169 @@ -20,8 +19,6 @@ namespace SharpCompress.Compressors.Rar.UnpackV2017; internal static class UnpackGlobal { - - // Maximum allowed number of compressed bits processed in quick mode. public const int MAX_QUICK_DECODE_BITS = 10; @@ -97,11 +94,11 @@ internal struct UnpackBlockHeader internal struct UnpackBlockTables { - public DecodeTable LD; // Decode literals. - public DecodeTable DD; // Decode distances. + public DecodeTable LD; // Decode literals. + public DecodeTable DD; // Decode distances. public DecodeTable LDD; // Decode lower bits of distances. - public DecodeTable RD; // Decode repeating distances. - public DecodeTable BD; // Decode bit lengths in Huffman table. + public DecodeTable RD; // Decode repeating distances. + public DecodeTable BD; // Decode bit lengths in Huffman table. public void Init() { @@ -113,8 +110,7 @@ internal struct UnpackBlockTables } }; - -#if RarV2017_RAR_SMP +/*#if RarV2017_RAR_SMP enum UNP_DEC_TYPE { UNPDT_LITERAL,UNPDT_MATCH,UNPDT_FULLREP,UNPDT_REP,UNPDT_FILTER }; @@ -161,7 +157,7 @@ if (Decoded!=NULL) free(Decoded); } }; -#endif +#endif*/ //struct UnpackFilter @@ -171,12 +167,12 @@ internal class UnpackFilter public uint BlockStart; public uint BlockLength; public byte Channels; + // uint Width; // byte PosR; public bool NextWindow; }; - //struct UnpackFilter30 internal class UnpackFilter30 { @@ -195,15 +191,21 @@ internal class UnpackFilter30 internal class AudioVariables // For RAR 2.0 archives only. { - public int K1, K2, K3, K4, K5; - public int D1, D2, D3, D4; + public int K1, + K2, + K3, + K4, + K5; + public int D1, + D2, + D3, + D4; public int LastDelta; public readonly uint[] Dif = new uint[11]; public uint ByteCount; public int LastChar; }; - // We can use the fragmented dictionary in case heap does not have the single // large enough memory block. It is slower than normal dictionary. internal partial class FragmentedWindow @@ -223,10 +225,8 @@ internal partial class FragmentedWindow //size_t GetBlockSize(size_t StartPos,size_t RequiredSize); }; - internal partial class Unpack { - //void Unpack5(bool Solid); //void Unpack5MT(bool Solid); //bool UnpReadBuf(); @@ -254,22 +254,22 @@ internal partial class Unpack //BitInput Inp; private BitInput Inp => this; // hopefully this gets inlined -#if RarV2017_RAR_SMP -void InitMT(); -bool UnpackLargeBlock(UnpackThreadData &D); -bool ProcessDecoded(UnpackThreadData &D); - -ThreadPool *UnpThreadPool; -UnpackThreadData *UnpThreadData; -uint MaxUserThreads; -byte *ReadBufMT; -#endif + /*#if RarV2017_RAR_SMP + void InitMT(); + bool UnpackLargeBlock(UnpackThreadData &D); + bool ProcessDecoded(UnpackThreadData &D); + + ThreadPool *UnpThreadPool; + UnpackThreadData *UnpThreadData; + uint MaxUserThreads; + byte *ReadBufMT; + #endif*/ private byte[] FilterSrcMemory = Array.Empty(); private byte[] FilterDstMemory = Array.Empty(); // Filters code, one entry per filter. - private readonly List Filters = new List(); + private readonly List Filters = new(); private readonly uint[] OldDist = new uint[4]; private uint OldDistPtr; @@ -279,7 +279,8 @@ byte *ReadBufMT; // array. In RAR3 last distance is always stored in OldDist[0]. private uint LastDist; - private size_t UnpPtr, WrPtr; + private size_t UnpPtr, + WrPtr; // Top border of read packed data. private int ReadTop; @@ -296,7 +297,7 @@ byte *ReadBufMT; private byte[] Window; - private readonly FragmentedWindow FragWindow = new FragmentedWindow(); + private readonly FragmentedWindow FragWindow = new(); private bool Fragmented; private int64 DestUnpSize; @@ -307,7 +308,6 @@ byte *ReadBufMT; private int64 WrittenFileSize; private bool FileExtracted; - /***************************** Unpack v 1.5 *********************************/ //void Unpack15(bool Solid); //void ShortLZ(); @@ -320,12 +320,29 @@ byte *ReadBufMT; //void CopyString15(uint Distance,uint Length); //uint DecodeNum(uint Num,uint StartPos,uint *DecTab,uint *PosTab); - private readonly ushort[] ChSet = new ushort[256], ChSetA = new ushort[256], ChSetB = new ushort[256], ChSetC = new ushort[256]; - private readonly byte[] NToPl = new byte[256], NToPlB = new byte[256], NToPlC = new byte[256]; - private uint FlagBuf, AvrPlc, AvrPlcB, AvrLn1, AvrLn2, AvrLn3; - private int Buf60, NumHuf, StMode, LCount, FlagsCnt; + private readonly ushort[] ChSet = new ushort[256], + ChSetA = new ushort[256], + ChSetB = new ushort[256], + ChSetC = new ushort[256]; + private readonly byte[] NToPl = new byte[256], + NToPlB = new byte[256], + NToPlC = new byte[256]; + private uint FlagBuf, + AvrPlc, + AvrPlcB, + AvrLn1, + AvrLn2, + AvrLn3; + private int Buf60, + NumHuf, + StMode, + LCount, + FlagsCnt; + + private uint Nhfb, + Nlzb, + MaxDist3; - private uint Nhfb, Nlzb, MaxDist3; /***************************** Unpack v 1.5 *********************************/ /***************************** Unpack v 2.0 *********************************/ @@ -335,9 +352,11 @@ byte *ReadBufMT; private readonly byte[] UnpOldTable20 = new byte[MC20 * 4]; private bool UnpAudioBlock; - private uint UnpChannels, UnpCurChannel; + private uint UnpChannels, + UnpCurChannel; private int UnpChannelDelta; + //void CopyString20(uint Length,uint Distance); //bool ReadTables20(); //void UnpWriteBuf20(); @@ -345,6 +364,7 @@ byte *ReadBufMT; //void ReadLastTables(); //byte DecodeAudio(int Delta); private AudioVariables[] AudV = new AudioVariables[4]; + /***************************** Unpack v 2.0 *********************************/ /***************************** Unpack v 3.0 *********************************/ @@ -363,7 +383,8 @@ byte *ReadBufMT; // because we can have a corrupt archive with one algorithm file // followed by another algorithm file with "solid" flag and we do not // want to reuse tables from one algorithm in another. - private bool TablesRead2, TablesRead5; + private bool TablesRead2, + TablesRead5; // Virtual machine to execute filters code. /*#if !RarV2017_RAR5ONLY @@ -372,26 +393,26 @@ byte *ReadBufMT; // Buffer to read VM filters code. We moved it here from AddVMCode // function to reduce time spent in BitInput constructor. - private readonly BitInput VMCodeInp = new BitInput(true); + private readonly BitInput VMCodeInp = new(true); // Filters code, one entry per filter. - private readonly List Filters30 = new List(); + private readonly List Filters30 = new(); // Filters stack, several entrances of same filter are possible. - private readonly List PrgStack = new List(); + private readonly List PrgStack = new(); // Lengths of preceding data blocks, one length of one last block // for every filter. Used to reduce the size required to write // the data block length if lengths are repeating. - private readonly List OldFilterLengths = new List(); + private readonly List OldFilterLengths = new(); -#if RarV2017_RAR_SMP -// More than 8 threads are unlikely to provide a noticeable gain -// for unpacking, but would use the additional memory. -void SetThreads(uint Threads) {MaxUserThreads=Min(Threads,8);} - -void UnpackDecode(UnpackThreadData &D); -#endif + /*#if RarV2017_RAR_SMP + // More than 8 threads are unlikely to provide a noticeable gain + // for unpacking, but would use the additional memory. + void SetThreads(uint Threads) {MaxUserThreads=Min(Threads,8);} + + void UnpackDecode(UnpackThreadData &D); + #endif*/ private size_t MaxWinSize; private size_t MaxWinMask; diff --git a/src/SharpCompress/Compressors/Rar/VM/RarVM.cs b/src/SharpCompress/Compressors/Rar/VM/RarVM.cs index 48b25c5c..f758b8a4 100644 --- a/src/SharpCompress/Compressors/Rar/VM/RarVM.cs +++ b/src/SharpCompress/Compressors/Rar/VM/RarVM.cs @@ -228,7 +228,6 @@ internal sealed class RarVM : BitInput break; case VMCommands.VM_CMP: - { var value1 = (VMFlags)GetValue(cmd.IsByteMode, Mem, op1); var result = value1 - GetValue(cmd.IsByteMode, Mem, op2); @@ -247,7 +246,6 @@ internal sealed class RarVM : BitInput break; case VMCommands.VM_CMPB: - { var value1 = (VMFlags)GetValue(true, Mem, op1); var result = value1 - GetValue(true, Mem, op2); @@ -265,7 +263,6 @@ internal sealed class RarVM : BitInput break; case VMCommands.VM_CMPD: - { var value1 = (VMFlags)GetValue(false, Mem, op1); var result = value1 - GetValue(false, Mem, op2); @@ -283,7 +280,6 @@ internal sealed class RarVM : BitInput break; case VMCommands.VM_ADD: - { var value1 = GetValue(cmd.IsByteMode, Mem, op1); var result = (int)( @@ -351,7 +347,6 @@ internal sealed class RarVM : BitInput break; case VMCommands.VM_SUB: - { var value1 = GetValue(cmd.IsByteMode, Mem, op1); var result = (int)( @@ -411,7 +406,6 @@ internal sealed class RarVM : BitInput break; case VMCommands.VM_INC: - { var result = (int)(GetValue(cmd.IsByteMode, Mem, op1) & (0xFFffFFffL + 1L)); if (cmd.IsByteMode) @@ -440,7 +434,6 @@ internal sealed class RarVM : BitInput break; case VMCommands.VM_DEC: - { var result = (int)(GetValue(cmd.IsByteMode, Mem, op1) & (0xFFffFFff - 1)); SetValue(cmd.IsByteMode, Mem, op1, result); @@ -463,7 +456,6 @@ internal sealed class RarVM : BitInput continue; case VMCommands.VM_XOR: - { var result = GetValue(cmd.IsByteMode, Mem, op1) ^ GetValue(cmd.IsByteMode, Mem, op2); @@ -475,7 +467,6 @@ internal sealed class RarVM : BitInput break; case VMCommands.VM_AND: - { var result = GetValue(cmd.IsByteMode, Mem, op1) & GetValue(cmd.IsByteMode, Mem, op2); @@ -487,7 +478,6 @@ internal sealed class RarVM : BitInput break; case VMCommands.VM_OR: - { var result = GetValue(cmd.IsByteMode, Mem, op1) | GetValue(cmd.IsByteMode, Mem, op2); @@ -499,7 +489,6 @@ internal sealed class RarVM : BitInput break; case VMCommands.VM_TEST: - { var result = GetValue(cmd.IsByteMode, Mem, op1) & GetValue(cmd.IsByteMode, Mem, op2); @@ -578,7 +567,6 @@ internal sealed class RarVM : BitInput break; case VMCommands.VM_SHL: - { var value1 = GetValue(cmd.IsByteMode, Mem, op1); var value2 = GetValue(cmd.IsByteMode, Mem, op2); @@ -596,7 +584,6 @@ internal sealed class RarVM : BitInput break; case VMCommands.VM_SHR: - { var value1 = GetValue(cmd.IsByteMode, Mem, op1); var value2 = GetValue(cmd.IsByteMode, Mem, op2); @@ -610,7 +597,6 @@ internal sealed class RarVM : BitInput break; case VMCommands.VM_SAR: - { var value1 = GetValue(cmd.IsByteMode, Mem, op1); var value2 = GetValue(cmd.IsByteMode, Mem, op2); @@ -624,7 +610,6 @@ internal sealed class RarVM : BitInput break; case VMCommands.VM_NEG: - { var result = -GetValue(cmd.IsByteMode, Mem, op1); flags = (VMFlags)( @@ -645,7 +630,6 @@ internal sealed class RarVM : BitInput break; case VMCommands.VM_PUSHA: - { for (int i = 0, SP = R[7] - 4; i < regCount; i++, SP -= 4) { @@ -656,7 +640,6 @@ internal sealed class RarVM : BitInput break; case VMCommands.VM_POPA: - { for (int i = 0, SP = R[7]; i < regCount; i++, SP += 4) { @@ -684,7 +667,6 @@ internal sealed class RarVM : BitInput break; case VMCommands.VM_XCHG: - { var value1 = GetValue(cmd.IsByteMode, Mem, op1); SetValue(cmd.IsByteMode, Mem, op1, GetValue(cmd.IsByteMode, Mem, op2)); @@ -693,7 +675,6 @@ internal sealed class RarVM : BitInput break; case VMCommands.VM_MUL: - { var result = (int)( ( @@ -707,7 +688,6 @@ internal sealed class RarVM : BitInput break; case VMCommands.VM_DIV: - { var divider = GetValue(cmd.IsByteMode, Mem, op2); if (divider != 0) @@ -719,7 +699,6 @@ internal sealed class RarVM : BitInput break; case VMCommands.VM_ADC: - { var value1 = GetValue(cmd.IsByteMode, Mem, op1); var FC = (int)(flags & VMFlags.VM_FC); @@ -749,7 +728,6 @@ internal sealed class RarVM : BitInput break; case VMCommands.VM_SBB: - { var value1 = GetValue(cmd.IsByteMode, Mem, op1); var FC = (int)(flags & VMFlags.VM_FC); @@ -1131,13 +1109,13 @@ internal sealed class RarVM : BitInput { VMStandardFilterSignature[] stdList = { - new VMStandardFilterSignature(53, 0xad576887, VMStandardFilters.VMSF_E8), - new VMStandardFilterSignature(57, 0x3cd7e57e, VMStandardFilters.VMSF_E8E9), - new VMStandardFilterSignature(120, 0x3769893f, VMStandardFilters.VMSF_ITANIUM), - new VMStandardFilterSignature(29, 0x0e06077d, VMStandardFilters.VMSF_DELTA), - new VMStandardFilterSignature(149, 0x1c2c5dc8, VMStandardFilters.VMSF_RGB), - new VMStandardFilterSignature(216, 0xbc85e701, VMStandardFilters.VMSF_AUDIO), - new VMStandardFilterSignature(40, 0x46b9c560, VMStandardFilters.VMSF_UPCASE) + new(53, 0xad576887, VMStandardFilters.VMSF_E8), + new(57, 0x3cd7e57e, VMStandardFilters.VMSF_E8E9), + new(120, 0x3769893f, VMStandardFilters.VMSF_ITANIUM), + new(29, 0x0e06077d, VMStandardFilters.VMSF_DELTA), + new(149, 0x1c2c5dc8, VMStandardFilters.VMSF_RGB), + new(216, 0xbc85e701, VMStandardFilters.VMSF_AUDIO), + new(40, 0x46b9c560, VMStandardFilters.VMSF_UPCASE) }; var CodeCRC = RarCRC.CheckCrc(0xffffffff, code, 0, code.Length) ^ 0xffffffff; for (var i = 0; i < stdList.Length; i++) @@ -1156,7 +1134,6 @@ internal sealed class RarVM : BitInput { case VMStandardFilters.VMSF_E8: case VMStandardFilters.VMSF_E8E9: - { var dataSize = R[4]; long fileOffset = R[6] & unchecked((int)0xFFffFFff); @@ -1211,7 +1188,6 @@ internal sealed class RarVM : BitInput break; case VMStandardFilters.VMSF_ITANIUM: - { var dataSize = R[4]; long fileOffset = R[6] & unchecked((int)0xFFffFFff); @@ -1269,7 +1245,6 @@ internal sealed class RarVM : BitInput break; case VMStandardFilters.VMSF_DELTA: - { var dataSize = R[4] & unchecked((int)0xFFffFFff); var channels = R[0] & unchecked((int)0xFFffFFff); @@ -1300,7 +1275,6 @@ internal sealed class RarVM : BitInput break; case VMStandardFilters.VMSF_RGB: - { // byte *SrcData=Mem,*DestData=SrcData+DataSize; int dataSize = R[4], @@ -1366,7 +1340,6 @@ internal sealed class RarVM : BitInput break; case VMStandardFilters.VMSF_AUDIO: - { int dataSize = R[4], channels = R[0]; @@ -1497,7 +1470,6 @@ internal sealed class RarVM : BitInput break; case VMStandardFilters.VMSF_UPCASE: - { int dataSize = R[4], srcPos = 0, diff --git a/src/SharpCompress/Compressors/Rar/VM/VMPreparedProgram.cs b/src/SharpCompress/Compressors/Rar/VM/VMPreparedProgram.cs index c0006e09..f1f9e0aa 100644 --- a/src/SharpCompress/Compressors/Rar/VM/VMPreparedProgram.cs +++ b/src/SharpCompress/Compressors/Rar/VM/VMPreparedProgram.cs @@ -4,13 +4,13 @@ namespace SharpCompress.Compressors.Rar.VM; internal class VMPreparedProgram { - internal List Commands = new List(); - internal List AltCommands = new List(); + internal List Commands = new(); + internal List AltCommands = new(); public int CommandCount { get; set; } - internal List GlobalData = new List(); - internal List StaticData = new List(); + internal List GlobalData = new(); + internal List StaticData = new(); // static data contained in DB operators internal int[] InitR = new int[7]; diff --git a/src/SharpCompress/Compressors/Shrink/BitStream.cs b/src/SharpCompress/Compressors/Shrink/BitStream.cs new file mode 100644 index 00000000..949ee6c3 --- /dev/null +++ b/src/SharpCompress/Compressors/Shrink/BitStream.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SharpCompress.Compressors.Shrink +{ + internal class BitStream + { + private byte[] _src; + private int _srcLen; + private int _byteIdx; + private int _bitIdx; + private int _bitsLeft; + private ulong _bitBuffer; + private static uint[] _maskBits = new uint[17] + { + 0U, + 1U, + 3U, + 7U, + 15U, + 31U, + 63U, + (uint)sbyte.MaxValue, + (uint)byte.MaxValue, + 511U, + 1023U, + 2047U, + 4095U, + 8191U, + 16383U, + (uint)short.MaxValue, + (uint)ushort.MaxValue + }; + + public BitStream(byte[] src, int srcLen) + { + _src = src; + _srcLen = srcLen; + _byteIdx = 0; + _bitIdx = 0; + } + + public int BytesRead => (_byteIdx << 3) + _bitIdx; + + private int NextByte() + { + if (_byteIdx >= _srcLen) + { + return 0; + } + + return _src[_byteIdx++]; + } + + public int NextBits(int nbits) + { + var result = 0; + if (nbits > _bitsLeft) + { + int num; + while (_bitsLeft <= 24 && (num = NextByte()) != 1234) + { + _bitBuffer |= (ulong)num << _bitsLeft; + _bitsLeft += 8; + } + } + result = (int)((long)_bitBuffer & (long)_maskBits[nbits]); + _bitBuffer >>= nbits; + _bitsLeft -= nbits; + return result; + } + + public bool Advance(int count) + { + if (_byteIdx > _srcLen) + { + return false; + } + return true; + } + } +} diff --git a/src/SharpCompress/Compressors/Shrink/HwUnshrink.cs b/src/SharpCompress/Compressors/Shrink/HwUnshrink.cs new file mode 100644 index 00000000..6ad5ab7c --- /dev/null +++ b/src/SharpCompress/Compressors/Shrink/HwUnshrink.cs @@ -0,0 +1,431 @@ +using System; + +namespace SharpCompress.Compressors.Shrink +{ + public class HwUnshrink + { + private const int MIN_CODE_SIZE = 9; + private const int MAX_CODE_SIZE = 13; + + private const ushort MAX_CODE = (ushort)((1U << MAX_CODE_SIZE) - 1); + private const ushort INVALID_CODE = ushort.MaxValue; + private const ushort CONTROL_CODE = 256; + private const ushort INC_CODE_SIZE = 1; + private const ushort PARTIAL_CLEAR = 2; + + private const int HASH_BITS = MAX_CODE_SIZE + 1; // For a load factor of 0.5. + private const int HASHTAB_SIZE = 1 << HASH_BITS; + private const ushort UNKNOWN_LEN = ushort.MaxValue; + + private struct CodeTabEntry + { + public int prefixCode; // INVALID_CODE means the entry is invalid. + public byte extByte; + public ushort len; + public int lastDstPos; + } + + private static void CodeTabInit(CodeTabEntry[] codeTab) + { + for (var i = 0; i <= byte.MaxValue; i++) + { + codeTab[i].prefixCode = (ushort)i; + codeTab[i].extByte = (byte)i; + codeTab[i].len = 1; + } + + for (var i = byte.MaxValue + 1; i <= MAX_CODE; i++) + { + codeTab[i].prefixCode = INVALID_CODE; + } + } + + private static void UnshrinkPartialClear(CodeTabEntry[] codeTab, ref CodeQueue queue) + { + var isPrefix = new bool[MAX_CODE + 1]; + int codeQueueSize; + + // Scan for codes that have been used as a prefix. + for (var i = CONTROL_CODE + 1; i <= MAX_CODE; i++) + { + if (codeTab[i].prefixCode != INVALID_CODE) + { + isPrefix[codeTab[i].prefixCode] = true; + } + } + + // Clear "non-prefix" codes in the table; populate the code queue. + codeQueueSize = 0; + for (var i = CONTROL_CODE + 1; i <= MAX_CODE; i++) + { + if (!isPrefix[i]) + { + codeTab[i].prefixCode = INVALID_CODE; + queue.codes[codeQueueSize++] = (ushort)i; + } + } + + queue.codes[codeQueueSize] = INVALID_CODE; // End-of-queue marker. + queue.nextIdx = 0; + } + + private static bool ReadCode( + BitStream stream, + ref int codeSize, + CodeTabEntry[] codeTab, + ref CodeQueue queue, + out int nextCode + ) + { + int code, + controlCode; + + code = (int)stream.NextBits(codeSize); + if (!stream.Advance(codeSize)) + { + nextCode = INVALID_CODE; + return false; + } + + // Handle regular codes (the common case). + if (code != CONTROL_CODE) + { + nextCode = code; + return true; + } + + // Handle control codes. + controlCode = (ushort)stream.NextBits(codeSize); + if (!stream.Advance(codeSize)) + { + nextCode = INVALID_CODE; + return true; + } + + if (controlCode == INC_CODE_SIZE && codeSize < MAX_CODE_SIZE) + { + codeSize++; + return ReadCode(stream, ref codeSize, codeTab, ref queue, out nextCode); + } + + if (controlCode == PARTIAL_CLEAR) + { + UnshrinkPartialClear(codeTab, ref queue); + return ReadCode(stream, ref codeSize, codeTab, ref queue, out nextCode); + } + + nextCode = INVALID_CODE; + return true; + } + + private static void CopyFromPrevPos(byte[] dst, int prevPos, int dstPos, int len) + { + if (dstPos + len > dst.Length) + { + // Not enough room in dst for the sloppy copy below. + Array.Copy(dst, prevPos, dst, dstPos, len); + return; + } + + if (prevPos + len > dstPos) + { + // Benign one-byte overlap possible in the KwKwK case. + //assert(prevPos + len == dstPos + 1); + //assert(dst[prevPos] == dst[prevPos + len - 1]); + } + + Buffer.BlockCopy(dst, prevPos, dst, dstPos, len); + } + + private static UnshrnkStatus OutputCode( + int code, + byte[] dst, + int dstPos, + int dstCap, + int prevCode, + CodeTabEntry[] codeTab, + ref CodeQueue queue, + out byte firstByte, + out int len + ) + { + int prefixCode; + + //assert(code <= MAX_CODE && code != CONTROL_CODE); + //assert(dstPos < dstCap); + firstByte = 0; + if (code <= byte.MaxValue) + { + // Output literal byte. + firstByte = (byte)code; + len = 1; + dst[dstPos] = (byte)code; + return UnshrnkStatus.Ok; + } + + if (codeTab[code].prefixCode == INVALID_CODE || codeTab[code].prefixCode == code) + { + // Reject invalid codes. Self-referential codes may exist in the table but cannot be used. + firstByte = 0; + len = 0; + return UnshrnkStatus.Error; + } + + if (codeTab[code].len != UNKNOWN_LEN) + { + // Output string with known length (the common case). + if (dstCap - dstPos < codeTab[code].len) + { + firstByte = 0; + len = 0; + return UnshrnkStatus.Full; + } + + CopyFromPrevPos(dst, codeTab[code].lastDstPos, dstPos, codeTab[code].len); + firstByte = dst[dstPos]; + len = codeTab[code].len; + return UnshrnkStatus.Ok; + } + + // Output a string of unknown length. + //assert(codeTab[code].len == UNKNOWN_LEN); + prefixCode = codeTab[code].prefixCode; + // assert(prefixCode > CONTROL_CODE); + + if (prefixCode == queue.codes[queue.nextIdx]) + { + // The prefix code hasn't been added yet, but we were just about to: the KwKwK case. + //assert(codeTab[prevCode].prefixCode != INVALID_CODE); + codeTab[prefixCode].prefixCode = prevCode; + codeTab[prefixCode].extByte = firstByte; + codeTab[prefixCode].len = (ushort)(codeTab[prevCode].len + 1); + codeTab[prefixCode].lastDstPos = codeTab[prevCode].lastDstPos; + dst[dstPos] = firstByte; + } + else if (codeTab[prefixCode].prefixCode == INVALID_CODE) + { + // The prefix code is still invalid. + firstByte = 0; + len = 0; + return UnshrnkStatus.Error; + } + + // Output the prefix string, then the extension byte. + len = codeTab[prefixCode].len + 1; + if (dstCap - dstPos < len) + { + firstByte = 0; + len = 0; + return UnshrnkStatus.Full; + } + + CopyFromPrevPos(dst, codeTab[prefixCode].lastDstPos, dstPos, codeTab[prefixCode].len); + dst[dstPos + len - 1] = codeTab[code].extByte; + firstByte = dst[dstPos]; + + // Update the code table now that the string has a length and pos. + //assert(prevCode != code); + codeTab[code].len = (ushort)len; + codeTab[code].lastDstPos = dstPos; + + return UnshrnkStatus.Ok; + } + + public static UnshrnkStatus Unshrink( + byte[] src, + int srcLen, + out int srcUsed, + byte[] dst, + int dstCap, + out int dstUsed + ) + { + var codeTab = new CodeTabEntry[HASHTAB_SIZE]; + var queue = new CodeQueue(); + var stream = new BitStream(src, srcLen); + int codeSize, + dstPos, + len; + int currCode, + prevCode, + newCode; + byte firstByte; + + CodeTabInit(codeTab); + CodeQueueInit(ref queue); + codeSize = MIN_CODE_SIZE; + dstPos = 0; + + // Handle the first code separately since there is no previous code. + if (!ReadCode(stream, ref codeSize, codeTab, ref queue, out currCode)) + { + srcUsed = stream.BytesRead; + dstUsed = 0; + return UnshrnkStatus.Ok; + } + + //assert(currCode != CONTROL_CODE); + if (currCode > byte.MaxValue) + { + srcUsed = stream.BytesRead; + dstUsed = 0; + return UnshrnkStatus.Error; // The first code must be a literal. + } + + if (dstPos == dstCap) + { + srcUsed = stream.BytesRead; + dstUsed = 0; + return UnshrnkStatus.Full; + } + + firstByte = (byte)currCode; + dst[dstPos] = (byte)currCode; + codeTab[currCode].lastDstPos = dstPos; + dstPos++; + + prevCode = currCode; + while (ReadCode(stream, ref codeSize, codeTab, ref queue, out currCode)) + { + if (currCode == INVALID_CODE) + { + srcUsed = stream.BytesRead; + dstUsed = 0; + return UnshrnkStatus.Error; + } + + if (dstPos == dstCap) + { + srcUsed = stream.BytesRead; + dstUsed = 0; + return UnshrnkStatus.Full; + } + + // Handle KwKwK: next code used before being added. + if (currCode == queue.codes[queue.nextIdx]) + { + if (codeTab[prevCode].prefixCode == INVALID_CODE) + { + // The previous code is no longer valid. + srcUsed = stream.BytesRead; + dstUsed = 0; + return UnshrnkStatus.Error; + } + + // Extend the previous code with its first byte. + //assert(currCode != prevCode); + codeTab[currCode].prefixCode = prevCode; + codeTab[currCode].extByte = firstByte; + codeTab[currCode].len = (ushort)(codeTab[prevCode].len + 1); + codeTab[currCode].lastDstPos = codeTab[prevCode].lastDstPos; + //assert(dstPos < dstCap); + dst[dstPos] = firstByte; + } + + // Output the string represented by the current code. + var status = OutputCode( + currCode, + dst, + dstPos, + dstCap, + prevCode, + codeTab, + ref queue, + out firstByte, + out len + ); + if (status != UnshrnkStatus.Ok) + { + srcUsed = stream.BytesRead; + dstUsed = 0; + return status; + } + + // Verify that the output matches walking the prefixes. + var c = currCode; + for (var i = 0; i < len; i++) + { + // assert(codeTab[c].len == len - i); + //assert(codeTab[c].extByte == dst[dstPos + len - i - 1]); + c = codeTab[c].prefixCode; + } + + // Add a new code to the string table if there's room. + // The string is the previous code's string extended with the first byte of the current code's string. + newCode = CodeQueueRemoveNext(ref queue); + if (newCode != INVALID_CODE) + { + //assert(codeTab[prevCode].lastDstPos < dstPos); + codeTab[newCode].prefixCode = prevCode; + codeTab[newCode].extByte = firstByte; + codeTab[newCode].len = (ushort)(codeTab[prevCode].len + 1); + codeTab[newCode].lastDstPos = codeTab[prevCode].lastDstPos; + + if (codeTab[prevCode].prefixCode == INVALID_CODE) + { + // prevCode was invalidated in a partial clearing. Until that code is re-used, the + // string represented by newCode is indeterminate. + codeTab[newCode].len = UNKNOWN_LEN; + } + // If prevCode was invalidated in a partial clearing, it's possible that newCode == prevCode, + // in which case it will never be used or cleared. + } + + codeTab[currCode].lastDstPos = dstPos; + dstPos += len; + + prevCode = currCode; + } + + srcUsed = stream.BytesRead; + dstUsed = dstPos; + + return UnshrnkStatus.Ok; + } + + public enum UnshrnkStatus + { + Ok, + Full, + Error + } + + private struct CodeQueue + { + public int nextIdx; + public ushort[] codes; + } + + private static void CodeQueueInit(ref CodeQueue q) + { + int codeQueueSize; + ushort code; + + codeQueueSize = 0; + q.codes = new ushort[MAX_CODE - CONTROL_CODE + 2]; + + for (code = CONTROL_CODE + 1; code <= MAX_CODE; code++) + { + q.codes[codeQueueSize++] = code; + } + + //assert(codeQueueSize < q.codes.Length); + q.codes[codeQueueSize] = INVALID_CODE; // End-of-queue marker. + q.nextIdx = 0; + } + + private static ushort CodeQueueNext(ref CodeQueue q) => + //assert(q.nextIdx < q.codes.Length); + q.codes[q.nextIdx]; + + private static ushort CodeQueueRemoveNext(ref CodeQueue q) + { + var code = CodeQueueNext(ref q); + if (code != INVALID_CODE) + { + q.nextIdx++; + } + return code; + } + } +} diff --git a/src/SharpCompress/Compressors/Shrink/ShrinkStream.cs b/src/SharpCompress/Compressors/Shrink/ShrinkStream.cs new file mode 100644 index 00000000..65b2eb37 --- /dev/null +++ b/src/SharpCompress/Compressors/Shrink/ShrinkStream.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SharpCompress.Compressors.Shrink; + +internal class ShrinkStream : Stream +{ + private Stream inStream; + private CompressionMode _compressionMode; + + private ulong _compressedSize; + private long _uncompressedSize; + private byte[] _byteOut; + private long _outBytesCount; + + public ShrinkStream( + Stream stream, + CompressionMode compressionMode, + long compressedSize, + long uncompressedSize + ) + { + inStream = stream; + _compressionMode = compressionMode; + + _compressedSize = (ulong)compressedSize; + _uncompressedSize = uncompressedSize; + _byteOut = new byte[_uncompressedSize]; + _outBytesCount = 0L; + } + + public override bool CanRead => true; + + public override bool CanSeek => true; + + public override bool CanWrite => false; + + public override long Length => _uncompressedSize; + + public override long Position + { + get => _outBytesCount; + set => throw new NotImplementedException(); + } + + public override void Flush() => throw new NotImplementedException(); + + public override int Read(byte[] buffer, int offset, int count) + { + if (inStream.Position == (long)_compressedSize) + { + return 0; + } + var src = new byte[_compressedSize]; + inStream.Read(src, offset, (int)_compressedSize); + var srcUsed = 0; + var dstUsed = 0; + + HwUnshrink.Unshrink( + src, + (int)_compressedSize, + out srcUsed, + _byteOut, + (int)_uncompressedSize, + out dstUsed + ); + _outBytesCount = _byteOut.Length; + + for (var index = 0; index < _outBytesCount; ++index) + { + buffer[offset + index] = _byteOut[index]; + } + var tmp = _outBytesCount; + _outBytesCount = 0; + return (int)tmp; + } + + public override long Seek(long offset, SeekOrigin origin) => + throw new NotImplementedException(); + + public override void SetLength(long value) => throw new NotImplementedException(); + + public override void Write(byte[] buffer, int offset, int count) => + throw new NotImplementedException(); +} diff --git a/src/SharpCompress/Compressors/Xz/ReadOnlyStream.cs b/src/SharpCompress/Compressors/Xz/ReadOnlyStream.cs index 71614df9..75188483 100644 --- a/src/SharpCompress/Compressors/Xz/ReadOnlyStream.cs +++ b/src/SharpCompress/Compressors/Xz/ReadOnlyStream.cs @@ -1,4 +1,4 @@ -#nullable disable +#nullable disable using System; using System.IO; @@ -23,7 +23,7 @@ public abstract class ReadOnlyStream : Stream set => throw new NotSupportedException(); } - public override void Flush() => throw new NotSupportedException(); + public override void Flush() { } public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException(); diff --git a/src/SharpCompress/Compressors/Xz/XZBlock.cs b/src/SharpCompress/Compressors/Xz/XZBlock.cs index ea907609..ddd7eaf3 100644 --- a/src/SharpCompress/Compressors/Xz/XZBlock.cs +++ b/src/SharpCompress/Compressors/Xz/XZBlock.cs @@ -14,7 +14,7 @@ public sealed class XZBlock : XZReadOnlyStream public int BlockHeaderSize => (_blockHeaderSizeByte + 1) * 4; public ulong? CompressedSize { get; private set; } public ulong? UncompressedSize { get; private set; } - public Stack Filters { get; private set; } = new Stack(); + public Stack Filters { get; private set; } = new(); public bool HeaderIsLoaded { get; private set; } private CheckType _checkType; private readonly int _checkSize; diff --git a/src/SharpCompress/Compressors/Xz/XZIndex.cs b/src/SharpCompress/Compressors/Xz/XZIndex.cs index 631f27b2..ddfd7c99 100644 --- a/src/SharpCompress/Compressors/Xz/XZIndex.cs +++ b/src/SharpCompress/Compressors/Xz/XZIndex.cs @@ -13,7 +13,7 @@ public class XZIndex private readonly BinaryReader _reader; public long StreamStartPosition { get; private set; } public ulong NumberOfRecords { get; private set; } - public List Records { get; } = new List(); + public List Records { get; } = new(); private readonly bool _indexMarkerAlreadyVerified; diff --git a/src/SharpCompress/Crypto/BlockTransformer.cs b/src/SharpCompress/Crypto/BlockTransformer.cs new file mode 100644 index 00000000..ed9ead44 --- /dev/null +++ b/src/SharpCompress/Crypto/BlockTransformer.cs @@ -0,0 +1,19 @@ +#nullable disable + +using System; +using System.Security.Cryptography; + +namespace SharpCompress.Crypto; + +internal class BlockTransformer(ICryptoTransform transformer) : IDisposable +{ + public byte[] ProcessBlock(ReadOnlySpan cipherText) + { + var decryptedBytes = new byte[cipherText.Length]; + transformer.TransformBlock(cipherText.ToArray(), 0, cipherText.Length, decryptedBytes, 0); + + return decryptedBytes; + } + + public void Dispose() { } +} diff --git a/src/SharpCompress/Crypto/Crc32Stream.cs b/src/SharpCompress/Crypto/Crc32Stream.cs index 7f0af6ca..5f5ccd87 100644 --- a/src/SharpCompress/Crypto/Crc32Stream.cs +++ b/src/SharpCompress/Crypto/Crc32Stream.cs @@ -6,27 +6,17 @@ using System.IO; namespace SharpCompress.Crypto; [CLSCompliant(false)] -public sealed class Crc32Stream : Stream +public sealed class Crc32Stream(Stream stream, uint polynomial, uint seed) : Stream { - public const uint DefaultPolynomial = 0xedb88320u; - public const uint DefaultSeed = 0xffffffffu; + public const uint DEFAULT_POLYNOMIAL = 0xedb88320u; + public const uint DEFAULT_SEED = 0xffffffffu; - private static uint[] defaultTable; + private static uint[] _defaultTable; - private readonly uint[] table; - private uint hash; - - private readonly Stream stream; + private readonly uint[] _table = InitializeTable(polynomial); public Crc32Stream(Stream stream) - : this(stream, DefaultPolynomial, DefaultSeed) { } - - public Crc32Stream(Stream stream, uint polynomial, uint seed) - { - this.stream = stream; - table = InitializeTable(polynomial); - hash = seed; - } + : this(stream, DEFAULT_POLYNOMIAL, DEFAULT_SEED) { } public Stream WrappedStream => stream; @@ -45,20 +35,20 @@ public sealed class Crc32Stream : Stream { stream.Write(buffer); - hash = CalculateCrc(table, hash, buffer); + seed = CalculateCrc(_table, seed, buffer); } #endif public override void Write(byte[] buffer, int offset, int count) { stream.Write(buffer, offset, count); - hash = CalculateCrc(table, hash, buffer.AsSpan(offset, count)); + seed = CalculateCrc(_table, seed, buffer.AsSpan(offset, count)); } public override void WriteByte(byte value) { stream.WriteByte(value); - hash = CalculateCrc(table, hash, value); + seed = CalculateCrc(_table, seed, value); } public override bool CanRead => stream.CanRead; @@ -71,21 +61,21 @@ public sealed class Crc32Stream : Stream set => throw new NotSupportedException(); } - public uint Crc => ~hash; + public uint Crc => ~seed; - public static uint Compute(byte[] buffer) => Compute(DefaultSeed, buffer); + public static uint Compute(byte[] buffer) => Compute(DEFAULT_SEED, buffer); public static uint Compute(uint seed, byte[] buffer) => - Compute(DefaultPolynomial, seed, buffer); + Compute(DEFAULT_POLYNOMIAL, seed, buffer); public static uint Compute(uint polynomial, uint seed, ReadOnlySpan buffer) => ~CalculateCrc(InitializeTable(polynomial), seed, buffer); private static uint[] InitializeTable(uint polynomial) { - if (polynomial == DefaultPolynomial && defaultTable != null) + if (polynomial == DEFAULT_POLYNOMIAL && _defaultTable != null) { - return defaultTable; + return _defaultTable; } var createTable = new uint[256]; @@ -107,9 +97,9 @@ public sealed class Crc32Stream : Stream createTable[i] = entry; } - if (polynomial == DefaultPolynomial) + if (polynomial == DEFAULT_POLYNOMIAL) { - defaultTable = createTable; + _defaultTable = createTable; } return createTable; diff --git a/src/SharpCompress/Crypto/CryptoException.cs b/src/SharpCompress/Crypto/CryptoException.cs deleted file mode 100644 index 234fd5e5..00000000 --- a/src/SharpCompress/Crypto/CryptoException.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace SharpCompress.Crypto; - -public class CryptoException : Exception -{ - public CryptoException() { } - - public CryptoException(string message) - : base(message) { } - - public CryptoException(string message, Exception exception) - : base(message, exception) { } -} diff --git a/src/SharpCompress/Crypto/DataLengthException.cs b/src/SharpCompress/Crypto/DataLengthException.cs deleted file mode 100644 index 828c1a8f..00000000 --- a/src/SharpCompress/Crypto/DataLengthException.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; - -namespace SharpCompress.Crypto; - -public class DataLengthException : CryptoException -{ - /** - * base constructor. - */ - - public DataLengthException() { } - - /** - * create a DataLengthException with the given message. - * - * @param message the message to be carried with the exception. - */ - - public DataLengthException(string message) - : base(message) { } - - public DataLengthException(string message, Exception exception) - : base(message, exception) { } -} diff --git a/src/SharpCompress/Crypto/IBlockCipher.cs b/src/SharpCompress/Crypto/IBlockCipher.cs deleted file mode 100644 index 50eb5fb3..00000000 --- a/src/SharpCompress/Crypto/IBlockCipher.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; - -namespace SharpCompress.Crypto; - -/// Base interface for a symmetric key block cipher. -public interface IBlockCipher -{ - /// The name of the algorithm this cipher implements. - string AlgorithmName { get; } - - /// Initialise the cipher. - /// Initialise for encryption if true, for decryption if false. - /// The key or other data required by the cipher. - void Init(bool forEncryption, ICipherParameters parameters); - - /// The block size for this cipher, in bytes. - int GetBlockSize(); - - /// Indicates whether this cipher can handle partial blocks. - bool IsPartialBlockOkay { get; } - - /// Process a block. - /// The input buffer. - /// The output buffer. - /// If input block is wrong size, or outBuf too small. - /// The number of bytes processed and produced. - int ProcessBlock(ReadOnlySpan inBuf, Span outBuf); - - /// - /// Reset the cipher to the same state as it was after the last init (if there was one). - /// - void Reset(); -} diff --git a/src/SharpCompress/Crypto/ICipherParameters.cs b/src/SharpCompress/Crypto/ICipherParameters.cs deleted file mode 100644 index 9934637a..00000000 --- a/src/SharpCompress/Crypto/ICipherParameters.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace SharpCompress.Crypto; - -public interface ICipherParameters { } diff --git a/src/SharpCompress/Crypto/KeyParameter.cs b/src/SharpCompress/Crypto/KeyParameter.cs deleted file mode 100644 index fce334f9..00000000 --- a/src/SharpCompress/Crypto/KeyParameter.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; - -namespace SharpCompress.Crypto; - -public class KeyParameter : ICipherParameters -{ - private readonly byte[] key; - - public KeyParameter(byte[] key) - { - if (key is null) - { - throw new ArgumentNullException(nameof(key)); - } - - this.key = (byte[])key.Clone(); - } - - public KeyParameter(byte[] key, int keyOff, int keyLen) - { - if (key is null) - { - throw new ArgumentNullException(nameof(key)); - } - if (keyOff < 0 || keyOff > key.Length) - { - throw new ArgumentOutOfRangeException(nameof(keyOff)); - } - if (keyLen < 0 || (keyOff + keyLen) > key.Length) - { - throw new ArgumentOutOfRangeException(nameof(keyLen)); - } - - this.key = new byte[keyLen]; - Array.Copy(key, keyOff, this.key, 0, keyLen); - } - - public byte[] GetKey() => (byte[])key.Clone(); -} diff --git a/src/SharpCompress/Crypto/RijndaelEngine.cs b/src/SharpCompress/Crypto/RijndaelEngine.cs deleted file mode 100644 index 98dd5bc1..00000000 --- a/src/SharpCompress/Crypto/RijndaelEngine.cs +++ /dev/null @@ -1,1909 +0,0 @@ -using System; - -namespace SharpCompress.Crypto; - -public sealed class RijndaelEngine : IBlockCipher -{ - private const int MAXROUNDS = 14; - - private const int MAXKC = (256 / 4); - - private static ReadOnlySpan Logtable => - new byte[] - { - 0, - 0, - 25, - 1, - 50, - 2, - 26, - 198, - 75, - 199, - 27, - 104, - 51, - 238, - 223, - 3, - 100, - 4, - 224, - 14, - 52, - 141, - 129, - 239, - 76, - 113, - 8, - 200, - 248, - 105, - 28, - 193, - 125, - 194, - 29, - 181, - 249, - 185, - 39, - 106, - 77, - 228, - 166, - 114, - 154, - 201, - 9, - 120, - 101, - 47, - 138, - 5, - 33, - 15, - 225, - 36, - 18, - 240, - 130, - 69, - 53, - 147, - 218, - 142, - 150, - 143, - 219, - 189, - 54, - 208, - 206, - 148, - 19, - 92, - 210, - 241, - 64, - 70, - 131, - 56, - 102, - 221, - 253, - 48, - 191, - 6, - 139, - 98, - 179, - 37, - 226, - 152, - 34, - 136, - 145, - 16, - 126, - 110, - 72, - 195, - 163, - 182, - 30, - 66, - 58, - 107, - 40, - 84, - 250, - 133, - 61, - 186, - 43, - 121, - 10, - 21, - 155, - 159, - 94, - 202, - 78, - 212, - 172, - 229, - 243, - 115, - 167, - 87, - 175, - 88, - 168, - 80, - 244, - 234, - 214, - 116, - 79, - 174, - 233, - 213, - 231, - 230, - 173, - 232, - 44, - 215, - 117, - 122, - 235, - 22, - 11, - 245, - 89, - 203, - 95, - 176, - 156, - 169, - 81, - 160, - 127, - 12, - 246, - 111, - 23, - 196, - 73, - 236, - 216, - 67, - 31, - 45, - 164, - 118, - 123, - 183, - 204, - 187, - 62, - 90, - 251, - 96, - 177, - 134, - 59, - 82, - 161, - 108, - 170, - 85, - 41, - 157, - 151, - 178, - 135, - 144, - 97, - 190, - 220, - 252, - 188, - 149, - 207, - 205, - 55, - 63, - 91, - 209, - 83, - 57, - 132, - 60, - 65, - 162, - 109, - 71, - 20, - 42, - 158, - 93, - 86, - 242, - 211, - 171, - 68, - 17, - 146, - 217, - 35, - 32, - 46, - 137, - 180, - 124, - 184, - 38, - 119, - 153, - 227, - 165, - 103, - 74, - 237, - 222, - 197, - 49, - 254, - 24, - 13, - 99, - 140, - 128, - 192, - 247, - 112, - 7 - }; - - private static ReadOnlySpan Alogtable => - new byte[] - { - 0, - 3, - 5, - 15, - 17, - 51, - 85, - 255, - 26, - 46, - 114, - 150, - 161, - 248, - 19, - 53, - 95, - 225, - 56, - 72, - 216, - 115, - 149, - 164, - 247, - 2, - 6, - 10, - 30, - 34, - 102, - 170, - 229, - 52, - 92, - 228, - 55, - 89, - 235, - 38, - 106, - 190, - 217, - 112, - 144, - 171, - 230, - 49, - 83, - 245, - 4, - 12, - 20, - 60, - 68, - 204, - 79, - 209, - 104, - 184, - 211, - 110, - 178, - 205, - 76, - 212, - 103, - 169, - 224, - 59, - 77, - 215, - 98, - 166, - 241, - 8, - 24, - 40, - 120, - 136, - 131, - 158, - 185, - 208, - 107, - 189, - 220, - 127, - 129, - 152, - 179, - 206, - 73, - 219, - 118, - 154, - 181, - 196, - 87, - 249, - 16, - 48, - 80, - 240, - 11, - 29, - 39, - 105, - 187, - 214, - 97, - 163, - 254, - 25, - 43, - 125, - 135, - 146, - 173, - 236, - 47, - 113, - 147, - 174, - 233, - 32, - 96, - 160, - 251, - 22, - 58, - 78, - 210, - 109, - 183, - 194, - 93, - 231, - 50, - 86, - 250, - 21, - 63, - 65, - 195, - 94, - 226, - 61, - 71, - 201, - 64, - 192, - 91, - 237, - 44, - 116, - 156, - 191, - 218, - 117, - 159, - 186, - 213, - 100, - 172, - 239, - 42, - 126, - 130, - 157, - 188, - 223, - 122, - 142, - 137, - 128, - 155, - 182, - 193, - 88, - 232, - 35, - 101, - 175, - 234, - 37, - 111, - 177, - 200, - 67, - 197, - 84, - 252, - 31, - 33, - 99, - 165, - 244, - 7, - 9, - 27, - 45, - 119, - 153, - 176, - 203, - 70, - 202, - 69, - 207, - 74, - 222, - 121, - 139, - 134, - 145, - 168, - 227, - 62, - 66, - 198, - 81, - 243, - 14, - 18, - 54, - 90, - 238, - 41, - 123, - 141, - 140, - 143, - 138, - 133, - 148, - 167, - 242, - 13, - 23, - 57, - 75, - 221, - 124, - 132, - 151, - 162, - 253, - 28, - 36, - 108, - 180, - 199, - 82, - 246, - 1, - 3, - 5, - 15, - 17, - 51, - 85, - 255, - 26, - 46, - 114, - 150, - 161, - 248, - 19, - 53, - 95, - 225, - 56, - 72, - 216, - 115, - 149, - 164, - 247, - 2, - 6, - 10, - 30, - 34, - 102, - 170, - 229, - 52, - 92, - 228, - 55, - 89, - 235, - 38, - 106, - 190, - 217, - 112, - 144, - 171, - 230, - 49, - 83, - 245, - 4, - 12, - 20, - 60, - 68, - 204, - 79, - 209, - 104, - 184, - 211, - 110, - 178, - 205, - 76, - 212, - 103, - 169, - 224, - 59, - 77, - 215, - 98, - 166, - 241, - 8, - 24, - 40, - 120, - 136, - 131, - 158, - 185, - 208, - 107, - 189, - 220, - 127, - 129, - 152, - 179, - 206, - 73, - 219, - 118, - 154, - 181, - 196, - 87, - 249, - 16, - 48, - 80, - 240, - 11, - 29, - 39, - 105, - 187, - 214, - 97, - 163, - 254, - 25, - 43, - 125, - 135, - 146, - 173, - 236, - 47, - 113, - 147, - 174, - 233, - 32, - 96, - 160, - 251, - 22, - 58, - 78, - 210, - 109, - 183, - 194, - 93, - 231, - 50, - 86, - 250, - 21, - 63, - 65, - 195, - 94, - 226, - 61, - 71, - 201, - 64, - 192, - 91, - 237, - 44, - 116, - 156, - 191, - 218, - 117, - 159, - 186, - 213, - 100, - 172, - 239, - 42, - 126, - 130, - 157, - 188, - 223, - 122, - 142, - 137, - 128, - 155, - 182, - 193, - 88, - 232, - 35, - 101, - 175, - 234, - 37, - 111, - 177, - 200, - 67, - 197, - 84, - 252, - 31, - 33, - 99, - 165, - 244, - 7, - 9, - 27, - 45, - 119, - 153, - 176, - 203, - 70, - 202, - 69, - 207, - 74, - 222, - 121, - 139, - 134, - 145, - 168, - 227, - 62, - 66, - 198, - 81, - 243, - 14, - 18, - 54, - 90, - 238, - 41, - 123, - 141, - 140, - 143, - 138, - 133, - 148, - 167, - 242, - 13, - 23, - 57, - 75, - 221, - 124, - 132, - 151, - 162, - 253, - 28, - 36, - 108, - 180, - 199, - 82, - 246, - 1 - }; - - private static ReadOnlySpan S => - new byte[] - { - 99, - 124, - 119, - 123, - 242, - 107, - 111, - 197, - 48, - 1, - 103, - 43, - 254, - 215, - 171, - 118, - 202, - 130, - 201, - 125, - 250, - 89, - 71, - 240, - 173, - 212, - 162, - 175, - 156, - 164, - 114, - 192, - 183, - 253, - 147, - 38, - 54, - 63, - 247, - 204, - 52, - 165, - 229, - 241, - 113, - 216, - 49, - 21, - 4, - 199, - 35, - 195, - 24, - 150, - 5, - 154, - 7, - 18, - 128, - 226, - 235, - 39, - 178, - 117, - 9, - 131, - 44, - 26, - 27, - 110, - 90, - 160, - 82, - 59, - 214, - 179, - 41, - 227, - 47, - 132, - 83, - 209, - 0, - 237, - 32, - 252, - 177, - 91, - 106, - 203, - 190, - 57, - 74, - 76, - 88, - 207, - 208, - 239, - 170, - 251, - 67, - 77, - 51, - 133, - 69, - 249, - 2, - 127, - 80, - 60, - 159, - 168, - 81, - 163, - 64, - 143, - 146, - 157, - 56, - 245, - 188, - 182, - 218, - 33, - 16, - 255, - 243, - 210, - 205, - 12, - 19, - 236, - 95, - 151, - 68, - 23, - 196, - 167, - 126, - 61, - 100, - 93, - 25, - 115, - 96, - 129, - 79, - 220, - 34, - 42, - 144, - 136, - 70, - 238, - 184, - 20, - 222, - 94, - 11, - 219, - 224, - 50, - 58, - 10, - 73, - 6, - 36, - 92, - 194, - 211, - 172, - 98, - 145, - 149, - 228, - 121, - 231, - 200, - 55, - 109, - 141, - 213, - 78, - 169, - 108, - 86, - 244, - 234, - 101, - 122, - 174, - 8, - 186, - 120, - 37, - 46, - 28, - 166, - 180, - 198, - 232, - 221, - 116, - 31, - 75, - 189, - 139, - 138, - 112, - 62, - 181, - 102, - 72, - 3, - 246, - 14, - 97, - 53, - 87, - 185, - 134, - 193, - 29, - 158, - 225, - 248, - 152, - 17, - 105, - 217, - 142, - 148, - 155, - 30, - 135, - 233, - 206, - 85, - 40, - 223, - 140, - 161, - 137, - 13, - 191, - 230, - 66, - 104, - 65, - 153, - 45, - 15, - 176, - 84, - 187, - 22 - }; - - private static ReadOnlySpan Si => - new byte[] - { - 82, - 9, - 106, - 213, - 48, - 54, - 165, - 56, - 191, - 64, - 163, - 158, - 129, - 243, - 215, - 251, - 124, - 227, - 57, - 130, - 155, - 47, - 255, - 135, - 52, - 142, - 67, - 68, - 196, - 222, - 233, - 203, - 84, - 123, - 148, - 50, - 166, - 194, - 35, - 61, - 238, - 76, - 149, - 11, - 66, - 250, - 195, - 78, - 8, - 46, - 161, - 102, - 40, - 217, - 36, - 178, - 118, - 91, - 162, - 73, - 109, - 139, - 209, - 37, - 114, - 248, - 246, - 100, - 134, - 104, - 152, - 22, - 212, - 164, - 92, - 204, - 93, - 101, - 182, - 146, - 108, - 112, - 72, - 80, - 253, - 237, - 185, - 218, - 94, - 21, - 70, - 87, - 167, - 141, - 157, - 132, - 144, - 216, - 171, - 0, - 140, - 188, - 211, - 10, - 247, - 228, - 88, - 5, - 184, - 179, - 69, - 6, - 208, - 44, - 30, - 143, - 202, - 63, - 15, - 2, - 193, - 175, - 189, - 3, - 1, - 19, - 138, - 107, - 58, - 145, - 17, - 65, - 79, - 103, - 220, - 234, - 151, - 242, - 207, - 206, - 240, - 180, - 230, - 115, - 150, - 172, - 116, - 34, - 231, - 173, - 53, - 133, - 226, - 249, - 55, - 232, - 28, - 117, - 223, - 110, - 71, - 241, - 26, - 113, - 29, - 41, - 197, - 137, - 111, - 183, - 98, - 14, - 170, - 24, - 190, - 27, - 252, - 86, - 62, - 75, - 198, - 210, - 121, - 32, - 154, - 219, - 192, - 254, - 120, - 205, - 90, - 244, - 31, - 221, - 168, - 51, - 136, - 7, - 199, - 49, - 177, - 18, - 16, - 89, - 39, - 128, - 236, - 95, - 96, - 81, - 127, - 169, - 25, - 181, - 74, - 13, - 45, - 229, - 122, - 159, - 147, - 201, - 156, - 239, - 160, - 224, - 59, - 77, - 174, - 42, - 245, - 176, - 200, - 235, - 187, - 60, - 131, - 83, - 153, - 97, - 23, - 43, - 4, - 126, - 186, - 119, - 214, - 38, - 225, - 105, - 20, - 99, - 85, - 33, - 12, - 125 - }; - - private static ReadOnlySpan rcon => - new byte[] - { - 0x01, - 0x02, - 0x04, - 0x08, - 0x10, - 0x20, - 0x40, - 0x80, - 0x1b, - 0x36, - 0x6c, - 0xd8, - 0xab, - 0x4d, - 0x9a, - 0x2f, - 0x5e, - 0xbc, - 0x63, - 0xc6, - 0x97, - 0x35, - 0x6a, - 0xd4, - 0xb3, - 0x7d, - 0xfa, - 0xef, - 0xc5, - 0x91 - }; - - private static readonly byte[][] shifts0 = - { - new byte[] { 0, 8, 16, 24 }, - new byte[] { 0, 8, 16, 24 }, - new byte[] { 0, 8, 16, 24 }, - new byte[] { 0, 8, 16, 32 }, - new byte[] { 0, 8, 24, 32 } - }; - - private static readonly byte[][] shifts1 = - { - new byte[] { 0, 24, 16, 8 }, - new byte[] { 0, 32, 24, 16 }, - new byte[] { 0, 40, 32, 24 }, - new byte[] { 0, 48, 40, 24 }, - new byte[] { 0, 56, 40, 32 } - }; - - /** - * multiply two elements of GF(2^m) - * needed for MixColumn and InvMixColumn - */ - - private byte Mul0x2(int b) - { - if (b != 0) - { - return Alogtable[25 + (Logtable[b] & 0xff)]; - } - return 0; - } - - private byte Mul0x3(int b) - { - if (b != 0) - { - return Alogtable[1 + (Logtable[b] & 0xff)]; - } - return 0; - } - - private byte Mul0x9(int b) - { - if (b >= 0) - { - return Alogtable[199 + b]; - } - return 0; - } - - private byte Mul0xb(int b) - { - if (b >= 0) - { - return Alogtable[104 + b]; - } - return 0; - } - - private byte Mul0xd(int b) - { - if (b >= 0) - { - return Alogtable[238 + b]; - } - return 0; - } - - private byte Mul0xe(int b) - { - if (b >= 0) - { - return Alogtable[223 + b]; - } - return 0; - } - - /** - * xor corresponding text input and round key input bytes - */ - - private void KeyAddition(long[] rk) - { - A0 ^= rk[0]; - A1 ^= rk[1]; - A2 ^= rk[2]; - A3 ^= rk[3]; - } - - private long Shift(long r, int shift) - { - //return (((long)((ulong) r >> shift) | (r << (BC - shift)))) & BC_MASK; - - var temp = (ulong)r >> shift; - - // NB: This corrects for Mono Bug #79087 (fixed in 1.1.17) - if (shift > 31) - { - temp &= 0xFFFFFFFFUL; - } - - return ((long)temp | (r << (BC - shift))) & BC_MASK; - } - - /** - * Row 0 remains unchanged - * The other three rows are shifted a variable amount - */ - - private void ShiftRow(byte[] shiftsSC) - { - A1 = Shift(A1, shiftsSC[1]); - A2 = Shift(A2, shiftsSC[2]); - A3 = Shift(A3, shiftsSC[3]); - } - - private long ApplyS(long r, ReadOnlySpan box) - { - long res = 0; - - for (var j = 0; j < BC; j += 8) - { - res |= (long)(box[(int)((r >> j) & 0xff)] & 0xff) << j; - } - - return res; - } - - /** - * Replace every byte of the input by the byte at that place - * in the nonlinear S-box - */ - - private void Substitution(ReadOnlySpan box) - { - A0 = ApplyS(A0, box); - A1 = ApplyS(A1, box); - A2 = ApplyS(A2, box); - A3 = ApplyS(A3, box); - } - - /** - * Mix the bytes of every column in a linear way - */ - - private void MixColumn() - { - long r0, - r1, - r2, - r3; - - r0 = r1 = r2 = r3 = 0; - - for (var j = 0; j < BC; j += 8) - { - var a0 = (int)((A0 >> j) & 0xff); - var a1 = (int)((A1 >> j) & 0xff); - var a2 = (int)((A2 >> j) & 0xff); - var a3 = (int)((A3 >> j) & 0xff); - - r0 |= (long)((Mul0x2(a0) ^ Mul0x3(a1) ^ a2 ^ a3) & 0xff) << j; - - r1 |= (long)((Mul0x2(a1) ^ Mul0x3(a2) ^ a3 ^ a0) & 0xff) << j; - - r2 |= (long)((Mul0x2(a2) ^ Mul0x3(a3) ^ a0 ^ a1) & 0xff) << j; - - r3 |= (long)((Mul0x2(a3) ^ Mul0x3(a0) ^ a1 ^ a2) & 0xff) << j; - } - - A0 = r0; - A1 = r1; - A2 = r2; - A3 = r3; - } - - /** - * Mix the bytes of every column in a linear way - * This is the opposite operation of Mixcolumn - */ - - private void InvMixColumn() - { - long r0, - r1, - r2, - r3; - - r0 = r1 = r2 = r3 = 0; - for (var j = 0; j < BC; j += 8) - { - var a0 = (int)((A0 >> j) & 0xff); - var a1 = (int)((A1 >> j) & 0xff); - var a2 = (int)((A2 >> j) & 0xff); - var a3 = (int)((A3 >> j) & 0xff); - - // - // pre-lookup the log table - // - a0 = (a0 != 0) ? (Logtable[a0 & 0xff] & 0xff) : -1; - a1 = (a1 != 0) ? (Logtable[a1 & 0xff] & 0xff) : -1; - a2 = (a2 != 0) ? (Logtable[a2 & 0xff] & 0xff) : -1; - a3 = (a3 != 0) ? (Logtable[a3 & 0xff] & 0xff) : -1; - - r0 |= (long)((Mul0xe(a0) ^ Mul0xb(a1) ^ Mul0xd(a2) ^ Mul0x9(a3)) & 0xff) << j; - - r1 |= (long)((Mul0xe(a1) ^ Mul0xb(a2) ^ Mul0xd(a3) ^ Mul0x9(a0)) & 0xff) << j; - - r2 |= (long)((Mul0xe(a2) ^ Mul0xb(a3) ^ Mul0xd(a0) ^ Mul0x9(a1)) & 0xff) << j; - - r3 |= (long)((Mul0xe(a3) ^ Mul0xb(a0) ^ Mul0xd(a1) ^ Mul0x9(a2)) & 0xff) << j; - } - - A0 = r0; - A1 = r1; - A2 = r2; - A3 = r3; - } - - /** - * Calculate the necessary round keys - * The number of calculations depends on keyBits and blockBits - */ - - private long[][] GenerateWorkingKey(byte[] key) - { - int t, - rconpointer = 0; - var keyBits = key.Length * 8; - var tk = new byte[4, MAXKC]; - - //long[,] W = new long[MAXROUNDS+1,4]; - var W = new long[MAXROUNDS + 1][]; - - for (var i = 0; i < MAXROUNDS + 1; i++) - { - W[i] = new long[4]; - } - - var KC = keyBits switch - { - 128 => 4, - 160 => 5, - 192 => 6, - 224 => 7, - 256 => 8, - _ => throw new ArgumentException("Key length not 128/160/192/224/256 bits."), - }; - if (keyBits >= blockBits) - { - ROUNDS = KC + 6; - } - else - { - ROUNDS = (BC / 8) + 6; - } - - // - // copy the key into the processing area - // - var index = 0; - - for (var i = 0; i < key.Length; i++) - { - tk[i % 4, i / 4] = key[index++]; - } - - t = 0; - - // - // copy values into round key array - // - for (var j = 0; (j < KC) && (t < (ROUNDS + 1) * (BC / 8)); j++, t++) - { - for (var i = 0; i < 4; i++) - { - W[t / (BC / 8)][i] |= (long)(tk[i, j] & 0xff) << ((t * 8) % BC); - } - } - - // - // while not enough round key material calculated - // calculate new values - // - while (t < (ROUNDS + 1) * (BC / 8)) - { - for (var i = 0; i < 4; i++) - { - tk[i, 0] ^= S[tk[(i + 1) % 4, KC - 1] & 0xff]; - } - tk[0, 0] ^= rcon[rconpointer++]; - - if (KC <= 6) - { - for (var j = 1; j < KC; j++) - { - for (var i = 0; i < 4; i++) - { - tk[i, j] ^= tk[i, j - 1]; - } - } - } - else - { - for (var j = 1; j < 4; j++) - { - for (var i = 0; i < 4; i++) - { - tk[i, j] ^= tk[i, j - 1]; - } - } - for (var i = 0; i < 4; i++) - { - tk[i, 4] ^= S[tk[i, 3] & 0xff]; - } - for (var j = 5; j < KC; j++) - { - for (var i = 0; i < 4; i++) - { - tk[i, j] ^= tk[i, j - 1]; - } - } - } - - // - // copy values into round key array - // - for (var j = 0; (j < KC) && (t < (ROUNDS + 1) * (BC / 8)); j++, t++) - { - for (var i = 0; i < 4; i++) - { - W[t / (BC / 8)][i] |= (long)(tk[i, j] & 0xff) << ((t * 8) % (BC)); - } - } - } - return W; - } - - private readonly int BC; - private readonly long BC_MASK; - private int ROUNDS; - private readonly int blockBits; - private long[][]? workingKey; - private long A0, - A1, - A2, - A3; - private bool forEncryption; - private readonly byte[] shifts0SC; - private readonly byte[] shifts1SC; - - /** - * default constructor - 128 bit block size. - */ - - public RijndaelEngine() - : this(128) { } - - /** - * basic constructor - set the cipher up for a given blocksize - * - * @param blocksize the blocksize in bits, must be 128, 192, or 256. - */ - - public RijndaelEngine(int blockBits) - { - switch (blockBits) - { - case 128: - BC = 32; - BC_MASK = 0xffffffffL; - shifts0SC = shifts0[0]; - shifts1SC = shifts1[0]; - break; - case 160: - BC = 40; - BC_MASK = 0xffffffffffL; - shifts0SC = shifts0[1]; - shifts1SC = shifts1[1]; - break; - case 192: - BC = 48; - BC_MASK = 0xffffffffffffL; - shifts0SC = shifts0[2]; - shifts1SC = shifts1[2]; - break; - case 224: - BC = 56; - BC_MASK = 0xffffffffffffffL; - shifts0SC = shifts0[3]; - shifts1SC = shifts1[3]; - break; - case 256: - BC = 64; - BC_MASK = unchecked((long)0xffffffffffffffffL); - shifts0SC = shifts0[4]; - shifts1SC = shifts1[4]; - break; - default: - throw new ArgumentException("unknown blocksize to Rijndael"); - } - - this.blockBits = blockBits; - } - - /** - * initialise a Rijndael cipher. - * - * @param forEncryption whether or not we are for encryption. - * @param parameters the parameters required to set up the cipher. - * @exception ArgumentException if the parameters argument is - * inappropriate. - */ - - public void Init(bool forEncryption, ICipherParameters parameters) - { - if (parameters is KeyParameter parameter) - { - workingKey = GenerateWorkingKey(parameter.GetKey()); - this.forEncryption = forEncryption; - return; - } - - throw new ArgumentException( - "invalid parameter passed to Rijndael init - " + parameters.GetType() - ); - } - - public string AlgorithmName => "Rijndael"; - - public bool IsPartialBlockOkay => false; - - public int GetBlockSize() => BC / 2; - - public int ProcessBlock(ReadOnlySpan input, Span output) - { - if (workingKey is null) - { - throw new InvalidOperationException("Rijndael engine not initialised"); - } - - if (BC / 2 > input.Length) - { - throw new DataLengthException("input buffer too short"); - } - - if (BC / 2 > output.Length) - { - throw new DataLengthException("output buffer too short"); - } - - UnPackBlock(input); - - if (forEncryption) - { - EncryptBlock(workingKey); - } - else - { - DecryptBlock(workingKey); - } - - PackBlock(output); - - return BC / 2; - } - - public void Reset() { } - - private void UnPackBlock(ReadOnlySpan bytes) - { - var index = 0; - - A0 = bytes[index++] & 0xff; - A1 = bytes[index++] & 0xff; - A2 = bytes[index++] & 0xff; - A3 = bytes[index++] & 0xff; - - for (var j = 8; j != BC; j += 8) - { - A0 |= (long)(bytes[index++] & 0xff) << j; - A1 |= (long)(bytes[index++] & 0xff) << j; - A2 |= (long)(bytes[index++] & 0xff) << j; - A3 |= (long)(bytes[index++] & 0xff) << j; - } - } - - private void PackBlock(Span bytes) - { - var index = 0; - - for (var j = 0; j != BC; j += 8) - { - bytes[index++] = (byte)(A0 >> j); - bytes[index++] = (byte)(A1 >> j); - bytes[index++] = (byte)(A2 >> j); - bytes[index++] = (byte)(A3 >> j); - } - } - - private void EncryptBlock(long[][] rk) - { - int r; - - // - // begin with a key addition - // - KeyAddition(rk[0]); - - // - // ROUNDS-1 ordinary rounds - // - for (r = 1; r < ROUNDS; r++) - { - Substitution(S); - ShiftRow(shifts0SC); - MixColumn(); - KeyAddition(rk[r]); - } - - // - // Last round is special: there is no MixColumn - // - Substitution(S); - ShiftRow(shifts0SC); - KeyAddition(rk[ROUNDS]); - } - - private void DecryptBlock(long[][] rk) - { - int r; - - // To decrypt: apply the inverse operations of the encrypt routine, - // in opposite order - // - // (KeyAddition is an involution: it 's equal to its inverse) - // (the inverse of Substitution with table S is Substitution with the inverse table of S) - // (the inverse of Shiftrow is Shiftrow over a suitable distance) - // - - // First the special round: - // without InvMixColumn - // with extra KeyAddition - // - KeyAddition(rk[ROUNDS]); - Substitution(Si); - ShiftRow(shifts1SC); - - // - // ROUNDS-1 ordinary rounds - // - for (r = ROUNDS - 1; r > 0; r--) - { - KeyAddition(rk[r]); - InvMixColumn(); - Substitution(Si); - ShiftRow(shifts1SC); - } - - // - // End with the extra key addition - // - KeyAddition(rk[0]); - } -} diff --git a/src/SharpCompress/Factories/Factory.cs b/src/SharpCompress/Factories/Factory.cs index 7ec68508..baef3b85 100644 --- a/src/SharpCompress/Factories/Factory.cs +++ b/src/SharpCompress/Factories/Factory.cs @@ -19,7 +19,7 @@ public abstract class Factory : IFactory RegisterFactory(new TarFactory()); } - private static readonly HashSet _factories = new HashSet(); + private static readonly HashSet _factories = new(); /// /// Gets the collection of registered . diff --git a/src/SharpCompress/Factories/GZipFactory.cs b/src/SharpCompress/Factories/GZipFactory.cs index 5e522f5b..be1b4931 100644 --- a/src/SharpCompress/Factories/GZipFactory.cs +++ b/src/SharpCompress/Factories/GZipFactory.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; using System.IO; using System.IO.Compression; - using SharpCompress.Archives; using SharpCompress.Archives.GZip; using SharpCompress.Archives.Tar; diff --git a/src/SharpCompress/Factories/TarFactory.cs b/src/SharpCompress/Factories/TarFactory.cs index 61965b86..715a05d2 100644 --- a/src/SharpCompress/Factories/TarFactory.cs +++ b/src/SharpCompress/Factories/TarFactory.cs @@ -1,12 +1,12 @@ using System.Collections.Generic; using System.IO; - using SharpCompress.Archives; using SharpCompress.Archives.Tar; using SharpCompress.Common; using SharpCompress.Compressors; using SharpCompress.Compressors.BZip2; using SharpCompress.Compressors.LZMA; +using SharpCompress.Compressors.Lzw; using SharpCompress.Compressors.Xz; using SharpCompress.IO; using SharpCompress.Readers; @@ -161,6 +161,19 @@ public class TarFactory } } + rewindableStream.Rewind(false); + if (LzwStream.IsLzwStream(rewindableStream)) + { + rewindableStream.Rewind(false); + var testStream = new LzwStream(rewindableStream); + if (TarArchive.IsTarFile(testStream)) + { + rewindableStream.Rewind(true); + reader = new TarReader(rewindableStream, options, CompressionType.Lzw); + return true; + } + } + return false; } diff --git a/src/SharpCompress/Factories/ZipFactory.cs b/src/SharpCompress/Factories/ZipFactory.cs index dca697dc..780d2a9c 100644 --- a/src/SharpCompress/Factories/ZipFactory.cs +++ b/src/SharpCompress/Factories/ZipFactory.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.IO; - using SharpCompress.Archives; using SharpCompress.Archives.Zip; using SharpCompress.Common; diff --git a/src/SharpCompress/IO/BufferedSubStream.cs b/src/SharpCompress/IO/BufferedSubStream.cs index 99e2bfef..243d1ca9 100644 --- a/src/SharpCompress/IO/BufferedSubStream.cs +++ b/src/SharpCompress/IO/BufferedSubStream.cs @@ -1,24 +1,16 @@ -using System; +using System; using System.IO; namespace SharpCompress.IO; -internal class BufferedSubStream : NonDisposingStream +internal class BufferedSubStream(Stream stream, long origin, long bytesToRead) + : NonDisposingStream(stream, throwOnDispose: false) { - private long position; - private int cacheOffset; - private int cacheLength; - private readonly byte[] cache; + private int _cacheOffset; + private int _cacheLength; + private readonly byte[] _cache = new byte[32 << 10]; - public BufferedSubStream(Stream stream, long origin, long bytesToRead) - : base(stream, throwOnDispose: false) - { - position = origin; - BytesLeftToRead = bytesToRead; - cache = new byte[32 << 10]; - } - - private long BytesLeftToRead { get; set; } + private long BytesLeftToRead { get; set; } = bytesToRead; public override bool CanRead => true; @@ -26,7 +18,7 @@ internal class BufferedSubStream : NonDisposingStream public override bool CanWrite => false; - public override void Flush() => throw new NotSupportedException(); + public override void Flush() { } public override long Length => BytesLeftToRead; @@ -45,22 +37,22 @@ internal class BufferedSubStream : NonDisposingStream if (count > 0) { - if (cacheLength == 0) + if (_cacheLength == 0) { - cacheOffset = 0; - Stream.Position = position; - cacheLength = Stream.Read(cache, 0, cache.Length); - position += cacheLength; + _cacheOffset = 0; + Stream.Position = origin; + _cacheLength = Stream.Read(_cache, 0, _cache.Length); + origin += _cacheLength; } - if (count > cacheLength) + if (count > _cacheLength) { - count = cacheLength; + count = _cacheLength; } - Buffer.BlockCopy(cache, cacheOffset, buffer, offset, count); - cacheOffset += count; - cacheLength -= count; + Buffer.BlockCopy(_cache, _cacheOffset, buffer, offset, count); + _cacheOffset += count; + _cacheLength -= count; BytesLeftToRead -= count; } diff --git a/src/SharpCompress/IO/DataDescriptorStream.cs b/src/SharpCompress/IO/DataDescriptorStream.cs index 8ba9eb0f..1802556c 100644 --- a/src/SharpCompress/IO/DataDescriptorStream.cs +++ b/src/SharpCompress/IO/DataDescriptorStream.cs @@ -8,18 +8,18 @@ public class DataDescriptorStream : Stream { private readonly Stream _stream; private long _start; - private int _search_position; + private int _searchPosition; private bool _isDisposed; private bool _done; - private static byte[] DataDescriptorMarker = new byte[] { 0x50, 0x4b, 0x07, 0x08 }; - private static long DataDescriptorSize = 24; + private static byte[] _dataDescriptorMarker = new byte[] { 0x50, 0x4b, 0x07, 0x08 }; + private static long _dataDescriptorSize = 24; public DataDescriptorStream(Stream stream) { _stream = stream; _start = _stream.Position; - _search_position = 0; + _searchPosition = 0; _done = false; } @@ -45,13 +45,13 @@ public class DataDescriptorStream : Stream public override bool CanWrite => false; - public override void Flush() => throw new NotSupportedException(); + public override void Flush() { } public override long Length => _stream.Length; public override long Position { - get => _stream.Position; + get => _stream.Position - _start; set => _stream.Position = value; } @@ -60,20 +60,20 @@ public class DataDescriptorStream : Stream var br = new BinaryReader(stream); br.ReadUInt32(); br.ReadUInt32(); // CRC32 can be checked if we calculate it - var compressed_size = br.ReadUInt32(); - var uncompressed_size = br.ReadUInt32(); - var uncompressed_64bit = br.ReadInt64(); + var compressedSize = br.ReadUInt32(); + var uncompressedSize = br.ReadUInt32(); + var uncompressed64Bit = br.ReadInt64(); - stream.Position -= DataDescriptorSize; + stream.Position -= _dataDescriptorSize; - var test_64bit = ((long)uncompressed_size << 32) | compressed_size; + var test64Bit = ((long)uncompressedSize << 32) | compressedSize; - if (test_64bit == size && test_64bit == uncompressed_64bit) + if (test64Bit == size && test64Bit == uncompressed64Bit) { return true; } - if (compressed_size == size && compressed_size == uncompressed_size) + if (compressedSize == size && compressedSize == uncompressedSize) { return true; } @@ -88,24 +88,24 @@ public class DataDescriptorStream : Stream return 0; } - int read = _stream.Read(buffer, offset, count); + var read = _stream.Read(buffer, offset, count); - for (int i = 0; i < read; i++) + for (var i = 0; i < read; i++) { - if (buffer[offset + i] == DataDescriptorMarker[_search_position]) + if (buffer[offset + i] == _dataDescriptorMarker[_searchPosition]) { - _search_position++; + _searchPosition++; - if (_search_position == 4) + if (_searchPosition == 4) { - _search_position = 0; + _searchPosition = 0; - if (read - i > DataDescriptorSize) + if (read - i > _dataDescriptorSize) { var check = new MemoryStream( buffer, offset + i - 3, - (int)DataDescriptorSize + (int)_dataDescriptorSize ); _done = validate_data_descriptor( check, @@ -131,15 +131,15 @@ public class DataDescriptorStream : Stream } else { - _search_position = 0; + _searchPosition = 0; } } - if (_search_position > 0) + if (_searchPosition > 0) { - read -= _search_position; - _stream.Position -= _search_position; - _search_position = 0; + read -= _searchPosition; + _stream.Position -= _searchPosition; + _searchPosition = 0; } return read; diff --git a/src/SharpCompress/IO/ListeningStream.cs b/src/SharpCompress/IO/ListeningStream.cs index a1bf715b..0acf1056 100644 --- a/src/SharpCompress/IO/ListeningStream.cs +++ b/src/SharpCompress/IO/ListeningStream.cs @@ -5,13 +5,13 @@ namespace SharpCompress.IO; internal class ListeningStream : Stream { - private long currentEntryTotalReadBytes; - private readonly IExtractionListener listener; + private long _currentEntryTotalReadBytes; + private readonly IExtractionListener _listener; public ListeningStream(IExtractionListener listener, Stream stream) { Stream = stream; - this.listener = listener; + this._listener = listener; } protected override void Dispose(bool disposing) @@ -44,8 +44,8 @@ internal class ListeningStream : Stream public override int Read(byte[] buffer, int offset, int count) { var read = Stream.Read(buffer, offset, count); - currentEntryTotalReadBytes += read; - listener.FireCompressedBytesRead(currentEntryTotalReadBytes, currentEntryTotalReadBytes); + _currentEntryTotalReadBytes += read; + _listener.FireCompressedBytesRead(_currentEntryTotalReadBytes, _currentEntryTotalReadBytes); return read; } @@ -57,8 +57,8 @@ internal class ListeningStream : Stream return -1; } - ++currentEntryTotalReadBytes; - listener.FireCompressedBytesRead(currentEntryTotalReadBytes, currentEntryTotalReadBytes); + ++_currentEntryTotalReadBytes; + _listener.FireCompressedBytesRead(_currentEntryTotalReadBytes, _currentEntryTotalReadBytes); return value; } diff --git a/src/SharpCompress/IO/ReadOnlySubStream.cs b/src/SharpCompress/IO/ReadOnlySubStream.cs index c0b39d54..cd39335e 100644 --- a/src/SharpCompress/IO/ReadOnlySubStream.cs +++ b/src/SharpCompress/IO/ReadOnlySubStream.cs @@ -29,7 +29,7 @@ internal class ReadOnlySubStream : NonDisposingStream public override bool CanWrite => false; - public override void Flush() => throw new NotSupportedException(); + public override void Flush() { } public override long Length => throw new NotSupportedException(); @@ -72,8 +72,8 @@ internal class ReadOnlySubStream : NonDisposingStream #if !NETFRAMEWORK && !NETSTANDARD2_0 public override int Read(Span buffer) { - var slice_len = BytesLeftToRead < buffer.Length ? BytesLeftToRead : buffer.Length; - var read = Stream.Read(buffer.Slice(0, (int)slice_len)); + var sliceLen = BytesLeftToRead < buffer.Length ? BytesLeftToRead : buffer.Length; + var read = Stream.Read(buffer.Slice(0, (int)sliceLen)); if (read > 0) { BytesLeftToRead -= read; diff --git a/src/SharpCompress/IO/RewindableStream.cs b/src/SharpCompress/IO/RewindableStream.cs index ccdc8ee0..c7febd1d 100644 --- a/src/SharpCompress/IO/RewindableStream.cs +++ b/src/SharpCompress/IO/RewindableStream.cs @@ -5,98 +5,98 @@ namespace SharpCompress.IO; public class RewindableStream : Stream { - private readonly Stream stream; - private MemoryStream bufferStream = new MemoryStream(); - private bool isRewound; - private bool isDisposed; + private readonly Stream _stream; + private MemoryStream _bufferStream = new(); + private bool _isRewound; + private bool _isDisposed; - public RewindableStream(Stream stream) => this.stream = stream; + public RewindableStream(Stream stream) => this._stream = stream; internal bool IsRecording { get; private set; } protected override void Dispose(bool disposing) { - if (isDisposed) + if (_isDisposed) { return; } - isDisposed = true; + _isDisposed = true; base.Dispose(disposing); if (disposing) { - stream.Dispose(); + _stream.Dispose(); } } public void Rewind(bool stopRecording) { - isRewound = true; + _isRewound = true; IsRecording = !stopRecording; - bufferStream.Position = 0; + _bufferStream.Position = 0; } public void Rewind(MemoryStream buffer) { - if (bufferStream.Position >= buffer.Length) + if (_bufferStream.Position >= buffer.Length) { - bufferStream.Position -= buffer.Length; + _bufferStream.Position -= buffer.Length; } else { - bufferStream.TransferTo(buffer); + _bufferStream.TransferTo(buffer); //create new memorystream to allow proper resizing as memorystream could be a user provided buffer //https://github.com/adamhathcock/sharpcompress/issues/306 - bufferStream = new MemoryStream(); + _bufferStream = new MemoryStream(); buffer.Position = 0; - buffer.TransferTo(bufferStream); - bufferStream.Position = 0; + buffer.TransferTo(_bufferStream); + _bufferStream.Position = 0; } - isRewound = true; + _isRewound = true; } public void StartRecording() { //if (isRewound && bufferStream.Position != 0) // throw new System.NotImplementedException(); - if (bufferStream.Position != 0) + if (_bufferStream.Position != 0) { - var data = bufferStream.ToArray(); - var position = bufferStream.Position; - bufferStream.SetLength(0); - bufferStream.Write(data, (int)position, data.Length - (int)position); - bufferStream.Position = 0; + var data = _bufferStream.ToArray(); + var position = _bufferStream.Position; + _bufferStream.SetLength(0); + _bufferStream.Write(data, (int)position, data.Length - (int)position); + _bufferStream.Position = 0; } IsRecording = true; } public override bool CanRead => true; - public override bool CanSeek => stream.CanSeek; + public override bool CanSeek => _stream.CanSeek; public override bool CanWrite => false; - public override void Flush() => throw new NotSupportedException(); + public override void Flush() { } - public override long Length => stream.Length; + public override long Length => _stream.Length; public override long Position { - get => stream.Position + bufferStream.Position - bufferStream.Length; + get => _stream.Position + _bufferStream.Position - _bufferStream.Length; set { - if (!isRewound) + if (!_isRewound) { - stream.Position = value; + _stream.Position = value; } - else if (value < stream.Position - bufferStream.Length || value >= stream.Position) + else if (value < _stream.Position - _bufferStream.Length || value >= _stream.Position) { - stream.Position = value; - isRewound = false; - bufferStream.SetLength(0); + _stream.Position = value; + _isRewound = false; + _bufferStream.SetLength(0); } else { - bufferStream.Position = value - stream.Position + bufferStream.Length; + _bufferStream.Position = value - _stream.Position + _bufferStream.Length; } } } @@ -110,32 +110,32 @@ public class RewindableStream : Stream return 0; } int read; - if (isRewound && bufferStream.Position != bufferStream.Length) + if (_isRewound && _bufferStream.Position != _bufferStream.Length) { // don't read more than left - var readCount = Math.Min(count, (int)(bufferStream.Length - bufferStream.Position)); - read = bufferStream.Read(buffer, offset, readCount); + var readCount = Math.Min(count, (int)(_bufferStream.Length - _bufferStream.Position)); + read = _bufferStream.Read(buffer, offset, readCount); if (read < readCount) { - var tempRead = stream.Read(buffer, offset + read, count - read); + var tempRead = _stream.Read(buffer, offset + read, count - read); if (IsRecording) { - bufferStream.Write(buffer, offset + read, tempRead); + _bufferStream.Write(buffer, offset + read, tempRead); } read += tempRead; } - if (bufferStream.Position == bufferStream.Length && !IsRecording) + if (_bufferStream.Position == _bufferStream.Length && !IsRecording) { - isRewound = false; - bufferStream.SetLength(0); + _isRewound = false; + _bufferStream.SetLength(0); } return read; } - read = stream.Read(buffer, offset, count); + read = _stream.Read(buffer, offset, count); if (IsRecording) { - bufferStream.Write(buffer, offset, read); + _bufferStream.Write(buffer, offset, read); } return read; } diff --git a/src/SharpCompress/IO/SourceStream.cs b/src/SharpCompress/IO/SourceStream.cs index 26ce0af1..46c0a3f6 100644 --- a/src/SharpCompress/IO/SourceStream.cs +++ b/src/SharpCompress/IO/SourceStream.cs @@ -11,8 +11,8 @@ public class SourceStream : Stream private long _prevSize; private readonly List _files; private readonly List _streams; - private readonly Func _getFilePart; - private readonly Func _getStreamPart; + private readonly Func? _getFilePart; + private readonly Func? _getStreamPart; private int _stream; public SourceStream(FileInfo file, Func getPart, ReaderOptions options) @@ -38,8 +38,8 @@ public class SourceStream : Stream if (!IsFileMode) { _streams.Add(stream!); - _getStreamPart = getStreamPart!; - _getFilePart = _ => null!; + _getStreamPart = getStreamPart; + _getFilePart = _ => null; if (stream is FileStream fileStream) { _files.Add(new FileInfo(fileStream.Name)); @@ -49,8 +49,8 @@ public class SourceStream : Stream { _files.Add(file!); _streams.Add(_files[0].OpenRead()); - _getFilePart = getFilePart!; - _getStreamPart = _ => null!; + _getFilePart = getFilePart; + _getStreamPart = _ => null; } _stream = 0; _prevSize = 0; @@ -78,7 +78,7 @@ public class SourceStream : Stream { if (IsFileMode) { - var f = _getFilePart(_streams.Count); + var f = _getFilePart.NotNull("GetFilePart is null")(_streams.Count); if (f == null) { _stream = _streams.Count - 1; @@ -90,7 +90,7 @@ public class SourceStream : Stream } else { - var s = _getStreamPart(_streams.Count); + var s = _getStreamPart.NotNull("GetStreamPart is null")(_streams.Count); if (s == null) { _stream = _streams.Count - 1; diff --git a/src/SharpCompress/Lazy.cs b/src/SharpCompress/Lazy.cs deleted file mode 100644 index 7a6abd55..00000000 --- a/src/SharpCompress/Lazy.cs +++ /dev/null @@ -1,27 +0,0 @@ -#nullable disable - -using System; - -namespace SharpCompress; - -public class Lazy -{ - private readonly Func _lazyFunc; - private bool _evaluated; - private T _value; - - public Lazy(Func lazyFunc) => _lazyFunc = lazyFunc; - - public T Value - { - get - { - if (!_evaluated) - { - _value = _lazyFunc(); - _evaluated = true; - } - return _value; - } - } -} diff --git a/src/SharpCompress/LazyReadOnlyCollection.cs b/src/SharpCompress/LazyReadOnlyCollection.cs index 8f5ee834..cc9cb3fd 100644 --- a/src/SharpCompress/LazyReadOnlyCollection.cs +++ b/src/SharpCompress/LazyReadOnlyCollection.cs @@ -8,7 +8,7 @@ namespace SharpCompress; internal sealed class LazyReadOnlyCollection : ICollection { - private readonly List backing = new List(); + private readonly List backing = new(); private readonly IEnumerator source; private bool fullyLoaded; diff --git a/src/SharpCompress/NotNullExtensions.cs b/src/SharpCompress/NotNullExtensions.cs new file mode 100644 index 00000000..88e95c41 --- /dev/null +++ b/src/SharpCompress/NotNullExtensions.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace SharpCompress; + +public static class NotNullExtensions +{ + public static IEnumerable Empty(this IEnumerable? source) => + source ?? Enumerable.Empty(); + + public static IEnumerable Empty(this T? source) + { + if (source is null) + { + return Enumerable.Empty(); + } + return source.AsEnumerable(); + } + +#if NETFRAMEWORK || NETSTANDARD + public static T NotNull(this T? obj, string? message = null) + where T : class + { + if (obj is null) + { + throw new ArgumentNullException(message ?? "Value is null"); + } + return obj; + } + + public static T NotNull(this T? obj, string? message = null) + where T : struct + { + if (obj is null) + { + throw new ArgumentNullException(message ?? "Value is null"); + } + return obj.Value; + } +#else + + public static T NotNull( + [NotNull] this T? obj, + [CallerArgumentExpression(nameof(obj))] string? paramName = null + ) + where T : class + { + ArgumentNullException.ThrowIfNull(obj, paramName); + return obj; + } + + public static T NotNull( + [NotNull] this T? obj, + [CallerArgumentExpression(nameof(obj))] string? paramName = null + ) + where T : struct + { + ArgumentNullException.ThrowIfNull(obj, paramName); + return obj.Value; + } +#endif +} diff --git a/src/SharpCompress/Readers/AbstractReader.cs b/src/SharpCompress/Readers/AbstractReader.cs index 7927937f..b36acd53 100644 --- a/src/SharpCompress/Readers/AbstractReader.cs +++ b/src/SharpCompress/Readers/AbstractReader.cs @@ -13,9 +13,9 @@ public abstract class AbstractReader : IReader, IReaderExtracti where TEntry : Entry where TVolume : Volume { - private bool completed; - private IEnumerator? entriesForCurrentReadStream; - private bool wroteCurrentEntry; + private bool _completed; + private IEnumerator? _entriesForCurrentReadStream; + private bool _wroteCurrentEntry; public event EventHandler>? EntryExtractionProgress; @@ -35,18 +35,18 @@ public abstract class AbstractReader : IReader, IReaderExtracti /// /// Current volume that the current entry resides in /// - public abstract TVolume Volume { get; } + public abstract TVolume? Volume { get; } /// /// Current file entry /// - public TEntry Entry => entriesForCurrentReadStream!.Current; + public TEntry Entry => _entriesForCurrentReadStream.NotNull().Current; #region IDisposable Members public void Dispose() { - entriesForCurrentReadStream?.Dispose(); + _entriesForCurrentReadStream?.Dispose(); Volume?.Dispose(); } @@ -61,7 +61,7 @@ public abstract class AbstractReader : IReader, IReaderExtracti /// public void Cancel() { - if (!completed) + if (!_completed) { Cancelled = true; } @@ -69,35 +69,35 @@ public abstract class AbstractReader : IReader, IReaderExtracti public bool MoveToNextEntry() { - if (completed) + if (_completed) { return false; } if (Cancelled) { - throw new InvalidOperationException("Reader has been cancelled."); + throw new ReaderCancelledException("Reader has been cancelled."); } - if (entriesForCurrentReadStream is null) + if (_entriesForCurrentReadStream is null) { return LoadStreamForReading(RequestInitialStream()); } - if (!wroteCurrentEntry) + if (!_wroteCurrentEntry) { SkipEntry(); } - wroteCurrentEntry = false; + _wroteCurrentEntry = false; if (NextEntryForCurrentStream()) { return true; } - completed = true; + _completed = true; return false; } protected bool LoadStreamForReading(Stream stream) { - entriesForCurrentReadStream?.Dispose(); - if ((stream is null) || (!stream.CanRead)) + _entriesForCurrentReadStream?.Dispose(); + if (stream is null || !stream.CanRead) { throw new MultipartStreamRequiredException( "File is split into multiple archives: '" @@ -105,13 +105,15 @@ public abstract class AbstractReader : IReader, IReaderExtracti + "'. A new readable stream is required. Use Cancel if it was intended." ); } - entriesForCurrentReadStream = GetEntries(stream).GetEnumerator(); - return entriesForCurrentReadStream.MoveNext(); + _entriesForCurrentReadStream = GetEntries(stream).GetEnumerator(); + return _entriesForCurrentReadStream.MoveNext(); } - protected virtual Stream RequestInitialStream() => Volume.Stream; + protected virtual Stream RequestInitialStream() => + Volume.NotNull("Volume isn't loaded.").Stream; - internal virtual bool NextEntryForCurrentStream() => entriesForCurrentReadStream!.MoveNext(); + internal virtual bool NextEntryForCurrentStream() => + _entriesForCurrentReadStream.NotNull().MoveNext(); protected abstract IEnumerable GetEntries(Stream stream); @@ -149,7 +151,7 @@ public abstract class AbstractReader : IReader, IReaderExtracti public void WriteEntryTo(Stream writableStream) { - if (wroteCurrentEntry) + if (_wroteCurrentEntry) { throw new ArgumentException("WriteEntryTo or OpenEntryStream can only be called once."); } @@ -166,7 +168,7 @@ public abstract class AbstractReader : IReader, IReaderExtracti } Write(writableStream); - wroteCurrentEntry = true; + _wroteCurrentEntry = true; } internal void Write(Stream writeStream) @@ -178,20 +180,19 @@ public abstract class AbstractReader : IReader, IReaderExtracti public EntryStream OpenEntryStream() { - if (wroteCurrentEntry) + if (_wroteCurrentEntry) { throw new ArgumentException("WriteEntryTo or OpenEntryStream can only be called once."); } var stream = GetEntryStream(); - wroteCurrentEntry = true; + _wroteCurrentEntry = true; return stream; } /// /// Retains a reference to the entry stream, so we can check whether it completed later. /// - protected EntryStream CreateEntryStream(Stream decompressed) => - new EntryStream(this, decompressed); + protected EntryStream CreateEntryStream(Stream decompressed) => new(this, decompressed); protected virtual EntryStream GetEntryStream() => CreateEntryStream(Entry.Parts.First().GetCompressedStream()); diff --git a/src/SharpCompress/Readers/GZip/GZipReader.cs b/src/SharpCompress/Readers/GZip/GZipReader.cs index 27394cc7..73bc4a9d 100644 --- a/src/SharpCompress/Readers/GZip/GZipReader.cs +++ b/src/SharpCompress/Readers/GZip/GZipReader.cs @@ -7,8 +7,8 @@ namespace SharpCompress.Readers.GZip; public class GZipReader : AbstractReader { - internal GZipReader(Stream stream, ReaderOptions options) - : base(options, ArchiveType.GZip) => Volume = new GZipVolume(stream, options); + private GZipReader(Stream stream, ReaderOptions options) + : base(options, ArchiveType.GZip) => Volume = new GZipVolume(stream, options, 0); public override GZipVolume Volume { get; } diff --git a/src/SharpCompress/Readers/Rar/MultiVolumeRarReader.cs b/src/SharpCompress/Readers/Rar/MultiVolumeRarReader.cs index 76899e0c..556ca9f5 100644 --- a/src/SharpCompress/Readers/Rar/MultiVolumeRarReader.cs +++ b/src/SharpCompress/Readers/Rar/MultiVolumeRarReader.cs @@ -17,7 +17,7 @@ internal class MultiVolumeRarReader : RarReader internal MultiVolumeRarReader(IEnumerable streams, ReaderOptions options) : base(options) => this.streams = streams.GetEnumerator(); - internal override void ValidateArchive(RarVolume archive) { } + protected override void ValidateArchive(RarVolume archive) { } protected override Stream RequestInitialStream() { diff --git a/src/SharpCompress/Readers/Rar/RarReader.cs b/src/SharpCompress/Readers/Rar/RarReader.cs index da8c132b..2398cfac 100644 --- a/src/SharpCompress/Readers/Rar/RarReader.cs +++ b/src/SharpCompress/Readers/Rar/RarReader.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -13,17 +14,16 @@ namespace SharpCompress.Readers.Rar; public abstract class RarReader : AbstractReader { private RarVolume? volume; - internal Lazy UnpackV2017 { get; } = - new Lazy(() => new Compressors.Rar.UnpackV2017.Unpack()); - internal Lazy UnpackV1 { get; } = - new Lazy(() => new Compressors.Rar.UnpackV1.Unpack()); + private Lazy UnpackV2017 { get; } = + new(() => new Compressors.Rar.UnpackV2017.Unpack()); + private Lazy UnpackV1 { get; } = new(() => new Compressors.Rar.UnpackV1.Unpack()); internal RarReader(ReaderOptions options) : base(options, ArchiveType.Rar) { } - internal abstract void ValidateArchive(RarVolume archive); + protected abstract void ValidateArchive(RarVolume archive); - public override RarVolume Volume => volume!; + public override RarVolume? Volume => volume; /// /// Opens a RarReader for Non-seeking usage with a single volume @@ -51,7 +51,7 @@ public abstract class RarReader : AbstractReader protected override IEnumerable GetEntries(Stream stream) { - volume = new RarReaderVolume(stream, Options); + volume = new RarReaderVolume(stream, Options, 0); foreach (var fp in volume.ReadFileParts()) { ValidateArchive(volume); @@ -64,6 +64,11 @@ public abstract class RarReader : AbstractReader protected override EntryStream GetEntryStream() { + if (Entry.IsRedir) + { + throw new InvalidOperationException("no stream for redirect entry"); + } + var stream = new MultiVolumeReadOnlyStream( CreateFilePartEnumerableForCurrentEntry().Cast(), this @@ -72,6 +77,14 @@ public abstract class RarReader : AbstractReader { return CreateEntryStream(new RarCrcStream(UnpackV1.Value, Entry.FileHeader, stream)); } + + if (Entry.FileHeader.FileCrc.Length > 5) + { + return CreateEntryStream( + new RarBLAKE2spStream(UnpackV2017.Value, Entry.FileHeader, stream) + ); + } + return CreateEntryStream(new RarCrcStream(UnpackV2017.Value, Entry.FileHeader, stream)); } } diff --git a/src/SharpCompress/Readers/Rar/RarReaderVolume.cs b/src/SharpCompress/Readers/Rar/RarReaderVolume.cs index 85beb897..d614260a 100644 --- a/src/SharpCompress/Readers/Rar/RarReaderVolume.cs +++ b/src/SharpCompress/Readers/Rar/RarReaderVolume.cs @@ -8,7 +8,7 @@ namespace SharpCompress.Readers.Rar; public class RarReaderVolume : RarVolume { - internal RarReaderVolume(Stream stream, ReaderOptions options, int index = 0) + internal RarReaderVolume(Stream stream, ReaderOptions options, int index) : base(StreamingMode.Streaming, stream, options, index) { } internal override RarFilePart CreateFilePart(MarkHeader markHeader, FileHeader fileHeader) => diff --git a/src/SharpCompress/Readers/Rar/SingleVolumeRarReader.cs b/src/SharpCompress/Readers/Rar/SingleVolumeRarReader.cs index 768aa115..165c92f7 100644 --- a/src/SharpCompress/Readers/Rar/SingleVolumeRarReader.cs +++ b/src/SharpCompress/Readers/Rar/SingleVolumeRarReader.cs @@ -11,7 +11,7 @@ internal class SingleVolumeRarReader : RarReader internal SingleVolumeRarReader(Stream stream, ReaderOptions options) : base(options) => this.stream = stream; - internal override void ValidateArchive(RarVolume archive) + protected override void ValidateArchive(RarVolume archive) { if (archive.IsMultiVolume) { diff --git a/src/SharpCompress/Readers/Tar/TarReader.cs b/src/SharpCompress/Readers/Tar/TarReader.cs index c9802e46..a686dc11 100644 --- a/src/SharpCompress/Readers/Tar/TarReader.cs +++ b/src/SharpCompress/Readers/Tar/TarReader.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using SharpCompress.Archives.GZip; @@ -8,9 +8,10 @@ using SharpCompress.Common.Tar; using SharpCompress.Compressors; using SharpCompress.Compressors.BZip2; using SharpCompress.Compressors.Deflate; -using SharpCompress.IO; using SharpCompress.Compressors.LZMA; +using SharpCompress.Compressors.Lzw; using SharpCompress.Compressors.Xz; +using SharpCompress.IO; namespace SharpCompress.Readers.Tar; @@ -30,33 +31,16 @@ public class TarReader : AbstractReader protected override Stream RequestInitialStream() { var stream = base.RequestInitialStream(); - switch (compressionType) + return compressionType switch { - case CompressionType.BZip2: - { - return new BZip2Stream(stream, CompressionMode.Decompress, false); - } - case CompressionType.GZip: - { - return new GZipStream(stream, CompressionMode.Decompress); - } - case CompressionType.LZip: - { - return new LZipStream(stream, CompressionMode.Decompress); - } - case CompressionType.Xz: - { - return new XZStream(stream); - } - case CompressionType.None: - { - return stream; - } - default: - { - throw new NotSupportedException("Invalid compression type: " + compressionType); - } - } + CompressionType.BZip2 => new BZip2Stream(stream, CompressionMode.Decompress, false), + CompressionType.GZip => new GZipStream(stream, CompressionMode.Decompress), + CompressionType.LZip => new LZipStream(stream, CompressionMode.Decompress), + CompressionType.Xz => new XZStream(stream), + CompressionType.Lzw => new LzwStream(stream), + CompressionType.None => stream, + _ => throw new NotSupportedException("Invalid compression type: " + compressionType) + }; } #region Open @@ -71,12 +55,12 @@ public class TarReader : AbstractReader { stream.CheckNotNull(nameof(stream)); options = options ?? new ReaderOptions(); - RewindableStream rewindableStream = new RewindableStream(stream); + var rewindableStream = new RewindableStream(stream); rewindableStream.StartRecording(); if (GZipArchive.IsGZipFile(rewindableStream)) { rewindableStream.Rewind(false); - GZipStream testStream = new GZipStream(rewindableStream, CompressionMode.Decompress); + var testStream = new GZipStream(rewindableStream, CompressionMode.Decompress); if (TarArchive.IsTarFile(testStream)) { rewindableStream.Rewind(true); @@ -89,11 +73,7 @@ public class TarReader : AbstractReader if (BZip2Stream.IsBZip2(rewindableStream)) { rewindableStream.Rewind(false); - BZip2Stream testStream = new BZip2Stream( - rewindableStream, - CompressionMode.Decompress, - false - ); + var testStream = new BZip2Stream(rewindableStream, CompressionMode.Decompress, false); if (TarArchive.IsTarFile(testStream)) { rewindableStream.Rewind(true); @@ -106,7 +86,7 @@ public class TarReader : AbstractReader if (LZipStream.IsLZipFile(rewindableStream)) { rewindableStream.Rewind(false); - LZipStream testStream = new LZipStream(rewindableStream, CompressionMode.Decompress); + var testStream = new LZipStream(rewindableStream, CompressionMode.Decompress); if (TarArchive.IsTarFile(testStream)) { rewindableStream.Rewind(true); diff --git a/src/SharpCompress/Readers/Zip/ZipReader.cs b/src/SharpCompress/Readers/Zip/ZipReader.cs index b00690f0..cda3f3f8 100644 --- a/src/SharpCompress/Readers/Zip/ZipReader.cs +++ b/src/SharpCompress/Readers/Zip/ZipReader.cs @@ -69,7 +69,6 @@ public class ZipReader : AbstractReader switch (h.ZipHeaderType) { case ZipHeaderType.LocalEntry: - { yield return new ZipEntry( new StreamingZipFilePart((LocalEntryHeader)h, stream) diff --git a/src/SharpCompress/SharpCompress.csproj b/src/SharpCompress/SharpCompress.csproj index efdd0a47..cbceba12 100644 --- a/src/SharpCompress/SharpCompress.csproj +++ b/src/SharpCompress/SharpCompress.csproj @@ -2,14 +2,14 @@ SharpCompress - Pure C# Decompression/Compression en-US - 0.34.1 - 0.34.1 - 0.34.1 + 0.37.2 + 0.37.2 + 0.37.2 Adam Hathcock - net462;netstandard2.0;netstandard2.1;net6.0;net7.0 + net462;netstandard2.0;netstandard2.1;net6.0;net8.0 SharpCompress ../../SharpCompress.snk - False + true SharpCompress rar;unrar;zip;unzip;bzip2;gzip;tar;7zip;lzip;xz https://github.com/adamhathcock/sharpcompress @@ -17,28 +17,39 @@ Copyright (c) 2014 Adam Hathcock false false - SharpCompress is a compression library for NET Standard 2.0/2.1/NET 6.0/NET 7.0 that can unrar, decompress 7zip, decompress xz, zip/unzip, tar/untar lzip/unlzip, bzip2/unbzip2 and gzip/ungzip with forward-only reading and file random access APIs. Write support for zip/tar/bzip2/gzip is implemented. + SharpCompress is a compression library for NET Standard 2.0/NET Standard 2.1/NET 6.0/NET 8.0 that can unrar, decompress 7zip, decompress xz, zip/unzip, tar/untar lzip/unlzip, bzip2/unbzip2 and gzip/ungzip with forward-only reading and file random access APIs. Write support for zip/tar/bzip2/gzip is implemented. true true snupkg - true latest - True + true + README.md - - - - + + true + + + + all + + + - - - - - + + + + + + + - - + + + + + + diff --git a/src/SharpCompress/Utility.cs b/src/SharpCompress/Utility.cs index 9471c3e1..bcecafb4 100644 --- a/src/SharpCompress/Utility.cs +++ b/src/SharpCompress/Utility.cs @@ -9,8 +9,7 @@ namespace SharpCompress; [CLSCompliant(false)] public static class Utility { - public static ReadOnlyCollection ToReadOnly(this ICollection items) => - new ReadOnlyCollection(items); + public static ReadOnlyCollection ToReadOnly(this ICollection items) => new(items); /// /// Performs an unsigned bitwise right shift with the specified number @@ -278,8 +277,42 @@ public static class Utility long total = 0; while (ReadTransferBlock(source, array, out var count)) { - total += count; destination.Write(array, 0, count); + total += count; + } + return total; + } + finally + { + ArrayPool.Shared.Return(array); + } + } + + public static long TransferTo(this Stream source, Stream destination, long maxLength) + { + var array = GetTransferByteArray(); + var maxReadSize = array.Length; + try + { + long total = 0; + var remaining = maxLength; + if (remaining < maxReadSize) + { + maxReadSize = (int)remaining; + } + while (ReadTransferBlock(source, array, maxReadSize, out var count)) + { + destination.Write(array, 0, count); + total += count; + if (remaining - count < 0) + { + break; + } + remaining -= count; + if (remaining < maxReadSize) + { + maxReadSize = (int)remaining; + } } return total; } @@ -319,6 +352,16 @@ public static class Utility private static bool ReadTransferBlock(Stream source, byte[] array, out int count) => (count = source.Read(array, 0, array.Length)) != 0; + private static bool ReadTransferBlock(Stream source, byte[] array, int size, out int count) + { + if (size > array.Length) + { + size = array.Length; + } + count = source.Read(array, 0, size); + return count != 0; + } + private static byte[] GetTransferByteArray() => ArrayPool.Shared.Rent(81920); public static bool ReadFully(this Stream stream, byte[] buffer) diff --git a/src/SharpCompress/Writers/AbstractWriter.cs b/src/SharpCompress/Writers/AbstractWriter.cs index 209c49f7..1820c986 100644 --- a/src/SharpCompress/Writers/AbstractWriter.cs +++ b/src/SharpCompress/Writers/AbstractWriter.cs @@ -1,28 +1,24 @@ -#nullable disable - -using System; +using System; using System.IO; using SharpCompress.Common; namespace SharpCompress.Writers; -public abstract class AbstractWriter : IWriter +#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. +public abstract class AbstractWriter(ArchiveType type, WriterOptions writerOptions) : IWriter { private bool _isDisposed; - protected AbstractWriter(ArchiveType type, WriterOptions writerOptions) - { - WriterType = type; - WriterOptions = writerOptions; - } + //always initializes the stream - protected void InitalizeStream(Stream stream) => OutputStream = stream; + protected void InitializeStream(Stream stream) => OutputStream = stream; protected Stream OutputStream { get; private set; } +#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - public ArchiveType WriterType { get; } + public ArchiveType WriterType { get; } = type; - protected WriterOptions WriterOptions { get; } + protected WriterOptions WriterOptions { get; } = writerOptions; public abstract void Write(string filename, Stream source, DateTime? modificationTime); diff --git a/src/SharpCompress/Writers/GZip/GZipWriter.cs b/src/SharpCompress/Writers/GZip/GZipWriter.cs index 5c990a7d..f30fdb55 100644 --- a/src/SharpCompress/Writers/GZip/GZipWriter.cs +++ b/src/SharpCompress/Writers/GZip/GZipWriter.cs @@ -18,7 +18,7 @@ public sealed class GZipWriter : AbstractWriter { destination = NonDisposingStream.Create(destination); } - InitalizeStream( + InitializeStream( new GZipStream( destination, CompressionMode.Compress, diff --git a/src/SharpCompress/Writers/IWriterFactory.cs b/src/SharpCompress/Writers/IWriterFactory.cs index 8e7bc701..094a5553 100644 --- a/src/SharpCompress/Writers/IWriterFactory.cs +++ b/src/SharpCompress/Writers/IWriterFactory.cs @@ -1,5 +1,4 @@ using System.IO; - using SharpCompress.Factories; namespace SharpCompress.Writers; diff --git a/src/SharpCompress/Writers/Tar/TarWriter.cs b/src/SharpCompress/Writers/Tar/TarWriter.cs index 2427db37..a3e572f1 100644 --- a/src/SharpCompress/Writers/Tar/TarWriter.cs +++ b/src/SharpCompress/Writers/Tar/TarWriter.cs @@ -32,19 +32,16 @@ public class TarWriter : AbstractWriter case CompressionType.None: break; case CompressionType.BZip2: - { destination = new BZip2Stream(destination, CompressionMode.Compress, false); } break; case CompressionType.GZip: - { destination = new GZipStream(destination, CompressionMode.Compress); } break; case CompressionType.LZip: - { destination = new LZipStream(destination, CompressionMode.Compress); } @@ -56,7 +53,7 @@ public class TarWriter : AbstractWriter ); } } - InitalizeStream(destination); + InitializeStream(destination); } public override void Write(string filename, Stream source, DateTime? modificationTime) => @@ -90,8 +87,7 @@ public class TarWriter : AbstractWriter header.Name = NormalizeFilename(filename); header.Size = realSize; header.Write(OutputStream); - - size = source.TransferTo(OutputStream); + size = source.TransferTo(OutputStream, realSize); PadTo512(size.Value); } diff --git a/src/SharpCompress/Writers/WriterFactory.cs b/src/SharpCompress/Writers/WriterFactory.cs index fcbf2d68..50e1fdfc 100644 --- a/src/SharpCompress/Writers/WriterFactory.cs +++ b/src/SharpCompress/Writers/WriterFactory.cs @@ -1,7 +1,6 @@ using System; using System.IO; using System.Linq; - using SharpCompress.Common; namespace SharpCompress.Writers; @@ -10,8 +9,8 @@ public static class WriterFactory { public static IWriter Open(Stream stream, ArchiveType archiveType, WriterOptions writerOptions) { - var factory = Factories.Factory.Factories - .OfType() + var factory = Factories + .Factory.Factories.OfType() .FirstOrDefault(item => item.KnownArchiveType == archiveType); if (factory != null) diff --git a/src/SharpCompress/Writers/WriterOptions.cs b/src/SharpCompress/Writers/WriterOptions.cs index 9d323c2b..e71f86f6 100644 --- a/src/SharpCompress/Writers/WriterOptions.cs +++ b/src/SharpCompress/Writers/WriterOptions.cs @@ -9,5 +9,5 @@ public class WriterOptions : OptionsBase public CompressionType CompressionType { get; set; } public static implicit operator WriterOptions(CompressionType compressionType) => - new WriterOptions(compressionType); + new(compressionType); } diff --git a/src/SharpCompress/Writers/Zip/ZipWriter.cs b/src/SharpCompress/Writers/Zip/ZipWriter.cs index e1fd222c..1fb66a2b 100644 --- a/src/SharpCompress/Writers/Zip/ZipWriter.cs +++ b/src/SharpCompress/Writers/Zip/ZipWriter.cs @@ -19,7 +19,7 @@ public class ZipWriter : AbstractWriter { private readonly CompressionType compressionType; private readonly CompressionLevel compressionLevel; - private readonly List entries = new List(); + private readonly List entries = new(); private readonly string zipComment; private long streamPosition; private PpmdProperties? ppmdProps; @@ -42,14 +42,14 @@ public class ZipWriter : AbstractWriter { destination = NonDisposingStream.Create(destination); } - InitalizeStream(destination); + InitializeStream(destination); } private PpmdProperties PpmdProperties => ppmdProps ??= new PpmdProperties(); protected override void Dispose(bool isDisposing) { - if (isDisposing) + if (isDisposing && OutputStream is not null) { ulong size = 0; foreach (var entry in entries) @@ -61,34 +61,16 @@ public class ZipWriter : AbstractWriter base.Dispose(isDisposing); } - private static ZipCompressionMethod ToZipCompressionMethod(CompressionType compressionType) - { - switch (compressionType) + private static ZipCompressionMethod ToZipCompressionMethod(CompressionType compressionType) => + compressionType switch { - case CompressionType.None: - { - return ZipCompressionMethod.None; - } - case CompressionType.Deflate: - { - return ZipCompressionMethod.Deflate; - } - case CompressionType.BZip2: - { - return ZipCompressionMethod.BZip2; - } - case CompressionType.LZMA: - { - return ZipCompressionMethod.LZMA; - } - case CompressionType.PPMd: - { - return ZipCompressionMethod.PPMd; - } - default: - throw new InvalidFormatException("Invalid compression method: " + compressionType); - } - } + CompressionType.None => ZipCompressionMethod.None, + CompressionType.Deflate => ZipCompressionMethod.Deflate, + CompressionType.BZip2 => ZipCompressionMethod.BZip2, + CompressionType.LZMA => ZipCompressionMethod.LZMA, + CompressionType.PPMd => ZipCompressionMethod.PPMd, + _ => throw new InvalidFormatException("Invalid compression method: " + compressionType) + }; public override void Write(string entryPath, Stream source, DateTime? modificationTime) => Write( @@ -132,7 +114,7 @@ public class ZipWriter : AbstractWriter streamPosition += headersize; return new ZipWritingStream( this, - OutputStream, + OutputStream.NotNull(), entry, compression, options.DeflateCompressionLevel ?? compressionLevel @@ -304,7 +286,10 @@ public class ZipWriter : AbstractWriter // Write normal end of central directory record OutputStream.Write(stackalloc byte[] { 80, 75, 5, 6, 0, 0, 0, 0 }); - BinaryPrimitives.WriteUInt16LittleEndian(intBuf, (ushort)entries.Count); + BinaryPrimitives.WriteUInt16LittleEndian( + intBuf, + (ushort)(entries.Count < 0xFFFF ? entries.Count : 0xFFFF) + ); OutputStream.Write(intBuf.Slice(0, 2)); OutputStream.Write(intBuf.Slice(0, 2)); BinaryPrimitives.WriteUInt32LittleEndian(intBuf, sizevalue); @@ -321,7 +306,7 @@ public class ZipWriter : AbstractWriter internal class ZipWritingStream : Stream { - private readonly CRC32 crc = new CRC32(); + private readonly CRC32 crc = new(); private readonly ZipCentralDirectoryEntry entry; private readonly Stream originalStream; private readonly Stream writeStream; @@ -434,13 +419,14 @@ public class ZipWriter : AbstractWriter return; } + var countingCount = counting?.Count ?? 0; entry.Crc = (uint)crc.Crc32Result; - entry.Compressed = counting!.Count; + entry.Compressed = countingCount; entry.Decompressed = decompressed; var zip64 = entry.Compressed >= uint.MaxValue || entry.Decompressed >= uint.MaxValue; - var compressedvalue = zip64 ? uint.MaxValue : (uint)counting.Count; + var compressedvalue = zip64 ? uint.MaxValue : (uint)countingCount; var decompressedvalue = zip64 ? uint.MaxValue : (uint)entry.Decompressed; if (originalStream.CanSeek) @@ -448,7 +434,7 @@ public class ZipWriter : AbstractWriter originalStream.Position = (long)(entry.HeaderOffset + 6); originalStream.WriteByte(0); - if (counting.Count == 0 && entry.Decompressed == 0) + if (countingCount == 0 && entry.Decompressed == 0) { // set compression to STORED for zero byte files (no compression data) originalStream.Position = (long)(entry.HeaderOffset + 8); @@ -535,11 +521,12 @@ public class ZipWriter : AbstractWriter // if we can prevent the writes from happening if (entry.Zip64HeaderOffset == 0) { + var countingCount = counting?.Count ?? 0; // Pre-check, the counting.Count is not exact, as we do not know the size before having actually compressed it if ( limitsExceeded || ((decompressed + (uint)count) > uint.MaxValue) - || (counting!.Count + (uint)count) > uint.MaxValue + || (countingCount + (uint)count) > uint.MaxValue ) { throw new NotSupportedException( @@ -554,8 +541,9 @@ public class ZipWriter : AbstractWriter if (entry.Zip64HeaderOffset == 0) { + var countingCount = counting?.Count ?? 0; // Post-check, this is accurate - if ((decompressed > uint.MaxValue) || counting!.Count > uint.MaxValue) + if ((decompressed > uint.MaxValue) || countingCount > uint.MaxValue) { // We have written the data, so the archive is now broken // Throwing the exception here, allows us to avoid diff --git a/src/SharpCompress/packages.lock.json b/src/SharpCompress/packages.lock.json new file mode 100644 index 00000000..b06d9c98 --- /dev/null +++ b/src/SharpCompress/packages.lock.json @@ -0,0 +1,338 @@ +{ + "version": 2, + "dependencies": { + ".NETFramework,Version=v4.6.2": { + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net462": "1.0.3" + } + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "System.Memory": { + "type": "Direct", + "requested": "[4.5.5, )", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "System.Text.Encoding.CodePages": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "OZIsVplFGaVY90G2SbpgU7EnCoOO5pw1t4ic21dBF3/1omrJFpAGoNAVpPyMVOC90/hvgkGG3VFqR13YgZMQfg==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "ZstdSharp.Port": { + "type": "Direct", + "requested": "[0.8.0, )", + "resolved": "0.8.0", + "contentHash": "Z62eNBIu8E8YtbqlMy57tK3dV1+m2b9NhPeaYovB5exmLKvrGCqOhJTzrEUH5VyUWU6vwX3c1XHJGhW5HVs8dA==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "System.Memory": "4.5.5" + } + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.NETFramework.ReferenceAssemblies.net462": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "IzAV30z22ESCeQfxP29oVf4qEo8fBGXLXSU6oacv/9Iqe6PzgHDKCaWfwMBak7bSJQM0F5boXWoZS+kChztRIQ==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.Numerics.Vectors": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + }, + "System.Threading.Tasks.Extensions": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + } + }, + ".NETStandard,Version=v2.0": { + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "NETStandard.Library": { + "type": "Direct", + "requested": "[2.0.3, )", + "resolved": "2.0.3", + "contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0" + } + }, + "System.Memory": { + "type": "Direct", + "requested": "[4.5.5, )", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.4.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "System.Text.Encoding.CodePages": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "OZIsVplFGaVY90G2SbpgU7EnCoOO5pw1t4ic21dBF3/1omrJFpAGoNAVpPyMVOC90/hvgkGG3VFqR13YgZMQfg==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "ZstdSharp.Port": { + "type": "Direct", + "requested": "[0.8.0, )", + "resolved": "0.8.0", + "contentHash": "Z62eNBIu8E8YtbqlMy57tK3dV1+m2b9NhPeaYovB5exmLKvrGCqOhJTzrEUH5VyUWU6vwX3c1XHJGhW5HVs8dA==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "System.Memory": "4.5.5" + } + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.NETCore.Platforms": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.Numerics.Vectors": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==" + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + }, + "System.Threading.Tasks.Extensions": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + } + }, + ".NETStandard,Version=v2.1": { + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==" + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "System.Text.Encoding.CodePages": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "OZIsVplFGaVY90G2SbpgU7EnCoOO5pw1t4ic21dBF3/1omrJFpAGoNAVpPyMVOC90/hvgkGG3VFqR13YgZMQfg==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "ZstdSharp.Port": { + "type": "Direct", + "requested": "[0.8.0, )", + "resolved": "0.8.0", + "contentHash": "Z62eNBIu8E8YtbqlMy57tK3dV1+m2b9NhPeaYovB5exmLKvrGCqOhJTzrEUH5VyUWU6vwX3c1XHJGhW5HVs8dA==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.Numerics.Vectors": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==" + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + }, + "System.Memory": { + "type": "CentralTransitive", + "requested": "[4.5.5, )", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.4.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + } + }, + "net6.0": { + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "ZstdSharp.Port": { + "type": "Direct", + "requested": "[0.8.0, )", + "resolved": "0.8.0", + "contentHash": "Z62eNBIu8E8YtbqlMy57tK3dV1+m2b9NhPeaYovB5exmLKvrGCqOhJTzrEUH5VyUWU6vwX3c1XHJGhW5HVs8dA==" + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + } + }, + "net8.0": { + "Microsoft.NET.ILLink.Tasks": { + "type": "Direct", + "requested": "[8.0.4, )", + "resolved": "8.0.4", + "contentHash": "PZb5nfQ+U19nhnmnR9T1jw+LTmozhuG2eeuzuW5A7DqxD/UXW2ucjmNJqnqOuh8rdPzM3MQXoF8AfFCedJdCUw==" + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "ZstdSharp.Port": { + "type": "Direct", + "requested": "[0.8.0, )", + "resolved": "0.8.0", + "contentHash": "Z62eNBIu8E8YtbqlMy57tK3dV1+m2b9NhPeaYovB5exmLKvrGCqOhJTzrEUH5VyUWU6vwX3c1XHJGhW5HVs8dA==" + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + } + } + } +} \ No newline at end of file diff --git a/tests/SharpCompress.Test/ADCTest.cs b/tests/SharpCompress.Test/ADCTest.cs index 11bb2fb0..8ef40a17 100644 --- a/tests/SharpCompress.Test/ADCTest.cs +++ b/tests/SharpCompress.Test/ADCTest.cs @@ -24,7 +24,6 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. using System.IO; -using SharpCompress.Compressors; using SharpCompress.Compressors.ADC; using SharpCompress.Compressors.Deflate; using SharpCompress.Crypto; @@ -32,7 +31,7 @@ using Xunit; namespace SharpCompress.Test; -public class ADCTest : TestBase +public class AdcTest : TestBase { [Fact] public void TestBuffer() @@ -65,14 +64,14 @@ public class ADCTest : TestBase } [Fact] - public void TestADCStreamWholeChunk() + public void TestAdcStreamWholeChunk() { using var decFs = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "adc_decompressed.bin")); var decompressed = new byte[decFs.Length]; decFs.Read(decompressed, 0, decompressed.Length); using var cmpFs = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "adc_compressed.bin")); - using var decStream = new ADCStream(cmpFs, CompressionMode.Decompress); + using var decStream = new ADCStream(cmpFs); var test = new byte[262144]; decStream.Read(test, 0, test.Length); @@ -81,14 +80,14 @@ public class ADCTest : TestBase } [Fact] - public void TestADCStream() + public void TestAdcStream() { using var decFs = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "adc_decompressed.bin")); var decompressed = new byte[decFs.Length]; decFs.Read(decompressed, 0, decompressed.Length); using var cmpFs = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "adc_compressed.bin")); - using var decStream = new ADCStream(cmpFs, CompressionMode.Decompress); + using var decStream = new ADCStream(cmpFs); using var decMs = new MemoryStream(); var test = new byte[512]; var count = 0; @@ -115,11 +114,11 @@ public class ADCTest : TestBase decFs.Seek(0, SeekOrigin.Begin); - var crc32a = crcStream.Crc; + var crc32A = crcStream.Crc; - var crc32b = Crc32Stream.Compute(memory.ToArray()); + var crc32B = Crc32Stream.Compute(memory.ToArray()); - Assert.Equal(crc32, crc32a); - Assert.Equal(crc32, crc32b); + Assert.Equal(crc32, crc32A); + Assert.Equal(crc32, crc32B); } } diff --git a/tests/SharpCompress.Test/ArchiveTests.cs b/tests/SharpCompress.Test/ArchiveTests.cs index 08377a0e..7d94b29d 100644 --- a/tests/SharpCompress.Test/ArchiveTests.cs +++ b/tests/SharpCompress.Test/ArchiveTests.cs @@ -101,7 +101,7 @@ public class ArchiveTests : ReaderTests { entry.WriteToDirectory( SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } + new ExtractionOptions { ExtractFullPath = true, Overwrite = true } ); } } @@ -138,19 +138,12 @@ public class ArchiveTests : ReaderTests ) ) { - try + foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) { - foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) - { - entry.WriteToDirectory( - SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } - ); - } - } - catch (IndexOutOfRangeException) - { - throw; + entry.WriteToDirectory( + SCRATCH_FILES_PATH, + new ExtractionOptions { ExtractFullPath = true, Overwrite = true } + ); } } VerifyFiles(); @@ -177,19 +170,12 @@ public class ArchiveTests : ReaderTests ) ) { - try + foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) { - foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) - { - entry.WriteToDirectory( - SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } - ); - } - } - catch (IndexOutOfRangeException) - { - throw; + entry.WriteToDirectory( + SCRATCH_FILES_PATH, + new ExtractionOptions { ExtractFullPath = true, Overwrite = true } + ); } } VerifyFiles(); @@ -206,39 +192,29 @@ public class ArchiveTests : ReaderTests testArchives.Select(x => Path.Combine(TEST_ARCHIVES_PATH, x)) ); - protected void ArchiveOpenEntryVolumeIndexTest( + private void ArchiveOpenEntryVolumeIndexTest( int[][] results, ReaderOptions? readerOptions, IEnumerable testArchives ) { var src = testArchives.ToArray(); - using var archive = ArchiveFactory.Open( - testArchives.Select(f => new FileInfo(f)), - readerOptions - ); - try + using var archive = ArchiveFactory.Open(src.Select(f => new FileInfo(f)), readerOptions); + var idx = 0; + foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) { - var idx = 0; - foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) - { - Assert.Equal(entry.VolumeIndexFirst, results[idx][0]); - Assert.Equal(entry.VolumeIndexLast, results[idx][1]); - Assert.Equal( - src[entry.VolumeIndexFirst], - archive.Volumes.First(a => a.Index == entry.VolumeIndexFirst).FileName - ); - Assert.Equal( - src[entry.VolumeIndexLast], - archive.Volumes.First(a => a.Index == entry.VolumeIndexLast).FileName - ); + Assert.Equal(entry.VolumeIndexFirst, results[idx][0]); + Assert.Equal(entry.VolumeIndexLast, results[idx][1]); + Assert.Equal( + src[entry.VolumeIndexFirst], + archive.Volumes.First(a => a.Index == entry.VolumeIndexFirst).FileName + ); + Assert.Equal( + src[entry.VolumeIndexLast], + archive.Volumes.First(a => a.Index == entry.VolumeIndexLast).FileName + ); - idx++; - } - } - catch (IndexOutOfRangeException) - { - throw; + idx++; } } @@ -251,13 +227,32 @@ public class ArchiveTests : ReaderTests { entry.WriteToDirectory( SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } + new ExtractionOptions { ExtractFullPath = true, Overwrite = true } ); } } VerifyFiles(); } + protected void ArchiveFileSkip( + string testArchive, + string fileOrder, + ReaderOptions? readerOptions = null + ) + { + if (!Environment.OSVersion.IsWindows()) + { + fileOrder = fileOrder.Replace('\\', '/'); + } + var expected = new Stack(fileOrder.Split(' ')); + testArchive = Path.Combine(TEST_ARCHIVES_PATH, testArchive); + using var archive = ArchiveFactory.Open(testArchive, readerOptions); + foreach (var entry in archive.Entries) + { + Assert.Equal(expected.Pop(), entry.Key); + } + } + /// /// Demonstrate the ExtractionOptions.PreserveFileTime and ExtractionOptions.PreserveAttributes extract options /// @@ -270,7 +265,7 @@ public class ArchiveTests : ReaderTests { entry.WriteToDirectory( SCRATCH_FILES_PATH, - new ExtractionOptions() + new ExtractionOptions { ExtractFullPath = true, Overwrite = true, @@ -286,25 +281,25 @@ public class ArchiveTests : ReaderTests protected void ArchiveDeltaDistanceRead(string testArchive) { testArchive = Path.Combine(TEST_ARCHIVES_PATH, testArchive); - using (var archive = Archives.ArchiveFactory.Open(testArchive, null)) - using (var reader = archive.ExtractAllEntries()) - while (reader.MoveToNextEntry()) + using var archive = ArchiveFactory.Open(testArchive); + using var reader = archive.ExtractAllEntries(); + while (reader.MoveToNextEntry()) + { + if (!reader.Entry.IsDirectory) { - if (!reader.Entry.IsDirectory) + var memory = new MemoryStream(); + reader.WriteEntryTo(memory); + + memory.Position = 0; + + for (var y = 0; y < 9; y++) + for (var x = 0; x < 256; x++) { - var memory = new MemoryStream(); - reader.WriteEntryTo(memory); - - memory.Position = 0; - - for (int y = 0; y < 9; y++) - for (int x = 0; x < 256; x++) - { - Assert.Equal(x, memory.ReadByte()); - } - - Assert.Equal((int)-1, memory.ReadByte()); + Assert.Equal(x, memory.ReadByte()); } + + Assert.Equal(-1, memory.ReadByte()); } + } } } diff --git a/tests/SharpCompress.Test/BZip2/BZip2ReaderTests.cs b/tests/SharpCompress.Test/BZip2/BZip2ReaderTests.cs new file mode 100644 index 00000000..e0fa0583 --- /dev/null +++ b/tests/SharpCompress.Test/BZip2/BZip2ReaderTests.cs @@ -0,0 +1,21 @@ +using System; +using System.IO; +using SharpCompress.Common; +using SharpCompress.IO; +using SharpCompress.Readers; +using SharpCompress.Readers.GZip; +using Xunit; + +namespace SharpCompress.Test.BZip2; + +public class BZip2ReaderTests : ReaderTests +{ + [Fact] + public void BZip2_Reader_Factory() + { + Stream stream = new MemoryStream( + new byte[] { 0x42, 0x5a, 0x68, 0x34, 0x31, 0x41, 0x59, 0x26, 0x53, 0x59, 0x35 } + ); + Assert.Throws(typeof(InvalidOperationException), () => ReaderFactory.Open(stream)); + } +} diff --git a/tests/SharpCompress.Test/Filters/BranchExecTests.cs b/tests/SharpCompress.Test/Filters/BranchExecTests.cs index 5c576dcc..ca9686a4 100644 --- a/tests/SharpCompress.Test/Filters/BranchExecTests.cs +++ b/tests/SharpCompress.Test/Filters/BranchExecTests.cs @@ -12,9 +12,8 @@ namespace SharpCompress.Test.Filters; public class BranchExecTests { - private static byte[] x86resultData { get; } = - new byte[] - { + private static byte[] X86ResultData { get; } = + [ 0x12, 0x00, 0x00, @@ -92,12 +91,11 @@ public class BranchExecTests 0x00, 0x00, 0x1C, - 0x00, - }; + 0x00 + ]; - private static byte[] x86Data { get; } = - new byte[] - { + private static byte[] X86Data { get; } = + [ 0x12, 0x00, 0x00, @@ -175,12 +173,11 @@ public class BranchExecTests 0x00, 0x00, 0x1C, - 0x00, - }; + 0x00 + ]; - private static byte[] ppcResultData { get; } = - new byte[] - { + private static byte[] PpcResultData { get; } = + [ 0xF8, 0x6B, 0x2E, @@ -277,11 +274,10 @@ public class BranchExecTests 0xB2, 0xD4, 0xED - }; + ]; - private static byte[] ppcData { get; } = - new byte[] - { + private static byte[] PpcData { get; } = + [ 0xF8, 0x6B, 0x2E, @@ -378,11 +374,10 @@ public class BranchExecTests 0xB2, 0xD4, 0xED - }; + ]; - private static byte[] armResultData { get; } = - new byte[] - { + private static byte[] ArmResultData { get; } = + [ 0x7C, 0xFC, 0x0A, @@ -479,11 +474,10 @@ public class BranchExecTests 0xC6, 0x8F, 0xE2 - }; + ]; - private static byte[] armData { get; } = - new byte[] - { + private static byte[] ArmData { get; } = + [ 0x7C, 0xFC, 0x0A, @@ -580,11 +574,10 @@ public class BranchExecTests 0xC6, 0x8F, 0xE2 - }; + ]; - private static byte[] armtResultData { get; } = - new byte[] - { + private static byte[] ArmtResultData { get; } = + [ 0x95, 0x23, 0xB6, @@ -697,11 +690,10 @@ public class BranchExecTests 0xED, 0x11, 0x0F - }; + ]; - private static byte[] armtData { get; } = - new byte[] - { + private static byte[] ArmtData { get; } = + [ 0x95, 0x23, 0xB6, @@ -814,11 +806,10 @@ public class BranchExecTests 0xED, 0x11, 0x0F - }; + ]; - private static byte[] ia64ResultData { get; } = - new byte[] - { + private static byte[] Ia64ResultData { get; } = + [ 0x4D, 0xF8, 0xF2, @@ -915,11 +906,10 @@ public class BranchExecTests 0x72, 0xD5, 0x0D - }; + ]; - private static byte[] ia64Data { get; } = - new byte[] - { + private static byte[] Ia64Data { get; } = + [ 0x4D, 0xF8, 0xF2, @@ -1016,11 +1006,10 @@ public class BranchExecTests 0x72, 0xD5, 0x0D - }; + ]; - private static byte[] sparcResultData { get; } = - new byte[] - { + private static byte[] SparcResultData { get; } = + [ 0x78, 0x2E, 0x73, @@ -1098,12 +1087,11 @@ public class BranchExecTests 0x91, 0x00, 0x10, - 0x00, - }; + 0x00 + ]; - private static byte[] sparcData { get; } = - new byte[] - { + private static byte[] SparcData { get; } = + [ 0x78, 0x2E, 0x73, @@ -1181,8 +1169,8 @@ public class BranchExecTests 0x91, 0x00, 0x10, - 0x00, - }; + 0x00 + ]; private void CompareBuffer(byte[] testBuffer, byte[] targetBuffer) => Assert.Equal(testBuffer, targetBuffer); @@ -1192,53 +1180,53 @@ public class BranchExecTests { uint state = 0; uint ip = 0x2000; - var testData = x86Data; + var testData = X86Data; BranchExecFilter.X86Converter(testData, ip, ref state); - CompareBuffer(testData, x86resultData); + CompareBuffer(testData, X86ResultData); } [Fact] - public void PowerPCConverterDecodeTest() + public void PowerPcConverterDecodeTest() { uint ip = 0x6A0; - var testData = ppcData; + var testData = PpcData; BranchExecFilter.PowerPCConverter(testData, ip); - CompareBuffer(testData, ppcResultData); + CompareBuffer(testData, PpcResultData); } [Fact] - public void ARMConverteDecoderTest() + public void ArmConverteDecoderTest() { uint ip = 0x3C00; - var testData = armData; + var testData = ArmData; BranchExecFilter.ARMConverter(testData, ip); - CompareBuffer(testData, armResultData); + CompareBuffer(testData, ArmResultData); } [Fact] - public void ARMTConverterDecodeTest() + public void ArmtConverterDecodeTest() { uint ip = 0xA00; - var testData = armtData; + var testData = ArmtData; BranchExecFilter.ARMTConverter(testData, ip); - CompareBuffer(testData, armtResultData); + CompareBuffer(testData, ArmtResultData); } [Fact] - public void IA64ConverterDecodeTest() + public void Ia64ConverterDecodeTest() { uint ip = 0xAA0; - var testData = ia64Data; + var testData = Ia64Data; BranchExecFilter.IA64Converter(testData, ip); - CompareBuffer(testData, ia64ResultData); + CompareBuffer(testData, Ia64ResultData); } [Fact] - public void SPARCConverterDecodeTest() + public void SparcConverterDecodeTest() { uint ip = 0x100; - var testData = sparcData; + var testData = SparcData; BranchExecFilter.SPARCConverter(testData, ip); - CompareBuffer(testData, sparcResultData); + CompareBuffer(testData, SparcResultData); } } diff --git a/tests/SharpCompress.Test/GZip/GZipArchiveTests.cs b/tests/SharpCompress.Test/GZip/GZipArchiveTests.cs index e47c339c..a8e215f7 100644 --- a/tests/SharpCompress.Test/GZip/GZipArchiveTests.cs +++ b/tests/SharpCompress.Test/GZip/GZipArchiveTests.cs @@ -3,6 +3,8 @@ using System.IO; using System.Linq; using SharpCompress.Archives; using SharpCompress.Archives.GZip; +using SharpCompress.Archives.Tar; +using SharpCompress.Common; using Xunit; namespace SharpCompress.Test.GZip; @@ -18,7 +20,7 @@ public class GZipArchiveTests : ArchiveTests using (var archive = ArchiveFactory.Open(stream)) { var entry = archive.Entries.First(); - entry.WriteToFile(Path.Combine(SCRATCH_FILES_PATH, entry.Key)); + entry.WriteToFile(Path.Combine(SCRATCH_FILES_PATH, entry.Key.NotNull())); var size = entry.Size; var scratch = new FileInfo(Path.Combine(SCRATCH_FILES_PATH, "Tar.tar")); @@ -40,7 +42,7 @@ public class GZipArchiveTests : ArchiveTests using (var archive = GZipArchive.Open(stream)) { var entry = archive.Entries.First(); - entry.WriteToFile(Path.Combine(SCRATCH_FILES_PATH, entry.Key)); + entry.WriteToFile(Path.Combine(SCRATCH_FILES_PATH, entry.Key.NotNull())); var size = entry.Size; var scratch = new FileInfo(Path.Combine(SCRATCH_FILES_PATH, "Tar.tar")); @@ -92,7 +94,8 @@ public class GZipArchiveTests : ArchiveTests Assert.Equal(size, tarStream.Length); using (var entryStream = archiveEntry.OpenEntryStream()) { - var result = Archives.Tar.TarArchive.IsTarFile(entryStream); + var result = TarArchive.IsTarFile(entryStream); + Assert.True(result); } Assert.Equal(size, tarStream.Length); using (var entryStream = archiveEntry.OpenEntryStream()) @@ -104,7 +107,7 @@ public class GZipArchiveTests : ArchiveTests } [Fact] - public void TestGzCrcWithMostSignificaltBitNotNegative() + public void TestGzCrcWithMostSignificantBitNotNegative() { using var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz")); using var archive = GZipArchive.Open(stream); @@ -114,4 +117,12 @@ public class GZipArchiveTests : ArchiveTests Assert.InRange(entry.Crc, 0L, 0xFFFFFFFFL); } } + + [Fact] + public void TestGzArchiveTypeGzip() + { + using var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz")); + using var archive = GZipArchive.Open(stream); + Assert.Equal(archive.Type, ArchiveType.GZip); + } } diff --git a/tests/SharpCompress.Test/GZip/GZipReaderTests.cs b/tests/SharpCompress.Test/GZip/GZipReaderTests.cs index b5a3d501..9d475fd5 100644 --- a/tests/SharpCompress.Test/GZip/GZipReaderTests.cs +++ b/tests/SharpCompress.Test/GZip/GZipReaderTests.cs @@ -1,6 +1,7 @@ using System.IO; using SharpCompress.Common; using SharpCompress.IO; +using SharpCompress.Readers.GZip; using Xunit; namespace SharpCompress.Test.GZip; @@ -17,7 +18,7 @@ public class GZipReaderTests : ReaderTests { //read only as GZip itme using Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz")); - using var reader = Readers.GZip.GZipReader.Open(new RewindableStream(stream)); + using var reader = GZipReader.Open(new RewindableStream(stream)); while (reader.MoveToNextEntry()) // Crash here { Assert.NotEqual(0, reader.Entry.Size); diff --git a/tests/SharpCompress.Test/Mocks/FlushOnDisposeStream.cs b/tests/SharpCompress.Test/Mocks/FlushOnDisposeStream.cs index 194e7788..56cd0161 100644 --- a/tests/SharpCompress.Test/Mocks/FlushOnDisposeStream.cs +++ b/tests/SharpCompress.Test/Mocks/FlushOnDisposeStream.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; namespace SharpCompress.Test.Mocks; @@ -7,30 +7,26 @@ namespace SharpCompress.Test.Mocks; // CryptoStream doesn't always trigger the Flush, so this class is used instead // See https://referencesource.microsoft.com/#mscorlib/system/security/cryptography/cryptostream.cs,141 -public class FlushOnDisposeStream : Stream, IDisposable +public class FlushOnDisposeStream(Stream innerStream) : Stream { - private Stream inner; - - public FlushOnDisposeStream(Stream innerStream) => inner = innerStream; - - public override bool CanRead => inner.CanRead; + public override bool CanRead => innerStream.CanRead; public override bool CanSeek => false; public override bool CanWrite => false; - public override long Length => inner.Length; + public override long Length => innerStream.Length; public override long Position { - get => inner.Position; - set => inner.Position = value; + get => innerStream.Position; + set => innerStream.Position = value; } - public override void Flush() => throw new NotImplementedException(); + public override void Flush() { } public override int Read(byte[] buffer, int offset, int count) => - inner.Read(buffer, offset, count); + innerStream.Read(buffer, offset, count); public override long Seek(long offset, SeekOrigin origin) => throw new NotImplementedException(); @@ -44,8 +40,8 @@ public class FlushOnDisposeStream : Stream, IDisposable { if (disposing) { - inner.Flush(); - inner.Close(); + innerStream.Flush(); + innerStream.Close(); } base.Dispose(disposing); diff --git a/tests/SharpCompress.Test/Mocks/ForwardOnlyStream.cs b/tests/SharpCompress.Test/Mocks/ForwardOnlyStream.cs index cef93a38..180b8416 100644 --- a/tests/SharpCompress.Test/Mocks/ForwardOnlyStream.cs +++ b/tests/SharpCompress.Test/Mocks/ForwardOnlyStream.cs @@ -1,16 +1,12 @@ -using System; +using System; using System.IO; namespace SharpCompress.Test.Mocks; -public class ForwardOnlyStream : Stream +public class ForwardOnlyStream(Stream stream) : Stream { - private readonly Stream stream; - public bool IsDisposed { get; private set; } - public ForwardOnlyStream(Stream stream) => this.stream = stream; - protected override void Dispose(bool disposing) { if (!IsDisposed) @@ -29,7 +25,7 @@ public class ForwardOnlyStream : Stream public override bool CanSeek => false; public override bool CanWrite => false; - public override void Flush() => throw new NotSupportedException(); + public override void Flush() { } public override long Length => throw new NotSupportedException(); diff --git a/tests/SharpCompress.Test/Mocks/TestStream.cs b/tests/SharpCompress.Test/Mocks/TestStream.cs index 66923df3..da7d65cc 100644 --- a/tests/SharpCompress.Test/Mocks/TestStream.cs +++ b/tests/SharpCompress.Test/Mocks/TestStream.cs @@ -2,23 +2,13 @@ namespace SharpCompress.Test.Mocks; -public class TestStream : Stream +public class TestStream(Stream stream, bool read, bool write, bool seek) : Stream { - private readonly Stream stream; - public TestStream(Stream stream) : this(stream, stream.CanRead, stream.CanWrite, stream.CanSeek) { } public bool IsDisposed { get; private set; } - public TestStream(Stream stream, bool read, bool write, bool seek) - { - this.stream = stream; - CanRead = read; - CanWrite = write; - CanSeek = seek; - } - protected override void Dispose(bool disposing) { base.Dispose(disposing); @@ -26,11 +16,11 @@ public class TestStream : Stream IsDisposed = true; } - public override bool CanRead { get; } + public override bool CanRead { get; } = read; - public override bool CanSeek { get; } + public override bool CanSeek { get; } = seek; - public override bool CanWrite { get; } + public override bool CanWrite { get; } = write; public override void Flush() => stream.Flush(); diff --git a/tests/SharpCompress.Test/OperatingSystemExtensions.cs b/tests/SharpCompress.Test/OperatingSystemExtensions.cs new file mode 100644 index 00000000..c8ca768e --- /dev/null +++ b/tests/SharpCompress.Test/OperatingSystemExtensions.cs @@ -0,0 +1,11 @@ +using System; + +namespace SharpCompress.Test; + +public static class OperatingSystemExtensions +{ + public static bool IsWindows(this OperatingSystem os) => + os.Platform == PlatformID.Win32NT + || os.Platform == PlatformID.Win32Windows + || os.Platform == PlatformID.Win32S; +} diff --git a/tests/SharpCompress.Test/Rar/RarArchiveTests.cs b/tests/SharpCompress.Test/Rar/RarArchiveTests.cs index 6496e15e..373af692 100644 --- a/tests/SharpCompress.Test/Rar/RarArchiveTests.cs +++ b/tests/SharpCompress.Test/Rar/RarArchiveTests.cs @@ -22,11 +22,16 @@ public class RarArchiveTests : ArchiveTests () => ReadRarPassword("Rar.encrypted_filesAndHeader.rar", null) ); - /*[Fact] - public void Rar5_EncryptedFileAndHeader_Archive() - { + [Fact] + public void Rar5_EncryptedFileAndHeader_Archive() => ReadRarPassword("Rar5.encrypted_filesAndHeader.rar", "test"); - }*/ + + [Fact] + public void Rar5_EncryptedFileAndHeader_Archive_Err() => + Assert.Throws( + typeof(CryptographicException), + () => ReadRarPassword("Rar5.encrypted_filesAndHeader.rar", "failed") + ); [Fact] public void Rar5_EncryptedFileAndHeader_NoPasswordExceptionTest() => @@ -39,20 +44,23 @@ public class RarArchiveTests : ArchiveTests public void Rar_EncryptedFileOnly_Archive() => ReadRarPassword("Rar.encrypted_filesOnly.rar", "test"); - /*[Fact] - public void Rar5_EncryptedFileOnly_Archive() - { + [Fact] + public void Rar_EncryptedFileOnly_Archive_Err() => + Assert.Throws( + typeof(CryptographicException), + () => ReadRarPassword("Rar5.encrypted_filesOnly.rar", "failed") + ); + + [Fact] + public void Rar5_EncryptedFileOnly_Archive() => ReadRarPassword("Rar5.encrypted_filesOnly.rar", "test"); - }*/ [Fact] public void Rar_Encrypted_Archive() => ReadRarPassword("Rar.Encrypted.rar", "test"); - /*[Fact] - public void Rar5_Encrypted_Archive() - { + [Fact] + public void Rar5_Encrypted_Archive() => ReadRarPassword("Rar5.encrypted_filesAndHeader.rar", "test"); - }*/ private void ReadRarPassword(string testArchive, string? password) { @@ -60,7 +68,7 @@ public class RarArchiveTests : ArchiveTests using ( var archive = RarArchive.Open( stream, - new ReaderOptions() { Password = password, LeaveStreamOpen = true } + new ReaderOptions { Password = password, LeaveStreamOpen = true } ) ) { @@ -71,7 +79,7 @@ public class RarArchiveTests : ArchiveTests Assert.Equal(CompressionType.Rar, entry.CompressionType); entry.WriteToDirectory( SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } + new ExtractionOptions { ExtractFullPath = true, Overwrite = true } ); } } @@ -90,7 +98,7 @@ public class RarArchiveTests : ArchiveTests using ( var archive = RarArchive.Open( Path.Combine(TEST_ARCHIVES_PATH, archiveName), - new ReaderOptions() { Password = password, LeaveStreamOpen = true } + new ReaderOptions { Password = password, LeaveStreamOpen = true } ) ) { @@ -98,7 +106,7 @@ public class RarArchiveTests : ArchiveTests { entry.WriteToDirectory( SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } + new ExtractionOptions { ExtractFullPath = true, Overwrite = true } ); } } @@ -129,7 +137,7 @@ public class RarArchiveTests : ArchiveTests { entry.WriteToDirectory( SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } + new ExtractionOptions { ExtractFullPath = true, Overwrite = true } ); } } @@ -138,13 +146,13 @@ public class RarArchiveTests : ArchiveTests public void Rar_Jpg_ArchiveStreamRead() { using var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Rar.jpeg.jpg")); - using (var archive = RarArchive.Open(stream, new ReaderOptions() { LookForHeader = true })) + using (var archive = RarArchive.Open(stream, new ReaderOptions { LookForHeader = true })) { foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) { entry.WriteToDirectory( SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } + new ExtractionOptions { ExtractFullPath = true, Overwrite = true } ); } } @@ -167,7 +175,7 @@ public class RarArchiveTests : ArchiveTests { entry.WriteToDirectory( SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } + new ExtractionOptions { ExtractFullPath = true, Overwrite = true } ); } } @@ -221,30 +229,28 @@ public class RarArchiveTests : ArchiveTests [Fact] public void Rar_Multi_ArchiveStreamRead() => DoRar_Multi_ArchiveStreamRead( - new[] - { + [ "Rar.multi.part01.rar", "Rar.multi.part02.rar", "Rar.multi.part03.rar", "Rar.multi.part04.rar", "Rar.multi.part05.rar", "Rar.multi.part06.rar" - }, + ], false ); [Fact] public void Rar5_Multi_ArchiveStreamRead() => DoRar_Multi_ArchiveStreamRead( - new[] - { + [ "Rar5.multi.part01.rar", "Rar5.multi.part02.rar", "Rar5.multi.part03.rar", "Rar5.multi.part04.rar", "Rar5.multi.part05.rar", "Rar5.multi.part06.rar" - }, + ], false ); @@ -258,7 +264,7 @@ public class RarArchiveTests : ArchiveTests { entry.WriteToDirectory( SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } + new ExtractionOptions { ExtractFullPath = true, Overwrite = true } ); } } @@ -266,15 +272,14 @@ public class RarArchiveTests : ArchiveTests [Fact] public void Rar5_MultiSolid_ArchiveStreamRead() => DoRar_Multi_ArchiveStreamRead( - new[] - { + [ "Rar.multi.solid.part01.rar", "Rar.multi.solid.part02.rar", "Rar.multi.solid.part03.rar", "Rar.multi.solid.part04.rar", "Rar.multi.solid.part05.rar", "Rar.multi.solid.part06.rar" - }, + ], true ); @@ -312,7 +317,7 @@ public class RarArchiveTests : ArchiveTests using ( var archive = RarArchive.Open( Path.Combine(TEST_ARCHIVES_PATH, "Rar.jpeg.jpg"), - new ReaderOptions() { LookForHeader = true } + new ReaderOptions { LookForHeader = true } ) ) { @@ -320,7 +325,7 @@ public class RarArchiveTests : ArchiveTests { entry.WriteToDirectory( SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } + new ExtractionOptions { ExtractFullPath = true, Overwrite = true } ); } } @@ -336,8 +341,7 @@ public class RarArchiveTests : ArchiveTests [Fact] public void Rar2_Multi_ArchiveStreamRead() => DoRar_Multi_ArchiveStreamRead( - new[] - { + [ "Rar2.multi.rar", "Rar2.multi.r00", "Rar2.multi.r01", @@ -345,7 +349,7 @@ public class RarArchiveTests : ArchiveTests "Rar2.multi.r03", "Rar2.multi.r04", "Rar2.multi.r05" - }, + ], false ); @@ -459,8 +463,7 @@ public class RarArchiveTests : ArchiveTests [Fact] public void Rar4_Multi_ArchiveStreamRead() => DoRar_Multi_ArchiveStreamRead( - new[] - { + [ "Rar4.multi.part01.rar", "Rar4.multi.part02.rar", "Rar4.multi.part03.rar", @@ -468,7 +471,7 @@ public class RarArchiveTests : ArchiveTests "Rar4.multi.part05.rar", "Rar4.multi.part06.rar", "Rar4.multi.part07.rar" - }, + ], false ); @@ -477,15 +480,14 @@ public class RarArchiveTests : ArchiveTests public void Rar4_Split_ArchiveStreamRead() => ArchiveStreamMultiRead( null, - new[] - { + [ "Rar4.split.001", "Rar4.split.002", "Rar4.split.003", "Rar4.split.004", "Rar4.split.005", "Rar4.split.006" - } + ] ); //will detect and load other files @@ -512,15 +514,14 @@ public class RarArchiveTests : ArchiveTests public void Rar4_Split_ArchiveStreamFirstFileRead() => ArchiveStreamMultiRead( null, - new[] - { - "Rar4.split.001", + [ + "Rar4.split.001" //"Rar4.split.002", //"Rar4.split.003", //"Rar4.split.004", //"Rar4.split.005", //"Rar4.split.006" - } + ] ); //open with ArchiveFactory.Open and stream @@ -553,12 +554,11 @@ public class RarArchiveTests : ArchiveTests [Fact] public void Rar4_Multi_ArchiveOpenEntryVolumeIndexTest() => ArchiveOpenEntryVolumeIndexTest( - new[] - { - new[] { 0, 1 }, //exe - Rar4.multi.part01.rar to Rar4.multi.part02.rar - new[] { 1, 5 }, //jpg - Rar4.multi.part02.rar to Rar4.multi.part06.rar - new[] { 5, 6 } //txt - Rar4.multi.part06.rar to Rar4.multi.part07.rar - }, + [ + [0, 1], //exe - Rar4.multi.part01.rar to Rar4.multi.part02.rar + [1, 5], //jpg - Rar4.multi.part02.rar to Rar4.multi.part06.rar + [5, 6] //txt - Rar4.multi.part06.rar to Rar4.multi.part07.rar + ], null, "Rar4.multi.part01.rar", "Rar4.multi.part02.rar", @@ -600,4 +600,37 @@ public class RarArchiveTests : ArchiveTests Assert.True(archive.IsMultipartVolume()); Assert.False(archive.IsFirstVolume()); } + + [Fact] + public void Rar5_CRC_Blake2_Archive() => ArchiveFileRead("Rar5.crc_blake2.rar"); + + [Fact] + void Rar_Iterate_Archive() => + ArchiveFileSkip("Rar.rar", "Failure jpg exe Empty jpg\\test.jpg exe\\test.exe тест.txt"); + + [Fact] + public void Rar2_Iterate_Archive() => + ArchiveFileSkip("Rar2.rar", "Failure Empty тест.txt jpg\\test.jpg exe\\test.exe jpg exe"); + + [Fact] + public void Rar4_Iterate_Archive() => + ArchiveFileSkip("Rar4.rar", "Failure Empty jpg exe тест.txt jpg\\test.jpg exe\\test.exe"); + + [Fact] + public void Rar5_Iterate_Archive() => + ArchiveFileSkip("Rar5.rar", "Failure jpg exe Empty тест.txt jpg\\test.jpg exe\\test.exe"); + + [Fact] + public void Rar_Encrypted_Iterate_Archive() => + ArchiveFileSkip( + "Rar.encrypted_filesOnly.rar", + "Failure jpg exe Empty тест.txt jpg\\test.jpg exe\\test.exe" + ); + + [Fact] + public void Rar5_Encrypted_Iterate_Archive() => + ArchiveFileSkip( + "Rar5.encrypted_filesOnly.rar", + "Failure jpg exe Empty тест.txt jpg\\test.jpg exe\\test.exe" + ); } diff --git a/tests/SharpCompress.Test/Rar/RarHeaderFactoryTest.cs b/tests/SharpCompress.Test/Rar/RarHeaderFactoryTest.cs index f2b1913c..70015fcf 100644 --- a/tests/SharpCompress.Test/Rar/RarHeaderFactoryTest.cs +++ b/tests/SharpCompress.Test/Rar/RarHeaderFactoryTest.cs @@ -11,10 +11,10 @@ namespace SharpCompress.Test.Rar; /// public class RarHeaderFactoryTest : TestBase { - private readonly RarHeaderFactory rarHeaderFactory; + private readonly RarHeaderFactory _rarHeaderFactory; public RarHeaderFactoryTest() => - rarHeaderFactory = new RarHeaderFactory( + _rarHeaderFactory = new RarHeaderFactory( StreamingMode.Seekable, new ReaderOptions { LeaveStreamOpen = true } ); @@ -40,11 +40,11 @@ public class RarHeaderFactoryTest : TestBase FileMode.Open, FileAccess.Read ); - foreach (var header in rarHeaderFactory.ReadHeaders(stream)) + foreach (var header in _rarHeaderFactory.ReadHeaders(stream)) { if (header.HeaderType == HeaderType.Archive || header.HeaderType == HeaderType.Crypt) { - Assert.Equal(isEncrypted, rarHeaderFactory.IsEncrypted); + Assert.Equal(isEncrypted, _rarHeaderFactory.IsEncrypted); break; } } diff --git a/tests/SharpCompress.Test/Rar/RarReaderTests.cs b/tests/SharpCompress.Test/Rar/RarReaderTests.cs index 097b0fc8..6ba605a5 100644 --- a/tests/SharpCompress.Test/Rar/RarReaderTests.cs +++ b/tests/SharpCompress.Test/Rar/RarReaderTests.cs @@ -13,29 +13,27 @@ public class RarReaderTests : ReaderTests [Fact] public void Rar_Multi_Reader() => DoRar_Multi_Reader( - new[] - { + [ "Rar.multi.part01.rar", "Rar.multi.part02.rar", "Rar.multi.part03.rar", "Rar.multi.part04.rar", "Rar.multi.part05.rar", "Rar.multi.part06.rar" - } + ] ); [Fact] public void Rar5_Multi_Reader() => DoRar_Multi_Reader( - new[] - { + [ "Rar5.multi.part01.rar", "Rar5.multi.part02.rar", "Rar5.multi.part03.rar", "Rar5.multi.part04.rar", "Rar5.multi.part05.rar", "Rar5.multi.part06.rar" - } + ] ); private void DoRar_Multi_Reader(string[] archives) @@ -52,7 +50,7 @@ public class RarReaderTests : ReaderTests { reader.WriteEntryToDirectory( SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } + new ExtractionOptions { ExtractFullPath = true, Overwrite = true } ); } } @@ -62,15 +60,14 @@ public class RarReaderTests : ReaderTests [Fact] public void Rar_Multi_Reader_Encrypted() => DoRar_Multi_Reader_Encrypted( - new[] - { + [ "Rar.EncryptedParts.part01.rar", "Rar.EncryptedParts.part02.rar", "Rar.EncryptedParts.part03.rar", "Rar.EncryptedParts.part04.rar", "Rar.EncryptedParts.part05.rar", "Rar.EncryptedParts.part06.rar" - } + ] ); private void DoRar_Multi_Reader_Encrypted(string[] archives) => @@ -81,7 +78,7 @@ public class RarReaderTests : ReaderTests archives .Select(s => Path.Combine(TEST_ARCHIVES_PATH, s)) .Select(p => File.OpenRead(p)), - new ReaderOptions() { Password = "test" } + new ReaderOptions { Password = "test" } ) ) { @@ -89,7 +86,7 @@ public class RarReaderTests : ReaderTests { reader.WriteEntryToDirectory( SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } + new ExtractionOptions { ExtractFullPath = true, Overwrite = true } ); } } @@ -99,29 +96,27 @@ public class RarReaderTests : ReaderTests [Fact] public void Rar_Multi_Reader_Delete_Files() => DoRar_Multi_Reader_Delete_Files( - new[] - { + [ "Rar.multi.part01.rar", "Rar.multi.part02.rar", "Rar.multi.part03.rar", "Rar.multi.part04.rar", "Rar.multi.part05.rar", "Rar.multi.part06.rar" - } + ] ); [Fact] public void Rar5_Multi_Reader_Delete_Files() => DoRar_Multi_Reader_Delete_Files( - new[] - { + [ "Rar5.multi.part01.rar", "Rar5.multi.part02.rar", "Rar5.multi.part03.rar", "Rar5.multi.part04.rar", "Rar5.multi.part05.rar", "Rar5.multi.part06.rar" - } + ] ); private void DoRar_Multi_Reader_Delete_Files(string[] archives) @@ -143,7 +138,7 @@ public class RarReaderTests : ReaderTests { reader.WriteEntryToDirectory( SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } + new ExtractionOptions { ExtractFullPath = true, Overwrite = true } ); } } @@ -171,33 +166,28 @@ public class RarReaderTests : ReaderTests [Fact] public void Rar5_Reader() => Read("Rar5.rar", CompressionType.Rar); + [Fact] + public void Rar5_CRC_Blake2_Reader() => Read("Rar5.crc_blake2.rar", CompressionType.Rar); + [Fact] public void Rar_EncryptedFileAndHeader_Reader() => ReadRar("Rar.encrypted_filesAndHeader.rar", "test"); - /*[Fact] - public void Rar5_EncryptedFileAndHeader_Reader() - { + [Fact] + public void Rar5_EncryptedFileAndHeader_Reader() => ReadRar("Rar5.encrypted_filesAndHeader.rar", "test"); - }*/ [Fact] public void Rar_EncryptedFileOnly_Reader() => ReadRar("Rar.encrypted_filesOnly.rar", "test"); - /*[Fact] - public void Rar5_EncryptedFileOnly_Reader() - { - ReadRar("Rar5.encrypted_filesOnly.rar", "test"); - }*/ + [Fact] + public void Rar5_EncryptedFileOnly_Reader() => ReadRar("Rar5.encrypted_filesOnly.rar", "test"); [Fact] public void Rar_Encrypted_Reader() => ReadRar("Rar.Encrypted.rar", "test"); - /*[Fact] - public void Rar5_Encrypted_Reader() - { - ReadRar("Rar5.encrypted_filesOnly.rar", "test"); - }*/ + [Fact] + public void Rar5_Encrypted_Reader() => ReadRar("Rar5.encrypted_filesOnly.rar", "test"); private void ReadRar(string testArchive, string password) => Read(testArchive, CompressionType.Rar, new ReaderOptions { Password = password }); @@ -219,7 +209,7 @@ public class RarReaderTests : ReaderTests { Assert.Equal(CompressionType.Rar, reader.Entry.CompressionType); using var entryStream = reader.OpenEntryStream(); - var file = Path.GetFileName(reader.Entry.Key); + var file = Path.GetFileName(reader.Entry.Key).NotNull(); var folder = Path.GetDirectoryName(reader.Entry.Key) ?? throw new ArgumentNullException(); @@ -244,16 +234,14 @@ public class RarReaderTests : ReaderTests using ( var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Rar.Audio_program.rar")) ) - using ( - var reader = ReaderFactory.Open(stream, new ReaderOptions() { LookForHeader = true }) - ) + using (var reader = ReaderFactory.Open(stream, new ReaderOptions { LookForHeader = true })) { while (reader.MoveToNextEntry()) { Assert.Equal(CompressionType.Rar, reader.Entry.CompressionType); reader.WriteEntryToDirectory( SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } + new ExtractionOptions { ExtractFullPath = true, Overwrite = true } ); } } @@ -267,14 +255,14 @@ public class RarReaderTests : ReaderTests public void Rar_Jpg_Reader() { using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Rar.jpeg.jpg"))) - using (var reader = RarReader.Open(stream, new ReaderOptions() { LookForHeader = true })) + using (var reader = RarReader.Open(stream, new ReaderOptions { LookForHeader = true })) { while (reader.MoveToNextEntry()) { Assert.Equal(CompressionType.Rar, reader.Entry.CompressionType); reader.WriteEntryToDirectory( SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } + new ExtractionOptions { ExtractFullPath = true, Overwrite = true } ); } } @@ -284,6 +272,12 @@ public class RarReaderTests : ReaderTests [Fact] public void Rar_Solid_Reader() => Read("Rar.solid.rar", CompressionType.Rar); + [Fact] + public void Rar_Comment_Reader() => Read("Rar.comment.rar", CompressionType.Rar); + + [Fact] + public void Rar5_Comment_Reader() => Read("Rar5.comment.rar", CompressionType.Rar); + [Fact] public void Rar5_Solid_Reader() => Read("Rar5.solid.rar", CompressionType.Rar); @@ -296,15 +290,15 @@ public class RarReaderTests : ReaderTests private void DoRar_Solid_Skip_Reader(string filename) { using var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, filename)); - using var reader = ReaderFactory.Open(stream, new ReaderOptions() { LookForHeader = true }); + using var reader = ReaderFactory.Open(stream, new ReaderOptions { LookForHeader = true }); while (reader.MoveToNextEntry()) { - if (reader.Entry.Key.Contains("jpg")) + if (reader.Entry.Key.NotNull().Contains("jpg")) { Assert.Equal(CompressionType.Rar, reader.Entry.CompressionType); reader.WriteEntryToDirectory( SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } + new ExtractionOptions { ExtractFullPath = true, Overwrite = true } ); } } @@ -319,15 +313,15 @@ public class RarReaderTests : ReaderTests private void DoRar_Reader_Skip(string filename) { using var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, filename)); - using var reader = ReaderFactory.Open(stream, new ReaderOptions() { LookForHeader = true }); + using var reader = ReaderFactory.Open(stream, new ReaderOptions { LookForHeader = true }); while (reader.MoveToNextEntry()) { - if (reader.Entry.Key.Contains("jpg")) + if (reader.Entry.Key.NotNull().Contains("jpg")) { Assert.Equal(CompressionType.Rar, reader.Entry.CompressionType); reader.WriteEntryToDirectory( SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } + new ExtractionOptions { ExtractFullPath = true, Overwrite = true } ); } } @@ -347,19 +341,15 @@ public class RarReaderTests : ReaderTests "Rar.EncryptedParts.part06.rar" }; - using ( - var reader = RarReader.Open( - archives - .Select(s => Path.Combine(TEST_ARCHIVES_PATH, s)) - .Select(p => File.OpenRead(p)), - new ReaderOptions() { Password = "test" } - ) - ) + using var reader = RarReader.Open( + archives + .Select(s => Path.Combine(TEST_ARCHIVES_PATH, s)) + .Select(p => File.OpenRead(p)), + new ReaderOptions { Password = "test" } + ); + while (reader.MoveToNextEntry()) { - while (reader.MoveToNextEntry()) - { - // - } + // } } @@ -369,7 +359,7 @@ public class RarReaderTests : ReaderTests ); using var reader = ReaderFactory.Open( stream, - new ReaderOptions() { LookForHeader = true } + new ReaderOptions { LookForHeader = true } ); while (reader.MoveToNextEntry()) { @@ -377,4 +367,55 @@ public class RarReaderTests : ReaderTests } } } + + [Fact] + public void Rar_Iterate_Reader() => + Iterate( + "Rar.rar", + "Failure jpg exe Empty jpg\\test.jpg exe\\test.exe тест.txt", + CompressionType.Rar + ); + + [Fact] + public void Rar2_Iterate_Archive() => + Iterate( + "Rar2.rar", + "Failure Empty тест.txt jpg\\test.jpg exe\\test.exe jpg exe", + CompressionType.Rar + ); + + [Fact] + public void Rar4_Iterate_Archive() => + Iterate( + "Rar4.rar", + "Failure Empty jpg exe тест.txt jpg\\test.jpg exe\\test.exe", + CompressionType.Rar + ); + + [Fact] + public void Rar5_Iterate_Archive() => + Iterate( + "Rar5.rar", + "Failure jpg exe Empty тест.txt jpg\\test.jpg exe\\test.exe", + CompressionType.Rar + ); + + [Fact] + public void Rar_Encrypted_Iterate_Archive() => + Iterate( + "Rar.encrypted_filesOnly.rar", + "Failure jpg exe Empty тест.txt jpg\\test.jpg exe\\test.exe", + CompressionType.Rar + ); + + [Fact] + public void Rar5_Encrypted_Iterate_Archive() => + Assert.Throws( + () => + Iterate( + "Rar5.encrypted_filesOnly.rar", + "Failure jpg exe Empty тест.txt jpg\\test.jpg exe\\test.exe", + CompressionType.Rar + ) + ); } diff --git a/tests/SharpCompress.Test/ReaderTests.cs b/tests/SharpCompress.Test/ReaderTests.cs index be1dbe07..7ea1d7b7 100644 --- a/tests/SharpCompress.Test/ReaderTests.cs +++ b/tests/SharpCompress.Test/ReaderTests.cs @@ -1,3 +1,5 @@ +using System; +using System.Collections.Generic; using System.IO; using SharpCompress.Common; using SharpCompress.IO; @@ -62,9 +64,33 @@ public abstract class ReaderTests : TestBase Assert.Equal(expectedCompression, reader.Entry.CompressionType); reader.WriteEntryToDirectory( SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } + new ExtractionOptions { ExtractFullPath = true, Overwrite = true } ); } } } + + protected void Iterate( + string testArchive, + string fileOrder, + CompressionType expectedCompression, + ReaderOptions? options = null + ) + { + if (!Environment.OSVersion.IsWindows()) + { + fileOrder = fileOrder.Replace('\\', '/'); + } + var expected = new Stack(fileOrder.Split(' ')); + + testArchive = Path.Combine(TEST_ARCHIVES_PATH, testArchive); + using var file = File.OpenRead(testArchive); + using var forward = new ForwardOnlyStream(file); + using var reader = ReaderFactory.Open(forward, options); + while (reader.MoveToNextEntry()) + { + Assert.Equal(expectedCompression, reader.Entry.CompressionType); + Assert.Equal(expected.Pop(), reader.Entry.Key); + } + } } diff --git a/tests/SharpCompress.Test/SevenZip/SevenZipArchiveTests.cs b/tests/SharpCompress.Test/SevenZip/SevenZipArchiveTests.cs index e77e288c..32d978a2 100644 --- a/tests/SharpCompress.Test/SevenZip/SevenZipArchiveTests.cs +++ b/tests/SharpCompress.Test/SevenZip/SevenZipArchiveTests.cs @@ -25,17 +25,17 @@ public class SevenZipArchiveTests : ArchiveTests [Fact] public void SevenZipArchive_LZMAAES_StreamRead() => - ArchiveStreamRead("7Zip.LZMA.Aes.7z", new ReaderOptions() { Password = "testpassword" }); + ArchiveStreamRead("7Zip.LZMA.Aes.7z", new ReaderOptions { Password = "testpassword" }); [Fact] public void SevenZipArchive_LZMAAES_PathRead() => - ArchiveFileRead("7Zip.LZMA.Aes.7z", new ReaderOptions() { Password = "testpassword" }); + ArchiveFileRead("7Zip.LZMA.Aes.7z", new ReaderOptions { Password = "testpassword" }); [Fact] public void SevenZipArchive_LZMAAES_NoPasswordExceptionTest() => Assert.Throws( typeof(CryptographicException), - () => ArchiveFileRead("7Zip.LZMA.Aes.7z", new ReaderOptions() { Password = null }) + () => ArchiveFileRead("7Zip.LZMA.Aes.7z", new ReaderOptions { Password = null }) ); //was failing with ArgumentNullException not CryptographicException like rar [Fact] @@ -135,6 +135,9 @@ public class SevenZipArchiveTests : ArchiveTests ) ); + [Fact] + public void SevenZipArchive_EOS_FileRead() => ArchiveFileRead("7Zip.eos.7z"); + [Fact] public void SevenZipArchive_Delta_FileRead() => ArchiveFileRead("7Zip.delta.7z"); @@ -159,6 +162,12 @@ public class SevenZipArchiveTests : ArchiveTests [Fact] public void SevenZipArchive_SPARC_FileRead() => ArchiveFileRead("7Zip.SPARC.7z"); + [Fact] + public void SevenZipArchive_ARM64_FileRead() => ArchiveFileRead("7Zip.ARM64.7z"); + + [Fact] + public void SevenZipArchive_RISCV_FileRead() => ArchiveFileRead("7Zip.RISCV.7z"); + [Fact] public void SevenZipArchive_Filters_FileRead() => ArchiveFileRead("7Zip.Filters.7z"); @@ -173,7 +182,7 @@ public class SevenZipArchiveTests : ArchiveTests using (var archive = SevenZipArchive.Open(stream)) { var entry = archive.Entries.First(); - entry.WriteToFile(Path.Combine(SCRATCH_FILES_PATH, entry.Key)); + entry.WriteToFile(Path.Combine(SCRATCH_FILES_PATH, entry.Key.NotNull())); var size = entry.Size; var scratch = new FileInfo(Path.Combine(SCRATCH_FILES_PATH, "7Zip.Tar.tar")); diff --git a/tests/SharpCompress.Test/SharpCompress.Test.csproj b/tests/SharpCompress.Test/SharpCompress.Test.csproj index c20664a7..3bfeaa3e 100644 --- a/tests/SharpCompress.Test/SharpCompress.Test.csproj +++ b/tests/SharpCompress.Test/SharpCompress.Test.csproj @@ -1,6 +1,6 @@  - net7.0;net462 + net8.0;net462 SharpCompress.Test SharpCompress.Test @@ -8,16 +8,13 @@ - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + + + + - + diff --git a/tests/SharpCompress.Test/Streams/LzmaStreamTests.cs b/tests/SharpCompress.Test/Streams/LzmaStreamTests.cs index 2f336329..2eea6607 100644 --- a/tests/SharpCompress.Test/Streams/LzmaStreamTests.cs +++ b/tests/SharpCompress.Test/Streams/LzmaStreamTests.cs @@ -1,6 +1,5 @@ using System; using System.Buffers; -using System.Buffers.Binary; using System.IO; using SharpCompress.Compressors.LZMA; using Xunit; @@ -20,9 +19,8 @@ public class LzmaStreamTests Assert.Equal('X', decompressor.ReadByte()); } - private static byte[] lzmaData { get; } = - new byte[] - { + private static byte[] LzmaData { get; } = + [ 0x5D, 0x00, 0x20, @@ -177,14 +175,13 @@ public class LzmaStreamTests 0x76, 0x03, 0x90 - }; + ]; /// - /// The decoded data for . + /// The decoded data for . /// - private static byte[] lzmaResultData { get; } = - new byte[] - { + private static byte[] LzmaResultData { get; } = + [ 0x01, 0x00, 0xFD, @@ -513,12 +510,12 @@ public class LzmaStreamTests 0x00, 0x00, 0x00 - }; + ]; [Fact] public void TestLzmaBuffer() { - var input = new MemoryStream(lzmaData); + var input = new MemoryStream(LzmaData); using var output = new MemoryStream(); var properties = new byte[5]; input.Read(properties, 0, 5); @@ -531,19 +528,15 @@ public class LzmaStreamTests coder.SetDecoderProperties(properties); coder.Code(input, output, input.Length, fileLength, null); - Assert.Equal(output.ToArray(), lzmaResultData); + Assert.Equal(output.ToArray(), LzmaResultData); } [Fact] public void TestLzmaStreamEncodingWritesData() { - using MemoryStream inputStream = new MemoryStream(lzmaResultData); + using var inputStream = new MemoryStream(LzmaResultData); using MemoryStream outputStream = new(); - using LzmaStream lzmaStream = new LzmaStream( - LzmaEncoderProperties.Default, - false, - outputStream - ); + using var lzmaStream = new LzmaStream(LzmaEncoderProperties.Default, false, outputStream); inputStream.CopyTo(lzmaStream); lzmaStream.Close(); Assert.NotEqual(0, outputStream.Length); @@ -552,13 +545,9 @@ public class LzmaStreamTests [Fact] public void TestLzmaEncodingAccuracy() { - var input = new MemoryStream(lzmaResultData); + var input = new MemoryStream(LzmaResultData); var compressed = new MemoryStream(); - LzmaStream lzmaEncodingStream = new LzmaStream( - LzmaEncoderProperties.Default, - false, - compressed - ); + var lzmaEncodingStream = new LzmaStream(LzmaEncoderProperties.Default, false, compressed); input.CopyTo(lzmaEncodingStream); lzmaEncodingStream.Close(); compressed.Position = 0; @@ -569,10 +558,10 @@ public class LzmaStreamTests compressed, compressed.Length, output, - lzmaResultData.LongLength + LzmaResultData.LongLength ); - Assert.Equal(output.ToArray(), lzmaResultData); + Assert.Equal(output.ToArray(), LzmaResultData); } private static void DecompressLzmaStream( @@ -583,7 +572,7 @@ public class LzmaStreamTests long decompressedSize ) { - LzmaStream lzmaStream = new LzmaStream( + var lzmaStream = new LzmaStream( properties, compressedStream, compressedSize, @@ -592,12 +581,12 @@ public class LzmaStreamTests false ); - byte[] buffer = ArrayPool.Shared.Rent(1024); + var buffer = ArrayPool.Shared.Rent(1024); long totalRead = 0; while (totalRead < decompressedSize) { - int toRead = (int)Math.Min(buffer.Length, decompressedSize - totalRead); - int read = lzmaStream.Read(buffer, 0, toRead); + var toRead = (int)Math.Min(buffer.Length, decompressedSize - totalRead); + var read = lzmaStream.Read(buffer, 0, toRead); if (read > 0) { decompressedStream.Write(buffer, 0, read); diff --git a/tests/SharpCompress.Test/Tar/TarArchiveTests.cs b/tests/SharpCompress.Test/Tar/TarArchiveTests.cs index dddb32a6..bbf96c00 100644 --- a/tests/SharpCompress.Test/Tar/TarArchiveTests.cs +++ b/tests/SharpCompress.Test/Tar/TarArchiveTests.cs @@ -1,15 +1,15 @@ using System; using System.IO; using System.Linq; +using System.Text; using SharpCompress.Archives; using SharpCompress.Archives.Tar; using SharpCompress.Common; -using SharpCompress.Writers; -using Xunit; -using System.Text; using SharpCompress.Readers; -using SharpCompress.Writers.Tar; using SharpCompress.Readers.Tar; +using SharpCompress.Writers; +using SharpCompress.Writers.Tar; +using Xunit; namespace SharpCompress.Test.Tar; @@ -26,10 +26,10 @@ public class TarArchiveTests : ArchiveTests [Fact] public void Tar_FileName_Exactly_100_Characters() { - string archive = "Tar_FileName_Exactly_100_Characters.tar"; + var archive = "Tar_FileName_Exactly_100_Characters.tar"; // create the 100 char filename - string filename = + var filename = "filename_with_exactly_100_characters_______________________________________________________________X"; // Step 1: create a tar file containing a file with the test name @@ -37,7 +37,7 @@ public class TarArchiveTests : ArchiveTests using (var writer = WriterFactory.Open(stream, ArchiveType.Tar, CompressionType.None)) using (Stream inputStream = new MemoryStream()) { - StreamWriter sw = new StreamWriter(inputStream); + var sw = new StreamWriter(inputStream); sw.Write("dummy filecontent"); sw.Flush(); @@ -46,7 +46,7 @@ public class TarArchiveTests : ArchiveTests } // Step 2: check if the written tar file can be read correctly - string unmodified = Path.Combine(SCRATCH2_FILES_PATH, archive); + var unmodified = Path.Combine(SCRATCH2_FILES_PATH, archive); using (var archive2 = TarArchive.Open(unmodified)) { Assert.Equal(1, archive2.Entries.Count); @@ -65,29 +65,27 @@ public class TarArchiveTests : ArchiveTests [Fact] public void Tar_NonUstarArchiveWithLongNameDoesNotSkipEntriesAfterTheLongOne() { - string unmodified = Path.Combine(TEST_ARCHIVES_PATH, "very long filename.tar"); - using (var archive = TarArchive.Open(unmodified)) - { - Assert.Equal(5, archive.Entries.Count); - Assert.Contains("very long filename/", archive.Entries.Select(entry => entry.Key)); - Assert.Contains( - "very long filename/very long filename very long filename very long filename very long filename very long filename very long filename very long filename very long filename very long filename very long filename.jpg", - archive.Entries.Select(entry => entry.Key) - ); - Assert.Contains("z_file 1.txt", archive.Entries.Select(entry => entry.Key)); - Assert.Contains("z_file 2.txt", archive.Entries.Select(entry => entry.Key)); - Assert.Contains("z_file 3.txt", archive.Entries.Select(entry => entry.Key)); - } + var unmodified = Path.Combine(TEST_ARCHIVES_PATH, "very long filename.tar"); + using var archive = TarArchive.Open(unmodified); + Assert.Equal(5, archive.Entries.Count); + Assert.Contains("very long filename/", archive.Entries.Select(entry => entry.Key)); + Assert.Contains( + "very long filename/very long filename very long filename very long filename very long filename very long filename very long filename very long filename very long filename very long filename very long filename.jpg", + archive.Entries.Select(entry => entry.Key) + ); + Assert.Contains("z_file 1.txt", archive.Entries.Select(entry => entry.Key)); + Assert.Contains("z_file 2.txt", archive.Entries.Select(entry => entry.Key)); + Assert.Contains("z_file 3.txt", archive.Entries.Select(entry => entry.Key)); } [Fact] public void Tar_VeryLongFilepathReadback() { - string archive = "Tar_VeryLongFilepathReadback.tar"; + var archive = "Tar_VeryLongFilepathReadback.tar"; // create a very long filename - string longFilename = ""; - for (int i = 0; i < 600; i = longFilename.Length) + var longFilename = ""; + for (var i = 0; i < 600; i = longFilename.Length) { longFilename += i.ToString("D10") + "-"; } @@ -99,7 +97,7 @@ public class TarArchiveTests : ArchiveTests using (var writer = WriterFactory.Open(stream, ArchiveType.Tar, CompressionType.None)) using (Stream inputStream = new MemoryStream()) { - StreamWriter sw = new StreamWriter(inputStream); + var sw = new StreamWriter(inputStream); sw.Write("dummy filecontent"); sw.Flush(); @@ -108,7 +106,7 @@ public class TarArchiveTests : ArchiveTests } // Step 2: check if the written tar file can be read correctly - string unmodified = Path.Combine(SCRATCH2_FILES_PATH, archive); + var unmodified = Path.Combine(SCRATCH2_FILES_PATH, archive); using (var archive2 = TarArchive.Open(unmodified)) { Assert.Equal(1, archive2.Entries.Count); @@ -127,39 +125,37 @@ public class TarArchiveTests : ArchiveTests [Fact] public void Tar_UstarArchivePathReadLongName() { - string unmodified = Path.Combine(TEST_ARCHIVES_PATH, "ustar with long names.tar"); - using (var archive = TarArchive.Open(unmodified)) - { - Assert.Equal(6, archive.Entries.Count); - Assert.Contains("Directory/", archive.Entries.Select(entry => entry.Key)); - Assert.Contains( - "Directory/Some file with veeeeeeeeeery loooooooooong name", - archive.Entries.Select(entry => entry.Key) - ); - Assert.Contains( - "Directory/Directory with veeeeeeeeeery loooooooooong name/", - archive.Entries.Select(entry => entry.Key) - ); - Assert.Contains( - "Directory/Directory with veeeeeeeeeery loooooooooong name/Some file with veeeeeeeeeery loooooooooong name", - archive.Entries.Select(entry => entry.Key) - ); - Assert.Contains( - "Directory/Directory with veeeeeeeeeery loooooooooong name/Directory with veeeeeeeeeery loooooooooong name/", - archive.Entries.Select(entry => entry.Key) - ); - Assert.Contains( - "Directory/Directory with veeeeeeeeeery loooooooooong name/Directory with veeeeeeeeeery loooooooooong name/Some file with veeeeeeeeeery loooooooooong name", - archive.Entries.Select(entry => entry.Key) - ); - } + var unmodified = Path.Combine(TEST_ARCHIVES_PATH, "ustar with long names.tar"); + using var archive = TarArchive.Open(unmodified); + Assert.Equal(6, archive.Entries.Count); + Assert.Contains("Directory/", archive.Entries.Select(entry => entry.Key)); + Assert.Contains( + "Directory/Some file with veeeeeeeeeery loooooooooong name", + archive.Entries.Select(entry => entry.Key) + ); + Assert.Contains( + "Directory/Directory with veeeeeeeeeery loooooooooong name/", + archive.Entries.Select(entry => entry.Key) + ); + Assert.Contains( + "Directory/Directory with veeeeeeeeeery loooooooooong name/Some file with veeeeeeeeeery loooooooooong name", + archive.Entries.Select(entry => entry.Key) + ); + Assert.Contains( + "Directory/Directory with veeeeeeeeeery loooooooooong name/Directory with veeeeeeeeeery loooooooooong name/", + archive.Entries.Select(entry => entry.Key) + ); + Assert.Contains( + "Directory/Directory with veeeeeeeeeery loooooooooong name/Directory with veeeeeeeeeery loooooooooong name/Some file with veeeeeeeeeery loooooooooong name", + archive.Entries.Select(entry => entry.Key) + ); } [Fact] public void Tar_Create_New() { - string scratchPath = Path.Combine(SCRATCH_FILES_PATH, "Tar.tar"); - string unmodified = Path.Combine(TEST_ARCHIVES_PATH, "Tar.noEmptyDirs.tar"); + var scratchPath = Path.Combine(SCRATCH_FILES_PATH, "Tar.tar"); + var unmodified = Path.Combine(TEST_ARCHIVES_PATH, "Tar.noEmptyDirs.tar"); // var aropt = new Ar @@ -167,7 +163,7 @@ public class TarArchiveTests : ArchiveTests { archive.AddAllFromDirectory(ORIGINAL_FILES_PATH); var twopt = new TarWriterOptions(CompressionType.None, true); - twopt.ArchiveEncoding = new ArchiveEncoding() { Default = Encoding.GetEncoding(866) }; + twopt.ArchiveEncoding = new ArchiveEncoding { Default = Encoding.GetEncoding(866) }; archive.SaveTo(scratchPath, twopt); } CompareArchivesByPath(unmodified, scratchPath); @@ -176,10 +172,10 @@ public class TarArchiveTests : ArchiveTests [Fact] public void Tar_Random_Write_Add() { - string jpg = Path.Combine(ORIGINAL_FILES_PATH, "jpg", "test.jpg"); - string scratchPath = Path.Combine(SCRATCH_FILES_PATH, "Tar.mod.tar"); - string unmodified = Path.Combine(TEST_ARCHIVES_PATH, "Tar.mod.tar"); - string modified = Path.Combine(TEST_ARCHIVES_PATH, "Tar.noEmptyDirs.tar"); + var jpg = Path.Combine(ORIGINAL_FILES_PATH, "jpg", "test.jpg"); + var scratchPath = Path.Combine(SCRATCH_FILES_PATH, "Tar.mod.tar"); + var unmodified = Path.Combine(TEST_ARCHIVES_PATH, "Tar.mod.tar"); + var modified = Path.Combine(TEST_ARCHIVES_PATH, "Tar.noEmptyDirs.tar"); using (var archive = TarArchive.Open(unmodified)) { @@ -192,14 +188,14 @@ public class TarArchiveTests : ArchiveTests [Fact] public void Tar_Random_Write_Remove() { - string scratchPath = Path.Combine(SCRATCH_FILES_PATH, "Tar.mod.tar"); - string modified = Path.Combine(TEST_ARCHIVES_PATH, "Tar.mod.tar"); - string unmodified = Path.Combine(TEST_ARCHIVES_PATH, "Tar.noEmptyDirs.tar"); + var scratchPath = Path.Combine(SCRATCH_FILES_PATH, "Tar.mod.tar"); + var modified = Path.Combine(TEST_ARCHIVES_PATH, "Tar.mod.tar"); + var unmodified = Path.Combine(TEST_ARCHIVES_PATH, "Tar.noEmptyDirs.tar"); using (var archive = TarArchive.Open(unmodified)) { - var entry = archive.Entries.Single( - x => x.Key.EndsWith("jpg", StringComparison.OrdinalIgnoreCase) + var entry = archive.Entries.Single(x => + x.Key.NotNull().EndsWith("jpg", StringComparison.OrdinalIgnoreCase) ); archive.RemoveEntry(entry); archive.SaveTo(scratchPath, CompressionType.None); @@ -210,23 +206,19 @@ public class TarArchiveTests : ArchiveTests [Fact] public void Tar_Containing_Rar_Archive() { - string archiveFullPath = Path.Combine(TEST_ARCHIVES_PATH, "Tar.ContainsRar.tar"); - using (Stream stream = File.OpenRead(archiveFullPath)) - using (IArchive archive = ArchiveFactory.Open(stream)) - { - Assert.True(archive.Type == ArchiveType.Tar); - } + var archiveFullPath = Path.Combine(TEST_ARCHIVES_PATH, "Tar.ContainsRar.tar"); + using Stream stream = File.OpenRead(archiveFullPath); + using var archive = ArchiveFactory.Open(stream); + Assert.True(archive.Type == ArchiveType.Tar); } [Fact] public void Tar_Empty_Archive() { - string archiveFullPath = Path.Combine(TEST_ARCHIVES_PATH, "Tar.Empty.tar"); - using (Stream stream = File.OpenRead(archiveFullPath)) - using (IArchive archive = ArchiveFactory.Open(stream)) - { - Assert.True(archive.Type == ArchiveType.Tar); - } + var archiveFullPath = Path.Combine(TEST_ARCHIVES_PATH, "Tar.Empty.tar"); + using Stream stream = File.OpenRead(archiveFullPath); + using var archive = ArchiveFactory.Open(stream); + Assert.True(archive.Type == ArchiveType.Tar); } [Theory] @@ -234,26 +226,24 @@ public class TarArchiveTests : ArchiveTests [InlineData(128)] public void Tar_Japanese_Name(int length) { - using (var mstm = new MemoryStream()) + using var mstm = new MemoryStream(); + var enc = new ArchiveEncoding { Default = Encoding.UTF8 }; + var twopt = new TarWriterOptions(CompressionType.None, true); + twopt.ArchiveEncoding = enc; + var fname = new string((char)0x3042, length); + using (var tw = new TarWriter(mstm, twopt)) + using (var input = new MemoryStream(new byte[32])) { - var enc = new ArchiveEncoding() { Default = Encoding.UTF8 }; - var twopt = new TarWriterOptions(CompressionType.None, true); - twopt.ArchiveEncoding = enc; - var fname = new string((char)0x3042, length); - using (var tw = new TarWriter(mstm, twopt)) - using (var input = new MemoryStream(new byte[32])) + tw.Write(fname, input, null); + } + using (var inputMemory = new MemoryStream(mstm.ToArray())) + { + var tropt = new ReaderOptions { ArchiveEncoding = enc }; + using (var tr = TarReader.Open(inputMemory, tropt)) { - tw.Write(fname, input, null); - } - using (var inputMemory = new MemoryStream(mstm.ToArray())) - { - var tropt = new ReaderOptions() { ArchiveEncoding = enc }; - using (var tr = TarReader.Open(inputMemory, tropt)) + while (tr.MoveToNextEntry()) { - while (tr.MoveToNextEntry()) - { - Assert.Equal(fname, tr.Entry.Key); - } + Assert.Equal(fname, tr.Entry.Key); } } } @@ -269,36 +259,32 @@ public class TarArchiveTests : ArchiveTests }; var testBytes = Encoding.UTF8.GetBytes("This is a test."); - using (var memoryStream = new MemoryStream()) + using var memoryStream = new MemoryStream(); + using (var tarWriter = new TarWriter(memoryStream, tarWriterOptions)) + using (var testFileStream = new MemoryStream(testBytes)) { - using (var tarWriter = new TarWriter(memoryStream, tarWriterOptions)) - using (var testFileStream = new MemoryStream(testBytes)) - { - tarWriter.Write("test1.txt", testFileStream); - testFileStream.Position = 0; - tarWriter.Write("test2.txt", testFileStream); - } - - memoryStream.Position = 0; - - var numberOfEntries = 0; - - using (var archiveFactory = TarArchive.Open(memoryStream)) - { - foreach (var entry in archiveFactory.Entries) - { - ++numberOfEntries; - - using (var tarEntryStream = entry.OpenEntryStream()) - using (var testFileStream = new MemoryStream()) - { - tarEntryStream.CopyTo(testFileStream); - Assert.Equal(testBytes.Length, testFileStream.Length); - } - } - } - - Assert.Equal(2, numberOfEntries); + tarWriter.Write("test1.txt", testFileStream); + testFileStream.Position = 0; + tarWriter.Write("test2.txt", testFileStream); } + + memoryStream.Position = 0; + + var numberOfEntries = 0; + + using (var archiveFactory = TarArchive.Open(memoryStream)) + { + foreach (var entry in archiveFactory.Entries) + { + ++numberOfEntries; + + using var tarEntryStream = entry.OpenEntryStream(); + using var testFileStream = new MemoryStream(); + tarEntryStream.CopyTo(testFileStream); + Assert.Equal(testBytes.Length, testFileStream.Length); + } + } + + Assert.Equal(2, numberOfEntries); } } diff --git a/tests/SharpCompress.Test/Tar/TarReaderTests.cs b/tests/SharpCompress.Test/Tar/TarReaderTests.cs index 76629363..3b6a6a88 100644 --- a/tests/SharpCompress.Test/Tar/TarReaderTests.cs +++ b/tests/SharpCompress.Test/Tar/TarReaderTests.cs @@ -19,31 +19,30 @@ public class TarReaderTests : ReaderTests [Fact] public void Tar_Skip() { - using ( - Stream stream = new ForwardOnlyStream( - File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar")) - ) - ) - using (IReader reader = ReaderFactory.Open(stream)) + using Stream stream = new ForwardOnlyStream( + File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar")) + ); + using var reader = ReaderFactory.Open(stream); + var x = 0; + while (reader.MoveToNextEntry()) { - int x = 0; - while (reader.MoveToNextEntry()) + if (!reader.Entry.IsDirectory) { - if (!reader.Entry.IsDirectory) + x++; + if (x % 2 == 0) { - x++; - if (x % 2 == 0) - { - reader.WriteEntryToDirectory( - SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } - ); - } + reader.WriteEntryToDirectory( + SCRATCH_FILES_PATH, + new ExtractionOptions { ExtractFullPath = true, Overwrite = true } + ); } } } } + [Fact] + public void Tar_Z_Reader() => Read("Tar.tar.Z", CompressionType.Lzw); + [Fact] public void Tar_BZip2_Reader() => Read("Tar.tar.bz2", CompressionType.BZip2); @@ -56,6 +55,9 @@ public class TarReaderTests : ReaderTests [Fact] public void Tar_Xz_Reader() => Read("Tar.tar.xz", CompressionType.Xz); + [Fact] + public void Tar_GZip_OldGnu_Reader() => Read("Tar.oldgnu.tar.gz", CompressionType.GZip); + [Fact] public void Tar_BZip2_Entry_Stream() { @@ -67,24 +69,20 @@ public class TarReaderTests : ReaderTests if (!reader.Entry.IsDirectory) { Assert.Equal(CompressionType.BZip2, reader.Entry.CompressionType); - using (var entryStream = reader.OpenEntryStream()) + using var entryStream = reader.OpenEntryStream(); + var file = Path.GetFileName(reader.Entry.Key); + var folder = + Path.GetDirectoryName(reader.Entry.Key) + ?? throw new ArgumentNullException(); + var destdir = Path.Combine(SCRATCH_FILES_PATH, folder); + if (!Directory.Exists(destdir)) { - string file = Path.GetFileName(reader.Entry.Key); - string folder = - Path.GetDirectoryName(reader.Entry.Key) - ?? throw new ArgumentNullException(); - string destdir = Path.Combine(SCRATCH_FILES_PATH, folder); - if (!Directory.Exists(destdir)) - { - Directory.CreateDirectory(destdir); - } - string destinationFileName = Path.Combine(destdir, file); - - using (FileStream fs = File.OpenWrite(destinationFileName)) - { - entryStream.TransferTo(fs); - } + Directory.CreateDirectory(destdir); } + var destinationFileName = Path.Combine(destdir, file.NotNull()); + + using var fs = File.OpenWrite(destinationFileName); + entryStream.TransferTo(fs); } } } @@ -107,7 +105,7 @@ public class TarReaderTests : ReaderTests { if (!reader.Entry.IsDirectory) { - filePaths.Add(reader.Entry.Key); + filePaths.Add(reader.Entry.Key.NotNull("Entry Key is null")); } } } @@ -127,79 +125,77 @@ public class TarReaderTests : ReaderTests [Fact] public void Tar_BZip2_Skip_Entry_Stream() { - using (Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.bz2"))) - using (var reader = TarReader.Open(stream)) + using Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.bz2")); + using var reader = TarReader.Open(stream); + var names = new List(); + while (reader.MoveToNextEntry()) { - List names = new List(); - while (reader.MoveToNextEntry()) + if (!reader.Entry.IsDirectory) { - if (!reader.Entry.IsDirectory) - { - Assert.Equal(CompressionType.BZip2, reader.Entry.CompressionType); - using (var entryStream = reader.OpenEntryStream()) - { - entryStream.SkipEntry(); - names.Add(reader.Entry.Key); - } - } + Assert.Equal(CompressionType.BZip2, reader.Entry.CompressionType); + using var entryStream = reader.OpenEntryStream(); + entryStream.SkipEntry(); + names.Add(reader.Entry.Key.NotNull()); } - Assert.Equal(3, names.Count); } + Assert.Equal(3, names.Count); } [Fact] public void Tar_Containing_Rar_Reader() { - string archiveFullPath = Path.Combine(TEST_ARCHIVES_PATH, "Tar.ContainsRar.tar"); - using (Stream stream = File.OpenRead(archiveFullPath)) - using (IReader reader = ReaderFactory.Open(stream)) - { - Assert.True(reader.ArchiveType == ArchiveType.Tar); - } + var archiveFullPath = Path.Combine(TEST_ARCHIVES_PATH, "Tar.ContainsRar.tar"); + using Stream stream = File.OpenRead(archiveFullPath); + using var reader = ReaderFactory.Open(stream); + Assert.True(reader.ArchiveType == ArchiveType.Tar); } [Fact] public void Tar_With_TarGz_With_Flushed_EntryStream() { - string archiveFullPath = Path.Combine(TEST_ARCHIVES_PATH, "Tar.ContainsTarGz.tar"); - using (Stream stream = File.OpenRead(archiveFullPath)) - using (IReader reader = ReaderFactory.Open(stream)) - { - Assert.True(reader.MoveToNextEntry()); - Assert.Equal("inner.tar.gz", reader.Entry.Key); + var archiveFullPath = Path.Combine(TEST_ARCHIVES_PATH, "Tar.ContainsTarGz.tar"); + using Stream stream = File.OpenRead(archiveFullPath); + using var reader = ReaderFactory.Open(stream); + Assert.True(reader.MoveToNextEntry()); + Assert.Equal("inner.tar.gz", reader.Entry.Key); - using (var entryStream = reader.OpenEntryStream()) - { - using (FlushOnDisposeStream flushingStream = new FlushOnDisposeStream(entryStream)) - { - // Extract inner.tar.gz - using (var innerReader = ReaderFactory.Open(flushingStream)) - { - Assert.True(innerReader.MoveToNextEntry()); - Assert.Equal("test", innerReader.Entry.Key); - } - } - } - } + using var entryStream = reader.OpenEntryStream(); + using var flushingStream = new FlushOnDisposeStream(entryStream); + + // Extract inner.tar.gz + using var innerReader = ReaderFactory.Open(flushingStream); + Assert.True(innerReader.MoveToNextEntry()); + Assert.Equal("test", innerReader.Entry.Key); } [Fact] public void Tar_Broken_Stream() { - string archiveFullPath = Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar"); - using (Stream stream = File.OpenRead(archiveFullPath)) - using (IReader reader = ReaderFactory.Open(stream)) - { - var memoryStream = new MemoryStream(); + var archiveFullPath = Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar"); + using Stream stream = File.OpenRead(archiveFullPath); + using var reader = ReaderFactory.Open(stream); + var memoryStream = new MemoryStream(); - Action action = () => reader.MoveToNextEntry(); - var exception = Record.Exception(action); - Assert.Null(exception); - reader.MoveToNextEntry(); - reader.WriteEntryTo(memoryStream); - stream.Close(); - Assert.Throws(action); - } + Assert.True(reader.MoveToNextEntry()); + Assert.True(reader.MoveToNextEntry()); + reader.WriteEntryTo(memoryStream); + stream.Close(); + Assert.Throws(() => reader.MoveToNextEntry()); + } + + [Fact] + public void Tar_Corrupted() + { + var archiveFullPath = Path.Combine(TEST_ARCHIVES_PATH, "TarCorrupted.tar"); + using Stream stream = File.OpenRead(archiveFullPath); + using var reader = ReaderFactory.Open(stream); + var memoryStream = new MemoryStream(); + + Assert.True(reader.MoveToNextEntry()); + Assert.True(reader.MoveToNextEntry()); + reader.WriteEntryTo(memoryStream); + stream.Close(); + Assert.Throws(() => reader.MoveToNextEntry()); } #if !NETFRAMEWORK @@ -209,61 +205,55 @@ public class TarReaderTests : ReaderTests var isWindows = System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform( System.Runtime.InteropServices.OSPlatform.Windows ); - using ( - Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "TarWithSymlink.tar.gz")) - ) - using (var reader = TarReader.Open(stream)) + using Stream stream = File.OpenRead( + Path.Combine(TEST_ARCHIVES_PATH, "TarWithSymlink.tar.gz") + ); + using var reader = TarReader.Open(stream); + while (reader.MoveToNextEntry()) { - List names = new List(); - while (reader.MoveToNextEntry()) + if (reader.Entry.IsDirectory) { - if (reader.Entry.IsDirectory) + continue; + } + reader.WriteEntryToDirectory( + SCRATCH_FILES_PATH, + new ExtractionOptions { - continue; - } - reader.WriteEntryToDirectory( - SCRATCH_FILES_PATH, - new ExtractionOptions() + ExtractFullPath = true, + Overwrite = true, + WriteSymbolicLink = (sourcePath, targetPath) => { - ExtractFullPath = true, - Overwrite = true, - WriteSymbolicLink = (sourcePath, targetPath) => + if (!isWindows) { - if (!isWindows) + var link = new Mono.Unix.UnixSymbolicLinkInfo(sourcePath); + if (File.Exists(sourcePath)) { - var link = new Mono.Unix.UnixSymbolicLinkInfo(sourcePath); - if (File.Exists(sourcePath)) - { - link.Delete(); // equivalent to ln -s -f - } - link.CreateSymbolicLinkTo(targetPath); + link.Delete(); // equivalent to ln -s -f } + link.CreateSymbolicLinkTo(targetPath); } } - ); - if (!isWindows) + } + ); + if (!isWindows) + { + if (reader.Entry.LinkTarget != null) { - if (reader.Entry.LinkTarget != null) + var path = Path.Combine(SCRATCH_FILES_PATH, reader.Entry.Key.NotNull()); + var link = new Mono.Unix.UnixSymbolicLinkInfo(path); + if (link.HasContents) { - var path = System.IO.Path.Combine(SCRATCH_FILES_PATH, reader.Entry.Key); - var link = new Mono.Unix.UnixSymbolicLinkInfo(path); - if (link.HasContents) - { - // need to convert the link to an absolute path for comparison - var target = reader.Entry.LinkTarget; - var realTarget = System.IO.Path.GetFullPath( - System.IO.Path.Combine( - $"{System.IO.Path.GetDirectoryName(path)}", - target - ) - ); + // need to convert the link to an absolute path for comparison + var target = reader.Entry.LinkTarget; + var realTarget = Path.GetFullPath( + Path.Combine($"{Path.GetDirectoryName(path)}", target) + ); - Assert.Equal(realTarget, link.GetContents().ToString()); - } - else - { - Assert.True(false, "Symlink has no target"); - } + Assert.Equal(realTarget, link.GetContents().ToString()); + } + else + { + Assert.True(false, "Symlink has no target"); } } } diff --git a/tests/SharpCompress.Test/TestBase.cs b/tests/SharpCompress.Test/TestBase.cs index 3ad1fe79..184e6442 100644 --- a/tests/SharpCompress.Test/TestBase.cs +++ b/tests/SharpCompress.Test/TestBase.cs @@ -10,15 +10,15 @@ namespace SharpCompress.Test; public class TestBase : IDisposable { - private string SOLUTION_BASE_PATH; - protected string TEST_ARCHIVES_PATH; - protected string ORIGINAL_FILES_PATH; - protected string MISC_TEST_FILES_PATH; - private string SCRATCH_BASE_PATH; - public string SCRATCH_FILES_PATH; - protected string SCRATCH2_FILES_PATH; + private readonly string SOLUTION_BASE_PATH; + protected readonly string TEST_ARCHIVES_PATH; + protected readonly string ORIGINAL_FILES_PATH; + protected readonly string MISC_TEST_FILES_PATH; + private readonly string SCRATCH_BASE_PATH; + protected readonly string SCRATCH_FILES_PATH; + protected readonly string SCRATCH2_FILES_PATH; - public TestBase() + protected TestBase() { var index = AppDomain.CurrentDomain.BaseDirectory.IndexOf( "SharpCompress.Test", @@ -110,14 +110,14 @@ public class TestBase : IDisposable Assert.True(extracted.Contains(orig.Key)); CompareFilesByPath(orig.Single(), extracted[orig.Key].Single()); - CompareFilesByTimeAndAttribut(orig.Single(), extracted[orig.Key].Single()); + CompareFilesByTimeAndAttribute(orig.Single(), extracted[orig.Key].Single()); } } /// /// Verifies the files by extension also check modified time and attributes. /// - protected void VerifyFilesByExtensionEx() + private void VerifyFilesByExtensionEx() { var extracted = Directory .EnumerateFiles(SCRATCH_FILES_PATH, "*.*", SearchOption.AllDirectories) @@ -133,7 +133,7 @@ public class TestBase : IDisposable Assert.True(extracted.Contains(orig.Key)); CompareFilesByPath(orig.Single(), extracted[orig.Key].Single()); - CompareFilesByTimeAndAttribut(orig.Single(), extracted[orig.Key].Single()); + CompareFilesByTimeAndAttribute(orig.Single(), extracted[orig.Key].Single()); } } @@ -194,7 +194,7 @@ public class TestBase : IDisposable } } - protected void CompareFilesByTimeAndAttribut(string file1, string file2) + private void CompareFilesByTimeAndAttribute(string file1, string file2) { var fi1 = new FileInfo(file1); var fi2 = new FileInfo(file2); @@ -216,8 +216,8 @@ public class TestBase : IDisposable while (archive1.MoveToNextEntry()) { Assert.True(archive2.MoveToNextEntry()); - archive1Entries.Add(archive1.Entry.Key); - archive2Entries.Add(archive2.Entry.Key); + archive1Entries.Add(archive1.Entry.Key.NotNull()); + archive2Entries.Add(archive2.Entry.Key.NotNull()); } Assert.False(archive2.MoveToNextEntry()); } diff --git a/tests/SharpCompress.Test/WriterTests.cs b/tests/SharpCompress.Test/WriterTests.cs index 64ed6d2c..50695ef1 100644 --- a/tests/SharpCompress.Test/WriterTests.cs +++ b/tests/SharpCompress.Test/WriterTests.cs @@ -9,9 +9,9 @@ namespace SharpCompress.Test; public class WriterTests : TestBase { - private readonly ArchiveType type; + private readonly ArchiveType _type; - protected WriterTests(ArchiveType type) => this.type = type; + protected WriterTests(ArchiveType type) => _type = type; protected void Write( CompressionType compressionType, @@ -26,7 +26,7 @@ public class WriterTests : TestBase writerOptions.ArchiveEncoding.Default = encoding ?? Encoding.Default; - using var writer = WriterFactory.Open(stream, type, writerOptions); + using var writer = WriterFactory.Open(stream, _type, writerOptions); writer.WriteAll(ORIGINAL_FILES_PATH, "*", SearchOption.AllDirectories); } CompareArchivesByPath( @@ -43,7 +43,7 @@ public class WriterTests : TestBase using var reader = ReaderFactory.Open(NonDisposingStream.Create(stream), readerOptions); reader.WriteAllToDirectory( SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true } + new ExtractionOptions { ExtractFullPath = true } ); } VerifyFiles(); diff --git a/tests/SharpCompress.Test/Xz/Crc32Tests.cs b/tests/SharpCompress.Test/Xz/Crc32Tests.cs index 52b71078..eaa0b100 100644 --- a/tests/SharpCompress.Test/Xz/Crc32Tests.cs +++ b/tests/SharpCompress.Test/Xz/Crc32Tests.cs @@ -6,16 +6,16 @@ namespace SharpCompress.Test.Xz; public class Crc32Tests { - private const string SimpleString = @"The quick brown fox jumps over the lazy dog."; - private readonly byte[] SimpleBytes = Encoding.ASCII.GetBytes(SimpleString); - private const string SimpleString2 = + private const string SIMPLE_STRING = @"The quick brown fox jumps over the lazy dog."; + private readonly byte[] _simpleBytes = Encoding.ASCII.GetBytes(SIMPLE_STRING); + private const string SIMPLE_STRING2 = @"Life moves pretty fast. If you don't stop and look around once in a while, you could miss it."; - private readonly byte[] SimpleBytes2 = Encoding.ASCII.GetBytes(SimpleString2); + private readonly byte[] _simpleBytes2 = Encoding.ASCII.GetBytes(SIMPLE_STRING2); [Fact] public void ShortAsciiString() { - var actual = Crc32.Compute(SimpleBytes); + var actual = Crc32.Compute(_simpleBytes); Assert.Equal((uint)0x519025e9, actual); } @@ -23,7 +23,7 @@ public class Crc32Tests [Fact] public void ShortAsciiString2() { - var actual = Crc32.Compute(SimpleBytes2); + var actual = Crc32.Compute(_simpleBytes2); Assert.Equal((uint)0x6ee3ad88, actual); } diff --git a/tests/SharpCompress.Test/Xz/Crc64Tests.cs b/tests/SharpCompress.Test/Xz/Crc64Tests.cs index 79912e48..8cf71e1e 100644 --- a/tests/SharpCompress.Test/Xz/Crc64Tests.cs +++ b/tests/SharpCompress.Test/Xz/Crc64Tests.cs @@ -6,16 +6,16 @@ namespace SharpCompress.Test.Xz; public class Crc64Tests { - private const string SimpleString = @"The quick brown fox jumps over the lazy dog."; - private readonly byte[] SimpleBytes = Encoding.ASCII.GetBytes(SimpleString); - private const string SimpleString2 = + private const string SIMPLE_STRING = @"The quick brown fox jumps over the lazy dog."; + private readonly byte[] _simpleBytes = Encoding.ASCII.GetBytes(SIMPLE_STRING); + private const string SIMPLE_STRING2 = @"Life moves pretty fast. If you don't stop and look around once in a while, you could miss it."; - private readonly byte[] SimpleBytes2 = Encoding.ASCII.GetBytes(SimpleString2); + private readonly byte[] _simpleBytes2 = Encoding.ASCII.GetBytes(SIMPLE_STRING2); [Fact] public void ShortAsciiString() { - var actual = Crc64.Compute(SimpleBytes); + var actual = Crc64.Compute(_simpleBytes); Assert.Equal((ulong)0x7E210EB1B03E5A1D, actual); } @@ -23,7 +23,7 @@ public class Crc64Tests [Fact] public void ShortAsciiString2() { - var actual = Crc64.Compute(SimpleBytes2); + var actual = Crc64.Compute(_simpleBytes2); Assert.Equal((ulong)0x416B4150508661EE, actual); } diff --git a/tests/SharpCompress.Test/Xz/Filters/BCJTests.cs b/tests/SharpCompress.Test/Xz/Filters/BCJTests.cs index 427a7b98..bc16789d 100644 --- a/tests/SharpCompress.Test/Xz/Filters/BCJTests.cs +++ b/tests/SharpCompress.Test/Xz/Filters/BCJTests.cs @@ -9,56 +9,56 @@ using Xunit; namespace SharpCompress.Test.Xz.Filters; -public class BCJTests : XZTestsBase +public class BcjTests : XzTestsBase { - private readonly ArmFilter armFilter; - private readonly ArmThumbFilter armtFilter; - private readonly IA64Filter ia64Filter; - private readonly PowerPCFilter ppcFilter; - private readonly SparcFilter sparcFilter; - private readonly X86Filter x86Filter; + private readonly ArmFilter _armFilter; + private readonly ArmThumbFilter _armtFilter; + private readonly IA64Filter _ia64Filter; + private readonly PowerPCFilter _ppcFilter; + private readonly SparcFilter _sparcFilter; + private readonly X86Filter _x86Filter; - public BCJTests() + public BcjTests() { - armFilter = new ArmFilter(); - armtFilter = new ArmThumbFilter(); - ia64Filter = new IA64Filter(); - ppcFilter = new PowerPCFilter(); - sparcFilter = new SparcFilter(); - x86Filter = new X86Filter(); + _armFilter = new ArmFilter(); + _armtFilter = new ArmThumbFilter(); + _ia64Filter = new IA64Filter(); + _ppcFilter = new PowerPCFilter(); + _sparcFilter = new SparcFilter(); + _x86Filter = new X86Filter(); } [Fact] public void IsOnlyAllowedLast() { - Assert.False(armFilter.AllowAsLast); - Assert.True(armFilter.AllowAsNonLast); + Assert.False(_armFilter.AllowAsLast); + Assert.True(_armFilter.AllowAsNonLast); - Assert.False(armtFilter.AllowAsLast); - Assert.True(armtFilter.AllowAsNonLast); + Assert.False(_armtFilter.AllowAsLast); + Assert.True(_armtFilter.AllowAsNonLast); - Assert.False(ia64Filter.AllowAsLast); - Assert.True(ia64Filter.AllowAsNonLast); + Assert.False(_ia64Filter.AllowAsLast); + Assert.True(_ia64Filter.AllowAsNonLast); - Assert.False(ppcFilter.AllowAsLast); - Assert.True(ppcFilter.AllowAsNonLast); + Assert.False(_ppcFilter.AllowAsLast); + Assert.True(_ppcFilter.AllowAsNonLast); - Assert.False(sparcFilter.AllowAsLast); - Assert.True(sparcFilter.AllowAsNonLast); + Assert.False(_sparcFilter.AllowAsLast); + Assert.True(_sparcFilter.AllowAsNonLast); - Assert.False(x86Filter.AllowAsLast); - Assert.True(x86Filter.AllowAsNonLast); + Assert.False(_x86Filter.AllowAsLast); + Assert.True(_x86Filter.AllowAsNonLast); } [Fact] public void ChangesStreamSize() { - Assert.False(armFilter.ChangesDataSize); - Assert.False(armtFilter.ChangesDataSize); - Assert.False(ia64Filter.ChangesDataSize); - Assert.False(ppcFilter.ChangesDataSize); - Assert.False(sparcFilter.ChangesDataSize); - Assert.False(x86Filter.ChangesDataSize); + Assert.False(_armFilter.ChangesDataSize); + Assert.False(_armtFilter.ChangesDataSize); + Assert.False(_ia64Filter.ChangesDataSize); + Assert.False(_ppcFilter.ChangesDataSize); + Assert.False(_sparcFilter.ChangesDataSize); + Assert.False(_x86Filter.ChangesDataSize); } [Theory] @@ -67,22 +67,22 @@ public class BCJTests : XZTestsBase public void OnlyAcceptsOneByte(byte[] bytes) { InvalidDataException ex; - ex = Assert.Throws(() => armFilter.Init(bytes)); + ex = Assert.Throws(() => _armFilter.Init(bytes)); Assert.Equal("ARM properties unexpected length", ex.Message); - ex = Assert.Throws(() => armtFilter.Init(bytes)); + ex = Assert.Throws(() => _armtFilter.Init(bytes)); Assert.Equal("ARM Thumb properties unexpected length", ex.Message); - ex = Assert.Throws(() => ia64Filter.Init(bytes)); + ex = Assert.Throws(() => _ia64Filter.Init(bytes)); Assert.Equal("IA64 properties unexpected length", ex.Message); - ex = Assert.Throws(() => ppcFilter.Init(bytes)); + ex = Assert.Throws(() => _ppcFilter.Init(bytes)); Assert.Equal("PPC properties unexpected length", ex.Message); - ex = Assert.Throws(() => sparcFilter.Init(bytes)); + ex = Assert.Throws(() => _sparcFilter.Init(bytes)); Assert.Equal("SPARC properties unexpected length", ex.Message); - ex = Assert.Throws(() => x86Filter.Init(bytes)); + ex = Assert.Throws(() => _x86Filter.Init(bytes)); Assert.Equal("X86 properties unexpected length", ex.Message); } } diff --git a/tests/SharpCompress.Test/Xz/Filters/Lzma2Tests.cs b/tests/SharpCompress.Test/Xz/Filters/Lzma2Tests.cs index 83eed21c..e77bc2f8 100644 --- a/tests/SharpCompress.Test/Xz/Filters/Lzma2Tests.cs +++ b/tests/SharpCompress.Test/Xz/Filters/Lzma2Tests.cs @@ -1,25 +1,25 @@ using System; -using Xunit; using System.IO; using SharpCompress.Compressors.Xz.Filters; +using Xunit; namespace SharpCompress.Test.Xz.Filters; -public class Lzma2Tests : XZTestsBase +public class Lzma2Tests : XzTestsBase { - private readonly Lzma2Filter filter; + private readonly Lzma2Filter _filter; - public Lzma2Tests() => filter = new Lzma2Filter(); + public Lzma2Tests() => _filter = new Lzma2Filter(); [Fact] public void IsOnlyAllowedLast() { - Assert.True(filter.AllowAsLast); - Assert.False(filter.AllowAsNonLast); + Assert.True(_filter.AllowAsLast); + Assert.False(_filter.AllowAsNonLast); } [Fact] - public void ChangesStreamSize() => Assert.True(filter.ChangesDataSize); + public void ChangesStreamSize() => Assert.True(_filter.ChangesDataSize); [Theory] [InlineData(0, (uint)4 * 1024)] @@ -31,18 +31,18 @@ public class Lzma2Tests : XZTestsBase [InlineData(40, (uint)(1024 * 1024 * 1024 - 1) * 4 + 3)] public void CalculatesDictionarySize(byte inByte, uint dicSize) { - filter.Init(new[] { inByte }); - Assert.Equal(filter.DictionarySize, dicSize); + _filter.Init([inByte]); + Assert.Equal(_filter.DictionarySize, dicSize); } [Fact] public void CalculatesDictionarySizeError() { uint temp; - filter.Init(new byte[] { 41 }); + _filter.Init([41]); var ex = Assert.Throws(() => { - temp = filter.DictionarySize; + temp = _filter.DictionarySize; }); Assert.Equal("Dictionary size greater than UInt32.Max", ex.Message); } @@ -52,14 +52,14 @@ public class Lzma2Tests : XZTestsBase [InlineData(new byte[] { 0, 0 })] public void OnlyAcceptsOneByte(byte[] bytes) { - var ex = Assert.Throws(() => filter.Init(bytes)); + var ex = Assert.Throws(() => _filter.Init(bytes)); Assert.Equal("LZMA properties unexpected length", ex.Message); } [Fact] public void ReservedBytesThrow() { - var ex = Assert.Throws(() => filter.Init(new byte[] { 0xC0 })); + var ex = Assert.Throws(() => _filter.Init([0xC0])); Assert.Equal("Reserved bits used in LZMA properties", ex.Message); } } diff --git a/tests/SharpCompress.Test/Xz/XZBlockTests.cs b/tests/SharpCompress.Test/Xz/XZBlockTests.cs index 87e68d93..78873590 100644 --- a/tests/SharpCompress.Test/Xz/XZBlockTests.cs +++ b/tests/SharpCompress.Test/Xz/XZBlockTests.cs @@ -5,7 +5,7 @@ using Xunit; namespace SharpCompress.Test.Xz; -public class XZBlockTests : XZTestsBase +public class XzBlockTests : XzTestsBase { protected override void Rewind(Stream stream) => stream.Position = 12; @@ -28,10 +28,10 @@ public class XZBlockTests : XZTestsBase { var bytes = new byte[] { 0 }; using Stream indexBlockStream = new MemoryStream(bytes); - var XZBlock = new XZBlock(indexBlockStream, CheckType.CRC64, 8); + var xzBlock = new XZBlock(indexBlockStream, CheckType.CRC64, 8); Assert.Throws(() => { - ReadBytes(XZBlock, 1); + ReadBytes(xzBlock, 1); }); } @@ -42,10 +42,10 @@ public class XZBlockTests : XZTestsBase bytes[20]++; using Stream badCrcStream = new MemoryStream(bytes); Rewind(badCrcStream); - var XZBlock = new XZBlock(badCrcStream, CheckType.CRC64, 8); + var xzBlock = new XZBlock(badCrcStream, CheckType.CRC64, 8); var ex = Assert.Throws(() => { - ReadBytes(XZBlock, 1); + ReadBytes(xzBlock, 1); }); Assert.Equal("Block header corrupt", ex.Message); } @@ -53,24 +53,24 @@ public class XZBlockTests : XZTestsBase [Fact] public void CanReadM() { - var XZBlock = new XZBlock(CompressedStream, CheckType.CRC64, 8); - Assert.Equal(Encoding.ASCII.GetBytes("M"), ReadBytes(XZBlock, 1)); + var xzBlock = new XZBlock(CompressedStream, CheckType.CRC64, 8); + Assert.Equal(Encoding.ASCII.GetBytes("M"), ReadBytes(xzBlock, 1)); } [Fact] public void CanReadMary() { - var XZBlock = new XZBlock(CompressedStream, CheckType.CRC64, 8); - Assert.Equal(Encoding.ASCII.GetBytes("M"), ReadBytes(XZBlock, 1)); - Assert.Equal(Encoding.ASCII.GetBytes("a"), ReadBytes(XZBlock, 1)); - Assert.Equal(Encoding.ASCII.GetBytes("ry"), ReadBytes(XZBlock, 2)); + var xzBlock = new XZBlock(CompressedStream, CheckType.CRC64, 8); + Assert.Equal(Encoding.ASCII.GetBytes("M"), ReadBytes(xzBlock, 1)); + Assert.Equal(Encoding.ASCII.GetBytes("a"), ReadBytes(xzBlock, 1)); + Assert.Equal(Encoding.ASCII.GetBytes("ry"), ReadBytes(xzBlock, 2)); } [Fact] public void CanReadPoemWithStreamReader() { - var XZBlock = new XZBlock(CompressedStream, CheckType.CRC64, 8); - var sr = new StreamReader(XZBlock); + var xzBlock = new XZBlock(CompressedStream, CheckType.CRC64, 8); + var sr = new StreamReader(xzBlock); Assert.Equal(sr.ReadToEnd(), Original); } @@ -78,8 +78,8 @@ public class XZBlockTests : XZTestsBase public void NoopWhenNoPadding() { // CompressedStream's only block has no padding. - var XZBlock = new XZBlock(CompressedStream, CheckType.CRC64, 8); - var sr = new StreamReader(XZBlock); + var xzBlock = new XZBlock(CompressedStream, CheckType.CRC64, 8); + var sr = new StreamReader(xzBlock); sr.ReadToEnd(); Assert.Equal(0L, CompressedStream.Position % 4L); } @@ -88,8 +88,8 @@ public class XZBlockTests : XZTestsBase public void SkipsPaddingWhenPresent() { // CompressedIndexedStream's first block has 1-byte padding. - var XZBlock = new XZBlock(CompressedIndexedStream, CheckType.CRC64, 8); - var sr = new StreamReader(XZBlock); + var xzBlock = new XZBlock(CompressedIndexedStream, CheckType.CRC64, 8); + var sr = new StreamReader(xzBlock); sr.ReadToEnd(); Assert.Equal(0L, CompressedIndexedStream.Position % 4L); } diff --git a/tests/SharpCompress.Test/Xz/XZHeaderTests.cs b/tests/SharpCompress.Test/Xz/XZHeaderTests.cs index 3fa9dc86..8815b7a9 100644 --- a/tests/SharpCompress.Test/Xz/XZHeaderTests.cs +++ b/tests/SharpCompress.Test/Xz/XZHeaderTests.cs @@ -1,26 +1,24 @@ -using SharpCompress.Compressors.Xz; using System.IO; +using SharpCompress.Compressors.Xz; using Xunit; namespace SharpCompress.Test.Xz; -public class XZHeaderTests : XZTestsBase +public class XzHeaderTests : XzTestsBase { [Fact] public void ChecksMagicNumber() { var bytes = (byte[])Compressed.Clone(); bytes[3]++; - using (Stream badMagicNumberStream = new MemoryStream(bytes)) + using Stream badMagicNumberStream = new MemoryStream(bytes); + var br = new BinaryReader(badMagicNumberStream); + var header = new XZHeader(br); + var ex = Assert.Throws(() => { - BinaryReader br = new BinaryReader(badMagicNumberStream); - var header = new XZHeader(br); - var ex = Assert.Throws(() => - { - header.Process(); - }); - Assert.Equal("Invalid XZ Stream", ex.Message); - } + header.Process(); + }); + Assert.Equal("Invalid XZ Stream", ex.Message); } [Fact] @@ -28,42 +26,38 @@ public class XZHeaderTests : XZTestsBase { var bytes = (byte[])Compressed.Clone(); bytes[8]++; - using (Stream badCrcStream = new MemoryStream(bytes)) + using Stream badCrcStream = new MemoryStream(bytes); + var br = new BinaryReader(badCrcStream); + var header = new XZHeader(br); + var ex = Assert.Throws(() => { - BinaryReader br = new BinaryReader(badCrcStream); - var header = new XZHeader(br); - var ex = Assert.Throws(() => - { - header.Process(); - }); - Assert.Equal("Stream header corrupt", ex.Message); - } + header.Process(); + }); + Assert.Equal("Stream header corrupt", ex.Message); } [Fact] public void BadVersionIfCrcOkButStreamFlagUnknown() { var bytes = (byte[])Compressed.Clone(); - byte[] streamFlags = { 0x00, 0xF4 }; - byte[] crc = Crc32.Compute(streamFlags).ToLittleEndianBytes(); + byte[] streamFlags = [0x00, 0xF4]; + var crc = Crc32.Compute(streamFlags).ToLittleEndianBytes(); streamFlags.CopyTo(bytes, 6); crc.CopyTo(bytes, 8); - using (Stream badFlagStream = new MemoryStream(bytes)) + using Stream badFlagStream = new MemoryStream(bytes); + var br = new BinaryReader(badFlagStream); + var header = new XZHeader(br); + var ex = Assert.Throws(() => { - BinaryReader br = new BinaryReader(badFlagStream); - var header = new XZHeader(br); - var ex = Assert.Throws(() => - { - header.Process(); - }); - Assert.Equal("Unknown XZ Stream Version", ex.Message); - } + header.Process(); + }); + Assert.Equal("Unknown XZ Stream Version", ex.Message); } [Fact] public void ProcessesBlockCheckType() { - BinaryReader br = new BinaryReader(CompressedStream); + var br = new BinaryReader(CompressedStream); var header = new XZHeader(br); header.Process(); Assert.Equal(CheckType.CRC64, header.BlockCheckType); @@ -72,7 +66,7 @@ public class XZHeaderTests : XZTestsBase [Fact] public void CanCalculateBlockCheckSize() { - BinaryReader br = new BinaryReader(CompressedStream); + var br = new BinaryReader(CompressedStream); var header = new XZHeader(br); header.Process(); Assert.Equal(8, header.BlockCheckSize); diff --git a/tests/SharpCompress.Test/Xz/XZIndexTests.cs b/tests/SharpCompress.Test/Xz/XZIndexTests.cs index 9c9d15bf..b00b9d1a 100644 --- a/tests/SharpCompress.Test/Xz/XZIndexTests.cs +++ b/tests/SharpCompress.Test/Xz/XZIndexTests.cs @@ -1,10 +1,10 @@ -using SharpCompress.Compressors.Xz; -using System.IO; +using System.IO; +using SharpCompress.Compressors.Xz; using Xunit; namespace SharpCompress.Test.Xz; -public class XZIndexTests : XZTestsBase +public class XzIndexTests : XzTestsBase { protected override void RewindEmpty(Stream stream) => stream.Position = 12; @@ -15,29 +15,25 @@ public class XZIndexTests : XZTestsBase [Fact] public void RecordsStreamStartOnInit() { - using (Stream badStream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5 })) - { - BinaryReader br = new BinaryReader(badStream); - var index = new XZIndex(br, false); - Assert.Equal(0, index.StreamStartPosition); - } + using Stream badStream = new MemoryStream([1, 2, 3, 4, 5]); + var br = new BinaryReader(badStream); + var index = new XZIndex(br, false); + Assert.Equal(0, index.StreamStartPosition); } [Fact] public void ThrowsIfHasNoIndexMarker() { - using (Stream badStream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5 })) - { - BinaryReader br = new BinaryReader(badStream); - var index = new XZIndex(br, false); - Assert.Throws(() => index.Process()); - } + using Stream badStream = new MemoryStream([1, 2, 3, 4, 5]); + var br = new BinaryReader(badStream); + var index = new XZIndex(br, false); + Assert.Throws(() => index.Process()); } [Fact] public void ReadsNoRecord() { - BinaryReader br = new BinaryReader(CompressedEmptyStream); + var br = new BinaryReader(CompressedEmptyStream); var index = new XZIndex(br, false); index.Process(); Assert.Equal((ulong)0, index.NumberOfRecords); @@ -46,7 +42,7 @@ public class XZIndexTests : XZTestsBase [Fact] public void ReadsOneRecord() { - BinaryReader br = new BinaryReader(CompressedStream); + var br = new BinaryReader(CompressedStream); var index = new XZIndex(br, false); index.Process(); Assert.Equal((ulong)1, index.NumberOfRecords); @@ -55,7 +51,7 @@ public class XZIndexTests : XZTestsBase [Fact] public void ReadsMultipleRecords() { - BinaryReader br = new BinaryReader(CompressedIndexedStream); + var br = new BinaryReader(CompressedIndexedStream); var index = new XZIndex(br, false); index.Process(); Assert.Equal((ulong)2, index.NumberOfRecords); @@ -64,7 +60,7 @@ public class XZIndexTests : XZTestsBase [Fact] public void ReadsFirstRecord() { - BinaryReader br = new BinaryReader(CompressedStream); + var br = new BinaryReader(CompressedStream); var index = new XZIndex(br, false); index.Process(); Assert.Equal((ulong)OriginalBytes.Length, index.Records[0].UncompressedSize); @@ -74,31 +70,12 @@ public class XZIndexTests : XZTestsBase public void SkipsPadding() { // Index with 3-byte padding. - using ( - Stream badStream = new MemoryStream( - new byte[] - { - 0x00, - 0x01, - 0x10, - 0x80, - 0x01, - 0x00, - 0x00, - 0x00, - 0xB1, - 0x01, - 0xD9, - 0xC9, - 0xFF - } - ) - ) - { - BinaryReader br = new BinaryReader(badStream); - var index = new XZIndex(br, false); - index.Process(); - Assert.Equal(0L, badStream.Position % 4L); - } + using Stream badStream = new MemoryStream( + [0x00, 0x01, 0x10, 0x80, 0x01, 0x00, 0x00, 0x00, 0xB1, 0x01, 0xD9, 0xC9, 0xFF] + ); + var br = new BinaryReader(badStream); + var index = new XZIndex(br, false); + index.Process(); + Assert.Equal(0L, badStream.Position % 4L); } } diff --git a/tests/SharpCompress.Test/Xz/XZStreamTests.cs b/tests/SharpCompress.Test/Xz/XZStreamTests.cs index 1f50fb40..02c5020c 100644 --- a/tests/SharpCompress.Test/Xz/XZStreamTests.cs +++ b/tests/SharpCompress.Test/Xz/XZStreamTests.cs @@ -1,41 +1,35 @@ -using SharpCompress.Compressors.Xz; -using System.IO; +using System.IO; +using SharpCompress.Compressors.Xz; using Xunit; namespace SharpCompress.Test.Xz; -public class XZStreamTests : XZTestsBase +public class XzStreamTests : XzTestsBase { [Fact] public void CanReadEmptyStream() { - XZStream xz = new XZStream(CompressedEmptyStream); - using (var sr = new StreamReader(xz)) - { - string uncompressed = sr.ReadToEnd(); - Assert.Equal(OriginalEmpty, uncompressed); - } + var xz = new XZStream(CompressedEmptyStream); + using var sr = new StreamReader(xz); + var uncompressed = sr.ReadToEnd(); + Assert.Equal(OriginalEmpty, uncompressed); } [Fact] public void CanReadStream() { - XZStream xz = new XZStream(CompressedStream); - using (var sr = new StreamReader(xz)) - { - string uncompressed = sr.ReadToEnd(); - Assert.Equal(Original, uncompressed); - } + var xz = new XZStream(CompressedStream); + using var sr = new StreamReader(xz); + var uncompressed = sr.ReadToEnd(); + Assert.Equal(Original, uncompressed); } [Fact] public void CanReadIndexedStream() { - XZStream xz = new XZStream(CompressedIndexedStream); - using (var sr = new StreamReader(xz)) - { - string uncompressed = sr.ReadToEnd(); - Assert.Equal(OriginalIndexed, uncompressed); - } + var xz = new XZStream(CompressedIndexedStream); + using var sr = new StreamReader(xz); + var uncompressed = sr.ReadToEnd(); + Assert.Equal(OriginalIndexed, uncompressed); } } diff --git a/tests/SharpCompress.Test/Xz/XZTestsBase.cs b/tests/SharpCompress.Test/Xz/XZTestsBase.cs index 5a1b5cbb..a679934e 100644 --- a/tests/SharpCompress.Test/Xz/XZTestsBase.cs +++ b/tests/SharpCompress.Test/Xz/XZTestsBase.cs @@ -4,9 +4,9 @@ using System.Text; namespace SharpCompress.Test.Xz; -public abstract class XZTestsBase : IDisposable +public abstract class XzTestsBase : IDisposable { - public XZTestsBase() + public XzTestsBase() { RewindEmpty(CompressedEmptyStream); Rewind(CompressedStream); @@ -29,8 +29,7 @@ public abstract class XZTestsBase : IDisposable protected Stream CompressedEmptyStream { get; } = new MemoryStream(CompressedEmpty); protected static byte[] CompressedEmpty { get; } = - new byte[] - { + [ 0xfd, 0x37, 0x7a, @@ -63,7 +62,7 @@ public abstract class XZTestsBase : IDisposable 0x01, 0x59, 0x5a - }; + ]; protected static byte[] OriginalEmptyBytes => Encoding.ASCII.GetBytes(OriginalEmpty); @@ -72,8 +71,7 @@ public abstract class XZTestsBase : IDisposable protected Stream CompressedStream { get; } = new MemoryStream(Compressed); protected static byte[] Compressed { get; } = - new byte[] - { + [ 0xfd, 0x37, 0x7a, @@ -454,7 +452,7 @@ public abstract class XZTestsBase : IDisposable 0x04, 0x59, 0x5a - }; + ]; protected static byte[] OriginalBytes => Encoding.ASCII.GetBytes(Original); protected static string Original { get; } = @@ -481,8 +479,7 @@ public abstract class XZTestsBase : IDisposable protected Stream CompressedIndexedStream { get; } = new MemoryStream(CompressedIndexed); protected static byte[] CompressedIndexed { get; } = - new byte[] - { + [ 0xfd, 0x37, 0x7a, @@ -1123,7 +1120,7 @@ public abstract class XZTestsBase : IDisposable 0x01, 0x59, 0x5a - }; + ]; protected static byte[] OriginalIndexedBytes => Encoding.ASCII.GetBytes(OriginalIndexed); diff --git a/tests/SharpCompress.Test/Zip/Zip64Tests.cs b/tests/SharpCompress.Test/Zip/Zip64Tests.cs index 739867f7..a89b1128 100644 --- a/tests/SharpCompress.Test/Zip/Zip64Tests.cs +++ b/tests/SharpCompress.Test/Zip/Zip64Tests.cs @@ -3,6 +3,8 @@ using System.IO; using System.Linq; using SharpCompress.Archives; using SharpCompress.Common; +using SharpCompress.Common.Zip; +using SharpCompress.Compressors.Deflate; using SharpCompress.Readers; using SharpCompress.Readers.Zip; using SharpCompress.Test.Mocks; @@ -23,27 +25,27 @@ public class Zip64Tests : WriterTests [Trait("format", "zip64")] public void Zip64_Single_Large_File() => // One single file, requires zip64 - RunSingleTest(1, FOUR_GB_LIMIT, set_zip64: true, forward_only: false); + RunSingleTest(1, FOUR_GB_LIMIT, setZip64: true, forwardOnly: false); [Trait("format", "zip64")] public void Zip64_Two_Large_Files() => // One single file, requires zip64 - RunSingleTest(2, FOUR_GB_LIMIT, set_zip64: true, forward_only: false); + RunSingleTest(2, FOUR_GB_LIMIT, setZip64: true, forwardOnly: false); [Trait("format", "zip64")] public void Zip64_Two_Small_files() => // Multiple files, does not require zip64 - RunSingleTest(2, FOUR_GB_LIMIT / 2, set_zip64: false, forward_only: false); + RunSingleTest(2, FOUR_GB_LIMIT / 2, setZip64: false, forwardOnly: false); [Trait("format", "zip64")] public void Zip64_Two_Small_files_stream() => // Multiple files, does not require zip64, and works with streams - RunSingleTest(2, FOUR_GB_LIMIT / 2, set_zip64: false, forward_only: true); + RunSingleTest(2, FOUR_GB_LIMIT / 2, setZip64: false, forwardOnly: true); [Trait("format", "zip64")] public void Zip64_Two_Small_Files_Zip64() => // Multiple files, use zip64 even though it is not required - RunSingleTest(2, FOUR_GB_LIMIT / 2, set_zip64: true, forward_only: false); + RunSingleTest(2, FOUR_GB_LIMIT / 2, setZip64: true, forwardOnly: false); [Trait("format", "zip64")] public void Zip64_Single_Large_File_Fail() @@ -51,7 +53,7 @@ public class Zip64Tests : WriterTests try { // One single file, should fail - RunSingleTest(1, FOUR_GB_LIMIT, set_zip64: false, forward_only: false); + RunSingleTest(1, FOUR_GB_LIMIT, setZip64: false, forwardOnly: false); throw new InvalidOperationException("Test did not fail?"); } catch (NotSupportedException) { } @@ -63,7 +65,7 @@ public class Zip64Tests : WriterTests try { // One single file, should fail (fast) with zip64 - RunSingleTest(1, FOUR_GB_LIMIT, set_zip64: true, forward_only: true); + RunSingleTest(1, FOUR_GB_LIMIT, setZip64: true, forwardOnly: true); throw new InvalidOperationException("Test did not fail?"); } catch (NotSupportedException) { } @@ -75,7 +77,7 @@ public class Zip64Tests : WriterTests try { // One single file, should fail once the write discovers the problem - RunSingleTest(1, FOUR_GB_LIMIT, set_zip64: false, forward_only: true); + RunSingleTest(1, FOUR_GB_LIMIT, setZip64: false, forwardOnly: true); throw new InvalidOperationException("Test did not fail?"); } catch (NotSupportedException) { } @@ -84,9 +86,9 @@ public class Zip64Tests : WriterTests public void RunSingleTest( long files, long filesize, - bool set_zip64, - bool forward_only, - long write_chunk_size = 1024 * 1024, + bool setZip64, + bool forwardOnly, + long writeChunkSize = 1024 * 1024, string filename = "zip64-test.zip" ) { @@ -99,7 +101,7 @@ public class Zip64Tests : WriterTests if (!File.Exists(filename)) { - CreateZipArchive(filename, files, filesize, write_chunk_size, set_zip64, forward_only); + CreateZipArchive(filename, files, filesize, writeChunkSize, setZip64, forwardOnly); } var resForward = ReadForwardOnly(filename); @@ -138,23 +140,20 @@ public class Zip64Tests : WriterTests long files, long filesize, long chunksize, - bool set_zip64, - bool forward_only + bool setZip64, + bool forwardOnly ) { var data = new byte[chunksize]; // Use deflate for speed - var opts = new ZipWriterOptions(CompressionType.Deflate) { UseZip64 = set_zip64 }; + var opts = new ZipWriterOptions(CompressionType.Deflate) { UseZip64 = setZip64 }; // Use no compression to ensure we hit the limits (actually inflates a bit, but seems better than using method==Store) - var eo = new ZipWriterEntryOptions() - { - DeflateCompressionLevel = Compressors.Deflate.CompressionLevel.None - }; + var eo = new ZipWriterEntryOptions { DeflateCompressionLevel = CompressionLevel.None }; using var zip = File.OpenWrite(filename); - using var st = forward_only ? (Stream)new ForwardOnlyStream(zip) : zip; + using var st = forwardOnly ? (Stream)new ForwardOnlyStream(zip) : zip; using var zipWriter = (ZipWriter)WriterFactory.Open(st, ArchiveType.Zip, opts); for (var i = 0; i < files; i++) { @@ -173,9 +172,9 @@ public class Zip64Tests : WriterTests { long count = 0; long size = 0; - Common.Zip.ZipEntry? prev = null; + ZipEntry? prev = null; using (var fs = File.OpenRead(filename)) - using (var rd = ZipReader.Open(fs, new ReaderOptions() { LookForHeader = false })) + using (var rd = ZipReader.Open(fs, new ReaderOptions { LookForHeader = false })) { while (rd.MoveToNextEntry()) { diff --git a/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs b/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs index fde87dc6..494e02c9 100644 --- a/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs +++ b/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs @@ -16,6 +16,14 @@ public class ZipArchiveTests : ArchiveTests { public ZipArchiveTests() => UseExtensionInsteadOfNameToVerify = true; + [Fact] + public void Zip_Shrink_ArchiveStreamRead() + { + UseExtensionInsteadOfNameToVerify = true; + UseCaseInsensitiveToVerify = true; + ArchiveStreamRead("Zip.shrink.zip"); + } + [Fact] public void Zip_ZipX_ArchiveStreamRead() => ArchiveStreamRead("Zip.zipx"); @@ -178,14 +186,14 @@ public class ZipArchiveTests : ArchiveTests [Fact] public void Zip_Random_Write_Remove() { - string scratchPath = Path.Combine(SCRATCH_FILES_PATH, "Zip.deflate.mod.zip"); - string unmodified = Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.noEmptyDirs.zip"); - string modified = Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.mod.zip"); + var scratchPath = Path.Combine(SCRATCH_FILES_PATH, "Zip.deflate.mod.zip"); + var unmodified = Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.noEmptyDirs.zip"); + var modified = Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.mod.zip"); using (var archive = ZipArchive.Open(unmodified)) { - var entry = archive.Entries.Single( - x => x.Key.EndsWith("jpg", StringComparison.OrdinalIgnoreCase) + var entry = archive.Entries.Single(x => + x.Key.NotNull().EndsWith("jpg", StringComparison.OrdinalIgnoreCase) ); archive.RemoveEntry(entry); @@ -200,10 +208,10 @@ public class ZipArchiveTests : ArchiveTests [Fact] public void Zip_Random_Write_Add() { - string jpg = Path.Combine(ORIGINAL_FILES_PATH, "jpg", "test.jpg"); - string scratchPath = Path.Combine(SCRATCH_FILES_PATH, "Zip.deflate.mod.zip"); - string unmodified = Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.mod.zip"); - string modified = Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.mod2.zip"); + var jpg = Path.Combine(ORIGINAL_FILES_PATH, "jpg", "test.jpg"); + var scratchPath = Path.Combine(SCRATCH_FILES_PATH, "Zip.deflate.mod.zip"); + var unmodified = Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.mod.zip"); + var modified = Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.mod2.zip"); using (var archive = ZipArchive.Open(unmodified)) { @@ -220,12 +228,12 @@ public class ZipArchiveTests : ArchiveTests [Fact] public void Zip_Save_Twice() { - string scratchPath1 = Path.Combine(SCRATCH_FILES_PATH, "a.zip"); - string scratchPath2 = Path.Combine(SCRATCH_FILES_PATH, "b.zip"); + var scratchPath1 = Path.Combine(SCRATCH_FILES_PATH, "a.zip"); + var scratchPath2 = Path.Combine(SCRATCH_FILES_PATH, "b.zip"); using (var arc = ZipArchive.Create()) { - string str = "test.txt"; + var str = "test.txt"; var source = new MemoryStream(Encoding.UTF8.GetBytes(str)); arc.AddEntry("test.txt", source, true, source.Length); arc.SaveTo(scratchPath1, CompressionType.Deflate); @@ -238,42 +246,38 @@ public class ZipArchiveTests : ArchiveTests [Fact] public void Zip_Removal_Poly() { - string scratchPath = Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.noEmptyDirs.zip"); + var scratchPath = Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.noEmptyDirs.zip"); - using (ZipArchive vfs = (ZipArchive)ArchiveFactory.Open(scratchPath)) - { - var e = vfs.Entries.First( - v => v.Key.EndsWith("jpg", StringComparison.OrdinalIgnoreCase) - ); - vfs.RemoveEntry(e); - Assert.Null( - vfs.Entries.FirstOrDefault( - v => v.Key.EndsWith("jpg", StringComparison.OrdinalIgnoreCase) - ) - ); - Assert.Null( - ((IArchive)vfs).Entries.FirstOrDefault( - v => v.Key.EndsWith("jpg", StringComparison.OrdinalIgnoreCase) - ) - ); - } + using var vfs = (ZipArchive)ArchiveFactory.Open(scratchPath); + var e = vfs.Entries.First(v => + v.Key.NotNull().EndsWith("jpg", StringComparison.OrdinalIgnoreCase) + ); + vfs.RemoveEntry(e); + Assert.Null( + vfs.Entries.FirstOrDefault(v => + v.Key.NotNull().EndsWith("jpg", StringComparison.OrdinalIgnoreCase) + ) + ); + Assert.Null( + ((IArchive)vfs).Entries.FirstOrDefault(v => + v.Key.NotNull().EndsWith("jpg", StringComparison.OrdinalIgnoreCase) + ) + ); } [Fact] public void Zip_Create_NoDups() { - using (var arc = ZipArchive.Create()) - { - arc.AddEntry("1.txt", new MemoryStream()); - Assert.Throws(() => arc.AddEntry("\\1.txt", new MemoryStream())); - } + using var arc = ZipArchive.Create(); + arc.AddEntry("1.txt", new MemoryStream()); + Assert.Throws(() => arc.AddEntry("\\1.txt", new MemoryStream())); } [Fact] public void Zip_Create_Same_Stream() { - string scratchPath1 = Path.Combine(SCRATCH_FILES_PATH, "a.zip"); - string scratchPath2 = Path.Combine(SCRATCH_FILES_PATH, "b.zip"); + var scratchPath1 = Path.Combine(SCRATCH_FILES_PATH, "a.zip"); + var scratchPath2 = Path.Combine(SCRATCH_FILES_PATH, "b.zip"); using (var arc = ZipArchive.Create()) { @@ -318,8 +322,8 @@ public class ZipArchiveTests : ArchiveTests } File.Copy(file, newFileName); } - string scratchPath = Path.Combine(SCRATCH2_FILES_PATH, "Zip.deflate.noEmptyDirs.zip"); - string unmodified = Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.noEmptyDirs.zip"); + var scratchPath = Path.Combine(SCRATCH2_FILES_PATH, "Zip.deflate.noEmptyDirs.zip"); + var unmodified = Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.noEmptyDirs.zip"); using (var archive = ZipArchive.Create()) { @@ -385,19 +389,19 @@ public class ZipArchiveTests : ArchiveTests } File.Copy(file, newFileName); } - string scratchPath = Path.Combine(SCRATCH2_FILES_PATH, "Zip.deflate.noEmptyDirs.zip"); + var scratchPath = Path.Combine(SCRATCH2_FILES_PATH, "Zip.deflate.noEmptyDirs.zip"); using (var archive = ZipArchive.Create()) { archive.AddAllFromDirectory(SCRATCH_FILES_PATH); archive.RemoveEntry( - archive.Entries.Single( - x => x.Key.EndsWith("jpg", StringComparison.OrdinalIgnoreCase) + archive.Entries.Single(x => + x.Key.NotNull().EndsWith("jpg", StringComparison.OrdinalIgnoreCase) ) ); Assert.Null( - archive.Entries.FirstOrDefault( - x => x.Key.EndsWith("jpg", StringComparison.OrdinalIgnoreCase) + archive.Entries.FirstOrDefault(x => + x.Key.NotNull().EndsWith("jpg", StringComparison.OrdinalIgnoreCase) ) ); } @@ -410,7 +414,7 @@ public class ZipArchiveTests : ArchiveTests using ( var reader = ZipArchive.Open( Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.WinzipAES.zip"), - new ReaderOptions() { Password = "test" } + new ReaderOptions { Password = "test" } ) ) { @@ -418,7 +422,7 @@ public class ZipArchiveTests : ArchiveTests { entry.WriteToDirectory( SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } + new ExtractionOptions { ExtractFullPath = true, Overwrite = true } ); } } @@ -428,40 +432,32 @@ public class ZipArchiveTests : ArchiveTests [Fact] public void Zip_Deflate_WinzipAES_MultiOpenEntryStream() { - using ( - var reader = ZipArchive.Open( - Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.WinzipAES2.zip"), - new ReaderOptions() { Password = "test" } - ) - ) + using var reader = ZipArchive.Open( + Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.WinzipAES2.zip"), + new ReaderOptions { Password = "test" } + ); + foreach (var entry in reader.Entries.Where(x => !x.IsDirectory)) { - foreach (var entry in reader.Entries.Where(x => !x.IsDirectory)) - { - var stream = entry.OpenEntryStream(); - Assert.NotNull(stream); - var ex = Record.Exception(() => stream = entry.OpenEntryStream()); - Assert.Null(ex); - } + var stream = entry.OpenEntryStream(); + Assert.NotNull(stream); + var ex = Record.Exception(() => stream = entry.OpenEntryStream()); + Assert.Null(ex); } } [Fact] public void Zip_Read_Volume_Comment() { - using ( - var reader = ZipArchive.Open( - Path.Combine(TEST_ARCHIVES_PATH, "Zip.zip64.zip"), - new ReaderOptions() { Password = "test" } - ) - ) - { - var isComplete = reader.IsComplete; - Assert.Equal(1, reader.Volumes.Count); + using var reader = ZipArchive.Open( + Path.Combine(TEST_ARCHIVES_PATH, "Zip.zip64.zip"), + new ReaderOptions { Password = "test" } + ); + var isComplete = reader.IsComplete; + Assert.Equal(1, reader.Volumes.Count); - string expectedComment = - "Encoding:utf-8 || Compression:Deflate levelDefault || Encrypt:None || ZIP64:Always\r\nCreated at 2017-Jan-23 14:10:43 || DotNetZip Tool v1.9.1.8\r\nTest zip64 archive"; - Assert.Equal(expectedComment, reader.Volumes.First().Comment); - } + var expectedComment = + "Encoding:utf-8 || Compression:Deflate levelDefault || Encrypt:None || ZIP64:Always\r\nCreated at 2017-Jan-23 14:10:43 || DotNetZip Tool v1.9.1.8\r\nTest zip64 archive"; + Assert.Equal(expectedComment, reader.Volumes.First().Comment); } [Fact] @@ -470,7 +466,7 @@ public class ZipArchiveTests : ArchiveTests using ( var reader = ZipArchive.Open( Path.Combine(TEST_ARCHIVES_PATH, "Zip.bzip2.pkware.zip"), - new ReaderOptions() { Password = "test" } + new ReaderOptions { Password = "test" } ) ) { @@ -478,7 +474,7 @@ public class ZipArchiveTests : ArchiveTests { entry.WriteToDirectory( SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } + new ExtractionOptions { ExtractFullPath = true, Overwrite = true } ); } } @@ -488,10 +484,10 @@ public class ZipArchiveTests : ArchiveTests [Fact] public void Zip_Random_Entry_Access() { - string unmodified = Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.noEmptyDirs.zip"); + var unmodified = Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.noEmptyDirs.zip"); - ZipArchive a = ZipArchive.Open(unmodified); - int count = 0; + var a = ZipArchive.Open(unmodified); + var count = 0; foreach (var e in a.Entries) { count++; @@ -502,7 +498,7 @@ public class ZipArchiveTests : ArchiveTests a.Dispose(); a = ZipArchive.Open(unmodified); - int count2 = 0; + var count2 = 0; foreach (var e in a.Entries) { @@ -518,7 +514,7 @@ public class ZipArchiveTests : ArchiveTests } } - int count3 = 0; + var count3 = 0; foreach (var e in a.Entries) { count3++; @@ -530,29 +526,21 @@ public class ZipArchiveTests : ArchiveTests [Fact] public void Zip_Deflate_PKWear_Multipy_Entry_Access() { - string zipFile = Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.pkware.zip"); + var zipFile = Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.pkware.zip"); - using (FileStream fileStream = File.Open(zipFile, FileMode.Open)) + using var fileStream = File.Open(zipFile, FileMode.Open); + using var archive = ArchiveFactory.Open( + fileStream, + new ReaderOptions { Password = "12345678" } + ); + var entries = archive.Entries.Where(entry => !entry.IsDirectory); + foreach (var entry in entries) { - using ( - IArchive archive = ArchiveFactory.Open( - fileStream, - new ReaderOptions { Password = "12345678" } - ) - ) + for (var i = 0; i < 100; i++) { - var entries = archive.Entries.Where(entry => !entry.IsDirectory); - foreach (IArchiveEntry entry in entries) - { - for (var i = 0; i < 100; i++) - { - using (var memoryStream = new MemoryStream()) - using (Stream entryStream = entry.OpenEntryStream()) - { - entryStream.CopyTo(memoryStream); - } - } - } + using var memoryStream = new MemoryStream(); + using var entryStream = entry.OpenEntryStream(); + entryStream.CopyTo(memoryStream); } } } @@ -563,19 +551,17 @@ public class ZipArchiveTests : ArchiveTests //windows only because of the paths Skip.IfNot(Environment.OSVersion.Platform == PlatformID.Win32NT); - string zipFile = Path.Combine(TEST_ARCHIVES_PATH, "Zip.Evil.zip"); + var zipFile = Path.Combine(TEST_ARCHIVES_PATH, "Zip.Evil.zip"); Assert.ThrowsAny(() => { - using (var archive = ZipArchive.Open(zipFile)) + using var archive = ZipArchive.Open(zipFile); + foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) { - foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) - { - entry.WriteToDirectory( - SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } - ); - } + entry.WriteToDirectory( + SCRATCH_FILES_PATH, + new ExtractionOptions { ExtractFullPath = true, Overwrite = true } + ); } }); } @@ -590,9 +576,7 @@ public class ZipArchiveTests : ArchiveTests { MemoryStream stream = new NonSeekableMemoryStream(); - using ( - IWriter zipWriter = WriterFactory.Open(stream, ArchiveType.Zip, CompressionType.Deflate) - ) + using (var zipWriter = WriterFactory.Open(stream, ArchiveType.Zip, CompressionType.Deflate)) { zipWriter.Write("foo.txt", new MemoryStream(Array.Empty())); zipWriter.Write("foo2.txt", new MemoryStream(new byte[10])); @@ -605,16 +589,14 @@ public class ZipArchiveTests : ArchiveTests { foreach (var entry in zipArchive.Entries) { - using (var entryStream = entry.OpenEntryStream()) + using var entryStream = entry.OpenEntryStream(); + var tempStream = new MemoryStream(); + const int bufSize = 0x1000; + var buf = new byte[bufSize]; + var bytesRead = 0; + while ((bytesRead = entryStream.Read(buf, 0, bufSize)) > 0) { - MemoryStream tempStream = new MemoryStream(); - const int bufSize = 0x1000; - byte[] buf = new byte[bufSize]; - int bytesRead = 0; - while ((bytesRead = entryStream.Read(buf, 0, bufSize)) > 0) - { - tempStream.Write(buf, 0, bytesRead); - } + tempStream.Write(buf, 0, bytesRead); } } } @@ -623,119 +605,105 @@ public class ZipArchiveTests : ArchiveTests [Fact] public void Zip_BadLocalExtra_Read() { - string zipPath = Path.Combine(TEST_ARCHIVES_PATH, "Zip.badlocalextra.zip"); + var zipPath = Path.Combine(TEST_ARCHIVES_PATH, "Zip.badlocalextra.zip"); - using (ZipArchive za = ZipArchive.Open(zipPath)) + using var za = ZipArchive.Open(zipPath); + var ex = Record.Exception(() => { - var ex = Record.Exception(() => - { - var firstEntry = za.Entries.First(x => x.Key == "first.txt"); - var buffer = new byte[4096]; + var firstEntry = za.Entries.First(x => x.Key == "first.txt"); + var buffer = new byte[4096]; - using (var memoryStream = new MemoryStream()) - using (var firstStream = firstEntry.OpenEntryStream()) - { - firstStream.CopyTo(memoryStream); - Assert.Equal(199, memoryStream.Length); - } - }); + using var memoryStream = new MemoryStream(); + using var firstStream = firstEntry.OpenEntryStream(); + firstStream.CopyTo(memoryStream); + Assert.Equal(199, memoryStream.Length); + }); - Assert.Null(ex); - } + Assert.Null(ex); } [Fact] public void Zip_NoCompression_DataDescriptors_Read() { - string zipPath = Path.Combine(TEST_ARCHIVES_PATH, "Zip.none.datadescriptors.zip"); + var zipPath = Path.Combine(TEST_ARCHIVES_PATH, "Zip.none.datadescriptors.zip"); - using (ZipArchive za = ZipArchive.Open(zipPath)) + using var za = ZipArchive.Open(zipPath); + var firstEntry = za.Entries.First(x => x.Key == "first.txt"); + var buffer = new byte[4096]; + + using (var memoryStream = new MemoryStream()) + using (var firstStream = firstEntry.OpenEntryStream()) { - var firstEntry = za.Entries.First(x => x.Key == "first.txt"); - var buffer = new byte[4096]; - - using (var memoryStream = new MemoryStream()) - using (var firstStream = firstEntry.OpenEntryStream()) - { - firstStream.CopyTo(memoryStream); - Assert.Equal(199, memoryStream.Length); - } - - var len1 = 0; - var buffer1 = new byte[firstEntry.Size + 256]; - - using (var firstStream = firstEntry.OpenEntryStream()) - { - len1 = firstStream.Read(buffer1, 0, buffer.Length); - } - - Assert.Equal(199, len1); - -#if !NETFRAMEWORK && !NETSTANDARD2_0 - var len2 = 0; - var buffer2 = new byte[firstEntry.Size + 256]; - - using (var firstStream = firstEntry.OpenEntryStream()) - { - len2 = firstStream.Read(buffer2.AsSpan()); - } - Assert.Equal(len1, len2); - Assert.Equal(buffer1, buffer2); -#endif + firstStream.CopyTo(memoryStream); + Assert.Equal(199, memoryStream.Length); } + + var len1 = 0; + var buffer1 = new byte[firstEntry.Size + 256]; + + using (var firstStream = firstEntry.OpenEntryStream()) + { + len1 = firstStream.Read(buffer1, 0, buffer.Length); + } + + Assert.Equal(199, len1); + +#if !NETFRAMEWORK + var len2 = 0; + var buffer2 = new byte[firstEntry.Size + 256]; + + using (var firstStream = firstEntry.OpenEntryStream()) + { + len2 = firstStream.Read(buffer2.AsSpan()); + } + Assert.Equal(len1, len2); + Assert.Equal(buffer1, buffer2); +#endif } [Fact] public void Zip_LongComment_Read() { - string zipPath = Path.Combine(TEST_ARCHIVES_PATH, "Zip.LongComment.zip"); + var zipPath = Path.Combine(TEST_ARCHIVES_PATH, "Zip.LongComment.zip"); - using (ZipArchive za = ZipArchive.Open(zipPath)) - { - var count = za.Entries.Count; - Assert.Equal(1, count); - } + using var za = ZipArchive.Open(zipPath); + var count = za.Entries.Count; + Assert.Equal(1, count); } [Fact] public void Zip_Zip64_CompressedSizeExtraOnly_Read() { - string zipPath = Path.Combine(TEST_ARCHIVES_PATH, "Zip.zip64.compressedonly.zip"); + var zipPath = Path.Combine(TEST_ARCHIVES_PATH, "Zip.zip64.compressedonly.zip"); - using (ZipArchive za = ZipArchive.Open(zipPath)) - { - var firstEntry = za.Entries.First(x => x.Key == "test/test.txt"); + using var za = ZipArchive.Open(zipPath); + var firstEntry = za.Entries.First(x => x.Key == "test/test.txt"); - using (var memoryStream = new MemoryStream()) - using (var firstStream = firstEntry.OpenEntryStream()) - { - firstStream.CopyTo(memoryStream); - Assert.Equal(15, memoryStream.Length); - } - } + using var memoryStream = new MemoryStream(); + using var firstStream = firstEntry.OpenEntryStream(); + firstStream.CopyTo(memoryStream); + Assert.Equal(15, memoryStream.Length); } [Fact] public void Zip_Uncompressed_Read_All() { - string zipPath = Path.Combine(TEST_ARCHIVES_PATH, "Zip.uncompressed.zip"); - using (var stream = File.Open(zipPath, FileMode.Open, FileAccess.Read)) + var zipPath = Path.Combine(TEST_ARCHIVES_PATH, "Zip.uncompressed.zip"); + using var stream = File.Open(zipPath, FileMode.Open, FileAccess.Read); + var archive = ArchiveFactory.Open(stream); + var reader = archive.ExtractAllEntries(); + var entries = 0; + while (reader.MoveToNextEntry()) { - IArchive archive = ArchiveFactory.Open(stream); - IReader reader = archive.ExtractAllEntries(); - int entries = 0; - while (reader.MoveToNextEntry()) + using (var entryStream = reader.OpenEntryStream()) + using (var target = new MemoryStream()) { - using (var entryStream = reader.OpenEntryStream()) - using (var target = new MemoryStream()) - { - entryStream.CopyTo(target); - } - - entries++; + entryStream.CopyTo(target); } - Assert.Equal(4, entries); + + entries++; } + Assert.Equal(4, entries); } [Fact] @@ -750,19 +718,17 @@ public class ZipArchiveTests : ArchiveTests "DEADBEEF" }; var zipPath = Path.Combine(TEST_ARCHIVES_PATH, "Zip.uncompressed.zip"); - using (var stream = File.Open(zipPath, FileMode.Open, FileAccess.Read)) + using var stream = File.Open(zipPath, FileMode.Open, FileAccess.Read); + var archive = ArchiveFactory.Open(stream); + var reader = archive.ExtractAllEntries(); + var x = 0; + while (reader.MoveToNextEntry()) { - IArchive archive = ArchiveFactory.Open(stream); - IReader reader = archive.ExtractAllEntries(); - int x = 0; - while (reader.MoveToNextEntry()) - { - Assert.Equal(keys[x], reader.Entry.Key); - x++; - } - - Assert.Equal(4, x); + Assert.Equal(keys[x], reader.Entry.Key); + x++; } + + Assert.Equal(4, x); } [Fact] @@ -771,7 +737,7 @@ public class ZipArchiveTests : ArchiveTests var zipPath = Path.Combine(TEST_ARCHIVES_PATH, "Zip.UnicodePathExtra.zip"); using (var stream = File.Open(zipPath, FileMode.Open, FileAccess.Read)) { - IArchive archive = ArchiveFactory.Open( + var archive = ArchiveFactory.Open( stream, new ReaderOptions { @@ -781,13 +747,13 @@ public class ZipArchiveTests : ArchiveTests } } ); - IReader reader = archive.ExtractAllEntries(); + var reader = archive.ExtractAllEntries(); reader.MoveToNextEntry(); Assert.Equal("궖귛궖귙귪궖귗귪궖귙_wav.frq", reader.Entry.Key); } using (var stream = File.Open(zipPath, FileMode.Open, FileAccess.Read)) { - IArchive archive = ArchiveFactory.Open( + var archive = ArchiveFactory.Open( stream, new ReaderOptions { @@ -797,7 +763,7 @@ public class ZipArchiveTests : ArchiveTests } } ); - IReader reader = archive.ExtractAllEntries(); + var reader = archive.ExtractAllEntries(); reader.MoveToNextEntry(); Assert.Equal("きょきゅんきゃんきゅ_wav.frq", reader.Entry.Key); } diff --git a/tests/SharpCompress.Test/Zip/ZipReaderTests.cs b/tests/SharpCompress.Test/Zip/ZipReaderTests.cs index e5254c19..3296bcb0 100644 --- a/tests/SharpCompress.Test/Zip/ZipReaderTests.cs +++ b/tests/SharpCompress.Test/Zip/ZipReaderTests.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using SharpCompress.Archives; using SharpCompress.Common; using SharpCompress.IO; using SharpCompress.Readers; @@ -70,7 +71,7 @@ public class ZipReaderTests : ReaderTests { reader.WriteEntryToDirectory( SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } + new ExtractionOptions { ExtractFullPath = true, Overwrite = true } ); } } @@ -108,7 +109,7 @@ public class ZipReaderTests : ReaderTests using ( Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Zip.bzip2.pkware.zip")) ) - using (var reader = ZipReader.Open(stream, new ReaderOptions() { Password = "test" })) + using (var reader = ZipReader.Open(stream, new ReaderOptions { Password = "test" })) { while (reader.MoveToNextEntry()) { @@ -117,7 +118,7 @@ public class ZipReaderTests : ReaderTests Assert.Equal(CompressionType.BZip2, reader.Entry.CompressionType); reader.WriteEntryToDirectory( SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } + new ExtractionOptions { ExtractFullPath = true, Overwrite = true } ); } } @@ -139,7 +140,7 @@ public class ZipReaderTests : ReaderTests { reader.WriteEntryToDirectory( SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } + new ExtractionOptions { ExtractFullPath = true, Overwrite = true } ); } } @@ -160,7 +161,7 @@ public class ZipReaderTests : ReaderTests { reader.WriteEntryToDirectory( SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } + new ExtractionOptions { ExtractFullPath = true, Overwrite = true } ); } } @@ -176,7 +177,7 @@ public class ZipReaderTests : ReaderTests Path.Combine(TEST_ARCHIVES_PATH, "Zip.lzma.WinzipAES.zip") ) ) - using (var reader = ZipReader.Open(stream, new ReaderOptions() { Password = "test" })) + using (var reader = ZipReader.Open(stream, new ReaderOptions { Password = "test" })) { while (reader.MoveToNextEntry()) { @@ -185,7 +186,7 @@ public class ZipReaderTests : ReaderTests Assert.Equal(CompressionType.Unknown, reader.Entry.CompressionType); reader.WriteEntryToDirectory( SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } + new ExtractionOptions { ExtractFullPath = true, Overwrite = true } ); } } @@ -201,7 +202,7 @@ public class ZipReaderTests : ReaderTests Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.WinzipAES.zip") ) ) - using (var reader = ZipReader.Open(stream, new ReaderOptions() { Password = "test" })) + using (var reader = ZipReader.Open(stream, new ReaderOptions { Password = "test" })) { while (reader.MoveToNextEntry()) { @@ -210,7 +211,7 @@ public class ZipReaderTests : ReaderTests Assert.Equal(CompressionType.Unknown, reader.Entry.CompressionType); reader.WriteEntryToDirectory( SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } + new ExtractionOptions { ExtractFullPath = true, Overwrite = true } ); } } @@ -223,7 +224,7 @@ public class ZipReaderTests : ReaderTests { var count = 0; using (Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "zipcrypto.zip"))) - using (var reader = ZipReader.Open(stream, new ReaderOptions() { Password = "test" })) + using (var reader = ZipReader.Open(stream, new ReaderOptions { Password = "test" })) { while (reader.MoveToNextEntry()) { @@ -232,7 +233,7 @@ public class ZipReaderTests : ReaderTests Assert.Equal(CompressionType.None, reader.Entry.CompressionType); reader.WriteEntryToDirectory( SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } + new ExtractionOptions { ExtractFullPath = true, Overwrite = true } ); count++; } @@ -364,7 +365,7 @@ public class ZipReaderTests : ReaderTests { var zipPath = Path.Combine(TEST_ARCHIVES_PATH, "64bitstream.zip.7z"); using var stream = File.Open(zipPath, FileMode.Open, FileAccess.Read); - var archive = Archives.ArchiveFactory.Open(stream); + var archive = ArchiveFactory.Open(stream); var reader = archive.ExtractAllEntries(); reader.MoveToNextEntry(); var zipReader = ZipReader.Open(reader.OpenEntryStream()); diff --git a/tests/SharpCompress.Test/Zip/ZipWriterTests.cs b/tests/SharpCompress.Test/Zip/ZipWriterTests.cs index 530397bd..55435b4a 100644 --- a/tests/SharpCompress.Test/Zip/ZipWriterTests.cs +++ b/tests/SharpCompress.Test/Zip/ZipWriterTests.cs @@ -1,5 +1,4 @@ using System.Text; - using SharpCompress.Common; using Xunit; diff --git a/tests/SharpCompress.Test/packages.lock.json b/tests/SharpCompress.Test/packages.lock.json new file mode 100644 index 00000000..baeef64f --- /dev/null +++ b/tests/SharpCompress.Test/packages.lock.json @@ -0,0 +1,368 @@ +{ + "version": 2, + "dependencies": { + ".NETFramework,Version=v4.6.2": { + "FluentAssertions": { + "type": "Direct", + "requested": "[6.12.0, )", + "resolved": "6.12.0", + "contentHash": "ZXhHT2YwP9lajrwSKbLlFqsmCCvFJMoRSK9t7sImfnCyd0OB3MhgxdoMcVqxbq1iyxD6mD2fiackWmBb7ayiXQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.0" + } + }, + "Microsoft.NET.Test.Sdk": { + "type": "Direct", + "requested": "[17.9.0, )", + "resolved": "17.9.0", + "contentHash": "7GUNAUbJYn644jzwLm5BD3a2p9C1dmP8Hr6fDPDxgItQk9hBs1Svdxzz07KQ/UphMSmgza9AbijBJGmw5D658A==", + "dependencies": { + "Microsoft.CodeCoverage": "17.9.0" + } + }, + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net462": "1.0.3" + } + }, + "xunit": { + "type": "Direct", + "requested": "[2.7.1, )", + "resolved": "2.7.1", + "contentHash": "9o050yCLzPvHxbrAkaHnI1j+YfPvRw+/ncvKbTfzIhO4JvQA0rPgoICJxXHMkscfgXmLFwZ8107ehnMUVzE23A==", + "dependencies": { + "xunit.analyzers": "1.12.0", + "xunit.assert": "2.7.1", + "xunit.core": "[2.7.1]" + } + }, + "xunit.runner.visualstudio": { + "type": "Direct", + "requested": "[2.5.8, )", + "resolved": "2.5.8", + "contentHash": "ZJTm71neOfZcUnqdyY0A0Qgcg1162DoOq6+VpCCsOaD9rwCK5alcjOEHeu17sEekzq4qNv3kyelx6lUMsAt/eA==", + "dependencies": { + "Microsoft.TestPlatform.ObjectModel": "17.9.0" + } + }, + "Xunit.SkippableFact": { + "type": "Direct", + "requested": "[1.4.13, )", + "resolved": "1.4.13", + "contentHash": "IyzZNvJEtXGlXrzxDiSbtH5Lyxf4iJdRQADuyjGdDf00LjXRLJwIoezQNFhFGKTMtvk8IIgaSHxW4mAV4O7b8A==", + "dependencies": { + "Validation": "2.4.18", + "xunit.extensibility.execution": "2.4.0" + } + }, + "Microsoft.CodeCoverage": { + "type": "Transitive", + "resolved": "17.9.0", + "contentHash": "RGD37ZSrratfScYXm7M0HjvxMxZyWZL4jm+XgMZbkIY1UPgjUpbNA/t+WTGj/rC/0Hm9A3IrH3ywbKZkOCnoZA==" + }, + "Microsoft.NETFramework.ReferenceAssemblies.net462": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "IzAV30z22ESCeQfxP29oVf4qEo8fBGXLXSU6oacv/9Iqe6PzgHDKCaWfwMBak7bSJQM0F5boXWoZS+kChztRIQ==" + }, + "Microsoft.TestPlatform.ObjectModel": { + "type": "Transitive", + "resolved": "17.9.0", + "contentHash": "1ilw/8vgmjLyKU+2SKXKXaOqpYFJCQfGqGz+x0cosl981VzjrY74Sv6qAJv+neZMZ9ZMxF3ArN6kotaQ4uvEBw==", + "dependencies": { + "System.Reflection.Metadata": "1.6.0" + } + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.Collections.Immutable": { + "type": "Transitive", + "resolved": "1.5.0", + "contentHash": "EXKiDFsChZW0RjrZ4FYHu9aW6+P4MCgEDCklsVseRfhoO0F+dXeMSsMRAlVXIo06kGJ/zv+2w1a2uc2+kxxSaQ==" + }, + "System.Numerics.Vectors": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + }, + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "1.6.0", + "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==", + "dependencies": { + "System.Collections.Immutable": "1.5.0" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + }, + "System.Threading.Tasks.Extensions": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "Validation": { + "type": "Transitive", + "resolved": "2.4.18", + "contentHash": "NfvWJ1QeuZ1FQCkqgXTu1cOkRkbNCfxs4Tat+abXLwom6OXbULVhRGp34BTvVB4XPxj6VIAl7KfLfStXMt/Ehw==" + }, + "xunit.abstractions": { + "type": "Transitive", + "resolved": "2.0.3", + "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg==" + }, + "xunit.analyzers": { + "type": "Transitive", + "resolved": "1.12.0", + "contentHash": "w23LH3aXade2WXKvXi0oA/uV15fpgUMjsPq1x91iQckzgWApgAiijNHmfFQtqNPm41wfrdbRl7nSJRd0yux/dw==" + }, + "xunit.assert": { + "type": "Transitive", + "resolved": "2.7.1", + "contentHash": "JqvXS4yX+PtJn5BuqoKkYav7I0g4nXcxRbGTomDwVQjFccOdyfYKpuPOHX/DqrPCcL+MIHrGVdP3bveUXlvdnA==" + }, + "xunit.core": { + "type": "Transitive", + "resolved": "2.7.1", + "contentHash": "hcODgS+RXdjmXq0zQzmRbERQgY+bAGGx1bdH3370t/8CTGmIEU2qAc1dQAjIRpARsacR0cj6LLJDUF5BNQNKTQ==", + "dependencies": { + "xunit.extensibility.core": "[2.7.1]", + "xunit.extensibility.execution": "[2.7.1]" + } + }, + "xunit.extensibility.core": { + "type": "Transitive", + "resolved": "2.7.1", + "contentHash": "yLX4XlBFkvNYCzf+DEzlNk45KsSlu9W93IJHBmtUP96qZ9XyRYDFlwMj6BCcOhDKVNrZxSM8bqu4F/Qud4ehxA==", + "dependencies": { + "xunit.abstractions": "2.0.3" + } + }, + "xunit.extensibility.execution": { + "type": "Transitive", + "resolved": "2.7.1", + "contentHash": "ei3dKF8agj4UKkJ6KkaZ5/Gcif3if6hBsyzegIQJonZDSKZFvb0AyKtyOhDfggBaXVL5iXZExITdRkfjC95yhw==", + "dependencies": { + "xunit.extensibility.core": "[2.7.1]" + } + }, + "sharpcompress": { + "type": "Project", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "[8.0.0, )", + "System.Memory": "[4.5.5, )", + "System.Text.Encoding.CodePages": "[8.0.0, )", + "ZstdSharp.Port": "[0.8.0, )" + } + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "CentralTransitive", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "System.Memory": { + "type": "CentralTransitive", + "requested": "[4.5.5, )", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "System.Text.Encoding.CodePages": { + "type": "CentralTransitive", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "OZIsVplFGaVY90G2SbpgU7EnCoOO5pw1t4ic21dBF3/1omrJFpAGoNAVpPyMVOC90/hvgkGG3VFqR13YgZMQfg==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "ZstdSharp.Port": { + "type": "CentralTransitive", + "requested": "[0.8.0, )", + "resolved": "0.8.0", + "contentHash": "Z62eNBIu8E8YtbqlMy57tK3dV1+m2b9NhPeaYovB5exmLKvrGCqOhJTzrEUH5VyUWU6vwX3c1XHJGhW5HVs8dA==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "System.Memory": "4.5.5" + } + } + }, + "net8.0": { + "FluentAssertions": { + "type": "Direct", + "requested": "[6.12.0, )", + "resolved": "6.12.0", + "contentHash": "ZXhHT2YwP9lajrwSKbLlFqsmCCvFJMoRSK9t7sImfnCyd0OB3MhgxdoMcVqxbq1iyxD6mD2fiackWmBb7ayiXQ==", + "dependencies": { + "System.Configuration.ConfigurationManager": "4.4.0" + } + }, + "Microsoft.NET.Test.Sdk": { + "type": "Direct", + "requested": "[17.9.0, )", + "resolved": "17.9.0", + "contentHash": "7GUNAUbJYn644jzwLm5BD3a2p9C1dmP8Hr6fDPDxgItQk9hBs1Svdxzz07KQ/UphMSmgza9AbijBJGmw5D658A==", + "dependencies": { + "Microsoft.CodeCoverage": "17.9.0", + "Microsoft.TestPlatform.TestHost": "17.9.0" + } + }, + "Mono.Posix.NETStandard": { + "type": "Direct", + "requested": "[1.0.0, )", + "resolved": "1.0.0", + "contentHash": "vSN/L1uaVwKsiLa95bYu2SGkF0iY3xMblTfxc8alSziPuVfJpj3geVqHGAA75J7cZkMuKpFVikz82Lo6y6LLdA==" + }, + "xunit": { + "type": "Direct", + "requested": "[2.7.1, )", + "resolved": "2.7.1", + "contentHash": "9o050yCLzPvHxbrAkaHnI1j+YfPvRw+/ncvKbTfzIhO4JvQA0rPgoICJxXHMkscfgXmLFwZ8107ehnMUVzE23A==", + "dependencies": { + "xunit.analyzers": "1.12.0", + "xunit.assert": "2.7.1", + "xunit.core": "[2.7.1]" + } + }, + "xunit.runner.visualstudio": { + "type": "Direct", + "requested": "[2.5.8, )", + "resolved": "2.5.8", + "contentHash": "ZJTm71neOfZcUnqdyY0A0Qgcg1162DoOq6+VpCCsOaD9rwCK5alcjOEHeu17sEekzq4qNv3kyelx6lUMsAt/eA==" + }, + "Xunit.SkippableFact": { + "type": "Direct", + "requested": "[1.4.13, )", + "resolved": "1.4.13", + "contentHash": "IyzZNvJEtXGlXrzxDiSbtH5Lyxf4iJdRQADuyjGdDf00LjXRLJwIoezQNFhFGKTMtvk8IIgaSHxW4mAV4O7b8A==", + "dependencies": { + "Validation": "2.4.18", + "xunit.extensibility.execution": "2.4.0" + } + }, + "Microsoft.CodeCoverage": { + "type": "Transitive", + "resolved": "17.9.0", + "contentHash": "RGD37ZSrratfScYXm7M0HjvxMxZyWZL4jm+XgMZbkIY1UPgjUpbNA/t+WTGj/rC/0Hm9A3IrH3ywbKZkOCnoZA==" + }, + "Microsoft.TestPlatform.ObjectModel": { + "type": "Transitive", + "resolved": "17.9.0", + "contentHash": "1ilw/8vgmjLyKU+2SKXKXaOqpYFJCQfGqGz+x0cosl981VzjrY74Sv6qAJv+neZMZ9ZMxF3ArN6kotaQ4uvEBw==", + "dependencies": { + "System.Reflection.Metadata": "1.6.0" + } + }, + "Microsoft.TestPlatform.TestHost": { + "type": "Transitive", + "resolved": "17.9.0", + "contentHash": "Spmg7Wx49Ya3SxBjyeAR+nQpjMTKZwTwpZ7KyeOTIqI/WHNPnBU4HUvl5kuHPQAwGWqMy4FGZja1HvEwvoaDiA==", + "dependencies": { + "Microsoft.TestPlatform.ObjectModel": "17.9.0", + "Newtonsoft.Json": "13.0.1" + } + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.1", + "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" + }, + "System.Configuration.ConfigurationManager": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "gWwQv/Ug1qWJmHCmN17nAbxJYmQBM/E94QxKLksvUiiKB1Ld3Sc/eK1lgmbSjDFxkQhVuayI/cGFZhpBSodLrg==", + "dependencies": { + "System.Security.Cryptography.ProtectedData": "4.4.0" + } + }, + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "1.6.0", + "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==" + }, + "System.Security.Cryptography.ProtectedData": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "cJV7ScGW7EhatRsjehfvvYVBvtiSMKgN8bOVI0bQhnF5bU7vnHVIsH49Kva7i7GWaWYvmEzkYVk1TC+gZYBEog==" + }, + "Validation": { + "type": "Transitive", + "resolved": "2.4.18", + "contentHash": "NfvWJ1QeuZ1FQCkqgXTu1cOkRkbNCfxs4Tat+abXLwom6OXbULVhRGp34BTvVB4XPxj6VIAl7KfLfStXMt/Ehw==" + }, + "xunit.abstractions": { + "type": "Transitive", + "resolved": "2.0.3", + "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg==" + }, + "xunit.analyzers": { + "type": "Transitive", + "resolved": "1.12.0", + "contentHash": "w23LH3aXade2WXKvXi0oA/uV15fpgUMjsPq1x91iQckzgWApgAiijNHmfFQtqNPm41wfrdbRl7nSJRd0yux/dw==" + }, + "xunit.assert": { + "type": "Transitive", + "resolved": "2.7.1", + "contentHash": "JqvXS4yX+PtJn5BuqoKkYav7I0g4nXcxRbGTomDwVQjFccOdyfYKpuPOHX/DqrPCcL+MIHrGVdP3bveUXlvdnA==" + }, + "xunit.core": { + "type": "Transitive", + "resolved": "2.7.1", + "contentHash": "hcODgS+RXdjmXq0zQzmRbERQgY+bAGGx1bdH3370t/8CTGmIEU2qAc1dQAjIRpARsacR0cj6LLJDUF5BNQNKTQ==", + "dependencies": { + "xunit.extensibility.core": "[2.7.1]", + "xunit.extensibility.execution": "[2.7.1]" + } + }, + "xunit.extensibility.core": { + "type": "Transitive", + "resolved": "2.7.1", + "contentHash": "yLX4XlBFkvNYCzf+DEzlNk45KsSlu9W93IJHBmtUP96qZ9XyRYDFlwMj6BCcOhDKVNrZxSM8bqu4F/Qud4ehxA==", + "dependencies": { + "xunit.abstractions": "2.0.3" + } + }, + "xunit.extensibility.execution": { + "type": "Transitive", + "resolved": "2.7.1", + "contentHash": "ei3dKF8agj4UKkJ6KkaZ5/Gcif3if6hBsyzegIQJonZDSKZFvb0AyKtyOhDfggBaXVL5iXZExITdRkfjC95yhw==", + "dependencies": { + "xunit.extensibility.core": "[2.7.1]" + } + }, + "sharpcompress": { + "type": "Project", + "dependencies": { + "ZstdSharp.Port": "[0.8.0, )" + } + }, + "ZstdSharp.Port": { + "type": "CentralTransitive", + "requested": "[0.8.0, )", + "resolved": "0.8.0", + "contentHash": "Z62eNBIu8E8YtbqlMy57tK3dV1+m2b9NhPeaYovB5exmLKvrGCqOhJTzrEUH5VyUWU6vwX3c1XHJGhW5HVs8dA==" + } + } + } +} \ No newline at end of file diff --git a/tests/TestArchives/Archives/7Zip.ARM64.7z b/tests/TestArchives/Archives/7Zip.ARM64.7z new file mode 100644 index 00000000..2e9d6293 Binary files /dev/null and b/tests/TestArchives/Archives/7Zip.ARM64.7z differ diff --git a/tests/TestArchives/Archives/7Zip.Filters.7z b/tests/TestArchives/Archives/7Zip.Filters.7z index ea15f3b2..d066779b 100644 Binary files a/tests/TestArchives/Archives/7Zip.Filters.7z and b/tests/TestArchives/Archives/7Zip.Filters.7z differ diff --git a/tests/TestArchives/Archives/7Zip.RISCV.7z b/tests/TestArchives/Archives/7Zip.RISCV.7z new file mode 100644 index 00000000..b65c517b Binary files /dev/null and b/tests/TestArchives/Archives/7Zip.RISCV.7z differ diff --git a/tests/TestArchives/Archives/7Zip.eos.7z b/tests/TestArchives/Archives/7Zip.eos.7z new file mode 100644 index 00000000..2fe951ad Binary files /dev/null and b/tests/TestArchives/Archives/7Zip.eos.7z differ diff --git a/tests/TestArchives/Archives/Rar.comment.rar b/tests/TestArchives/Archives/Rar.comment.rar new file mode 100644 index 00000000..b0a909c7 Binary files /dev/null and b/tests/TestArchives/Archives/Rar.comment.rar differ diff --git a/tests/TestArchives/Archives/Rar5.comment.rar b/tests/TestArchives/Archives/Rar5.comment.rar new file mode 100644 index 00000000..d3a65de9 Binary files /dev/null and b/tests/TestArchives/Archives/Rar5.comment.rar differ diff --git a/tests/TestArchives/Archives/Tar.oldgnu.tar.gz b/tests/TestArchives/Archives/Tar.oldgnu.tar.gz new file mode 100644 index 00000000..fbffb7a2 Binary files /dev/null and b/tests/TestArchives/Archives/Tar.oldgnu.tar.gz differ diff --git a/tests/TestArchives/Archives/Tar.tar.Z b/tests/TestArchives/Archives/Tar.tar.Z new file mode 100644 index 00000000..0eb90fb1 Binary files /dev/null and b/tests/TestArchives/Archives/Tar.tar.Z differ diff --git a/tests/TestArchives/Archives/TarCorrupted.tar b/tests/TestArchives/Archives/TarCorrupted.tar new file mode 100644 index 00000000..a21c4fe5 Binary files /dev/null and b/tests/TestArchives/Archives/TarCorrupted.tar differ diff --git a/tests/TestArchives/Archives/Zip.shrink.zip b/tests/TestArchives/Archives/Zip.shrink.zip new file mode 100644 index 00000000..697c7e00 Binary files /dev/null and b/tests/TestArchives/Archives/Zip.shrink.zip differ