diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 00b1a064..393d1879 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,10 +3,11 @@ "isRoot": true, "tools": { "csharpier": { - "version": "0.28.1", + "version": "0.30.6", "commands": [ "dotnet-csharpier" - ] + ], + "rollForward": false } } } \ No newline at end of file diff --git a/Directory.Packages.props b/Directory.Packages.props index 6bc10b2c..1108acd8 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -1,17 +1,19 @@ - + - - + + - + + - - - - + + + + + diff --git a/FORMATS.md b/FORMATS.md index cca190ad..eb27ad2f 100644 --- a/FORMATS.md +++ b/FORMATS.md @@ -11,7 +11,7 @@ | Archive Format | Compression Format(s) | Compress/Decompress | Archive API | Reader API | Writer API | | ---------------------- | ------------------------------------------------- | ------------------- | --------------- | ---------- | ------------- | | Rar | Rar | Decompress (1) | RarArchive | RarReader | N/A | -| Zip (2) | None, DEFLATE, Deflate64, BZip2, LZMA/LZMA2, PPMd | Both | ZipArchive | ZipReader | ZipWriter | +| Zip (2) | None, Shrink, Reduce, Implode, DEFLATE, Deflate64, BZip2, LZMA/LZMA2, PPMd | Both | ZipArchive | ZipReader | ZipWriter | | Tar | None | Both | TarArchive | TarReader | TarWriter (3) | | Tar.GZip | DEFLATE | Both | TarArchive | TarReader | TarWriter (3) | | Tar.BZip2 | BZip2 | Both | TarArchive | TarReader | TarWriter (3) | diff --git a/SharpCompress.sln b/SharpCompress.sln index ab4995b1..a86d1652 100644 --- a/SharpCompress.sln +++ b/SharpCompress.sln @@ -20,6 +20,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Config", "Config", "{CDB425 .editorconfig = .editorconfig Directory.Packages.props = Directory.Packages.props NuGet.config = NuGet.config + .github\workflows\dotnetcore.yml = .github\workflows\dotnetcore.yml EndProjectSection EndProject Global diff --git a/USAGE.md b/USAGE.md index c96f9c1b..51b93774 100644 --- a/USAGE.md +++ b/USAGE.md @@ -27,7 +27,7 @@ To deal with the "correct" rules as well as the expectations of users, I've deci To be explicit though, consider always using the overloads that use `ReaderOptions` or `WriterOptions` and explicitly set `LeaveStreamOpen` the way you want. -If using Compression Stream classes directly and you don't want the wrapped stream to be closed. Use the `NonDisposingStream` as a wrapped to prevent the stream being disposed. The change in 0.21 simplified a lot even though the usage is a bit more convoluted. +If using Compression Stream classes directly and you don't want the wrapped stream to be closed. Use the `NonDisposingStream` as a wrapper to prevent the stream being disposed. The change in 0.21 simplified a lot even though the usage is a bit more convoluted. ## Samples diff --git a/build/Program.cs b/build/Program.cs index acafec41..39acd0c0 100644 --- a/build/Program.cs +++ b/build/Program.cs @@ -61,7 +61,7 @@ Target( Target( Test, DependsOn(Build), - ForEach("net8.0", "net462"), + ForEach("net8.0", "net48"), framework => { IEnumerable GetFiles(string d) @@ -69,7 +69,7 @@ Target( return Glob.Files(".", d); } - if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && framework == "net462") + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && framework == "net48") { return; } diff --git a/build/build.csproj b/build/build.csproj index 8e837d3a..e0dffc7f 100644 --- a/build/build.csproj +++ b/build/build.csproj @@ -2,7 +2,7 @@ Exe - net7.0 + net8.0 diff --git a/build/packages.lock.json b/build/packages.lock.json index 52565140..08406fdc 100644 --- a/build/packages.lock.json +++ b/build/packages.lock.json @@ -1,7 +1,7 @@ { - "version": 1, + "version": 2, "dependencies": { - "net7.0": { + "net8.0": { "Bullseye": { "type": "Direct", "requested": "[5.0.0, )", @@ -14,11 +14,31 @@ "resolved": "1.1.9", "contentHash": "AfK5+ECWYTP7G3AAdnU8IfVj+QpGjrh9GC2mpdcJzCvtQ4pnerAGwHsxJ9D4/RnhDUz2DSzd951O/lQjQby2Sw==" }, + "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" + } + }, "SimpleExec": { "type": "Direct", "requested": "[12.0.0, )", "resolved": "12.0.0", "contentHash": "ptxlWtxC8vM6Y6e3h9ZTxBBkOWnWrm/Sa1HT+2i1xcXY3Hx2hmKDZP5RShPf8Xr9D+ivlrXNy57ktzyH8kyt+Q==" + }, + "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==" } } } diff --git a/src/SharpCompress/Algorithms/Adler32.cs b/src/SharpCompress/Algorithms/Adler32.cs index 10dbfc4d..f7ea0eb3 100644 --- a/src/SharpCompress/Algorithms/Adler32.cs +++ b/src/SharpCompress/Algorithms/Adler32.cs @@ -141,7 +141,7 @@ internal static class Adler32 // From https://github.com/SixLabors/ImageSharp/bl 4, 3, 2, - 1 // tap2 + 1, // tap2 }; #endif diff --git a/src/SharpCompress/Archives/ArchiveFactory.cs b/src/SharpCompress/Archives/ArchiveFactory.cs index 3f479327..b81e2c0e 100644 --- a/src/SharpCompress/Archives/ArchiveFactory.cs +++ b/src/SharpCompress/Archives/ArchiveFactory.cs @@ -189,9 +189,10 @@ public static class ArchiveFactory foreach (var factory in Factory.Factories) { + var isArchive = factory.IsArchive(stream); stream.Position = startPosition; - if (factory.IsArchive(stream, null)) + if (isArchive) { type = factory.KnownArchiveType; return true; @@ -239,4 +240,6 @@ public static class ArchiveFactory } } } + + public static IArchiveFactory AutoFactory { get; } = new AutoArchiveFactory(); } diff --git a/src/SharpCompress/Archives/AutoArchiveFactory.cs b/src/SharpCompress/Archives/AutoArchiveFactory.cs new file mode 100644 index 00000000..14038ea1 --- /dev/null +++ b/src/SharpCompress/Archives/AutoArchiveFactory.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.IO; +using SharpCompress.Common; +using SharpCompress.Readers; + +namespace SharpCompress.Archives; + +class AutoArchiveFactory : IArchiveFactory +{ + public string Name => nameof(AutoArchiveFactory); + + public ArchiveType? KnownArchiveType => null; + + public IEnumerable GetSupportedExtensions() => throw new NotSupportedException(); + + public bool IsArchive(Stream stream, string? password = null) => + throw new NotSupportedException(); + + public FileInfo? GetFilePart(int index, FileInfo part1) => throw new NotSupportedException(); + + public IArchive Open(Stream stream, ReaderOptions? readerOptions = null) => + ArchiveFactory.Open(stream, readerOptions); + + public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) => + ArchiveFactory.Open(fileInfo, readerOptions); +} diff --git a/src/SharpCompress/Archives/GZip/GZipArchive.cs b/src/SharpCompress/Archives/GZip/GZipArchive.cs index 7a33a10a..58f98ab9 100644 --- a/src/SharpCompress/Archives/GZip/GZipArchive.cs +++ b/src/SharpCompress/Archives/GZip/GZipArchive.cs @@ -101,7 +101,7 @@ public class GZipArchive : AbstractWritableArchive /// /// private GZipArchive(SourceStream sourceStream) - : base(ArchiveType.Tar, sourceStream) { } + : base(ArchiveType.GZip, sourceStream) { } protected override IEnumerable LoadVolumes(SourceStream sourceStream) { diff --git a/src/SharpCompress/Archives/IArchiveExtensions.cs b/src/SharpCompress/Archives/IArchiveExtensions.cs index 382c3ddf..4cba5463 100644 --- a/src/SharpCompress/Archives/IArchiveExtensions.cs +++ b/src/SharpCompress/Archives/IArchiveExtensions.cs @@ -54,14 +54,26 @@ public static class IArchiveExtensions var entry = entries.Entry; if (entry.IsDirectory) { + var dirPath = Path.Combine(destination, entry.Key.NotNull("Entry Key is null")); + if ( + Path.GetDirectoryName(dirPath + "/") is { } emptyDirectory + && seenDirectories.Add(dirPath) + ) + { + Directory.CreateDirectory(emptyDirectory); + } continue; } - // Create each directory + // Create each directory if not already created var path = Path.Combine(destination, entry.Key.NotNull("Entry Key is null")); - if (Path.GetDirectoryName(path) is { } directory && seenDirectories.Add(path)) + if (Path.GetDirectoryName(path) is { } directory) { - Directory.CreateDirectory(directory); + if (!Directory.Exists(directory) && !seenDirectories.Contains(directory)) + { + Directory.CreateDirectory(directory); + seenDirectories.Add(directory); + } } // Write file diff --git a/src/SharpCompress/Archives/Rar/FileInfoRarArchiveVolume.cs b/src/SharpCompress/Archives/Rar/FileInfoRarArchiveVolume.cs index 6d9b44fd..65f31d01 100644 --- a/src/SharpCompress/Archives/Rar/FileInfoRarArchiveVolume.cs +++ b/src/SharpCompress/Archives/Rar/FileInfoRarArchiveVolume.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Collections.ObjectModel; using System.IO; using System.Linq; using SharpCompress.Common.Rar; diff --git a/src/SharpCompress/Archives/Rar/RarArchive.cs b/src/SharpCompress/Archives/Rar/RarArchive.cs index a05ad417..6a5c9781 100644 --- a/src/SharpCompress/Archives/Rar/RarArchive.cs +++ b/src/SharpCompress/Archives/Rar/RarArchive.cs @@ -14,6 +14,7 @@ namespace SharpCompress.Archives.Rar; public class RarArchive : AbstractArchive { + private bool _disposed; internal Lazy UnpackV2017 { get; } = new(() => new Compressors.Rar.UnpackV2017.Unpack()); internal Lazy UnpackV1 { get; } = new(() => new Compressors.Rar.UnpackV1.Unpack()); @@ -25,6 +26,20 @@ public class RarArchive : AbstractArchive private RarArchive(SourceStream sourceStream) : base(ArchiveType.Rar, sourceStream) { } + public override void Dispose() + { + if (!_disposed) + { + if (UnpackV1.IsValueCreated && UnpackV1.Value is IDisposable unpackV1) + { + unpackV1.Dispose(); + } + + _disposed = true; + base.Dispose(); + } + } + protected override IEnumerable LoadEntries(IEnumerable volumes) => RarArchiveEntryFactory.GetEntries(this, volumes, ReaderOptions); diff --git a/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs b/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs index ee165982..88a85e96 100644 --- a/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs +++ b/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs @@ -163,7 +163,7 @@ public class SevenZipArchive : AbstractArchive ); } } + else + { + throw new IncompleteArchiveException("Failed to read TAR header"); + } } } diff --git a/src/SharpCompress/AssemblyInfo.cs b/src/SharpCompress/AssemblyInfo.cs index 0270020d..e51d97fe 100644 --- a/src/SharpCompress/AssemblyInfo.cs +++ b/src/SharpCompress/AssemblyInfo.cs @@ -1,3 +1,7 @@ using System; +using System.Runtime.CompilerServices; [assembly: CLSCompliant(true)] +[assembly: InternalsVisibleTo( + "SharpCompress.Test,PublicKey=0024000004800000940000000602000000240000525341310004000001000100158bebf1433f76dffc356733c138babea7a47536c65ed8009b16372c6f4edbb20554db74a62687f56b97c20a6ce8c4b123280279e33c894e7b3aa93ab3c573656fde4db576cfe07dba09619ead26375b25d2c4a8e43f7be257d712b0dd2eb546f67adb09281338618a58ac834fc038dd7e2740a7ab3591826252e4f4516306dc" +)] diff --git a/src/SharpCompress/BufferPool.cs b/src/SharpCompress/BufferPool.cs deleted file mode 100644 index b11dbe6f..00000000 --- a/src/SharpCompress/BufferPool.cs +++ /dev/null @@ -1,33 +0,0 @@ -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/ArchiveType.cs b/src/SharpCompress/Common/ArchiveType.cs index 49b35387..f9c7db09 100644 --- a/src/SharpCompress/Common/ArchiveType.cs +++ b/src/SharpCompress/Common/ArchiveType.cs @@ -6,5 +6,5 @@ public enum ArchiveType Zip, Tar, SevenZip, - GZip + GZip, } diff --git a/src/SharpCompress/Common/CompressionType.cs b/src/SharpCompress/Common/CompressionType.cs index 20f166c4..45b6aeeb 100644 --- a/src/SharpCompress/Common/CompressionType.cs +++ b/src/SharpCompress/Common/CompressionType.cs @@ -16,5 +16,10 @@ public enum CompressionType Unknown, Deflate64, Shrink, - Lzw + Lzw, + Reduce1, + Reduce2, + Reduce3, + Reduce4, + Explode, } diff --git a/src/SharpCompress/Common/ExtractionMethods.cs b/src/SharpCompress/Common/ExtractionMethods.cs index 27d41648..799d47b3 100644 --- a/src/SharpCompress/Common/ExtractionMethods.cs +++ b/src/SharpCompress/Common/ExtractionMethods.cs @@ -37,6 +37,7 @@ internal static class ExtractionMethods options ??= new ExtractionOptions() { Overwrite = true }; var file = Path.GetFileName(entry.Key.NotNull("Entry Key is null")).NotNull("File is null"); + file = Utility.ReplaceInvalidFileNameChars(file); if (options.ExtractFullPath) { var folder = Path.GetDirectoryName(entry.Key.NotNull("Entry Key is null")) diff --git a/src/SharpCompress/Common/Rar/Headers/Flags.cs b/src/SharpCompress/Common/Rar/Headers/Flags.cs index 990dbc99..ef654ad5 100644 --- a/src/SharpCompress/Common/Rar/Headers/Flags.cs +++ b/src/SharpCompress/Common/Rar/Headers/Flags.cs @@ -13,7 +13,7 @@ public enum HeaderType : byte Sign, NewSub, EndArchive, - Crypt + Crypt, } internal static class HeaderCodeV diff --git a/src/SharpCompress/Common/Rar/RarVolume.cs b/src/SharpCompress/Common/Rar/RarVolume.cs index 274ac0a2..d05e16c2 100644 --- a/src/SharpCompress/Common/Rar/RarVolume.cs +++ b/src/SharpCompress/Common/Rar/RarVolume.cs @@ -61,9 +61,8 @@ public abstract class RarVolume : Volume var fh = (FileHeader)header; if (fh.FileName == "CMT") { - var part = CreateFilePart(lastMarkHeader!, fh); var buffer = new byte[fh.CompressedSize]; - part.GetCompressedStream().NotNull().Read(buffer, 0, buffer.Length); + fh.PackedStream.Read(buffer, 0, buffer.Length); Comment = Encoding.UTF8.GetString(buffer, 0, buffer.Length - 1); } } diff --git a/src/SharpCompress/Common/SevenZip/ArchiveReader.cs b/src/SharpCompress/Common/SevenZip/ArchiveReader.cs index 96488124..f280d64a 100644 --- a/src/SharpCompress/Common/SevenZip/ArchiveReader.cs +++ b/src/SharpCompress/Common/SevenZip/ArchiveReader.cs @@ -1220,23 +1220,46 @@ internal class ArchiveReader #region Public Methods - public void Open(Stream stream) + public void Open(Stream stream, bool lookForHeader) { Close(); _streamOrigin = stream.Position; _streamEnding = stream.Length; - // TODO: Check Signature! - _header = new byte[0x20]; - for (var offset = 0; offset < 0x20; ) + var canScan = lookForHeader ? 0x80000 - 20 : 0; + while (true) { - var delta = stream.Read(_header, offset, 0x20 - offset); - if (delta == 0) + // TODO: Check Signature! + _header = new byte[0x20]; + for (var offset = 0; offset < 0x20; ) { - throw new EndOfStreamException(); + var delta = stream.Read(_header, offset, 0x20 - offset); + if (delta == 0) + { + throw new EndOfStreamException(); + } + + offset += delta; } - offset += delta; + + if ( + !lookForHeader + || _header + .AsSpan(0, length: 6) + .SequenceEqual([0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C]) + ) + { + break; + } + + if (canScan == 0) + { + throw new InvalidFormatException("Unable to find 7z signature"); + } + + canScan--; + stream.Position = ++_streamOrigin; } _stream = stream; diff --git a/src/SharpCompress/Common/Tar/Headers/EntryType.cs b/src/SharpCompress/Common/Tar/Headers/EntryType.cs index 3e6877c7..2dc4dbd1 100644 --- a/src/SharpCompress/Common/Tar/Headers/EntryType.cs +++ b/src/SharpCompress/Common/Tar/Headers/EntryType.cs @@ -14,5 +14,5 @@ internal enum EntryType : byte LongName = (byte)'L', SparseFile = (byte)'S', VolumeHeader = (byte)'V', - GlobalExtendedHeader = (byte)'g' + GlobalExtendedHeader = (byte)'g', } diff --git a/src/SharpCompress/Common/Tar/Headers/TarHeader.cs b/src/SharpCompress/Common/Tar/Headers/TarHeader.cs index 8f87a028..1a04740f 100644 --- a/src/SharpCompress/Common/Tar/Headers/TarHeader.cs +++ b/src/SharpCompress/Common/Tar/Headers/TarHeader.cs @@ -101,57 +101,85 @@ internal sealed class TarHeader internal bool Read(BinaryReader reader) { - var buffer = ReadBlock(reader); - if (buffer.Length == 0) + string? longName = null; + string? longLinkName = null; + var hasLongValue = true; + byte[] buffer; + EntryType entryType; + + do + { + buffer = ReadBlock(reader); + + 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); + + // Check header checksum + if (!checkChecksum(buffer)) { return false; } - // for symlinks, additionally read the linkname - if (ReadEntryType(buffer) == EntryType.SymLink) - { - 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); + 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 = ReadAsciiInt64Base8oldGnu(buffer, 108, 7); GroupId = ReadAsciiInt64Base8oldGnu(buffer, 116, 7); - var unixTimeStamp = ReadAsciiInt64Base8(buffer, 136, 11); - LastModifiedTime = EPOCH.AddSeconds(unixTimeStamp).ToLocalTime(); + 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; } @@ -286,9 +314,45 @@ internal sealed class TarHeader (byte)' ', (byte)' ', (byte)' ', - (byte)' ' + (byte)' ', }; + internal static bool checkChecksum(byte[] buf) + { + const int eightSpacesChksum = 256; + var buffer = new Span(buf).Slice(0, 512); + int posix_sum = eightSpacesChksum; + int sun_sum = eightSpacesChksum; + + foreach (byte b in buffer) + { + posix_sum += b; + sun_sum += unchecked((sbyte)b); + } + + // Special case, empty file header + if (posix_sum == eightSpacesChksum) + { + return true; + } + + // Remove current checksum from calculation + foreach (byte b in buffer.Slice(148, 8)) + { + posix_sum -= b; + sun_sum -= unchecked((sbyte)b); + } + + // Read and compare checksum for header + var crc = ReadAsciiInt64Base8(buf, 148, 7); + if (crc != posix_sum && crc != sun_sum) + { + return false; + } + + return true; + } + internal static int RecalculateChecksum(byte[] buf) { // Set default value for checksum. That is 8 spaces. diff --git a/src/SharpCompress/Common/Zip/Headers/HeaderFlags.cs b/src/SharpCompress/Common/Zip/Headers/HeaderFlags.cs index 5aef3394..728f56b4 100644 --- a/src/SharpCompress/Common/Zip/Headers/HeaderFlags.cs +++ b/src/SharpCompress/Common/Zip/Headers/HeaderFlags.cs @@ -13,5 +13,5 @@ internal enum HeaderFlags : ushort EnhancedDeflate = 16, //Bit 11: Language encoding flag - Efs = 2048 + Efs = 2048, } diff --git a/src/SharpCompress/Common/Zip/Headers/LocalEntryHeaderExtraFactory.cs b/src/SharpCompress/Common/Zip/Headers/LocalEntryHeaderExtraFactory.cs index 88e4a187..daedd9a6 100644 --- a/src/SharpCompress/Common/Zip/Headers/LocalEntryHeaderExtraFactory.cs +++ b/src/SharpCompress/Common/Zip/Headers/LocalEntryHeaderExtraFactory.cs @@ -14,7 +14,7 @@ internal enum ExtraDataType : ushort // -Info-ZIP Unicode Path Extra Field UnicodePathExtraField = 0x7075, Zip64ExtendedInformationExtraField = 0x0001, - UnixTimeExtraField = 0x5455 + UnixTimeExtraField = 0x5455, } internal class ExtraData @@ -166,10 +166,10 @@ internal sealed class UnixTimeExtraField : ExtraData 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 flags = (RecordedTimeFlag)DataBytes[0]; + var isModifiedTimeSpecified = flags.HasFlag(RecordedTimeFlag.LastModified); + var isLastAccessTimeSpecified = flags.HasFlag(RecordedTimeFlag.LastAccessed); + var isCreationTimeSpecified = flags.HasFlag(RecordedTimeFlag.Created); var currentIndex = 1; DateTime? modifiedTime = null; DateTime? lastAccessTime = null; @@ -189,7 +189,7 @@ internal sealed class UnixTimeExtraField : ExtraData { if (currentIndex + 4 > DataBytes.Length) { - throw new ArchiveException("Invalid UnicodeExtraTime field"); + return Tuple.Create(null, null, null); } var lastAccessEpochTime = BinaryPrimitives.ReadInt32LittleEndian( @@ -206,7 +206,7 @@ internal sealed class UnixTimeExtraField : ExtraData { if (currentIndex + 4 > DataBytes.Length) { - throw new ArchiveException("Invalid UnicodeExtraTime field"); + return Tuple.Create(null, null, null); } var creationTimeEpochTime = BinaryPrimitives.ReadInt32LittleEndian( @@ -222,6 +222,15 @@ internal sealed class UnixTimeExtraField : ExtraData return Tuple.Create(modifiedTime, lastAccessTime, creationTime); } } + + [Flags] + private enum RecordedTimeFlag + { + None = 0, + LastModified = 1, + LastAccessed = 2, + Created = 4, + } } internal static class LocalEntryHeaderExtraFactory @@ -229,11 +238,14 @@ internal static class LocalEntryHeaderExtraFactory internal static ExtraData Create(ExtraDataType type, ushort length, byte[] extraData) => type switch { - ExtraDataType.UnicodePathExtraField - => new ExtraUnicodePathExtraField(type, length, extraData), - ExtraDataType.Zip64ExtendedInformationExtraField - => new Zip64ExtendedInformationExtraField(type, length, extraData), + ExtraDataType.UnicodePathExtraField => new ExtraUnicodePathExtraField( + type, + length, + extraData + ), + ExtraDataType.Zip64ExtendedInformationExtraField => + new Zip64ExtendedInformationExtraField(type, length, extraData), ExtraDataType.UnixTimeExtraField => new UnixTimeExtraField(type, length, extraData), - _ => new ExtraData(type, length, extraData) + _ => new ExtraData(type, length, extraData), }; } diff --git a/src/SharpCompress/Common/Zip/Headers/ZipHeaderType.cs b/src/SharpCompress/Common/Zip/Headers/ZipHeaderType.cs index 62c29d89..75cce8d8 100644 --- a/src/SharpCompress/Common/Zip/Headers/ZipHeaderType.cs +++ b/src/SharpCompress/Common/Zip/Headers/ZipHeaderType.cs @@ -8,5 +8,5 @@ internal enum ZipHeaderType DirectoryEnd, Split, Zip64DirectoryEnd, - Zip64DirectoryEndLocator + Zip64DirectoryEndLocator, } diff --git a/src/SharpCompress/Common/Zip/PkwareTraditionalCryptoStream.cs b/src/SharpCompress/Common/Zip/PkwareTraditionalCryptoStream.cs index 6ac48c6f..f8914c68 100644 --- a/src/SharpCompress/Common/Zip/PkwareTraditionalCryptoStream.cs +++ b/src/SharpCompress/Common/Zip/PkwareTraditionalCryptoStream.cs @@ -6,7 +6,7 @@ namespace SharpCompress.Common.Zip; internal enum CryptoMode { Encrypt, - Decrypt + Decrypt, } internal class PkwareTraditionalCryptoStream : Stream diff --git a/src/SharpCompress/Common/Zip/SeekableZipFilePart.cs b/src/SharpCompress/Common/Zip/SeekableZipFilePart.cs index e37008e5..54c4f527 100644 --- a/src/SharpCompress/Common/Zip/SeekableZipFilePart.cs +++ b/src/SharpCompress/Common/Zip/SeekableZipFilePart.cs @@ -1,6 +1,5 @@ using System.IO; using SharpCompress.Common.Zip.Headers; -using SharpCompress.IO; namespace SharpCompress.Common.Zip; @@ -8,18 +7,13 @@ internal class SeekableZipFilePart : ZipFilePart { private bool _isLocalHeaderLoaded; private readonly SeekableZipHeaderFactory _headerFactory; - private readonly DirectoryEntryHeader _directoryEntryHeader; internal SeekableZipFilePart( SeekableZipHeaderFactory headerFactory, DirectoryEntryHeader header, Stream stream ) - : base(header, stream) - { - _headerFactory = headerFactory; - _directoryEntryHeader = header; - } + : base(header, stream) => _headerFactory = headerFactory; internal override Stream GetCompressedStream() { @@ -44,16 +38,6 @@ internal class SeekableZipFilePart : ZipFilePart { BaseStream.Position = Header.DataStartPosition.NotNull(); - if ( - (Header.CompressedSize == 0) - && FlagUtility.HasFlag(Header.Flags, HeaderFlags.UsePostDataDescriptor) - && _directoryEntryHeader.HasData - && (_directoryEntryHeader.CompressedSize != 0) - ) - { - return new ReadOnlySubStream(BaseStream, _directoryEntryHeader.CompressedSize); - } - return BaseStream; } } diff --git a/src/SharpCompress/Common/Zip/SeekableZipHeaderFactory.cs b/src/SharpCompress/Common/Zip/SeekableZipHeaderFactory.cs index b99d0e2b..816b0b67 100644 --- a/src/SharpCompress/Common/Zip/SeekableZipHeaderFactory.cs +++ b/src/SharpCompress/Common/Zip/SeekableZipHeaderFactory.cs @@ -149,6 +149,12 @@ internal sealed class SeekableZipHeaderFactory : ZipHeaderFactory { throw new InvalidOperationException(); } + if (FlagUtility.HasFlag(localEntryHeader.Flags, HeaderFlags.UsePostDataDescriptor)) + { + localEntryHeader.Crc = directoryEntryHeader.Crc; + localEntryHeader.CompressedSize = directoryEntryHeader.CompressedSize; + localEntryHeader.UncompressedSize = directoryEntryHeader.UncompressedSize; + } return localEntryHeader; } } diff --git a/src/SharpCompress/Common/Zip/StreamingZipHeaderFactory.cs b/src/SharpCompress/Common/Zip/StreamingZipHeaderFactory.cs index c0af5408..508dfd1a 100644 --- a/src/SharpCompress/Common/Zip/StreamingZipHeaderFactory.cs +++ b/src/SharpCompress/Common/Zip/StreamingZipHeaderFactory.cs @@ -36,10 +36,7 @@ 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) @@ -49,7 +46,9 @@ internal class StreamingZipHeaderFactory : ZipHeaderFactory reader = ((StreamingZipFilePart)_lastEntryHeader.Part).FixStreamedFileLocation( ref rewindableStream ); + var pos = rewindableStream.CanSeek ? (long?)rewindableStream.Position : null; + var crc = reader.ReadUInt32(); if (crc == POST_DATA_DESCRIPTOR) { @@ -82,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(); diff --git a/src/SharpCompress/Common/Zip/WinzipAesKeySize.cs b/src/SharpCompress/Common/Zip/WinzipAesKeySize.cs index 7772bcaa..276a3504 100644 --- a/src/SharpCompress/Common/Zip/WinzipAesKeySize.cs +++ b/src/SharpCompress/Common/Zip/WinzipAesKeySize.cs @@ -4,5 +4,5 @@ internal enum WinzipAesKeySize { KeySize128 = 1, KeySize192 = 2, - KeySize256 = 3 + KeySize256 = 3, } diff --git a/src/SharpCompress/Common/Zip/ZipCompressionMethod.cs b/src/SharpCompress/Common/Zip/ZipCompressionMethod.cs index a98ed667..834197c0 100644 --- a/src/SharpCompress/Common/Zip/ZipCompressionMethod.cs +++ b/src/SharpCompress/Common/Zip/ZipCompressionMethod.cs @@ -4,6 +4,11 @@ internal enum ZipCompressionMethod { None = 0, Shrink = 1, + Reduce1 = 2, + Reduce2 = 3, + Reduce3 = 4, + Reduce4 = 5, + Explode = 6, Deflate = 8, Deflate64 = 9, BZip2 = 12, @@ -11,5 +16,5 @@ internal enum ZipCompressionMethod ZStd = 93, Xz = 95, PPMd = 98, - WinzipAes = 0x63 //http://www.winzip.com/aes_info.htm + WinzipAes = 0x63, //http://www.winzip.com/aes_info.htm } diff --git a/src/SharpCompress/Common/Zip/ZipEntry.cs b/src/SharpCompress/Common/Zip/ZipEntry.cs index c2cecf39..d8e947f8 100644 --- a/src/SharpCompress/Common/Zip/ZipEntry.cs +++ b/src/SharpCompress/Common/Zip/ZipEntry.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using SharpCompress.Common.Zip.Headers; namespace SharpCompress.Common.Zip; @@ -15,10 +16,19 @@ public class ZipEntry : Entry return; } _filePart = filePart; + LastModifiedTime = Utility.DosDateToDateTime( filePart.Header.LastModifiedDate, filePart.Header.LastModifiedTime ); + + var times = + filePart.Header.Extra.FirstOrDefault(header => + header.GetType() == typeof(UnixTimeExtraField) + ) as UnixTimeExtraField; + + LastAccessedTime = times?.UnicodeTimes.Item2; + CreatedTime = times?.UnicodeTimes.Item3; } public override CompressionType CompressionType => @@ -31,7 +41,12 @@ public class ZipEntry : Entry ZipCompressionMethod.PPMd => CompressionType.PPMd, ZipCompressionMethod.None => CompressionType.None, ZipCompressionMethod.Shrink => CompressionType.Shrink, - _ => CompressionType.Unknown + ZipCompressionMethod.Reduce1 => CompressionType.Reduce1, + ZipCompressionMethod.Reduce2 => CompressionType.Reduce2, + ZipCompressionMethod.Reduce3 => CompressionType.Reduce3, + ZipCompressionMethod.Reduce4 => CompressionType.Reduce4, + ZipCompressionMethod.Explode => CompressionType.Explode, + _ => CompressionType.Unknown, }; public override long Crc => _filePart?.Header.Crc ?? 0; @@ -46,9 +61,17 @@ public class ZipEntry : Entry public override DateTime? LastModifiedTime { get; } - public override DateTime? CreatedTime => null; + /// + /// + /// The returned time is UTC, not local. + /// + public override DateTime? CreatedTime { get; } - public override DateTime? LastAccessedTime => null; + /// + /// + /// The returned time is UTC, not local. + /// + public override DateTime? LastAccessedTime { get; } public override DateTime? ArchivedTime => null; diff --git a/src/SharpCompress/Common/Zip/ZipFilePart.cs b/src/SharpCompress/Common/Zip/ZipFilePart.cs index e8516150..85b566d9 100644 --- a/src/SharpCompress/Common/Zip/ZipFilePart.cs +++ b/src/SharpCompress/Common/Zip/ZipFilePart.cs @@ -7,8 +7,10 @@ using SharpCompress.Compressors; using SharpCompress.Compressors.BZip2; using SharpCompress.Compressors.Deflate; using SharpCompress.Compressors.Deflate64; +using SharpCompress.Compressors.Explode; using SharpCompress.Compressors.LZMA; using SharpCompress.Compressors.PPMd; +using SharpCompress.Compressors.Reduce; using SharpCompress.Compressors.Shrink; using SharpCompress.Compressors.Xz; using SharpCompress.IO; @@ -89,6 +91,32 @@ internal abstract class ZipFilePart : FilePart Header.UncompressedSize ); } + case ZipCompressionMethod.Reduce1: + { + return new ReduceStream(stream, Header.CompressedSize, Header.UncompressedSize, 1); + } + case ZipCompressionMethod.Reduce2: + { + return new ReduceStream(stream, Header.CompressedSize, Header.UncompressedSize, 2); + } + case ZipCompressionMethod.Reduce3: + { + return new ReduceStream(stream, Header.CompressedSize, Header.UncompressedSize, 3); + } + case ZipCompressionMethod.Reduce4: + { + return new ReduceStream(stream, Header.CompressedSize, Header.UncompressedSize, 4); + } + case ZipCompressionMethod.Explode: + { + return new ExplodeStream( + stream, + Header.CompressedSize, + Header.UncompressedSize, + Header.Flags + ); + } + case ZipCompressionMethod.Deflate: { return new DeflateStream(stream, CompressionMode.Decompress); @@ -203,6 +231,10 @@ internal abstract class ZipFilePart : FilePart { case ZipCompressionMethod.None: case ZipCompressionMethod.Shrink: + case ZipCompressionMethod.Reduce1: + case ZipCompressionMethod.Reduce2: + case ZipCompressionMethod.Reduce3: + case ZipCompressionMethod.Reduce4: 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 40fe178f..45869ff6 100644 --- a/src/SharpCompress/Common/Zip/ZipHeaderFactory.cs +++ b/src/SharpCompress/Common/Zip/ZipHeaderFactory.cs @@ -56,11 +56,11 @@ internal class ZipHeaderFactory case POST_DATA_DESCRIPTOR: { if ( - FlagUtility.HasFlag( + _lastEntryHeader != null + && FlagUtility.HasFlag( _lastEntryHeader.NotNull().Flags, HeaderFlags.UsePostDataDescriptor ) - && _lastEntryHeader != null ) { _lastEntryHeader.Crc = reader.ReadUInt32(); diff --git a/src/SharpCompress/Compressors/BZip2/BZip2Constants.cs b/src/SharpCompress/Compressors/BZip2/BZip2Constants.cs index cfeb02e4..85c63b39 100644 --- a/src/SharpCompress/Compressors/BZip2/BZip2Constants.cs +++ b/src/SharpCompress/Compressors/BZip2/BZip2Constants.cs @@ -555,6 +555,6 @@ internal class BZip2Constants 858, 364, 936, - 638 + 638, }; } diff --git a/src/SharpCompress/Compressors/BZip2/CBZip2InputStream.cs b/src/SharpCompress/Compressors/BZip2/CBZip2InputStream.cs index a541a04d..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() { diff --git a/src/SharpCompress/Compressors/BZip2/CBZip2OutputStream.cs b/src/SharpCompress/Compressors/BZip2/CBZip2OutputStream.cs index bf01c2c3..5e78c5fa 100644 --- a/src/SharpCompress/Compressors/BZip2/CBZip2OutputStream.cs +++ b/src/SharpCompress/Compressors/BZip2/CBZip2OutputStream.cs @@ -1829,7 +1829,7 @@ internal sealed class CBZip2OutputStream : Stream 88573, 265720, 797161, - 2391484 + 2391484, }; private void AllocateCompressStructures() diff --git a/src/SharpCompress/Compressors/BZip2/CRC.cs b/src/SharpCompress/Compressors/BZip2/CRC.cs index 9f593c40..6e8e034a 100644 --- a/src/SharpCompress/Compressors/BZip2/CRC.cs +++ b/src/SharpCompress/Compressors/BZip2/CRC.cs @@ -288,7 +288,7 @@ internal class CRC unchecked((int)0xbcb4666d), unchecked((int)0xb8757bda), unchecked((int)0xb5365d03), - unchecked((int)0xb1f740b4) + unchecked((int)0xb1f740b4), }; public CRC() => InitialiseCRC(); diff --git a/src/SharpCompress/Compressors/CompressionMode.cs b/src/SharpCompress/Compressors/CompressionMode.cs index 4eba1121..0bf15e03 100644 --- a/src/SharpCompress/Compressors/CompressionMode.cs +++ b/src/SharpCompress/Compressors/CompressionMode.cs @@ -3,5 +3,5 @@ namespace SharpCompress.Compressors; public enum CompressionMode { Compress = 0, - Decompress = 1 + Decompress = 1, } diff --git a/src/SharpCompress/Compressors/Deflate/DeflateManager.cs b/src/SharpCompress/Compressors/Deflate/DeflateManager.cs index cccd0237..933f5752 100644 --- a/src/SharpCompress/Compressors/Deflate/DeflateManager.cs +++ b/src/SharpCompress/Compressors/Deflate/DeflateManager.cs @@ -106,7 +106,7 @@ internal sealed partial class DeflateManager 5, 5, 5, - 0 + 0, }; // extra bits for each distance code @@ -141,7 +141,7 @@ internal sealed partial class DeflateManager 12, 12, 13, - 13 + 13, }; internal enum BlockState @@ -149,14 +149,14 @@ internal sealed partial class DeflateManager NeedMore = 0, // block not completed, need more input or more output BlockDone, // block flush performed FinishStarted, // finish started, need only more output at next deflate - FinishDone // finish done, accept no more input or output + FinishDone, // finish done, accept no more input or output } internal enum DeflateFlavor { Store, Fast, - Slow + Slow, } private const int MEM_LEVEL_MAX = 9; @@ -214,7 +214,7 @@ internal sealed partial class DeflateManager new Config(8, 16, 128, 128, DeflateFlavor.Slow), new Config(8, 32, 128, 256, DeflateFlavor.Slow), new Config(32, 128, 258, 1024, DeflateFlavor.Slow), - new Config(32, 258, 258, 4096, DeflateFlavor.Slow) + new Config(32, 258, 258, 4096, DeflateFlavor.Slow), }; private static readonly Config[] Table; @@ -233,7 +233,7 @@ internal sealed partial class DeflateManager "insufficient memory", "buffer error", "incompatible version", - "" + "", }; // preset dictionary flag in zlib header @@ -1793,7 +1793,7 @@ internal sealed partial class DeflateManager DeflateFlavor.Store => DeflateNone, DeflateFlavor.Fast => DeflateFast, DeflateFlavor.Slow => DeflateSlow, - _ => DeflateFunction + _ => DeflateFunction, }; internal int SetParams(CompressionLevel level, CompressionStrategy strategy) diff --git a/src/SharpCompress/Compressors/Deflate/FlushType.cs b/src/SharpCompress/Compressors/Deflate/FlushType.cs index bcf7fe10..446962a3 100644 --- a/src/SharpCompress/Compressors/Deflate/FlushType.cs +++ b/src/SharpCompress/Compressors/Deflate/FlushType.cs @@ -39,5 +39,5 @@ public enum FlushType Full, /// Signals the end of the compression/decompression stream. - Finish + Finish, } diff --git a/src/SharpCompress/Compressors/Deflate/InfTree.cs b/src/SharpCompress/Compressors/Deflate/InfTree.cs index 74f4069d..2ea4cbc4 100644 --- a/src/SharpCompress/Compressors/Deflate/InfTree.cs +++ b/src/SharpCompress/Compressors/Deflate/InfTree.cs @@ -1615,7 +1615,7 @@ internal sealed class InfTree 79, 0, 9, - 255 + 255, }; //UPGRADE_NOTE: Final was removed from the declaration of 'fixed_td'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" @@ -1716,7 +1716,7 @@ internal sealed class InfTree 193, 192, 5, - 24577 + 24577, }; // Tables for deflate from PKZIP's appnote.txt. @@ -1753,7 +1753,7 @@ internal sealed class InfTree 227, 258, 0, - 0 + 0, }; // see note #13 above about 258 @@ -1790,7 +1790,7 @@ internal sealed class InfTree 5, 0, 112, - 112 + 112, }; //UPGRADE_NOTE: Final was removed from the declaration of 'cpdist'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" @@ -1825,7 +1825,7 @@ internal sealed class InfTree 8193, 12289, 16385, - 24577 + 24577, }; //UPGRADE_NOTE: Final was removed from the declaration of 'cpdext'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" @@ -1860,7 +1860,7 @@ internal sealed class InfTree 12, 12, 13, - 13 + 13, }; // If BMAX needs to be larger than 16, then h and x[] should be uLong. diff --git a/src/SharpCompress/Compressors/Deflate/Inflate.cs b/src/SharpCompress/Compressors/Deflate/Inflate.cs index d768548b..fd1c764a 100644 --- a/src/SharpCompress/Compressors/Deflate/Inflate.cs +++ b/src/SharpCompress/Compressors/Deflate/Inflate.cs @@ -93,7 +93,7 @@ internal sealed class InflateBlocks 2, 14, 1, - 15 + 15, }; internal ZlibCodec _codec; // pointer back to this zlib stream @@ -815,7 +815,7 @@ internal sealed class InflateBlocks CODES = 6, // processing fixed or dynamic block DRY = 7, // output remaining window bytes DONE = 8, // finished last block, done - BAD = 9 // ot a data error--stuck here + BAD = 9, // ot a data error--stuck here } #endregion @@ -842,7 +842,7 @@ internal static class InternalInflateConstants 0x00001fff, 0x00003fff, 0x00007fff, - 0x0000ffff + 0x0000ffff, }; } @@ -2054,7 +2054,7 @@ internal sealed class InflateManager CHECK2 = 10, // two check bytes to go CHECK1 = 11, // one check byte to go DONE = 12, // finished check, done - BAD = 13 // got an error--stay here + BAD = 13, // got an error--stay here } #endregion diff --git a/src/SharpCompress/Compressors/Deflate/Tree.cs b/src/SharpCompress/Compressors/Deflate/Tree.cs index ec1eb51f..e5c9b52a 100644 --- a/src/SharpCompress/Compressors/Deflate/Tree.cs +++ b/src/SharpCompress/Compressors/Deflate/Tree.cs @@ -95,7 +95,7 @@ internal sealed partial class DeflateManager 2, 14, 1, - 15 + 15, }; // The lengths of the bit length codes are sent in order of decreasing @@ -618,7 +618,7 @@ internal sealed partial class DeflateManager 29, 29, 29, - 29 + 29, }; internal static readonly sbyte[] LengthCode = @@ -878,7 +878,7 @@ internal sealed partial class DeflateManager 27, 27, 27, - 28 + 28, }; internal static readonly int[] LengthBase = @@ -911,7 +911,7 @@ internal sealed partial class DeflateManager 160, 192, 224, - 0 + 0, }; internal static readonly int[] DistanceBase = @@ -945,7 +945,7 @@ internal sealed partial class DeflateManager 8192, 12288, 16384, - 24576 + 24576, }; internal short[] dyn_tree; // the dynamic tree diff --git a/src/SharpCompress/Compressors/Deflate/Zlib.cs b/src/SharpCompress/Compressors/Deflate/Zlib.cs index 1358e01b..9ba751d8 100644 --- a/src/SharpCompress/Compressors/Deflate/Zlib.cs +++ b/src/SharpCompress/Compressors/Deflate/Zlib.cs @@ -143,7 +143,7 @@ public enum CompressionLevel /// /// A synonym for BestCompression. /// - Level9 = BestCompression + Level9 = BestCompression, } /// @@ -171,7 +171,7 @@ public enum CompressionStrategy /// Using HuffmanOnly will force the compressor to do Huffman encoding only, with no /// string matching. /// - HuffmanOnly = 2 + HuffmanOnly = 2, } /// @@ -859,7 +859,7 @@ internal sealed class StaticTree 99, 8, 227, - 8 + 8, }; internal static readonly short[] distTreeCodes = @@ -923,7 +923,7 @@ internal sealed class StaticTree 7, 5, 23, - 5 + 5, }; // extra bits for each bit length code @@ -947,7 +947,7 @@ internal sealed class StaticTree 0, 2, 3, - 7 + 7, }; internal static readonly StaticTree Literals; diff --git a/src/SharpCompress/Compressors/Deflate/ZlibBaseStream.cs b/src/SharpCompress/Compressors/Deflate/ZlibBaseStream.cs index 94b1491d..f6372dff 100644 --- a/src/SharpCompress/Compressors/Deflate/ZlibBaseStream.cs +++ b/src/SharpCompress/Compressors/Deflate/ZlibBaseStream.cs @@ -39,7 +39,7 @@ internal enum ZlibStreamFlavor { ZLIB = 1950, DEFLATE = 1951, - GZIP = 1952 + GZIP = 1952, } internal class ZlibBaseStream : Stream @@ -655,6 +655,6 @@ internal class ZlibBaseStream : Stream { Writer, Reader, - Undefined + Undefined, } } diff --git a/src/SharpCompress/Compressors/Deflate64/BlockType.cs b/src/SharpCompress/Compressors/Deflate64/BlockType.cs index 5bd9eb83..b34ca903 100644 --- a/src/SharpCompress/Compressors/Deflate64/BlockType.cs +++ b/src/SharpCompress/Compressors/Deflate64/BlockType.cs @@ -8,5 +8,5 @@ internal enum BlockType { Uncompressed = 0, Static = 1, - Dynamic = 2 + Dynamic = 2, } diff --git a/src/SharpCompress/Compressors/Deflate64/FastEncoderStatus.cs b/src/SharpCompress/Compressors/Deflate64/FastEncoderStatus.cs index 71e3f680..37dafc68 100644 --- a/src/SharpCompress/Compressors/Deflate64/FastEncoderStatus.cs +++ b/src/SharpCompress/Compressors/Deflate64/FastEncoderStatus.cs @@ -111,7 +111,7 @@ internal static class FastEncoderStatics 0x7e, 0x7c, 0x1f, - 0x3f + 0x3f, }; internal static ReadOnlySpan B_FINAL_FAST_ENCODER_TREE_STRUCTURE_DATA => @@ -214,7 +214,7 @@ internal static class FastEncoderStatics 0x7e, 0x7c, 0x1f, - 0x3f + 0x3f, }; // Output a currentMatch with length matchLen (>= MIN_MATCH) and displacement matchPos @@ -762,7 +762,7 @@ internal static class FastEncoderStatics 0x0039e7f1, 0x003be7f1, 0x003de7f1, - 0x000047eb + 0x000047eb, }; internal static readonly uint[] FAST_ENCODER_DISTANCE_CODE_INFO = @@ -798,7 +798,7 @@ internal static class FastEncoderStatics 0x000007d5, 0x000017d5, 0x00000000, - 0x00000100 + 0x00000100, }; internal static readonly uint[] BIT_MASK = @@ -818,7 +818,7 @@ internal static class FastEncoderStatics 4095, 8191, 16383, - 32767 + 32767, }; internal static readonly byte[] EXTRA_LENGTH_BITS = { @@ -850,7 +850,7 @@ internal static class FastEncoderStatics 5, 5, 5, - 0 + 0, }; internal static readonly byte[] EXTRA_DISTANCE_BITS = { @@ -885,7 +885,7 @@ internal static class FastEncoderStatics 13, 13, 0, - 0 + 0, }; internal const int NUM_CHARS = 256; internal const int NUM_LENGTH_BASE_CODES = 29; diff --git a/src/SharpCompress/Compressors/Deflate64/InflaterManaged.cs b/src/SharpCompress/Compressors/Deflate64/InflaterManaged.cs index 101188db..5caf257d 100644 --- a/src/SharpCompress/Compressors/Deflate64/InflaterManaged.cs +++ b/src/SharpCompress/Compressors/Deflate64/InflaterManaged.cs @@ -70,7 +70,7 @@ internal sealed class InflaterManaged 5, 5, 5, - 16 + 16, }; // The base length for length code 257 - 285. @@ -105,7 +105,7 @@ internal sealed class InflaterManaged 163, 195, 227, - 3 + 3, }; // The base distance for distance code 0 - 31 @@ -143,7 +143,7 @@ internal sealed class InflaterManaged 16385, 24577, 32769, - 49153 + 49153, }; // code lengths for code length alphabet is stored in following order @@ -184,7 +184,7 @@ internal sealed class InflaterManaged 0x07, 0x17, 0x0f, - 0x1f + 0x1f, }; private readonly OutputWindow _output; diff --git a/src/SharpCompress/Compressors/Deflate64/InflaterState.cs b/src/SharpCompress/Compressors/Deflate64/InflaterState.cs index 711c3245..16d627e0 100644 --- a/src/SharpCompress/Compressors/Deflate64/InflaterState.cs +++ b/src/SharpCompress/Compressors/Deflate64/InflaterState.cs @@ -37,5 +37,5 @@ internal enum InflaterState ReadingFooter = 22, VerifyingFooter = 23, - Done = 24 // Finished + Done = 24, // Finished } diff --git a/src/SharpCompress/Compressors/Deflate64/MatchState.cs b/src/SharpCompress/Compressors/Deflate64/MatchState.cs index 858e91e8..cabc0bad 100644 --- a/src/SharpCompress/Compressors/Deflate64/MatchState.cs +++ b/src/SharpCompress/Compressors/Deflate64/MatchState.cs @@ -8,5 +8,5 @@ internal enum MatchState { HasSymbol = 1, HasMatch = 2, - HasSymbolAndMatch = 3 + HasSymbolAndMatch = 3, } diff --git a/src/SharpCompress/Compressors/Explode/ExplodeStream.cs b/src/SharpCompress/Compressors/Explode/ExplodeStream.cs new file mode 100644 index 00000000..11a016e0 --- /dev/null +++ b/src/SharpCompress/Compressors/Explode/ExplodeStream.cs @@ -0,0 +1,746 @@ +using System; +using System.IO; +using SharpCompress.Common.Zip.Headers; + +namespace SharpCompress.Compressors.Explode; + +public class ExplodeStream : Stream +{ + private const int INVALID_CODE = 99; + private const int WSIZE = 64 * 1024; + + private readonly long unCompressedSize; + private readonly int compressedSize; + private readonly HeaderFlags generalPurposeBitFlag; + private readonly Stream inStream; + + private huftNode[]? hufLiteralCodeTable; /* literal code table */ + private huftNode[] hufLengthCodeTable = []; /* length code table */ + private huftNode[] hufDistanceCodeTable = []; /* distance code table */ + + private int bitsForLiteralCodeTable; + private int bitsForLengthCodeTable; + private int bitsForDistanceCodeTable; + private int numOfUncodedLowerDistanceBits; /* number of uncoded lower distance bits */ + + private ulong bitBuffer; + private int bitBufferCount; + + private readonly byte[] windowsBuffer; + private uint maskForLiteralCodeTable; + private uint maskForLengthCodeTable; + private uint maskForDistanceCodeTable; + private uint maskForDistanceLowBits; + private long outBytesCount; + + private int windowIndex; + private int distance; + private int length; + + internal ExplodeStream( + Stream inStr, + long compressedSize, + long uncompressedSize, + HeaderFlags generalPurposeBitFlag + ) + { + inStream = inStr; + this.compressedSize = (int)compressedSize; + unCompressedSize = (long)uncompressedSize; + this.generalPurposeBitFlag = generalPurposeBitFlag; + explode_SetTables(); + + windowsBuffer = new byte[WSIZE]; + explode_var_init(); + } + + public override void Flush() + { + throw new NotImplementedException(); + } + + 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(); + } + + public override bool CanRead => true; + public override bool CanSeek => false; + public override bool CanWrite => false; + public override long Length => unCompressedSize; + public override long Position + { + get => outBytesCount; + set { } + } + + static uint[] mask_bits = new uint[] + { + 0x0000, + 0x0001, + 0x0003, + 0x0007, + 0x000f, + 0x001f, + 0x003f, + 0x007f, + 0x00ff, + 0x01ff, + 0x03ff, + 0x07ff, + 0x0fff, + 0x1fff, + 0x3fff, + 0x7fff, + 0xffff, + }; + + /* Tables for length and distance */ + static int[] cplen2 = new int[] + { + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + }; + + static int[] cplen3 = new int[] + { + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + }; + + static int[] extra = new int[] + { + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 8, + }; + + static int[] cpdist4 = new int[] + { + 1, + 65, + 129, + 193, + 257, + 321, + 385, + 449, + 513, + 577, + 641, + 705, + 769, + 833, + 897, + 961, + 1025, + 1089, + 1153, + 1217, + 1281, + 1345, + 1409, + 1473, + 1537, + 1601, + 1665, + 1729, + 1793, + 1857, + 1921, + 1985, + 2049, + 2113, + 2177, + 2241, + 2305, + 2369, + 2433, + 2497, + 2561, + 2625, + 2689, + 2753, + 2817, + 2881, + 2945, + 3009, + 3073, + 3137, + 3201, + 3265, + 3329, + 3393, + 3457, + 3521, + 3585, + 3649, + 3713, + 3777, + 3841, + 3905, + 3969, + 4033, + }; + + static int[] cpdist8 = new int[] + { + 1, + 129, + 257, + 385, + 513, + 641, + 769, + 897, + 1025, + 1153, + 1281, + 1409, + 1537, + 1665, + 1793, + 1921, + 2049, + 2177, + 2305, + 2433, + 2561, + 2689, + 2817, + 2945, + 3073, + 3201, + 3329, + 3457, + 3585, + 3713, + 3841, + 3969, + 4097, + 4225, + 4353, + 4481, + 4609, + 4737, + 4865, + 4993, + 5121, + 5249, + 5377, + 5505, + 5633, + 5761, + 5889, + 6017, + 6145, + 6273, + 6401, + 6529, + 6657, + 6785, + 6913, + 7041, + 7169, + 7297, + 7425, + 7553, + 7681, + 7809, + 7937, + 8065, + }; + + private int get_tree(int[] arrBitLengths, int numberExpected) + /* Get the bit lengths for a code representation from the compressed + stream. If get_tree() returns 4, then there is an error in the data. + Otherwise zero is returned. */ + { + /* get bit lengths */ + int inIndex = inStream.ReadByte() + 1; /* length/count pairs to read */ + int outIndex = 0; /* next code */ + do + { + int nextByte = inStream.ReadByte(); + int bitLengthOfCodes = (nextByte & 0xf) + 1; /* bits in code (1..16) */ + int numOfCodes = ((nextByte & 0xf0) >> 4) + 1; /* codes with those bits (1..16) */ + if (outIndex + numOfCodes > numberExpected) + return 4; /* don't overflow arrBitLengths[] */ + do + { + arrBitLengths[outIndex++] = bitLengthOfCodes; + } while ((--numOfCodes) != 0); + } while ((--inIndex) != 0); + + return outIndex != numberExpected ? 4 : 0; /* should have read numberExpected of them */ + } + + private int explode_SetTables() + { + int returnCode; /* return codes */ + int[] arrBitLengthsForCodes = new int[256]; /* bit lengths for codes */ + + bitsForLiteralCodeTable = 0; /* bits for tb */ + bitsForLengthCodeTable = 7; + bitsForDistanceCodeTable = (compressedSize) > 200000 ? 8 : 7; + + if ((generalPurposeBitFlag & HeaderFlags.Bit2) != 0) + /* With literal tree--minimum match length is 3 */ + { + bitsForLiteralCodeTable = 9; /* base table size for literals */ + if ((returnCode = get_tree(arrBitLengthsForCodes, 256)) != 0) + return returnCode; + + if ( + ( + returnCode = HuftTree.huftbuid( + arrBitLengthsForCodes, + 256, + 256, + [], + [], + out hufLiteralCodeTable, + ref bitsForLiteralCodeTable + ) + ) != 0 + ) + return returnCode; + + if ((returnCode = get_tree(arrBitLengthsForCodes, 64)) != 0) + return returnCode; + + if ( + ( + returnCode = HuftTree.huftbuid( + arrBitLengthsForCodes, + 64, + 0, + cplen3, + extra, + out hufLengthCodeTable, + ref bitsForLengthCodeTable + ) + ) != 0 + ) + return returnCode; + } + else + /* No literal tree--minimum match length is 2 */ + { + if ((returnCode = get_tree(arrBitLengthsForCodes, 64)) != 0) + return returnCode; + + hufLiteralCodeTable = null; + + if ( + ( + returnCode = HuftTree.huftbuid( + arrBitLengthsForCodes, + 64, + 0, + cplen2, + extra, + out hufLengthCodeTable, + ref bitsForLengthCodeTable + ) + ) != 0 + ) + return returnCode; + } + + if ((returnCode = get_tree(arrBitLengthsForCodes, 64)) != 0) + return (int)returnCode; + + if ((generalPurposeBitFlag & HeaderFlags.Bit1) != 0) /* true if 8K */ + { + numOfUncodedLowerDistanceBits = 7; + returnCode = HuftTree.huftbuid( + arrBitLengthsForCodes, + 64, + 0, + cpdist8, + extra, + out hufDistanceCodeTable, + ref bitsForDistanceCodeTable + ); + } + else /* else 4K */ + { + numOfUncodedLowerDistanceBits = 6; + returnCode = HuftTree.huftbuid( + arrBitLengthsForCodes, + 64, + 0, + cpdist4, + extra, + out hufDistanceCodeTable, + ref bitsForDistanceCodeTable + ); + } + + return returnCode; + } + + private void NeedBits(int numberOfBits) + { + while (bitBufferCount < (numberOfBits)) + { + bitBuffer |= (uint)inStream.ReadByte() << bitBufferCount; + bitBufferCount += 8; + } + } + + private void DumpBits(int numberOfBits) + { + bitBuffer >>= numberOfBits; + bitBufferCount -= numberOfBits; + } + + int DecodeHuft(huftNode[] htab, int bits, uint mask, out huftNode huftPointer, out int e) + { + NeedBits(bits); + + int tabOffset = (int)(~bitBuffer & mask); + huftPointer = htab[tabOffset]; + + while (true) + { + DumpBits(huftPointer.NumberOfBitsUsed); + e = huftPointer.NumberOfExtraBits; + if (e <= 32) + break; + if (e == INVALID_CODE) + return 1; + + e &= 31; + NeedBits(e); + + tabOffset = (int)(~bitBuffer & mask_bits[e]); + huftPointer = huftPointer.ChildNodes[tabOffset]; + } + + return 0; + } + + private void explode_var_init() + { + /* explode the coded data */ + bitBuffer = 0; + bitBufferCount = 0; + maskForLiteralCodeTable = mask_bits[bitsForLiteralCodeTable]; //only used in explode_lit + maskForLengthCodeTable = mask_bits[bitsForLengthCodeTable]; + maskForDistanceCodeTable = mask_bits[bitsForDistanceCodeTable]; + maskForDistanceLowBits = mask_bits[numOfUncodedLowerDistanceBits]; + outBytesCount = 0; + + windowIndex = 0; /* initialize bit buffer, window */ + } + + public override int Read(byte[] buffer, int offset, int count) + { + int countIndex = 0; + while (countIndex < count && outBytesCount < unCompressedSize) /* do until unCompressedSize bytes uncompressed */ + { + if (length == 0) + { + NeedBits(1); + bool literal = (bitBuffer & 1) == 1; + DumpBits(1); + + huftNode huftPointer; + if (literal) /* then literal--decode it */ + { + byte nextByte; + if (hufLiteralCodeTable != null) + { + /* get coded literal */ + if ( + DecodeHuft( + hufLiteralCodeTable, + bitsForLiteralCodeTable, + maskForLiteralCodeTable, + out huftPointer, + out _ + ) != 0 + ) + throw new Exception("Error decoding literal value"); + + nextByte = (byte)huftPointer.Value; + } + else + { + NeedBits(8); + nextByte = (byte)bitBuffer; + DumpBits(8); + } + + buffer[offset + (countIndex++)] = nextByte; + windowsBuffer[windowIndex++] = nextByte; + outBytesCount++; + + if (windowIndex == WSIZE) + windowIndex = 0; + + continue; + } + + NeedBits(numOfUncodedLowerDistanceBits); /* get distance low bits */ + distance = (int)(bitBuffer & maskForDistanceLowBits); + DumpBits(numOfUncodedLowerDistanceBits); + + /* get coded distance high bits */ + if ( + DecodeHuft( + hufDistanceCodeTable, + bitsForDistanceCodeTable, + maskForDistanceCodeTable, + out huftPointer, + out _ + ) != 0 + ) + throw new Exception("Error decoding distance high bits"); + + distance = windowIndex - (distance + huftPointer.Value); /* construct offset */ + + /* get coded length */ + if ( + DecodeHuft( + hufLengthCodeTable, + bitsForLengthCodeTable, + maskForLengthCodeTable, + out huftPointer, + out int extraBitLength + ) != 0 + ) + throw new Exception("Error decoding coded length"); + + length = huftPointer.Value; + + if (extraBitLength != 0) /* get length extra bits */ + { + NeedBits(8); + length += (int)(bitBuffer & 0xff); + DumpBits(8); + } + + if (length > (unCompressedSize - outBytesCount)) + length = (int)(unCompressedSize - outBytesCount); + + distance &= WSIZE - 1; + } + + while (length != 0 && countIndex < count) + { + byte nextByte = windowsBuffer[distance++]; + buffer[offset + (countIndex++)] = nextByte; + windowsBuffer[windowIndex++] = nextByte; + outBytesCount++; + + if (distance == WSIZE) + distance = 0; + + if (windowIndex == WSIZE) + windowIndex = 0; + + length--; + } + } + + return countIndex; + } +} diff --git a/src/SharpCompress/Compressors/Explode/HuftTree.cs b/src/SharpCompress/Compressors/Explode/HuftTree.cs new file mode 100644 index 00000000..d216cfc7 --- /dev/null +++ b/src/SharpCompress/Compressors/Explode/HuftTree.cs @@ -0,0 +1,269 @@ +/* + * This code has been converted to C# based on the original huft_tree code found in + * inflate.c -- by Mark Adler version c17e, 30 Mar 2007 + */ + +namespace SharpCompress.Compressors.Explode; + +public class huftNode +{ + public int NumberOfExtraBits; /* number of extra bits or operation */ + public int NumberOfBitsUsed; /* number of bits in this code or subcode */ + public int Value; /* literal, length base, or distance base */ + public huftNode[] ChildNodes = []; /* next level of table */ +} + +public static class HuftTree +{ + private const int INVALID_CODE = 99; + + /* If BMAX needs to be larger than 16, then h and x[] should be ulg. */ + private const int BMAX = 16; /* maximum bit length of any code (16 for explode) */ + private const int N_MAX = 288; /* maximum number of codes in any set */ + + public static int huftbuid( + int[] arrBitLengthForCodes, + int numberOfCodes, + int numberOfSimpleValueCodes, + int[] arrBaseValuesForNonSimpleCodes, + int[] arrExtraBitsForNonSimpleCodes, + out huftNode[] outHufTable, + ref int outBitsForTable + ) + /* Given a list of code lengths and a maximum table size, make a set of + tables to decode that set of codes. Return zero on success, one if + the given code set is incomplete (the tables are still built in this + case), two if the input is invalid (all zero length codes or an + oversubscribed set of lengths), and three if not enough memory. + The code with value 256 is special, and the tables are constructed + so that no bits beyond that code are fetched when that code is + decoded. */ + { + outHufTable = []; + + /* Generate counts for each bit length */ + int lengthOfEOBcode = numberOfCodes > 256 ? arrBitLengthForCodes[256] : BMAX; /* set length of EOB code, if any */ + + int[] arrBitLengthCount = new int[BMAX + 1]; + for (int i = 0; i < BMAX + 1; i++) + arrBitLengthCount[i] = 0; + + int pIndex = 0; + int counterCurrentCode = numberOfCodes; + do + { + arrBitLengthCount[arrBitLengthForCodes[pIndex]]++; + pIndex++; /* assume all entries <= BMAX */ + } while ((--counterCurrentCode) != 0); + + if (arrBitLengthCount[0] == numberOfCodes) /* null input--all zero length codes */ + { + return 0; + } + + /* Find minimum and maximum length, bound *outBitsForTable by those */ + int counter; + for (counter = 1; counter <= BMAX; counter++) + if (arrBitLengthCount[counter] != 0) + break; + + int numberOfBitsInCurrentCode = counter; /* minimum code length */ + if (outBitsForTable < counter) + outBitsForTable = counter; + + for (counterCurrentCode = BMAX; counterCurrentCode != 0; counterCurrentCode--) + if (arrBitLengthCount[counterCurrentCode] != 0) + break; + + int maximumCodeLength = counterCurrentCode; /* maximum code length */ + if (outBitsForTable > counterCurrentCode) + outBitsForTable = counterCurrentCode; + + /* Adjust last length count to fill out codes, if needed */ + int numberOfDummyCodesAdded; + for ( + numberOfDummyCodesAdded = 1 << counter; + counter < counterCurrentCode; + counter++, numberOfDummyCodesAdded <<= 1 + ) + if ((numberOfDummyCodesAdded -= arrBitLengthCount[counter]) < 0) + return 2; /* bad input: more codes than bits */ + + if ((numberOfDummyCodesAdded -= arrBitLengthCount[counterCurrentCode]) < 0) + return 2; + + arrBitLengthCount[counterCurrentCode] += numberOfDummyCodesAdded; + + /* Generate starting offsets into the value table for each length */ + int[] bitOffset = new int[BMAX + 1]; + bitOffset[1] = 0; + counter = 0; + pIndex = 1; + int xIndex = 2; + while ((--counterCurrentCode) != 0) + { /* note that i == g from above */ + bitOffset[xIndex++] = (counter += arrBitLengthCount[pIndex++]); + } + + /* Make a table of values in order of bit lengths */ + int[] arrValuesInOrderOfBitLength = new int[N_MAX]; + for (int i = 0; i < N_MAX; i++) + arrValuesInOrderOfBitLength[i] = 0; + + pIndex = 0; + counterCurrentCode = 0; + do + { + if ((counter = arrBitLengthForCodes[pIndex++]) != 0) + arrValuesInOrderOfBitLength[bitOffset[counter]++] = counterCurrentCode; + } while (++counterCurrentCode < numberOfCodes); + + numberOfCodes = bitOffset[maximumCodeLength]; /* set numberOfCodes to length of v */ + + /* Generate the Huffman codes and for each, make the table entries */ + bitOffset[0] = counterCurrentCode = 0; /* first Huffman code is zero */ + pIndex = 0; /* grab values in bit order */ + int tableLevel = -1; /* no tables yet--level -1 */ + int bitsBeforeThisTable = 0; + int[] arrLX = new int[BMAX + 1]; + int stackOfBitsPerTable = 1; /* stack of bits per table */ + arrLX[stackOfBitsPerTable - 1] = 0; /* no bits decoded yet */ + + huftNode[][] arrHufTableStack = new huftNode[BMAX][]; + huftNode[] pointerToCurrentTable = []; + int numberOfEntriesInCurrentTable = 0; + + bool first = true; + + /* go through the bit lengths (k already is bits in shortest code) */ + for (; numberOfBitsInCurrentCode <= maximumCodeLength; numberOfBitsInCurrentCode++) + { + int counterForCodes = arrBitLengthCount[numberOfBitsInCurrentCode]; + while ((counterForCodes--) != 0) + { + /* here i is the Huffman code of length k bits for value *p */ + /* make tables up to required level */ + while ( + numberOfBitsInCurrentCode + > bitsBeforeThisTable + arrLX[stackOfBitsPerTable + tableLevel] + ) + { + bitsBeforeThisTable += arrLX[stackOfBitsPerTable + (tableLevel++)]; /* add bits already decoded */ + + /* compute minimum size table less than or equal to *outBitsForTable bits */ + numberOfEntriesInCurrentTable = + (numberOfEntriesInCurrentTable = maximumCodeLength - bitsBeforeThisTable) + > outBitsForTable + ? outBitsForTable + : numberOfEntriesInCurrentTable; /* upper limit */ + int fBitCounter1 = + 1 << (counter = numberOfBitsInCurrentCode - bitsBeforeThisTable); + if (fBitCounter1 > counterForCodes + 1) /* try a k-w bit table */ + { /* too few codes for k-w bit table */ + fBitCounter1 -= counterForCodes + 1; /* deduct codes from patterns left */ + xIndex = numberOfBitsInCurrentCode; + while (++counter < numberOfEntriesInCurrentTable) /* try smaller tables up to z bits */ + { + if ((fBitCounter1 <<= 1) <= arrBitLengthCount[++xIndex]) + break; /* enough codes to use up j bits */ + fBitCounter1 -= arrBitLengthCount[xIndex]; /* else deduct codes from patterns */ + } + } + if ( + bitsBeforeThisTable + counter > lengthOfEOBcode + && bitsBeforeThisTable < lengthOfEOBcode + ) + counter = lengthOfEOBcode - bitsBeforeThisTable; /* make EOB code end at table */ + + numberOfEntriesInCurrentTable = 1 << counter; /* table entries for j-bit table */ + arrLX[stackOfBitsPerTable + tableLevel] = counter; /* set table size in stack */ + + /* allocate and link in new table */ + pointerToCurrentTable = new huftNode[numberOfEntriesInCurrentTable]; + + // set the pointer, pointed to by *outHufTable to the second huft in pointertoCurrentTable + if (first) + { + outHufTable = pointerToCurrentTable; /* link to list for huft_free() */ + first = false; + } + + arrHufTableStack[tableLevel] = pointerToCurrentTable; /* table starts after link */ + + /* connect to last table, if there is one */ + if (tableLevel != 0) + { + bitOffset[tableLevel] = counterCurrentCode; /* save pattern for backing up */ + + huftNode vHuft = new huftNode + { + NumberOfBitsUsed = arrLX[stackOfBitsPerTable + tableLevel - 1], /* bits to dump before this table */ + NumberOfExtraBits = 32 + counter, /* bits in this table */ + ChildNodes = pointerToCurrentTable, /* pointer to this table */ + }; + + counter = + (counterCurrentCode & ((1 << bitsBeforeThisTable) - 1)) + >> (bitsBeforeThisTable - arrLX[stackOfBitsPerTable + tableLevel - 1]); + arrHufTableStack[tableLevel - 1][counter] = vHuft; /* connect to last table */ + } + } + + /* set up table entry in r */ + huftNode vHuft1 = new huftNode + { + NumberOfBitsUsed = numberOfBitsInCurrentCode - bitsBeforeThisTable, + }; + + if (pIndex >= numberOfCodes) + vHuft1.NumberOfExtraBits = INVALID_CODE; /* out of values--invalid code */ + else if (arrValuesInOrderOfBitLength[pIndex] < numberOfSimpleValueCodes) + { + vHuft1.NumberOfExtraBits = ( + arrValuesInOrderOfBitLength[pIndex] < 256 ? 32 : 31 + ); /* 256 is end-of-block code */ + vHuft1.Value = arrValuesInOrderOfBitLength[pIndex++]; /* simple code is just the value */ + } + else + { + vHuft1.NumberOfExtraBits = arrExtraBitsForNonSimpleCodes[ + arrValuesInOrderOfBitLength[pIndex] - numberOfSimpleValueCodes + ]; /* non-simple--look up in lists */ + vHuft1.Value = arrBaseValuesForNonSimpleCodes[ + arrValuesInOrderOfBitLength[pIndex++] - numberOfSimpleValueCodes + ]; + } + + /* fill code-like entries with r */ + int fBitCounter2 = 1 << (numberOfBitsInCurrentCode - bitsBeforeThisTable); + for ( + counter = counterCurrentCode >> bitsBeforeThisTable; + counter < numberOfEntriesInCurrentTable; + counter += fBitCounter2 + ) + pointerToCurrentTable[counter] = vHuft1; + + /* backwards increment the k-bit code i */ + for ( + counter = 1 << (numberOfBitsInCurrentCode - 1); + (counterCurrentCode & counter) != 0; + counter >>= 1 + ) + counterCurrentCode ^= counter; + counterCurrentCode ^= counter; + + /* backup over finished tables */ + while ( + (counterCurrentCode & ((1 << bitsBeforeThisTable) - 1)) != bitOffset[tableLevel] + ) + bitsBeforeThisTable -= arrLX[stackOfBitsPerTable + (--tableLevel)]; + } + } + + /* return actual size of base table */ + outBitsForTable = arrLX[stackOfBitsPerTable]; + + /* Return true (1) if we were given an incomplete table */ + return (numberOfDummyCodesAdded != 0 && maximumCodeLength != 1) ? 1 : 0; + } +} diff --git a/src/SharpCompress/Compressors/Filters/BCJFilter.cs b/src/SharpCompress/Compressors/Filters/BCJFilter.cs index 83a93397..e7f00c98 100644 --- a/src/SharpCompress/Compressors/Filters/BCJFilter.cs +++ b/src/SharpCompress/Compressors/Filters/BCJFilter.cs @@ -13,7 +13,7 @@ internal class BCJFilter : Filter true, false, false, - false + false, }; private static readonly int[] MASK_TO_BIT_NUMBER = { 0, 1, 2, 2, 3, 3, 3, 3 }; diff --git a/src/SharpCompress/Compressors/Filters/BCJFilterIA64.cs b/src/SharpCompress/Compressors/Filters/BCJFilterIA64.cs index 07eaf319..44323ae7 100644 --- a/src/SharpCompress/Compressors/Filters/BCJFilterIA64.cs +++ b/src/SharpCompress/Compressors/Filters/BCJFilterIA64.cs @@ -39,7 +39,7 @@ internal class BCJFilterIA64 : Filter 4, 4, 0, - 0 + 0, }; public BCJFilterIA64(bool isEncoder, Stream baseStream) diff --git a/src/SharpCompress/Compressors/LZMA/ICoder.cs b/src/SharpCompress/Compressors/LZMA/ICoder.cs index dcddf929..95134979 100644 --- a/src/SharpCompress/Compressors/LZMA/ICoder.cs +++ b/src/SharpCompress/Compressors/LZMA/ICoder.cs @@ -147,7 +147,7 @@ internal enum CoderPropId /// /// Specifies mode with end marker. /// - EndMarker + EndMarker, } internal interface ISetCoderProperties diff --git a/src/SharpCompress/Compressors/LZMA/LZipStream.cs b/src/SharpCompress/Compressors/LZMA/LZipStream.cs index 5bb35a75..6570e908 100644 --- a/src/SharpCompress/Compressors/LZMA/LZipStream.cs +++ b/src/SharpCompress/Compressors/LZMA/LZipStream.cs @@ -201,8 +201,8 @@ public sealed class LZipStream : Stream (byte)'I', (byte)'P', 1, - 113 - ]; + 113, + }; public static void WriteHeaderSize(Stream stream) => // hard coding the dictionary size encoding @@ -224,5 +224,5 @@ public sealed class LZipStream : Stream (byte)((dictionarySize >> 8) & 0xff), (byte)((dictionarySize >> 16) & 0xff), (byte)((dictionarySize >> 24) & 0xff) - ]; + }; } diff --git a/src/SharpCompress/Compressors/LZMA/LzmaEncoder.cs b/src/SharpCompress/Compressors/LZMA/LzmaEncoder.cs index 0c2a91dc..e9fa1bab 100644 --- a/src/SharpCompress/Compressors/LZMA/LzmaEncoder.cs +++ b/src/SharpCompress/Compressors/LZMA/LzmaEncoder.cs @@ -13,7 +13,7 @@ internal class Encoder : ICoder, ISetCoderProperties, IWriteCoderProperties private enum EMatchFinderType { Bt2, - Bt4 + Bt4, } private const uint K_IFINITY_PRICE = 0xFFFFFFF; diff --git a/src/SharpCompress/Compressors/LZMA/LzmaEncoderProperties.cs b/src/SharpCompress/Compressors/LZMA/LzmaEncoderProperties.cs index 274e757b..198ba471 100644 --- a/src/SharpCompress/Compressors/LZMA/LzmaEncoderProperties.cs +++ b/src/SharpCompress/Compressors/LZMA/LzmaEncoderProperties.cs @@ -38,7 +38,7 @@ public class LzmaEncoderProperties CoderPropId.Algorithm, CoderPropId.NumFastBytes, CoderPropId.MatchFinder, - CoderPropId.EndMarker + CoderPropId.EndMarker, }; _properties = new object[] { @@ -49,7 +49,7 @@ public class LzmaEncoderProperties algorithm, numFastBytes, mf, - eos + eos, }; } } diff --git a/src/SharpCompress/Compressors/PPMd/H/ModelPPM.cs b/src/SharpCompress/Compressors/PPMd/H/ModelPPM.cs index d4e91921..f02ec847 100644 --- a/src/SharpCompress/Compressors/PPMd/H/ModelPPM.cs +++ b/src/SharpCompress/Compressors/PPMd/H/ModelPPM.cs @@ -132,7 +132,7 @@ internal class ModelPpm 0x64A1, 0x5ABC, 0x6632, - 0x6051 + 0x6051, }; // Temp fields diff --git a/src/SharpCompress/Compressors/PPMd/I1/Model.cs b/src/SharpCompress/Compressors/PPMd/I1/Model.cs index 9a1d090f..cd7f7ac4 100644 --- a/src/SharpCompress/Compressors/PPMd/I1/Model.cs +++ b/src/SharpCompress/Compressors/PPMd/I1/Model.cs @@ -59,7 +59,7 @@ internal partial class Model 0x64A1, 0x5ABC, 0x6632, - 0x6051 + 0x6051, }; private static ReadOnlySpan EXPONENTIAL_ESCAPES => diff --git a/src/SharpCompress/Compressors/PPMd/I1/ModelRestorationMethod.cs b/src/SharpCompress/Compressors/PPMd/I1/ModelRestorationMethod.cs index 0a504c99..9f4ccc43 100644 --- a/src/SharpCompress/Compressors/PPMd/I1/ModelRestorationMethod.cs +++ b/src/SharpCompress/Compressors/PPMd/I1/ModelRestorationMethod.cs @@ -24,5 +24,5 @@ internal enum ModelRestorationMethod /// /// Freeze the context tree (in some cases may result in poor compression). /// - Freeze = 2 + Freeze = 2, } diff --git a/src/SharpCompress/Compressors/PPMd/PpmdVersion.cs b/src/SharpCompress/Compressors/PPMd/PpmdVersion.cs index 791034ac..57626660 100644 --- a/src/SharpCompress/Compressors/PPMd/PpmdVersion.cs +++ b/src/SharpCompress/Compressors/PPMd/PpmdVersion.cs @@ -4,5 +4,5 @@ public enum PpmdVersion { H, H7Z, - I1 + I1, } diff --git a/src/SharpCompress/Compressors/Rar/RarBLAKE2spStream.cs b/src/SharpCompress/Compressors/Rar/RarBLAKE2spStream.cs index cf93cffb..0cf7c85c 100644 --- a/src/SharpCompress/Compressors/Rar/RarBLAKE2spStream.cs +++ b/src/SharpCompress/Compressors/Rar/RarBLAKE2spStream.cs @@ -27,7 +27,7 @@ internal class RarBLAKE2spStream : RarStream 0x510E527FU, 0x9B05688CU, 0x1F83D9ABU, - 0x5BE0CD19U + 0x5BE0CD19U, }; static readonly byte[][] k_BLAKE2S_Sigma = diff --git a/src/SharpCompress/Compressors/Rar/RarCRC.cs b/src/SharpCompress/Compressors/Rar/RarCRC.cs index 8f4fb47e..7e6fc6cc 100644 --- a/src/SharpCompress/Compressors/Rar/RarCRC.cs +++ b/src/SharpCompress/Compressors/Rar/RarCRC.cs @@ -9,7 +9,7 @@ internal static class RarCRC public static uint CheckCrc(uint startCrc, byte b) => (crcTab[((int)startCrc ^ b) & 0xff] ^ (startCrc >> 8)); - public static uint CheckCrc(uint startCrc, byte[] data, int offset, int count) + public static uint CheckCrc(uint startCrc, ReadOnlySpan data, int offset, int count) { var size = Math.Min(data.Length - offset, count); diff --git a/src/SharpCompress/Compressors/Rar/RarStream.cs b/src/SharpCompress/Compressors/Rar/RarStream.cs index bf5d3209..073fcaa0 100644 --- a/src/SharpCompress/Compressors/Rar/RarStream.cs +++ b/src/SharpCompress/Compressors/Rar/RarStream.cs @@ -1,6 +1,7 @@ #nullable disable using System; +using System.Buffers; using System.IO; using SharpCompress.Common.Rar.Headers; @@ -14,7 +15,7 @@ internal class RarStream : Stream private bool fetch; - private byte[] tmpBuffer = BufferPool.Rent(65536); + private byte[] tmpBuffer = ArrayPool.Shared.Rent(65536); private int tmpOffset; private int tmpCount; @@ -42,7 +43,7 @@ internal class RarStream : Stream { if (disposing) { - BufferPool.Return(this.tmpBuffer); + ArrayPool.Shared.Return(this.tmpBuffer); this.tmpBuffer = null; } isDisposed = true; @@ -143,11 +144,11 @@ internal class RarStream : Stream this.tmpBuffer.Length * 2 > this.tmpCount + count ? this.tmpBuffer.Length * 2 : this.tmpCount + count; - var newBuffer = BufferPool.Rent(newLength); + var newBuffer = ArrayPool.Shared.Rent(newLength); Buffer.BlockCopy(this.tmpBuffer, 0, newBuffer, 0, this.tmpCount); var oldBuffer = this.tmpBuffer; this.tmpBuffer = newBuffer; - BufferPool.Return(oldBuffer); + ArrayPool.Shared.Return(oldBuffer); } } } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/CodeType.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/CodeType.cs index 0ec7943d..67f24def 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/CodeType.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/CodeType.cs @@ -10,5 +10,5 @@ internal enum CodeType CODE_STARTFILE, CODE_ENDFILE, CODE_VM, - CODE_VMDATA + CODE_VMDATA, } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/FilterType.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/FilterType.cs index 67e38c45..d09fcc16 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/FilterType.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/FilterType.cs @@ -12,5 +12,5 @@ internal enum FilterType : byte FILTER_RGB, FILTER_ITANIUM, FILTER_PPM, - FILTER_NONE + FILTER_NONE, } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/PPM/BlockTypes.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/PPM/BlockTypes.cs index 3b4a9372..ded3c34b 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV1/PPM/BlockTypes.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/PPM/BlockTypes.cs @@ -3,5 +3,5 @@ namespace SharpCompress.Compressors.Rar.UnpackV1.PPM; internal enum BlockTypes { BLOCK_LZ = 0, - BLOCK_PPM = 1 + BLOCK_PPM = 1, } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack.cs index e6d4153d..c62d5906 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack.cs @@ -1,6 +1,7 @@ #nullable disable using System; +using System.Buffers; using System.Collections.Generic; using System.IO; using SharpCompress.Common; @@ -12,14 +13,28 @@ using SharpCompress.Compressors.Rar.VM; namespace SharpCompress.Compressors.Rar.UnpackV1; -internal sealed partial class Unpack : BitInput, IRarUnpack +internal sealed partial class Unpack : BitInput, IRarUnpack, IDisposable { private readonly BitInput Inp; + private bool disposed; public Unpack() => // to ease in porting Unpack50.cs Inp = this; + public void Dispose() + { + if (!disposed) + { + if (!externalWindow) + { + ArrayPool.Shared.Return(window); + window = null; + } + disposed = true; + } + } + public bool FileExtracted { get; private set; } public long DestSize @@ -74,7 +89,7 @@ internal sealed partial class Unpack : BitInput, IRarUnpack private BlockTypes unpBlockType; - //private bool externalWindow; + private bool externalWindow; private long writtenFileSize; @@ -104,22 +119,21 @@ internal sealed partial class Unpack : BitInput, IRarUnpack 2, 14, 0, - 12 + 12, }; private FileHeader fileHeader; private void Init(byte[] window) { - if (window is null) + if (this.window is null && window is null) { - this.window = new byte[PackDef.MAXWINSIZE]; + this.window = ArrayPool.Shared.Rent(PackDef.MAXWINSIZE); } - else + else if (window is not null) { this.window = window; - - //externalWindow = true; + externalWindow = true; } inAddr = 0; UnpInitData(false); @@ -175,31 +189,24 @@ internal sealed partial class Unpack : BitInput, IRarUnpack private void UnstoreFile() { - var buffer = new byte[0x10000]; - while (true) + Span buffer = stackalloc byte[(int)Math.Min(0x10000, destUnpSize)]; + do { - var code = readStream.Read(buffer, 0, (int)Math.Min(buffer.Length, destUnpSize)); + var code = readStream.Read(buffer); if (code == 0 || code == -1) { break; } code = code < destUnpSize ? code : (int)destUnpSize; - writeStream.Write(buffer, 0, code); - if (destUnpSize >= 0) - { - destUnpSize -= code; - } - if (suspended) - { - return; - } - } + writeStream.Write(buffer.Slice(0, code)); + destUnpSize -= code; + } while (!suspended && destUnpSize > 0); } private void Unpack29(bool solid) { - var DDecode = new int[PackDef.DC]; - var DBits = new byte[PackDef.DC]; + Span DDecode = stackalloc int[PackDef.DC]; + Span DBits = stackalloc byte[PackDef.DC]; int Bits; @@ -859,9 +866,9 @@ internal sealed partial class Unpack : BitInput, IRarUnpack private bool ReadTables() { - var bitLength = new byte[PackDef.BC]; + Span bitLength = stackalloc byte[PackDef.BC]; + Span table = stackalloc byte[PackDef.HUFF_TABLE_SIZE]; - var table = new byte[PackDef.HUFF_TABLE_SIZE]; if (inAddr > readTop - 25) { if (!unpReadBuf()) @@ -989,7 +996,7 @@ internal sealed partial class Unpack : BitInput, IRarUnpack // memcpy(unpOldTable,table,sizeof(unpOldTable)); - Buffer.BlockCopy(table, 0, unpOldTable, 0, unpOldTable.Length); + table.CopyTo(unpOldTable); return (true); } @@ -1190,7 +1197,7 @@ internal sealed partial class Unpack : BitInput, IRarUnpack { return (false); } - var VMCode = new byte[VMCodeSize]; + Span VMCode = stackalloc byte[VMCodeSize]; for (var I = 0; I < VMCodeSize; I++) { if (Inp.Overflow(3)) diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack15.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack15.cs index 8513c24b..4c6e3894 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack15.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack15.cs @@ -88,7 +88,7 @@ internal partial class Unpack 0xf000, 0xf200, 0xf200, - 0xffff + 0xffff, }; private static readonly int[] PosL1 = { 0, 0, 0, 2, 3, 5, 7, 11, 16, 20, 24, 32, 32 }; @@ -106,7 +106,7 @@ internal partial class Unpack 0xf000, 0xf200, 0xf240, - 0xffff + 0xffff, }; private static readonly int[] PosL2 = { 0, 0, 0, 0, 5, 7, 9, 13, 18, 22, 26, 34, 36 }; @@ -123,7 +123,7 @@ internal partial class Unpack 0xf200, 0xf200, 0xf200, - 0xffff + 0xffff, }; private static readonly int[] PosHf0 = { 0, 0, 0, 0, 0, 8, 16, 24, 33, 33, 33, 33, 33 }; @@ -139,7 +139,7 @@ internal partial class Unpack 0xf200, 0xf200, 0xf7e0, - 0xffff + 0xffff, }; private static readonly int[] PosHf1 = { 0, 0, 0, 0, 0, 0, 4, 44, 60, 76, 80, 80, 127 }; @@ -155,7 +155,7 @@ internal partial class Unpack 0xfa00, 0xffff, 0xffff, - 0xffff + 0xffff, }; private static readonly int[] PosHf2 = { 0, 0, 0, 0, 0, 0, 2, 7, 53, 117, 233, 0, 0 }; @@ -170,7 +170,7 @@ internal partial class Unpack 0xfe80, 0xffff, 0xffff, - 0xffff + 0xffff, }; private static readonly int[] PosHf3 = { 0, 0, 0, 0, 0, 0, 0, 2, 16, 218, 251, 0, 0 }; @@ -199,7 +199,7 @@ internal partial class Unpack 0x90, 0x98, 0x9c, - 0xb0 + 0xb0, }; private static readonly int[] ShortLen2 = { 2, 3, 3, 3, 4, 4, 5, 6, 6, 4, 4, 5, 6, 6, 4, 0 }; @@ -220,7 +220,7 @@ internal partial class Unpack 0x90, 0x98, 0x9c, - 0xb0 + 0xb0, }; private void unpack15(bool solid) diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack20.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack20.cs index 3a5d008d..809aaf60 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack20.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack20.cs @@ -64,7 +64,7 @@ internal partial class Unpack 128, 160, 192, - 224 + 224, }; private static ReadOnlySpan LBits => @@ -97,7 +97,7 @@ internal partial class Unpack 5, 5, 5, - 5 + 5, }; private static readonly int[] DDecode = @@ -149,7 +149,7 @@ internal partial class Unpack 786432, 851968, 917504, - 983040 + 983040, }; private static readonly int[] DBits = @@ -201,7 +201,7 @@ internal partial class Unpack 16, 16, 16, - 16 + 16, }; private static readonly int[] SDDecode = { 0, 4, 8, 16, 32, 64, 128, 192 }; @@ -391,8 +391,8 @@ internal partial class Unpack private bool ReadTables20() { - var BitLength = new byte[PackDef.BC20]; - var Table = new byte[PackDef.MC20 * 4]; + Span BitLength = stackalloc byte[PackDef.BC20]; + Span Table = stackalloc byte[PackDef.MC20 * 4]; int TableSize, N, I; diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/UnpackUtility.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/UnpackUtility.cs index b501337b..4b16e43d 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV1/UnpackUtility.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/UnpackUtility.cs @@ -181,7 +181,12 @@ internal static class UnpackUtility return (dec.DecodeNum[N]); } - internal static void makeDecodeTables(byte[] lenTab, int offset, Decode.Decode dec, int size) + internal static void makeDecodeTables( + Span lenTab, + int offset, + Decode.Decode dec, + int size + ) { Span lenCount = stackalloc int[16]; Span tmpPos = stackalloc int[16]; diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack15_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack15_cpp.cs index dabb0e08..c1886df8 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack15_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack15_cpp.cs @@ -19,7 +19,7 @@ internal partial class Unpack 0xf000, 0xf200, 0xf200, - 0xffff + 0xffff, }; private static readonly uint[] PosL1 = { 0, 0, 0, 2, 3, 5, 7, 11, 16, 20, 24, 32, 32 }; @@ -37,7 +37,7 @@ internal partial class Unpack 0xf000, 0xf200, 0xf240, - 0xffff + 0xffff, }; private static readonly uint[] PosL2 = { 0, 0, 0, 0, 5, 7, 9, 13, 18, 22, 26, 34, 36 }; @@ -54,7 +54,7 @@ internal partial class Unpack 0xf200, 0xf200, 0xf200, - 0xffff + 0xffff, }; private static readonly uint[] PosHf0 = { 0, 0, 0, 0, 0, 8, 16, 24, 33, 33, 33, 33, 33 }; @@ -70,7 +70,7 @@ internal partial class Unpack 0xf200, 0xf200, 0xf7e0, - 0xffff + 0xffff, }; private static readonly uint[] PosHf1 = { 0, 0, 0, 0, 0, 0, 4, 44, 60, 76, 80, 80, 127 }; @@ -86,7 +86,7 @@ internal partial class Unpack 0xfa00, 0xffff, 0xffff, - 0xffff + 0xffff, }; private static readonly uint[] PosHf2 = { 0, 0, 0, 0, 0, 0, 2, 7, 53, 117, 233, 0, 0 }; @@ -101,7 +101,7 @@ internal partial class Unpack 0xfe80, 0xffff, 0xffff, - 0xffff + 0xffff, }; private static readonly uint[] PosHf3 = { 0, 0, 0, 0, 0, 0, 0, 2, 16, 218, 251, 0, 0 }; @@ -225,7 +225,7 @@ internal partial class Unpack 6, 6, 4, - 0 + 0, }; public static readonly uint[] ShortXor1 = { @@ -243,7 +243,7 @@ internal partial class Unpack 0x90, 0x98, 0x9c, - 0xb0 + 0xb0, }; public static readonly uint[] ShortLen2 = { @@ -262,7 +262,7 @@ internal partial class Unpack 6, 6, 4, - 0 + 0, }; public static readonly uint[] ShortXor2 = { @@ -280,7 +280,7 @@ internal partial class Unpack 0x90, 0x98, 0x9c, - 0xb0 + 0xb0, }; } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack20_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack20_cpp.cs index 51dbff0b..d3125727 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack20_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack20_cpp.cs @@ -53,7 +53,7 @@ internal partial class Unpack 128, 160, 192, - 224 + 224, }; public static readonly byte[] LBits = { @@ -84,7 +84,7 @@ internal partial class Unpack 5, 5, 5, - 5 + 5, }; public static readonly uint[] DDecode = { @@ -135,7 +135,7 @@ internal partial class Unpack 786432, 851968, 917504, - 983040 + 983040, }; public static readonly byte[] DBits = { @@ -186,7 +186,7 @@ internal partial class Unpack 16, 16, 16, - 16 + 16, }; public static readonly byte[] SDDecode = { 0, 4, 8, 16, 32, 64, 128, 192 }; public static readonly byte[] SDBits = { 2, 2, 3, 4, 5, 6, 6, 6 }; diff --git a/src/SharpCompress/Compressors/Rar/VM/RarVM.cs b/src/SharpCompress/Compressors/Rar/VM/RarVM.cs index f758b8a4..f1ef3834 100644 --- a/src/SharpCompress/Compressors/Rar/VM/RarVM.cs +++ b/src/SharpCompress/Compressors/Rar/VM/RarVM.cs @@ -776,14 +776,14 @@ internal sealed class RarVM : BitInput } } - public void prepare(byte[] code, int codeSize, VMPreparedProgram prg) + public void prepare(ReadOnlySpan code, int codeSize, VMPreparedProgram prg) { InitBitInput(); var cpLength = Math.Min(MAX_SIZE, codeSize); // memcpy(inBuf,Code,Min(CodeSize,BitInput::MAX_SIZE)); - Buffer.BlockCopy(code, 0, InBuf, 0, cpLength); + code.Slice(0, cpLength).CopyTo(InBuf); byte xorSum = 0; for (var i = 1; i < codeSize; i++) { @@ -1105,7 +1105,7 @@ internal sealed class RarVM : BitInput } } - private VMStandardFilters IsStandardFilter(byte[] code, int codeSize) + private VMStandardFilters IsStandardFilter(ReadOnlySpan code, int codeSize) { VMStandardFilterSignature[] stdList = { @@ -1115,7 +1115,7 @@ internal sealed class RarVM : BitInput new(29, 0x0e06077d, VMStandardFilters.VMSF_DELTA), new(149, 0x1c2c5dc8, VMStandardFilters.VMSF_RGB), new(216, 0xbc85e701, VMStandardFilters.VMSF_AUDIO), - new(40, 0x46b9c560, VMStandardFilters.VMSF_UPCASE) + new(40, 0x46b9c560, VMStandardFilters.VMSF_UPCASE), }; var CodeCRC = RarCRC.CheckCrc(0xffffffff, code, 0, code.Length) ^ 0xffffffff; for (var i = 0; i < stdList.Length; i++) diff --git a/src/SharpCompress/Compressors/Rar/VM/VMCmdFlags.cs b/src/SharpCompress/Compressors/Rar/VM/VMCmdFlags.cs index 00de0b6a..9dbafe1b 100644 --- a/src/SharpCompress/Compressors/Rar/VM/VMCmdFlags.cs +++ b/src/SharpCompress/Compressors/Rar/VM/VMCmdFlags.cs @@ -53,6 +53,6 @@ internal class VMCmdFlags VMCF_OP2 | VMCF_BYTEMODE, VMCF_OP2 | VMCF_BYTEMODE | VMCF_USEFLAGS | VMCF_CHFLAGS, VMCF_OP2 | VMCF_BYTEMODE | VMCF_USEFLAGS | VMCF_CHFLAGS, - VMCF_OP0 + VMCF_OP0, }; } diff --git a/src/SharpCompress/Compressors/Rar/VM/VMCommands.cs b/src/SharpCompress/Compressors/Rar/VM/VMCommands.cs index 7bb1a396..a10eb072 100644 --- a/src/SharpCompress/Compressors/Rar/VM/VMCommands.cs +++ b/src/SharpCompress/Compressors/Rar/VM/VMCommands.cs @@ -66,5 +66,5 @@ internal enum VMCommands VM_NEGB = 52, VM_NEGD = 53, - VM_STANDARD = 54 + VM_STANDARD = 54, } diff --git a/src/SharpCompress/Compressors/Rar/VM/VMFlags.cs b/src/SharpCompress/Compressors/Rar/VM/VMFlags.cs index fa9a72e7..12765775 100644 --- a/src/SharpCompress/Compressors/Rar/VM/VMFlags.cs +++ b/src/SharpCompress/Compressors/Rar/VM/VMFlags.cs @@ -5,5 +5,5 @@ internal enum VMFlags None = 0, VM_FC = 1, VM_FZ = 2, - VM_FS = 80000000 + VM_FS = 80000000, } diff --git a/src/SharpCompress/Compressors/Rar/VM/VMOpType.cs b/src/SharpCompress/Compressors/Rar/VM/VMOpType.cs index bcc4214e..77994746 100644 --- a/src/SharpCompress/Compressors/Rar/VM/VMOpType.cs +++ b/src/SharpCompress/Compressors/Rar/VM/VMOpType.cs @@ -5,5 +5,5 @@ internal enum VMOpType VM_OPREG = 0, VM_OPINT = 1, VM_OPREGMEM = 2, - VM_OPNONE = 3 + VM_OPNONE = 3, } diff --git a/src/SharpCompress/Compressors/Rar/VM/VMStandardFilters.cs b/src/SharpCompress/Compressors/Rar/VM/VMStandardFilters.cs index 4b90b019..2c435ebb 100644 --- a/src/SharpCompress/Compressors/Rar/VM/VMStandardFilters.cs +++ b/src/SharpCompress/Compressors/Rar/VM/VMStandardFilters.cs @@ -9,5 +9,5 @@ internal enum VMStandardFilters VMSF_RGB = 4, VMSF_AUDIO = 5, VMSF_DELTA = 6, - VMSF_UPCASE = 7 + VMSF_UPCASE = 7, } diff --git a/src/SharpCompress/Compressors/Reduce/ReduceStream.cs b/src/SharpCompress/Compressors/Reduce/ReduceStream.cs new file mode 100644 index 00000000..1cb67f44 --- /dev/null +++ b/src/SharpCompress/Compressors/Reduce/ReduceStream.cs @@ -0,0 +1,249 @@ +using System; +using System.IO; + +namespace SharpCompress.Compressors.Reduce; + +public class ReduceStream : Stream +{ + private readonly long unCompressedSize; + private readonly long compressedSize; + private readonly Stream inStream; + + private long inByteCount; + private const int EOF = 1234; + + private readonly int factor; + private readonly int distanceMask; + private readonly int lengthMask; + + private long outBytesCount; + + private readonly byte[] windowsBuffer; + private int windowIndex; + private int length; + private int distance; + + public ReduceStream(Stream inStr, long compsize, long unCompSize, int factor) + { + inStream = inStr; + compressedSize = compsize; + unCompressedSize = unCompSize; + inByteCount = 0; + outBytesCount = 0; + + this.factor = factor; + distanceMask = (int)mask_bits[factor] << 8; + lengthMask = 0xff >> factor; + + windowIndex = 0; + length = 0; + distance = 0; + + windowsBuffer = new byte[WSIZE]; + + outByte = 0; + + LoadBitLengthTable(); + LoadNextByteTable(); + } + + public override void Flush() + { + throw new NotImplementedException(); + } + + 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(); + } + + public override bool CanRead => true; + public override bool CanSeek => false; + public override bool CanWrite => false; + public override long Length => unCompressedSize; + public override long Position + { + get => outBytesCount; + set { } + } + + private const int RunLengthCode = 144; + private const int WSIZE = 0x4000; + + private readonly uint[] mask_bits = new uint[] + { + 0x0000, + 0x0001, + 0x0003, + 0x0007, + 0x000f, + 0x001f, + 0x003f, + 0x007f, + 0x00ff, + 0x01ff, + 0x03ff, + 0x07ff, + 0x0fff, + 0x1fff, + 0x3fff, + 0x7fff, + 0xffff, + }; + + private int bitBufferCount; + private ulong bitBuffer; + + private int NEXTBYTE() + { + if (inByteCount == compressedSize) + return EOF; + inByteCount++; + return inStream.ReadByte(); + } + + private void READBITS(int nbits, out byte zdest) + { + if (nbits > bitBufferCount) + { + int temp; + while (bitBufferCount <= 8 * (int)(4 - 1) && (temp = NEXTBYTE()) != EOF) + { + bitBuffer |= (ulong)temp << bitBufferCount; + bitBufferCount += 8; + } + } + zdest = (byte)(bitBuffer & (ulong)mask_bits[nbits]); + bitBuffer >>= nbits; + bitBufferCount -= nbits; + } + + private byte[] bitCountTable = []; + + private void LoadBitLengthTable() + { + byte[] bitPos = { 0, 2, 4, 8, 16, 32, 64, 128, 255 }; + bitCountTable = new byte[256]; + + for (byte i = 1; i <= 8; i++) + { + int vMin = bitPos[i - 1] + 1; + int vMax = bitPos[i]; + for (int j = vMin; j <= vMax; j++) + { + bitCountTable[j] = i; + } + } + } + + private byte[][] nextByteTable = []; + + private void LoadNextByteTable() + { + nextByteTable = new byte[256][]; + for (int x = 255; x >= 0; x--) + { + READBITS(6, out byte Slen); + nextByteTable[x] = new byte[Slen]; + for (int i = 0; i < Slen; i++) + { + READBITS(8, out nextByteTable[x][i]); + } + } + } + + private byte outByte; + + private byte GetNextByte() + { + if (nextByteTable[outByte].Length == 0) + { + READBITS(8, out outByte); + return outByte; + } + READBITS(1, out byte nextBit); + if (nextBit == 1) + { + READBITS(8, out outByte); + return outByte; + } + READBITS(bitCountTable[nextByteTable[outByte].Length], out byte nextByteIndex); + outByte = nextByteTable[outByte][nextByteIndex]; + return outByte; + } + + public override int Read(byte[] buffer, int offset, int count) + { + int countIndex = 0; + while (countIndex < count && outBytesCount < unCompressedSize) + { + if (length == 0) + { + byte nextByte = GetNextByte(); + if (nextByte != RunLengthCode) + { + buffer[offset + (countIndex++)] = nextByte; + windowsBuffer[windowIndex++] = nextByte; + outBytesCount++; + if (windowIndex == WSIZE) + windowIndex = 0; + + continue; + } + + nextByte = GetNextByte(); + if (nextByte == 0) + { + buffer[offset + (countIndex++)] = RunLengthCode; + windowsBuffer[windowIndex++] = RunLengthCode; + outBytesCount++; + if (windowIndex == WSIZE) + windowIndex = 0; + + continue; + } + + int lengthDistanceByte = nextByte; + length = lengthDistanceByte & lengthMask; + if (length == lengthMask) + { + length += GetNextByte(); + } + length += 3; + + int distanceHighByte = (lengthDistanceByte << factor) & distanceMask; + distance = windowIndex - (distanceHighByte + GetNextByte() + 1); + + distance &= WSIZE - 1; + } + + while (length != 0 && countIndex < count) + { + byte nextByte = windowsBuffer[distance++]; + buffer[offset + (countIndex++)] = nextByte; + windowsBuffer[windowIndex++] = nextByte; + outBytesCount++; + + if (distance == WSIZE) + distance = 0; + + if (windowIndex == WSIZE) + windowIndex = 0; + + length--; + } + } + + return countIndex; + } +} diff --git a/src/SharpCompress/Compressors/Shrink/BitStream.cs b/src/SharpCompress/Compressors/Shrink/BitStream.cs index 9dc534e0..8bb69ead 100644 --- a/src/SharpCompress/Compressors/Shrink/BitStream.cs +++ b/src/SharpCompress/Compressors/Shrink/BitStream.cs @@ -26,7 +26,7 @@ namespace SharpCompress.Compressors.Shrink 8191U, 16383U, (uint)short.MaxValue, - (uint)ushort.MaxValue + (uint)ushort.MaxValue, }; public BitStream(byte[] src, int srcLen) diff --git a/src/SharpCompress/Compressors/Shrink/HwUnshrink.cs b/src/SharpCompress/Compressors/Shrink/HwUnshrink.cs index 6ad5ab7c..4506ccb1 100644 --- a/src/SharpCompress/Compressors/Shrink/HwUnshrink.cs +++ b/src/SharpCompress/Compressors/Shrink/HwUnshrink.cs @@ -387,7 +387,7 @@ namespace SharpCompress.Compressors.Shrink { Ok, Full, - Error + Error, } private struct CodeQueue diff --git a/src/SharpCompress/Compressors/Xz/CheckType.cs b/src/SharpCompress/Compressors/Xz/CheckType.cs index a94f8261..4c4fafcd 100644 --- a/src/SharpCompress/Compressors/Xz/CheckType.cs +++ b/src/SharpCompress/Compressors/Xz/CheckType.cs @@ -5,5 +5,5 @@ public enum CheckType : byte NONE = 0x00, CRC32 = 0x01, CRC64 = 0x04, - SHA256 = 0x0A + SHA256 = 0x0A, } diff --git a/src/SharpCompress/Compressors/Xz/Filters/BlockFilter.cs b/src/SharpCompress/Compressors/Xz/Filters/BlockFilter.cs index a7e3482a..eba9b1ac 100644 --- a/src/SharpCompress/Compressors/Xz/Filters/BlockFilter.cs +++ b/src/SharpCompress/Compressors/Xz/Filters/BlockFilter.cs @@ -16,20 +16,19 @@ public abstract class BlockFilter : ReadOnlyStream ArchArmFilter = 0x07, ArchArmThumbFilter = 0x08, ArchSparcFilter = 0x09, - Lzma2 = 0x21 + Lzma2 = 0x21, } - private static readonly Dictionary> FILTER_MAP = - new() - { - { FilterTypes.ArchX86Filter, () => new X86Filter() }, - { FilterTypes.ArchPowerPcFilter, () => new PowerPCFilter() }, - { FilterTypes.ArchIa64Filter, () => new IA64Filter() }, - { FilterTypes.ArchArmFilter, () => new ArmFilter() }, - { FilterTypes.ArchArmThumbFilter, () => new ArmThumbFilter() }, - { FilterTypes.ArchSparcFilter, () => new SparcFilter() }, - { FilterTypes.Lzma2, () => new Lzma2Filter() } - }; + private static readonly Dictionary> FILTER_MAP = new() + { + { FilterTypes.ArchX86Filter, () => new X86Filter() }, + { FilterTypes.ArchPowerPcFilter, () => new PowerPCFilter() }, + { FilterTypes.ArchIa64Filter, () => new IA64Filter() }, + { FilterTypes.ArchArmFilter, () => new ArmFilter() }, + { FilterTypes.ArchArmThumbFilter, () => new ArmThumbFilter() }, + { FilterTypes.ArchSparcFilter, () => new SparcFilter() }, + { FilterTypes.Lzma2, () => new Lzma2Filter() }, + }; public abstract bool AllowAsLast { get; } public abstract bool AllowAsNonLast { get; } diff --git a/src/SharpCompress/Compressors/Xz/XZBlock.cs b/src/SharpCompress/Compressors/Xz/XZBlock.cs index 34ce2333..cdb075ef 100644 --- a/src/SharpCompress/Compressors/Xz/XZBlock.cs +++ b/src/SharpCompress/Compressors/Xz/XZBlock.cs @@ -26,13 +26,14 @@ public sealed class XZBlock : XZReadOnlyStream private bool _endOfStream; private bool _paddingSkipped; private bool _crcChecked; - private ulong _bytesRead; + private readonly long _startPosition; public XZBlock(Stream stream, CheckType checkType, int checkSize) : base(stream) { _checkType = checkType; _checkSize = checkSize; + _startPosition = stream.Position; } public override int Read(byte[] buffer, int offset, int count) @@ -68,13 +69,12 @@ public sealed class XZBlock : XZReadOnlyStream CheckCrc(); } - _bytesRead += (ulong)bytesRead; return bytesRead; } private void SkipPadding() { - var bytes = (int)(BaseStream.Position % 4); + var bytes = (BaseStream.Position - _startPosition) % 4; if (bytes > 0) { var paddingBytes = new byte[4 - bytes]; diff --git a/src/SharpCompress/IO/StreamingMode.cs b/src/SharpCompress/IO/StreamingMode.cs index 0fdb674d..33f7d236 100644 --- a/src/SharpCompress/IO/StreamingMode.cs +++ b/src/SharpCompress/IO/StreamingMode.cs @@ -3,5 +3,5 @@ namespace SharpCompress.IO; public enum StreamingMode { Streaming, - Seekable + Seekable, } diff --git a/src/SharpCompress/LazyReadOnlyCollection.cs b/src/SharpCompress/LazyReadOnlyCollection.cs index cc9cb3fd..9c1b35eb 100644 --- a/src/SharpCompress/LazyReadOnlyCollection.cs +++ b/src/SharpCompress/LazyReadOnlyCollection.cs @@ -4,7 +4,7 @@ using System; using System.Collections; using System.Collections.Generic; -namespace SharpCompress; +namespace SharpCompress.Helpers; internal sealed class LazyReadOnlyCollection : ICollection { diff --git a/src/SharpCompress/NotNullExtensions.cs b/src/SharpCompress/NotNullExtensions.cs index 956a4fde..2245612e 100644 --- a/src/SharpCompress/NotNullExtensions.cs +++ b/src/SharpCompress/NotNullExtensions.cs @@ -4,9 +4,9 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Runtime.CompilerServices; -namespace SharpCompress; +namespace SharpCompress.Helpers; -public static class NotNullExtensions +internal static class NotNullExtensions { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IEnumerable Empty(this IEnumerable? source) => diff --git a/src/SharpCompress/ReadOnlyCollection.cs b/src/SharpCompress/ReadOnlyCollection.cs deleted file mode 100644 index 745a59aa..00000000 --- a/src/SharpCompress/ReadOnlyCollection.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; - -namespace SharpCompress; - -public class ReadOnlyCollection : ICollection -{ - private readonly ICollection collection; - - public ReadOnlyCollection(ICollection collection) => this.collection = collection; - - public void Add(T item) => throw new NotSupportedException(); - - public void Clear() => throw new NotSupportedException(); - - public bool Contains(T item) => collection.Contains(item); - - public void CopyTo(T[] array, int arrayIndex) => collection.CopyTo(array, arrayIndex); - - public int Count => collection.Count; - - public bool IsReadOnly => true; - - public bool Remove(T item) => throw new NotSupportedException(); - - public IEnumerator GetEnumerator() => collection.GetEnumerator(); - - IEnumerator IEnumerable.GetEnumerator() => throw new NotSupportedException(); -} diff --git a/src/SharpCompress/Readers/AbstractReader.cs b/src/SharpCompress/Readers/AbstractReader.cs index da6279a9..ac4e1784 100644 --- a/src/SharpCompress/Readers/AbstractReader.cs +++ b/src/SharpCompress/Readers/AbstractReader.cs @@ -44,7 +44,7 @@ public abstract class AbstractReader : IReader, IReaderExtracti #region IDisposable Members - public void Dispose() + public virtual void Dispose() { _entriesForCurrentReadStream?.Dispose(); Volume?.Dispose(); diff --git a/src/SharpCompress/Readers/Rar/RarReader.cs b/src/SharpCompress/Readers/Rar/RarReader.cs index 245001e3..aa73e30f 100644 --- a/src/SharpCompress/Readers/Rar/RarReader.cs +++ b/src/SharpCompress/Readers/Rar/RarReader.cs @@ -13,6 +13,7 @@ namespace SharpCompress.Readers.Rar; /// public abstract class RarReader : AbstractReader { + private bool _disposed; private RarVolume? volume; private Lazy UnpackV2017 { get; } = new(() => new Compressors.Rar.UnpackV2017.Unpack()); @@ -21,6 +22,20 @@ public abstract class RarReader : AbstractReader internal RarReader(ReaderOptions options) : base(options, ArchiveType.Rar) { } + public override void Dispose() + { + if (!_disposed) + { + if (UnpackV1.IsValueCreated && UnpackV1.Value is IDisposable unpackV1) + { + unpackV1.Dispose(); + } + + _disposed = true; + base.Dispose(); + } + } + protected abstract void ValidateArchive(RarVolume archive); public override RarVolume? Volume => volume; diff --git a/src/SharpCompress/Readers/Tar/TarReader.cs b/src/SharpCompress/Readers/Tar/TarReader.cs index a686dc11..46493446 100644 --- a/src/SharpCompress/Readers/Tar/TarReader.cs +++ b/src/SharpCompress/Readers/Tar/TarReader.cs @@ -39,7 +39,7 @@ public class TarReader : AbstractReader CompressionType.Xz => new XZStream(stream), CompressionType.Lzw => new LzwStream(stream), CompressionType.None => stream, - _ => throw new NotSupportedException("Invalid compression type: " + compressionType) + _ => throw new NotSupportedException("Invalid compression type: " + compressionType), }; } diff --git a/src/SharpCompress/SharpCompress.csproj b/src/SharpCompress/SharpCompress.csproj index a21d1726..b1397310 100644 --- a/src/SharpCompress/SharpCompress.csproj +++ b/src/SharpCompress/SharpCompress.csproj @@ -2,11 +2,11 @@ SharpCompress - Pure C# Decompression/Compression en-US - 0.37.0 - 0.37.0 - 0.37.0 + 0.39.0 + 0.39.0 + 0.39.0 Adam Hathcock - net462;netstandard2.0;netstandard2.1;net6.0;net8.0 + net48;net481;netstandard2.0;net6.0;net8.0 SharpCompress ../../SharpCompress.snk true @@ -14,34 +14,32 @@ rar;unrar;zip;unzip;bzip2;gzip;tar;7zip;lzip;xz https://github.com/adamhathcock/sharpcompress MIT - Copyright (c) 2014 Adam Hathcock + Copyright (c) 2025 Adam Hathcock false false - 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. + SharpCompress is a compression library for NET Standard 2.0/NET 4.8/NET 4.8.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 README.md + true + true + $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb - - - + + true + + + - - - - + + - - - - diff --git a/src/SharpCompress/Utility.cs b/src/SharpCompress/Utility.cs index bcecafb4..74974850 100644 --- a/src/SharpCompress/Utility.cs +++ b/src/SharpCompress/Utility.cs @@ -1,15 +1,17 @@ +global using SharpCompress.Helpers; using System; using System.Buffers; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.IO; +using System.Text; using SharpCompress.Readers; -namespace SharpCompress; +namespace SharpCompress.Helpers; -[CLSCompliant(false)] -public static class Utility +internal static class Utility { - public static ReadOnlyCollection ToReadOnly(this ICollection items) => new(items); + public static ReadOnlyCollection ToReadOnly(this IList items) => new(items); /// /// Performs an unsigned bitwise right shift with the specified number @@ -434,4 +436,17 @@ public static class Utility buffer[offset + 2] = (byte)(number >> 8); buffer[offset + 3] = (byte)number; } + + public static string ReplaceInvalidFileNameChars(string fileName) + { + var invalidChars = new HashSet(Path.GetInvalidFileNameChars()); + var sb = new StringBuilder(fileName.Length); + foreach (var c in fileName) + { + var newChar = invalidChars.Contains(c) ? '_' : c; + sb.Append(newChar); + } + + return sb.ToString(); + } } diff --git a/src/SharpCompress/Writers/Zip/ZipWriter.cs b/src/SharpCompress/Writers/Zip/ZipWriter.cs index 1fb66a2b..8e89da64 100644 --- a/src/SharpCompress/Writers/Zip/ZipWriter.cs +++ b/src/SharpCompress/Writers/Zip/ZipWriter.cs @@ -69,7 +69,7 @@ public class ZipWriter : AbstractWriter CompressionType.BZip2 => ZipCompressionMethod.BZip2, CompressionType.LZMA => ZipCompressionMethod.LZMA, CompressionType.PPMd => ZipCompressionMethod.PPMd, - _ => throw new InvalidFormatException("Invalid compression method: " + compressionType) + _ => throw new InvalidFormatException("Invalid compression method: " + compressionType), }; public override void Write(string entryPath, Stream source, DateTime? modificationTime) => @@ -100,7 +100,7 @@ public class ZipWriter : AbstractWriter ) { Comment = options.EntryComment, - ModificationTime = options.ModificationDateTime + ModificationTime = options.ModificationDateTime, }; // Use the archive default setting for zip64 and allow overrides diff --git a/src/SharpCompress/packages.lock.json b/src/SharpCompress/packages.lock.json index e6e0a13d..de18ff8d 100644 --- a/src/SharpCompress/packages.lock.json +++ b/src/SharpCompress/packages.lock.json @@ -1,16 +1,7 @@ { "version": 2, "dependencies": { - ".NETFramework,Version=v4.6.2": { - "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" - } - }, + ".NETFramework,Version=v4.8": { "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -21,43 +12,21 @@ "Microsoft.SourceLink.Common": "8.0.0" } }, - "System.Memory": { + "System.Buffers": { "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" - } + "requested": "[4.6.0, )", + "resolved": "4.6.0", + "contentHash": "lN6tZi7Q46zFzAbRYXTIvfXcyvQQgxnY7Xm6C6xQ9784dEL1amjM6S6Iw4ZpsvesAKnRVsM4scrDQaDqSClkjA==" }, "ZstdSharp.Port": { "type": "Direct", - "requested": "[0.7.6, )", - "resolved": "0.7.6", - "contentHash": "opRgjMeBTNpZQyJaZOWCHGGpQ+d9T2Eg3q8JPeBj65srJBf1F3JidO2h7sYkK2s0Mcga+skrqypQY39CF8kcfA==", + "requested": "[0.8.4, )", + "resolved": "0.8.4", + "contentHash": "eieSXq3kakCUXbgdxkKaRqWS6hF0KBJcqok9LlDCs60GOyrynLvPOcQ0pRw7shdPF7lh/VepJ9cP9n9HHc759g==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "5.0.0", - "System.Memory": "4.5.5" - } - }, - "Microsoft.Bcl.AsyncInterfaces": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==", - "dependencies": { - "System.Threading.Tasks.Extensions": "4.5.4" + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" } }, "Microsoft.Build.Tasks.Git": { @@ -65,21 +34,11 @@ "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", @@ -97,9 +56,129 @@ "dependencies": { "System.Runtime.CompilerServices.Unsafe": "4.5.3" } + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "CentralTransitive", + "requested": "[8.0.0, )", + "resolved": "5.0.0", + "contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "System.Memory": { + "type": "CentralTransitive", + "requested": "[4.6.0, )", + "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" + } + } + }, + ".NETFramework,Version=v4.8.1": { + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net481": "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.Buffers": { + "type": "Direct", + "requested": "[4.6.0, )", + "resolved": "4.6.0", + "contentHash": "lN6tZi7Q46zFzAbRYXTIvfXcyvQQgxnY7Xm6C6xQ9784dEL1amjM6S6Iw4ZpsvesAKnRVsM4scrDQaDqSClkjA==" + }, + "ZstdSharp.Port": { + "type": "Direct", + "requested": "[0.8.4, )", + "resolved": "0.8.4", + "contentHash": "eieSXq3kakCUXbgdxkKaRqWS6hF0KBJcqok9LlDCs60GOyrynLvPOcQ0pRw7shdPF7lh/VepJ9cP9n9HHc759g==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.NETFramework.ReferenceAssemblies.net481": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "Vv/20vgHS7VglVOVh8J3Iz/MA+VYKVRp9f7r2qiKBMuzviTOmocG70yq0Q8T5OTmCONkEAIJwETD1zhEfLkAXQ==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "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" + } + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "CentralTransitive", + "requested": "[8.0.0, )", + "resolved": "5.0.0", + "contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "System.Memory": { + "type": "CentralTransitive", + "requested": "[4.6.0, )", + "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" + } } }, ".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, )", @@ -119,15 +198,21 @@ "Microsoft.NETCore.Platforms": "1.1.0" } }, + "System.Buffers": { + "type": "Direct", + "requested": "[4.6.0, )", + "resolved": "4.6.0", + "contentHash": "lN6tZi7Q46zFzAbRYXTIvfXcyvQQgxnY7Xm6C6xQ9784dEL1amjM6S6Iw4ZpsvesAKnRVsM4scrDQaDqSClkjA==" + }, "System.Memory": { "type": "Direct", - "requested": "[4.5.5, )", - "resolved": "4.5.5", - "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "requested": "[4.6.0, )", + "resolved": "4.6.0", + "contentHash": "OEkbBQoklHngJ8UD8ez2AERSk2g+/qpAaSWWCBFbpH727HxDq5ydVkuncBaKcKfwRqXGWx64dS6G1SUScMsitg==", "dependencies": { - "System.Buffers": "4.5.1", - "System.Numerics.Vectors": "4.4.0", - "System.Runtime.CompilerServices.Unsafe": "4.5.3" + "System.Buffers": "4.6.0", + "System.Numerics.Vectors": "4.6.0", + "System.Runtime.CompilerServices.Unsafe": "6.1.0" } }, "System.Text.Encoding.CodePages": { @@ -142,20 +227,13 @@ }, "ZstdSharp.Port": { "type": "Direct", - "requested": "[0.7.6, )", - "resolved": "0.7.6", - "contentHash": "opRgjMeBTNpZQyJaZOWCHGGpQ+d9T2Eg3q8JPeBj65srJBf1F3JidO2h7sYkK2s0Mcga+skrqypQY39CF8kcfA==", + "requested": "[0.8.4, )", + "resolved": "0.8.4", + "contentHash": "eieSXq3kakCUXbgdxkKaRqWS6hF0KBJcqok9LlDCs60GOyrynLvPOcQ0pRw7shdPF7lh/VepJ9cP9n9HHc759g==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "5.0.0", - "System.Memory": "4.5.5" - } - }, - "Microsoft.Bcl.AsyncInterfaces": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==", - "dependencies": { - "System.Threading.Tasks.Extensions": "4.5.4" + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" } }, "Microsoft.Build.Tasks.Git": { @@ -173,20 +251,15 @@ "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==" + "resolved": "4.6.0", + "contentHash": "t+SoieZsRuEyiw/J+qXUbolyO219tKQQI0+2/YI+Qv7YdGValA6WiuokrNKqjrTNsy5ABWU11bdKOzUdheteXg==" }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + "resolved": "6.1.0", + "contentHash": "5o/HZxx6RVqYlhKSq8/zronDkALJZUT2Vz0hx43f0gwe8mwlM0y2nYlqdBwLMzr262Bwvpikeb/yEwkAa5PADg==" }, "System.Threading.Tasks.Extensions": { "type": "Transitive", @@ -197,7 +270,7 @@ } } }, - ".NETStandard,Version=v2.1": { + "net6.0": { "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -208,90 +281,17 @@ "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.7.6, )", - "resolved": "0.7.6", - "contentHash": "opRgjMeBTNpZQyJaZOWCHGGpQ+d9T2Eg3q8JPeBj65srJBf1F3JidO2h7sYkK2s0Mcga+skrqypQY39CF8kcfA==", - "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.NET.ILLink.Analyzers": { "type": "Direct", - "requested": "[7.0.100-1.23211.1, )", - "resolved": "7.0.100-1.23211.1", - "contentHash": "0GvbEgDGcUQA9KuWcQU1WwYHXt1tBzNr1Nls/M57rM7NA/AndFwCaCEoJpJkmxRY7xLlPDBnmGp8h5+FNqUngg==" - }, - "Microsoft.NET.ILLink.Tasks": { - "type": "Direct", - "requested": "[7.0.100-1.23211.1, )", - "resolved": "7.0.100-1.23211.1", - "contentHash": "tvG8XZYLjT0o3WicCyKBZysVWo1jC9HdCFmNRmddx3WbAz0UCsd0qKZqpiEo99VLA8Re+FzWK51OcRldQPbt2Q==" - }, - "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" - } + "requested": "[4.6.0, )", + "resolved": "4.6.0", + "contentHash": "lN6tZi7Q46zFzAbRYXTIvfXcyvQQgxnY7Xm6C6xQ9784dEL1amjM6S6Iw4ZpsvesAKnRVsM4scrDQaDqSClkjA==" }, "ZstdSharp.Port": { "type": "Direct", - "requested": "[0.7.6, )", - "resolved": "0.7.6", - "contentHash": "opRgjMeBTNpZQyJaZOWCHGGpQ+d9T2Eg3q8JPeBj65srJBf1F3JidO2h7sYkK2s0Mcga+skrqypQY39CF8kcfA==" + "requested": "[0.8.4, )", + "resolved": "0.8.4", + "contentHash": "eieSXq3kakCUXbgdxkKaRqWS6hF0KBJcqok9LlDCs60GOyrynLvPOcQ0pRw7shdPF7lh/VepJ9cP9n9HHc759g==" }, "Microsoft.Build.Tasks.Git": { "type": "Transitive", @@ -307,9 +307,9 @@ "net8.0": { "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[8.0.2, )", - "resolved": "8.0.2", - "contentHash": "hKTrehpfVzOhAz0mreaTAZgbz0DrMEbWq4n3hAo8Ks6WdxdqQhNPvzOqn9VygKuWf1bmxPdraqzTaXriO/sn0A==" + "requested": "[8.0.12, )", + "resolved": "8.0.12", + "contentHash": "FV4HnQ3JI15PHnJ5PGTbz+rYvrih42oLi/7UMIshNwCwUZhTq13UzrggtXk4ygrcMcN+4jsS6hhshx2p/Zd0ig==" }, "Microsoft.SourceLink.GitHub": { "type": "Direct", @@ -321,11 +321,17 @@ "Microsoft.SourceLink.Common": "8.0.0" } }, + "System.Buffers": { + "type": "Direct", + "requested": "[4.6.0, )", + "resolved": "4.6.0", + "contentHash": "lN6tZi7Q46zFzAbRYXTIvfXcyvQQgxnY7Xm6C6xQ9784dEL1amjM6S6Iw4ZpsvesAKnRVsM4scrDQaDqSClkjA==" + }, "ZstdSharp.Port": { "type": "Direct", - "requested": "[0.7.6, )", - "resolved": "0.7.6", - "contentHash": "opRgjMeBTNpZQyJaZOWCHGGpQ+d9T2Eg3q8JPeBj65srJBf1F3JidO2h7sYkK2s0Mcga+skrqypQY39CF8kcfA==" + "requested": "[0.8.4, )", + "resolved": "0.8.4", + "contentHash": "eieSXq3kakCUXbgdxkKaRqWS6hF0KBJcqok9LlDCs60GOyrynLvPOcQ0pRw7shdPF7lh/VepJ9cP9n9HHc759g==" }, "Microsoft.Build.Tasks.Git": { "type": "Transitive", diff --git a/tests/SharpCompress.Test/ArchiveTests.cs b/tests/SharpCompress.Test/ArchiveTests.cs index 7d94b29d..94f402c9 100644 --- a/tests/SharpCompress.Test/ArchiveTests.cs +++ b/tests/SharpCompress.Test/ArchiveTests.cs @@ -73,27 +73,45 @@ public class ArchiveTests : ReaderTests } } - protected void ArchiveStreamRead(string testArchive, ReaderOptions? readerOptions = null) + protected void ArchiveStreamRead(string testArchive, ReaderOptions? readerOptions = null) => + ArchiveStreamRead(ArchiveFactory.AutoFactory, testArchive, readerOptions); + + protected void ArchiveStreamRead( + IArchiveFactory archiveFactory, + string testArchive, + ReaderOptions? readerOptions = null + ) { testArchive = Path.Combine(TEST_ARCHIVES_PATH, testArchive); - ArchiveStreamRead(readerOptions, testArchive); + ArchiveStreamRead(archiveFactory, readerOptions, testArchive); } protected void ArchiveStreamRead( ReaderOptions? readerOptions = null, params string[] testArchives + ) => ArchiveStreamRead(ArchiveFactory.AutoFactory, readerOptions, testArchives); + + protected void ArchiveStreamRead( + IArchiveFactory archiveFactory, + ReaderOptions? readerOptions = null, + params string[] testArchives ) => ArchiveStreamRead( + archiveFactory, readerOptions, testArchives.Select(x => Path.Combine(TEST_ARCHIVES_PATH, x)) ); - protected void ArchiveStreamRead(ReaderOptions? readerOptions, IEnumerable testArchives) + protected void ArchiveStreamRead( + IArchiveFactory archiveFactory, + ReaderOptions? readerOptions, + IEnumerable testArchives + ) { foreach (var path in testArchives) { using (var stream = NonDisposingStream.Create(File.OpenRead(path), true)) - using (var archive = ArchiveFactory.Open(stream, readerOptions)) + using (var archive = archiveFactory.Open(stream, readerOptions)) { try { @@ -218,10 +236,14 @@ public class ArchiveTests : ReaderTests } } - protected void ArchiveFileRead(string testArchive, ReaderOptions? readerOptions = null) + protected void ArchiveFileRead( + IArchiveFactory archiveFactory, + string testArchive, + ReaderOptions? readerOptions = null + ) { testArchive = Path.Combine(TEST_ARCHIVES_PATH, testArchive); - using (var archive = ArchiveFactory.Open(testArchive, readerOptions)) + using (var archive = archiveFactory.Open(new FileInfo(testArchive), readerOptions)) { foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) { @@ -234,6 +256,9 @@ public class ArchiveTests : ReaderTests VerifyFiles(); } + protected void ArchiveFileRead(string testArchive, ReaderOptions? readerOptions = null) => + ArchiveFileRead(ArchiveFactory.AutoFactory, testArchive, readerOptions); + protected void ArchiveFileSkip( string testArchive, string fileOrder, @@ -270,7 +295,7 @@ public class ArchiveTests : ReaderTests ExtractFullPath = true, Overwrite = true, PreserveAttributes = true, - PreserveFileTime = true + PreserveFileTime = true, } ); } 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 ca9686a4..8cd3123d 100644 --- a/tests/SharpCompress.Test/Filters/BranchExecTests.cs +++ b/tests/SharpCompress.Test/Filters/BranchExecTests.cs @@ -91,7 +91,7 @@ public class BranchExecTests 0x00, 0x00, 0x1C, - 0x00 + 0x00, ]; private static byte[] X86Data { get; } = @@ -173,7 +173,7 @@ public class BranchExecTests 0x00, 0x00, 0x1C, - 0x00 + 0x00, ]; private static byte[] PpcResultData { get; } = @@ -273,7 +273,7 @@ public class BranchExecTests 0x7A, 0xB2, 0xD4, - 0xED + 0xED, ]; private static byte[] PpcData { get; } = @@ -373,7 +373,7 @@ public class BranchExecTests 0x7A, 0xB2, 0xD4, - 0xED + 0xED, ]; private static byte[] ArmResultData { get; } = @@ -473,7 +473,7 @@ public class BranchExecTests 0x00, 0xC6, 0x8F, - 0xE2 + 0xE2, ]; private static byte[] ArmData { get; } = @@ -573,7 +573,7 @@ public class BranchExecTests 0x00, 0xC6, 0x8F, - 0xE2 + 0xE2, ]; private static byte[] ArmtResultData { get; } = @@ -689,7 +689,7 @@ public class BranchExecTests 0x7D, 0xED, 0x11, - 0x0F + 0x0F, ]; private static byte[] ArmtData { get; } = @@ -805,7 +805,7 @@ public class BranchExecTests 0x7D, 0xED, 0x11, - 0x0F + 0x0F, ]; private static byte[] Ia64ResultData { get; } = @@ -905,7 +905,7 @@ public class BranchExecTests 0x2B, 0x72, 0xD5, - 0x0D + 0x0D, ]; private static byte[] Ia64Data { get; } = @@ -1005,7 +1005,7 @@ public class BranchExecTests 0x2B, 0x72, 0xD5, - 0x0D + 0x0D, ]; private static byte[] SparcResultData { get; } = @@ -1087,7 +1087,7 @@ public class BranchExecTests 0x91, 0x00, 0x10, - 0x00 + 0x00, ]; private static byte[] SparcData { get; } = @@ -1169,7 +1169,7 @@ public class BranchExecTests 0x91, 0x00, 0x10, - 0x00 + 0x00, ]; private void CompareBuffer(byte[] testBuffer, byte[] targetBuffer) => diff --git a/tests/SharpCompress.Test/GZip/GZipArchiveTests.cs b/tests/SharpCompress.Test/GZip/GZipArchiveTests.cs index 56b5cd2b..6a2b9795 100644 --- a/tests/SharpCompress.Test/GZip/GZipArchiveTests.cs +++ b/tests/SharpCompress.Test/GZip/GZipArchiveTests.cs @@ -106,7 +106,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); @@ -116,4 +116,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/Mocks/ForwardOnlyStream.cs b/tests/SharpCompress.Test/Mocks/ForwardOnlyStream.cs index 180b8416..ae91533c 100644 --- a/tests/SharpCompress.Test/Mocks/ForwardOnlyStream.cs +++ b/tests/SharpCompress.Test/Mocks/ForwardOnlyStream.cs @@ -31,7 +31,7 @@ public class ForwardOnlyStream(Stream stream) : Stream public override long Position { - get => throw new NotSupportedException(); + get => stream.Position; set => throw new NotSupportedException(); } diff --git a/tests/SharpCompress.Test/Rar/RarArchiveTests.cs b/tests/SharpCompress.Test/Rar/RarArchiveTests.cs index 373af692..81d2c089 100644 --- a/tests/SharpCompress.Test/Rar/RarArchiveTests.cs +++ b/tests/SharpCompress.Test/Rar/RarArchiveTests.cs @@ -235,7 +235,7 @@ public class RarArchiveTests : ArchiveTests "Rar.multi.part03.rar", "Rar.multi.part04.rar", "Rar.multi.part05.rar", - "Rar.multi.part06.rar" + "Rar.multi.part06.rar", ], false ); @@ -249,7 +249,7 @@ public class RarArchiveTests : ArchiveTests "Rar5.multi.part03.rar", "Rar5.multi.part04.rar", "Rar5.multi.part05.rar", - "Rar5.multi.part06.rar" + "Rar5.multi.part06.rar", ], false ); @@ -278,7 +278,7 @@ public class RarArchiveTests : ArchiveTests "Rar.multi.solid.part03.rar", "Rar.multi.solid.part04.rar", "Rar.multi.solid.part05.rar", - "Rar.multi.solid.part06.rar" + "Rar.multi.solid.part06.rar", ], true ); @@ -348,7 +348,7 @@ public class RarArchiveTests : ArchiveTests "Rar2.multi.r02", "Rar2.multi.r03", "Rar2.multi.r04", - "Rar2.multi.r05" + "Rar2.multi.r05", ], false ); @@ -424,7 +424,7 @@ public class RarArchiveTests : ArchiveTests "Rar4.split.003", "Rar4.split.004", "Rar4.split.005", - "Rar4.split.006" + "Rar4.split.006", } ); @@ -440,7 +440,7 @@ public class RarArchiveTests : ArchiveTests "Rar2.multi.r02", "Rar2.multi.r03", "Rar2.multi.r04", - "Rar2.multi.r05" + "Rar2.multi.r05", } ); @@ -456,7 +456,7 @@ public class RarArchiveTests : ArchiveTests "Rar4.multi.part04.rar", "Rar4.multi.part05.rar", "Rar4.multi.part06.rar", - "Rar4.multi.part07.rar" + "Rar4.multi.part07.rar", } ); @@ -470,7 +470,7 @@ public class RarArchiveTests : ArchiveTests "Rar4.multi.part04.rar", "Rar4.multi.part05.rar", "Rar4.multi.part06.rar", - "Rar4.multi.part07.rar" + "Rar4.multi.part07.rar", ], false ); @@ -486,7 +486,7 @@ public class RarArchiveTests : ArchiveTests "Rar4.split.003", "Rar4.split.004", "Rar4.split.005", - "Rar4.split.006" + "Rar4.split.006", ] ); @@ -515,7 +515,7 @@ public class RarArchiveTests : ArchiveTests ArchiveStreamMultiRead( null, [ - "Rar4.split.001" + "Rar4.split.001", //"Rar4.split.002", //"Rar4.split.003", //"Rar4.split.004", @@ -557,7 +557,7 @@ public class RarArchiveTests : ArchiveTests [ [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 + [5, 6], //txt - Rar4.multi.part06.rar to Rar4.multi.part07.rar ], null, "Rar4.multi.part01.rar", diff --git a/tests/SharpCompress.Test/Rar/RarReaderTests.cs b/tests/SharpCompress.Test/Rar/RarReaderTests.cs index 6ba605a5..5d3c0e64 100644 --- a/tests/SharpCompress.Test/Rar/RarReaderTests.cs +++ b/tests/SharpCompress.Test/Rar/RarReaderTests.cs @@ -19,7 +19,7 @@ public class RarReaderTests : ReaderTests "Rar.multi.part03.rar", "Rar.multi.part04.rar", "Rar.multi.part05.rar", - "Rar.multi.part06.rar" + "Rar.multi.part06.rar", ] ); @@ -32,7 +32,7 @@ public class RarReaderTests : ReaderTests "Rar5.multi.part03.rar", "Rar5.multi.part04.rar", "Rar5.multi.part05.rar", - "Rar5.multi.part06.rar" + "Rar5.multi.part06.rar", ] ); @@ -66,7 +66,7 @@ public class RarReaderTests : ReaderTests "Rar.EncryptedParts.part03.rar", "Rar.EncryptedParts.part04.rar", "Rar.EncryptedParts.part05.rar", - "Rar.EncryptedParts.part06.rar" + "Rar.EncryptedParts.part06.rar", ] ); @@ -102,7 +102,7 @@ public class RarReaderTests : ReaderTests "Rar.multi.part03.rar", "Rar.multi.part04.rar", "Rar.multi.part05.rar", - "Rar.multi.part06.rar" + "Rar.multi.part06.rar", ] ); @@ -115,7 +115,7 @@ public class RarReaderTests : ReaderTests "Rar5.multi.part03.rar", "Rar5.multi.part04.rar", "Rar5.multi.part05.rar", - "Rar5.multi.part06.rar" + "Rar5.multi.part06.rar", ] ); @@ -338,7 +338,7 @@ public class RarReaderTests : ReaderTests "Rar.EncryptedParts.part03.rar", "Rar.EncryptedParts.part04.rar", "Rar.EncryptedParts.part05.rar", - "Rar.EncryptedParts.part06.rar" + "Rar.EncryptedParts.part06.rar", }; using var reader = RarReader.Open( diff --git a/tests/SharpCompress.Test/SevenZip/SevenZipArchiveTests.cs b/tests/SharpCompress.Test/SevenZip/SevenZipArchiveTests.cs index 32d978a2..544741ef 100644 --- a/tests/SharpCompress.Test/SevenZip/SevenZipArchiveTests.cs +++ b/tests/SharpCompress.Test/SevenZip/SevenZipArchiveTests.cs @@ -4,6 +4,7 @@ using System.Linq; using SharpCompress.Archives; using SharpCompress.Archives.SevenZip; using SharpCompress.Common; +using SharpCompress.Factories; using SharpCompress.Readers; using Xunit; @@ -54,6 +55,14 @@ public class SevenZipArchiveTests : ArchiveTests [Fact] public void SevenZipArchive_LZMA2_PathRead() => ArchiveFileRead("7Zip.LZMA2.7z"); + [Fact] + public void SevenZipArchive_LZMA2_EXE_StreamRead() => + ArchiveStreamRead(new SevenZipFactory(), "7Zip.LZMA2.exe", new() { LookForHeader = true }); + + [Fact] + public void SevenZipArchive_LZMA2_EXE_PathRead() => + ArchiveFileRead(new SevenZipFactory(), "7Zip.LZMA2.exe", new() { LookForHeader = true }); + [Fact] public void SevenZipArchive_LZMA2AES_StreamRead() => ArchiveStreamRead("7Zip.LZMA2.Aes.7z", new ReaderOptions { Password = "testpassword" }); diff --git a/tests/SharpCompress.Test/SharpCompress.Test.csproj b/tests/SharpCompress.Test/SharpCompress.Test.csproj index 3bfeaa3e..22f99b20 100644 --- a/tests/SharpCompress.Test/SharpCompress.Test.csproj +++ b/tests/SharpCompress.Test/SharpCompress.Test.csproj @@ -1,8 +1,10 @@  - net8.0;net462 + net8.0;net48 SharpCompress.Test SharpCompress.Test + SharpCompress.Test.snk + true diff --git a/tests/SharpCompress.Test/SharpCompress.Test.snk b/tests/SharpCompress.Test/SharpCompress.Test.snk new file mode 100644 index 00000000..5a9ce097 Binary files /dev/null and b/tests/SharpCompress.Test/SharpCompress.Test.snk differ diff --git a/tests/SharpCompress.Test/Streams/LzmaStreamTests.cs b/tests/SharpCompress.Test/Streams/LzmaStreamTests.cs index 2eea6607..634929b1 100644 --- a/tests/SharpCompress.Test/Streams/LzmaStreamTests.cs +++ b/tests/SharpCompress.Test/Streams/LzmaStreamTests.cs @@ -174,7 +174,7 @@ public class LzmaStreamTests 0xF1, 0x76, 0x03, - 0x90 + 0x90, ]; /// @@ -509,7 +509,7 @@ public class LzmaStreamTests 0x0A, 0x00, 0x00, - 0x00 + 0x00, ]; [Fact] diff --git a/tests/SharpCompress.Test/Streams/ZlibBaseStreamTests.cs b/tests/SharpCompress.Test/Streams/ZlibBaseStreamTests.cs index 56f934d6..ab8e6656 100644 --- a/tests/SharpCompress.Test/Streams/ZlibBaseStreamTests.cs +++ b/tests/SharpCompress.Test/Streams/ZlibBaseStreamTests.cs @@ -34,7 +34,7 @@ public class ZLibBaseStreamTests 0x8b, 0xce, 0x49, - 0xd2 + 0xd2, }; var buf = new byte[plainData.Length * 2]; diff --git a/tests/SharpCompress.Test/Tar/TarArchiveTests.cs b/tests/SharpCompress.Test/Tar/TarArchiveTests.cs index bbf96c00..efbde472 100644 --- a/tests/SharpCompress.Test/Tar/TarArchiveTests.cs +++ b/tests/SharpCompress.Test/Tar/TarArchiveTests.cs @@ -252,7 +252,7 @@ public class TarArchiveTests : ArchiveTests [Fact] public void Tar_Read_One_At_A_Time() { - var archiveEncoding = new ArchiveEncoding { Default = Encoding.UTF8, }; + var archiveEncoding = new ArchiveEncoding { Default = Encoding.UTF8 }; var tarWriterOptions = new TarWriterOptions(CompressionType.None, true) { ArchiveEncoding = archiveEncoding, @@ -287,4 +287,12 @@ public class TarArchiveTests : ArchiveTests Assert.Equal(2, numberOfEntries); } + + [Fact] + public void Tar_Detect_Test() + { + var isTar = TarArchive.IsTarFile(Path.Combine(TEST_ARCHIVES_PATH, "false.positive.tar")); + + Assert.False(isTar); + } } diff --git a/tests/SharpCompress.Test/Tar/TarReaderTests.cs b/tests/SharpCompress.Test/Tar/TarReaderTests.cs index bb10db98..d2b01ef0 100644 --- a/tests/SharpCompress.Test/Tar/TarReaderTests.cs +++ b/tests/SharpCompress.Test/Tar/TarReaderTests.cs @@ -183,6 +183,21 @@ public class TarReaderTests : ReaderTests 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 [Fact] public void Tar_GZip_With_Symlink_Entries() @@ -217,7 +232,7 @@ public class TarReaderTests : ReaderTests } link.CreateSymbolicLinkTo(targetPath); } - } + }, } ); if (!isWindows) diff --git a/tests/SharpCompress.Test/TestBase.cs b/tests/SharpCompress.Test/TestBase.cs index 184e6442..f9cfb8a3 100644 --- a/tests/SharpCompress.Test/TestBase.cs +++ b/tests/SharpCompress.Test/TestBase.cs @@ -1,3 +1,4 @@ +global using SharpCompress.Helpers; using System; using System.Collections.Generic; using System.IO; diff --git a/tests/SharpCompress.Test/WriterTests.cs b/tests/SharpCompress.Test/WriterTests.cs index 50695ef1..ba08b3d2 100644 --- a/tests/SharpCompress.Test/WriterTests.cs +++ b/tests/SharpCompress.Test/WriterTests.cs @@ -22,7 +22,7 @@ public class WriterTests : TestBase { using (Stream stream = File.OpenWrite(Path.Combine(SCRATCH2_FILES_PATH, archive))) { - var writerOptions = new WriterOptions(compressionType) { LeaveStreamOpen = true, }; + var writerOptions = new WriterOptions(compressionType) { LeaveStreamOpen = true }; writerOptions.ArchiveEncoding.Default = encoding ?? Encoding.Default; diff --git a/tests/SharpCompress.Test/Xz/XZBlockTests.cs b/tests/SharpCompress.Test/Xz/XZBlockTests.cs index 0d6c7d9a..44245075 100644 --- a/tests/SharpCompress.Test/Xz/XZBlockTests.cs +++ b/tests/SharpCompress.Test/Xz/XZBlockTests.cs @@ -94,4 +94,19 @@ public class XzBlockTests : XzTestsBase sr.ReadToEnd(); Assert.Equal(0L, CompressedIndexedStream.Position % 4L); } + + [Fact] + public void HandlesPaddingInUnalignedBlock() + { + var compressedUnaligned = new byte[Compressed.Length + 1]; + Compressed.CopyTo(compressedUnaligned, 1); + var compressedUnalignedStream = new MemoryStream(compressedUnaligned); + compressedUnalignedStream.Position = 13; + + // Compressed's only block has no padding. + var xzBlock = new XZBlock(compressedUnalignedStream, CheckType.CRC64, 8); + var sr = new StreamReader(xzBlock); + sr.ReadToEnd(); + Assert.Equal(1L, compressedUnalignedStream.Position % 4L); + } } diff --git a/tests/SharpCompress.Test/Xz/XZTestsBase.cs b/tests/SharpCompress.Test/Xz/XZTestsBase.cs index a679934e..d1c15638 100644 --- a/tests/SharpCompress.Test/Xz/XZTestsBase.cs +++ b/tests/SharpCompress.Test/Xz/XZTestsBase.cs @@ -61,7 +61,7 @@ public abstract class XzTestsBase : IDisposable 0x00, 0x01, 0x59, - 0x5a + 0x5a, ]; protected static byte[] OriginalEmptyBytes => Encoding.ASCII.GetBytes(OriginalEmpty); @@ -451,7 +451,7 @@ public abstract class XzTestsBase : IDisposable 0x00, 0x04, 0x59, - 0x5a + 0x5a, ]; protected static byte[] OriginalBytes => Encoding.ASCII.GetBytes(Original); @@ -1119,7 +1119,7 @@ public abstract class XzTestsBase : IDisposable 0x00, 0x01, 0x59, - 0x5a + 0x5a, ]; protected static byte[] OriginalIndexedBytes => Encoding.ASCII.GetBytes(OriginalIndexed); diff --git a/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs b/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs index 494e02c9..8419f58f 100644 --- a/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs +++ b/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs @@ -16,14 +16,6 @@ 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"); @@ -129,7 +121,7 @@ public class ZipArchiveTests : ArchiveTests "Zip.deflate.split.003", "Zip.deflate.split.004", "Zip.deflate.split.005", - "Zip.deflate.split.006" + "Zip.deflate.split.006", } ); @@ -183,6 +175,54 @@ public class ZipArchiveTests : ArchiveTests [Fact] public void Zip_Zip64_ArchiveFileRead() => ArchiveFileRead("Zip.zip64.zip"); + [Fact] + public void Zip_Shrink_ArchiveStreamRead() + { + UseExtensionInsteadOfNameToVerify = true; + UseCaseInsensitiveToVerify = true; + ArchiveStreamRead("Zip.shrink.zip"); + } + + [Fact] + public void Zip_Implode_ArchiveStreamRead() + { + UseExtensionInsteadOfNameToVerify = true; + UseCaseInsensitiveToVerify = true; + ArchiveStreamRead("Zip.implode.zip"); + } + + [Fact] + public void Zip_Reduce1_ArchiveStreamRead() + { + UseExtensionInsteadOfNameToVerify = true; + UseCaseInsensitiveToVerify = true; + ArchiveStreamRead("Zip.reduce1.zip"); + } + + [Fact] + public void Zip_Reduce2_ArchiveStreamRead() + { + UseExtensionInsteadOfNameToVerify = true; + UseCaseInsensitiveToVerify = true; + ArchiveStreamRead("Zip.reduce2.zip"); + } + + [Fact] + public void Zip_Reduce3_ArchiveStreamRead() + { + UseExtensionInsteadOfNameToVerify = true; + UseCaseInsensitiveToVerify = true; + ArchiveStreamRead("Zip.reduce3.zip"); + } + + [Fact] + public void Zip_Reduce4_ArchiveStreamRead() + { + UseExtensionInsteadOfNameToVerify = true; + UseCaseInsensitiveToVerify = true; + ArchiveStreamRead("Zip.reduce4.zip"); + } + [Fact] public void Zip_Random_Write_Remove() { @@ -715,7 +755,7 @@ public class ZipArchiveTests : ArchiveTests "Folder/File2.rtf", "Folder2/File1.txt", "Folder2/File2.txt", - "DEADBEEF" + "DEADBEEF", }; var zipPath = Path.Combine(TEST_ARCHIVES_PATH, "Zip.uncompressed.zip"); using var stream = File.Open(zipPath, FileMode.Open, FileAccess.Read); @@ -744,7 +784,7 @@ public class ZipArchiveTests : ArchiveTests ArchiveEncoding = new ArchiveEncoding { Default = Encoding.GetEncoding("shift_jis"), - } + }, } ); var reader = archive.ExtractAllEntries(); @@ -760,7 +800,7 @@ public class ZipArchiveTests : ArchiveTests ArchiveEncoding = new ArchiveEncoding { Forced = Encoding.GetEncoding("shift_jis"), - } + }, } ); var reader = archive.ExtractAllEntries(); @@ -768,4 +808,16 @@ public class ZipArchiveTests : ArchiveTests Assert.Equal("きょきゅんきゃんきゅ_wav.frq", reader.Entry.Key); } } + + [Fact] + public void TestDataDescriptorRead() + { + using var archive = ArchiveFactory.Open( + Path.Combine(TEST_ARCHIVES_PATH, "Zip.none.datadescriptors.zip") + ); + var firstEntry = archive.Entries.First(); + Assert.Equal(199, firstEntry.Size); + using var _ = firstEntry.OpenEntryStream(); + Assert.Equal(199, firstEntry.Size); + } } diff --git a/tests/SharpCompress.Test/Zip/ZipReaderTests.cs b/tests/SharpCompress.Test/Zip/ZipReaderTests.cs index 3296bcb0..69c6326f 100644 --- a/tests/SharpCompress.Test/Zip/ZipReaderTests.cs +++ b/tests/SharpCompress.Test/Zip/ZipReaderTests.cs @@ -248,7 +248,7 @@ public class ZipReaderTests : ReaderTests var expected = new[] { new Tuple("foo.txt", Array.Empty()), - new Tuple("foo2.txt", new byte[10]) + new Tuple("foo2.txt", new byte[10]), }; using var memory = new MemoryStream(); diff --git a/tests/SharpCompress.Test/packages.lock.json b/tests/SharpCompress.Test/packages.lock.json index a7d04fce..babef83e 100644 --- a/tests/SharpCompress.Test/packages.lock.json +++ b/tests/SharpCompress.Test/packages.lock.json @@ -1,72 +1,65 @@ { "version": 2, "dependencies": { - ".NETFramework,Version=v4.6.2": { + ".NETFramework,Version=v4.8": { "FluentAssertions": { "type": "Direct", - "requested": "[6.12.0, )", - "resolved": "6.12.0", - "contentHash": "ZXhHT2YwP9lajrwSKbLlFqsmCCvFJMoRSK9t7sImfnCyd0OB3MhgxdoMcVqxbq1iyxD6mD2fiackWmBb7ayiXQ==", + "requested": "[7.0.0, )", + "resolved": "7.0.0", + "contentHash": "mTLbcU991EQ1SEmNbVBaGGGJy0YFzvGd1sYJGNZ07nlPKuyHSn1I22aeKzqQXgEiaKyRO6MSCto9eN9VxMwBdA==", "dependencies": { - "System.Threading.Tasks.Extensions": "4.5.0" + "System.Threading.Tasks.Extensions": "4.5.4" } }, "Microsoft.NET.Test.Sdk": { "type": "Direct", - "requested": "[17.9.0, )", - "resolved": "17.9.0", - "contentHash": "7GUNAUbJYn644jzwLm5BD3a2p9C1dmP8Hr6fDPDxgItQk9hBs1Svdxzz07KQ/UphMSmgza9AbijBJGmw5D658A==", + "requested": "[17.12.0, )", + "resolved": "17.12.0", + "contentHash": "kt/PKBZ91rFCWxVIJZSgVLk+YR+4KxTuHf799ho8WNiK5ZQpJNAEZCAWX86vcKrs+DiYjiibpYKdGZP6+/N17w==", "dependencies": { - "Microsoft.CodeCoverage": "17.9.0" + "Microsoft.CodeCoverage": "17.12.0" } }, - "Microsoft.NETFramework.ReferenceAssemblies": { + "Microsoft.SourceLink.GitHub": { "type": "Direct", - "requested": "[1.0.3, )", - "resolved": "1.0.3", - "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", "dependencies": { - "Microsoft.NETFramework.ReferenceAssemblies.net462": "1.0.3" + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" } }, "xunit": { "type": "Direct", - "requested": "[2.7.1, )", - "resolved": "2.7.1", - "contentHash": "9o050yCLzPvHxbrAkaHnI1j+YfPvRw+/ncvKbTfzIhO4JvQA0rPgoICJxXHMkscfgXmLFwZ8107ehnMUVzE23A==", + "requested": "[2.9.3, )", + "resolved": "2.9.3", + "contentHash": "TlXQBinK35LpOPKHAqbLY4xlEen9TBafjs0V5KnA4wZsoQLQJiirCR4CbIXvOH8NzkW4YeJKP5P/Bnrodm0h9Q==", "dependencies": { - "xunit.analyzers": "1.12.0", - "xunit.assert": "2.7.1", - "xunit.core": "[2.7.1]" + "xunit.analyzers": "1.18.0", + "xunit.assert": "2.9.3", + "xunit.core": "[2.9.3]" } }, "xunit.runner.visualstudio": { "type": "Direct", - "requested": "[2.5.8, )", - "resolved": "2.5.8", - "contentHash": "ZJTm71neOfZcUnqdyY0A0Qgcg1162DoOq6+VpCCsOaD9rwCK5alcjOEHeu17sEekzq4qNv3kyelx6lUMsAt/eA==", + "requested": "[3.0.1, )", + "resolved": "3.0.1", + "contentHash": "lbyYtsBxA8Pz8kaf5Xn/Mj1mL9z2nlBWdZhqFaj66nxXBa4JwiTDm4eGcpSMet6du9TOWI6bfha+gQR6+IHawg==", "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.9.0" + "Microsoft.TestPlatform.ObjectModel": "17.12.0" } }, "Xunit.SkippableFact": { "type": "Direct", - "requested": "[1.4.13, )", - "resolved": "1.4.13", - "contentHash": "IyzZNvJEtXGlXrzxDiSbtH5Lyxf4iJdRQADuyjGdDf00LjXRLJwIoezQNFhFGKTMtvk8IIgaSHxW4mAV4O7b8A==", + "requested": "[1.5.23, )", + "resolved": "1.5.23", + "contentHash": "JlKobLTlsGcuJ8OtoodxL63bUagHSVBnF+oQ2GgnkwNqK+XYjeYyhQasULi5Ebx1MNDGNbOMplQYr89mR+nItQ==", "dependencies": { - "Validation": "2.4.18", + "Validation": "2.5.51", "xunit.extensibility.execution": "2.4.0" } }, - "Microsoft.Bcl.AsyncInterfaces": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==", - "dependencies": { - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, "Microsoft.Build.Tasks.Git": { "type": "Transitive", "resolved": "8.0.0", @@ -74,13 +67,8 @@ }, "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==" + "resolved": "17.12.0", + "contentHash": "4svMznBd5JM21JIG2xZKGNanAHNXplxf/kQDFfLHXQ3OnpJkayRK/TjacFjA+EYmoyuNXHo/sOETEfcYtAzIrA==" }, "Microsoft.SourceLink.Common": { "type": "Transitive", @@ -89,17 +77,12 @@ }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", - "resolved": "17.9.0", - "contentHash": "1ilw/8vgmjLyKU+2SKXKXaOqpYFJCQfGqGz+x0cosl981VzjrY74Sv6qAJv+neZMZ9ZMxF3ArN6kotaQ4uvEBw==", + "resolved": "17.12.0", + "contentHash": "TDqkTKLfQuAaPcEb3pDDWnh7b3SyZF+/W9OZvWFp6eJCIiiYFdSB6taE2I6tWrFw5ywhzOb6sreoGJTI6m3rSQ==", "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", @@ -133,8 +116,8 @@ }, "Validation": { "type": "Transitive", - "resolved": "2.4.18", - "contentHash": "NfvWJ1QeuZ1FQCkqgXTu1cOkRkbNCfxs4Tat+abXLwom6OXbULVhRGp34BTvVB4XPxj6VIAl7KfLfStXMt/Ehw==" + "resolved": "2.5.51", + "contentHash": "g/Aug7PVWaenlJ0QUyt/mEetngkQNsMCuNeRVXbcJED1nZS7JcK+GTU4kz3jcQ7bFuKfi8PF4ExXH7XSFNuSLQ==" }, "xunit.abstractions": { "type": "Transitive", @@ -143,61 +126,64 @@ }, "xunit.analyzers": { "type": "Transitive", - "resolved": "1.12.0", - "contentHash": "w23LH3aXade2WXKvXi0oA/uV15fpgUMjsPq1x91iQckzgWApgAiijNHmfFQtqNPm41wfrdbRl7nSJRd0yux/dw==" + "resolved": "1.18.0", + "contentHash": "OtFMHN8yqIcYP9wcVIgJrq01AfTxijjAqVDy/WeQVSyrDC1RzBWeQPztL49DN2syXRah8TYnfvk035s7L95EZQ==" }, "xunit.assert": { "type": "Transitive", - "resolved": "2.7.1", - "contentHash": "JqvXS4yX+PtJn5BuqoKkYav7I0g4nXcxRbGTomDwVQjFccOdyfYKpuPOHX/DqrPCcL+MIHrGVdP3bveUXlvdnA==" + "resolved": "2.9.3", + "contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA==" }, "xunit.core": { "type": "Transitive", - "resolved": "2.7.1", - "contentHash": "hcODgS+RXdjmXq0zQzmRbERQgY+bAGGx1bdH3370t/8CTGmIEU2qAc1dQAjIRpARsacR0cj6LLJDUF5BNQNKTQ==", + "resolved": "2.9.3", + "contentHash": "BiAEvqGvyme19wE0wTKdADH+NloYqikiU0mcnmiNyXaF9HyHmE6sr/3DC5vnBkgsWaE6yPyWszKSPSApWdRVeQ==", "dependencies": { - "xunit.extensibility.core": "[2.7.1]", - "xunit.extensibility.execution": "[2.7.1]" + "xunit.extensibility.core": "[2.9.3]", + "xunit.extensibility.execution": "[2.9.3]" } }, "xunit.extensibility.core": { "type": "Transitive", - "resolved": "2.7.1", - "contentHash": "yLX4XlBFkvNYCzf+DEzlNk45KsSlu9W93IJHBmtUP96qZ9XyRYDFlwMj6BCcOhDKVNrZxSM8bqu4F/Qud4ehxA==", + "resolved": "2.9.3", + "contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==", "dependencies": { "xunit.abstractions": "2.0.3" } }, "xunit.extensibility.execution": { "type": "Transitive", - "resolved": "2.7.1", - "contentHash": "ei3dKF8agj4UKkJ6KkaZ5/Gcif3if6hBsyzegIQJonZDSKZFvb0AyKtyOhDfggBaXVL5iXZExITdRkfjC95yhw==", + "resolved": "2.9.3", + "contentHash": "yMb6vMESlSrE3Wfj7V6cjQ3S4TXdXpRqYeNEI3zsX31uTsGMJjEw6oD5F5u1cHnMptjhEECnmZSsPxB6ChZHDQ==", "dependencies": { - "xunit.extensibility.core": "[2.7.1]" + "xunit.extensibility.core": "[2.9.3]" } }, "sharpcompress": { "type": "Project", "dependencies": { - "Microsoft.SourceLink.GitHub": "[8.0.0, )", - "System.Memory": "[4.5.5, )", - "System.Text.Encoding.CodePages": "[8.0.0, )", - "ZstdSharp.Port": "[0.7.6, )" + "System.Buffers": "[4.6.0, )", + "ZstdSharp.Port": "[0.8.4, )" } }, - "Microsoft.SourceLink.GitHub": { + "Microsoft.Bcl.AsyncInterfaces": { "type": "CentralTransitive", "requested": "[8.0.0, )", - "resolved": "8.0.0", - "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "resolved": "5.0.0", + "contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==", "dependencies": { - "Microsoft.Build.Tasks.Git": "8.0.0", - "Microsoft.SourceLink.Common": "8.0.0" + "System.Threading.Tasks.Extensions": "4.5.4" } }, + "System.Buffers": { + "type": "CentralTransitive", + "requested": "[4.6.0, )", + "resolved": "4.6.0", + "contentHash": "lN6tZi7Q46zFzAbRYXTIvfXcyvQQgxnY7Xm6C6xQ9784dEL1amjM6S6Iw4ZpsvesAKnRVsM4scrDQaDqSClkjA==" + }, "System.Memory": { "type": "CentralTransitive", - "requested": "[4.5.5, )", + "requested": "[4.6.0, )", "resolved": "4.5.5", "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", "dependencies": { @@ -206,45 +192,46 @@ "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.7.6, )", - "resolved": "0.7.6", - "contentHash": "opRgjMeBTNpZQyJaZOWCHGGpQ+d9T2Eg3q8JPeBj65srJBf1F3JidO2h7sYkK2s0Mcga+skrqypQY39CF8kcfA==", + "requested": "[0.8.4, )", + "resolved": "0.8.4", + "contentHash": "eieSXq3kakCUXbgdxkKaRqWS6hF0KBJcqok9LlDCs60GOyrynLvPOcQ0pRw7shdPF7lh/VepJ9cP9n9HHc759g==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "5.0.0", - "System.Memory": "4.5.5" + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" } } }, "net8.0": { "FluentAssertions": { "type": "Direct", - "requested": "[6.12.0, )", - "resolved": "6.12.0", - "contentHash": "ZXhHT2YwP9lajrwSKbLlFqsmCCvFJMoRSK9t7sImfnCyd0OB3MhgxdoMcVqxbq1iyxD6mD2fiackWmBb7ayiXQ==", + "requested": "[7.0.0, )", + "resolved": "7.0.0", + "contentHash": "mTLbcU991EQ1SEmNbVBaGGGJy0YFzvGd1sYJGNZ07nlPKuyHSn1I22aeKzqQXgEiaKyRO6MSCto9eN9VxMwBdA==", "dependencies": { - "System.Configuration.ConfigurationManager": "4.4.0" + "System.Configuration.ConfigurationManager": "6.0.0" } }, "Microsoft.NET.Test.Sdk": { "type": "Direct", - "requested": "[17.9.0, )", - "resolved": "17.9.0", - "contentHash": "7GUNAUbJYn644jzwLm5BD3a2p9C1dmP8Hr6fDPDxgItQk9hBs1Svdxzz07KQ/UphMSmgza9AbijBJGmw5D658A==", + "requested": "[17.12.0, )", + "resolved": "17.12.0", + "contentHash": "kt/PKBZ91rFCWxVIJZSgVLk+YR+4KxTuHf799ho8WNiK5ZQpJNAEZCAWX86vcKrs+DiYjiibpYKdGZP6+/N17w==", "dependencies": { - "Microsoft.CodeCoverage": "17.9.0", - "Microsoft.TestPlatform.TestHost": "17.9.0" + "Microsoft.CodeCoverage": "17.12.0", + "Microsoft.TestPlatform.TestHost": "17.12.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" } }, "Mono.Posix.NETStandard": { @@ -255,28 +242,28 @@ }, "xunit": { "type": "Direct", - "requested": "[2.7.1, )", - "resolved": "2.7.1", - "contentHash": "9o050yCLzPvHxbrAkaHnI1j+YfPvRw+/ncvKbTfzIhO4JvQA0rPgoICJxXHMkscfgXmLFwZ8107ehnMUVzE23A==", + "requested": "[2.9.3, )", + "resolved": "2.9.3", + "contentHash": "TlXQBinK35LpOPKHAqbLY4xlEen9TBafjs0V5KnA4wZsoQLQJiirCR4CbIXvOH8NzkW4YeJKP5P/Bnrodm0h9Q==", "dependencies": { - "xunit.analyzers": "1.12.0", - "xunit.assert": "2.7.1", - "xunit.core": "[2.7.1]" + "xunit.analyzers": "1.18.0", + "xunit.assert": "2.9.3", + "xunit.core": "[2.9.3]" } }, "xunit.runner.visualstudio": { "type": "Direct", - "requested": "[2.5.8, )", - "resolved": "2.5.8", - "contentHash": "ZJTm71neOfZcUnqdyY0A0Qgcg1162DoOq6+VpCCsOaD9rwCK5alcjOEHeu17sEekzq4qNv3kyelx6lUMsAt/eA==" + "requested": "[3.0.1, )", + "resolved": "3.0.1", + "contentHash": "lbyYtsBxA8Pz8kaf5Xn/Mj1mL9z2nlBWdZhqFaj66nxXBa4JwiTDm4eGcpSMet6du9TOWI6bfha+gQR6+IHawg==" }, "Xunit.SkippableFact": { "type": "Direct", - "requested": "[1.4.13, )", - "resolved": "1.4.13", - "contentHash": "IyzZNvJEtXGlXrzxDiSbtH5Lyxf4iJdRQADuyjGdDf00LjXRLJwIoezQNFhFGKTMtvk8IIgaSHxW4mAV4O7b8A==", + "requested": "[1.5.23, )", + "resolved": "1.5.23", + "contentHash": "JlKobLTlsGcuJ8OtoodxL63bUagHSVBnF+oQ2GgnkwNqK+XYjeYyhQasULi5Ebx1MNDGNbOMplQYr89mR+nItQ==", "dependencies": { - "Validation": "2.4.18", + "Validation": "2.5.51", "xunit.extensibility.execution": "2.4.0" } }, @@ -287,8 +274,8 @@ }, "Microsoft.CodeCoverage": { "type": "Transitive", - "resolved": "17.9.0", - "contentHash": "RGD37ZSrratfScYXm7M0HjvxMxZyWZL4jm+XgMZbkIY1UPgjUpbNA/t+WTGj/rC/0Hm9A3IrH3ywbKZkOCnoZA==" + "resolved": "17.12.0", + "contentHash": "4svMznBd5JM21JIG2xZKGNanAHNXplxf/kQDFfLHXQ3OnpJkayRK/TjacFjA+EYmoyuNXHo/sOETEfcYtAzIrA==" }, "Microsoft.SourceLink.Common": { "type": "Transitive", @@ -297,21 +284,26 @@ }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", - "resolved": "17.9.0", - "contentHash": "1ilw/8vgmjLyKU+2SKXKXaOqpYFJCQfGqGz+x0cosl981VzjrY74Sv6qAJv+neZMZ9ZMxF3ArN6kotaQ4uvEBw==", + "resolved": "17.12.0", + "contentHash": "TDqkTKLfQuAaPcEb3pDDWnh7b3SyZF+/W9OZvWFp6eJCIiiYFdSB6taE2I6tWrFw5ywhzOb6sreoGJTI6m3rSQ==", "dependencies": { "System.Reflection.Metadata": "1.6.0" } }, "Microsoft.TestPlatform.TestHost": { "type": "Transitive", - "resolved": "17.9.0", - "contentHash": "Spmg7Wx49Ya3SxBjyeAR+nQpjMTKZwTwpZ7KyeOTIqI/WHNPnBU4HUvl5kuHPQAwGWqMy4FGZja1HvEwvoaDiA==", + "resolved": "17.12.0", + "contentHash": "MiPEJQNyADfwZ4pJNpQex+t9/jOClBGMiCiVVFuELCMSX2nmNfvUor3uFVxNNCg30uxDP8JDYfPnMXQzsfzYyg==", "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.9.0", + "Microsoft.TestPlatform.ObjectModel": "17.12.0", "Newtonsoft.Json": "13.0.1" } }, + "Microsoft.Win32.SystemEvents": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "hqTM5628jSsQiv+HGpiq3WKBl2c8v1KZfby2J6Pr7pEPlK9waPdgEO6b8A/+/xn/yZ9ulv8HuqK71ONy2tg67A==" + }, "Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.1", @@ -319,10 +311,19 @@ }, "System.Configuration.ConfigurationManager": { "type": "Transitive", - "resolved": "4.4.0", - "contentHash": "gWwQv/Ug1qWJmHCmN17nAbxJYmQBM/E94QxKLksvUiiKB1Ld3Sc/eK1lgmbSjDFxkQhVuayI/cGFZhpBSodLrg==", + "resolved": "6.0.0", + "contentHash": "7T+m0kDSlIPTHIkPMIu6m6tV6qsMqJpvQWW2jIc2qi7sn40qxFo0q+7mEQAhMPXZHMKnWrnv47ntGlM/ejvw3g==", "dependencies": { - "System.Security.Cryptography.ProtectedData": "4.4.0" + "System.Security.Cryptography.ProtectedData": "6.0.0", + "System.Security.Permissions": "6.0.0" + } + }, + "System.Drawing.Common": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "NfuoKUiP2nUWwKZN6twGqXioIe1zVD0RIj2t976A+czLHr2nY454RwwXs6JU9Htc6mwqL6Dn/nEL3dpVf2jOhg==", + "dependencies": { + "Microsoft.Win32.SystemEvents": "6.0.0" } }, "System.Reflection.Metadata": { @@ -330,15 +331,37 @@ "resolved": "1.6.0", "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==" }, + "System.Security.AccessControl": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "AUADIc0LIEQe7MzC+I0cl0rAT8RrTAKFHl53yHjEUzNVIaUlhFY11vc2ebiVJzVBuOzun6F7FBA+8KAbGTTedQ==" + }, "System.Security.Cryptography.ProtectedData": { "type": "Transitive", - "resolved": "4.4.0", - "contentHash": "cJV7ScGW7EhatRsjehfvvYVBvtiSMKgN8bOVI0bQhnF5bU7vnHVIsH49Kva7i7GWaWYvmEzkYVk1TC+gZYBEog==" + "resolved": "6.0.0", + "contentHash": "rp1gMNEZpvx9vP0JW0oHLxlf8oSiQgtno77Y4PLUBjSiDYoD77Y8uXHr1Ea5XG4/pIKhqAdxZ8v8OTUtqo9PeQ==" + }, + "System.Security.Permissions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "T/uuc7AklkDoxmcJ7LGkyX1CcSviZuLCa4jg3PekfJ7SU0niF0IVTXwUiNVP9DSpzou2PpxJ+eNY2IfDM90ZCg==", + "dependencies": { + "System.Security.AccessControl": "6.0.0", + "System.Windows.Extensions": "6.0.0" + } + }, + "System.Windows.Extensions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "IXoJOXIqc39AIe+CIR7koBtRGMiCt/LPM3lI+PELtDIy9XdyeSrwXFdWV9dzJ2Awl0paLWUaknLxFQ5HpHZUog==", + "dependencies": { + "System.Drawing.Common": "6.0.0" + } }, "Validation": { "type": "Transitive", - "resolved": "2.4.18", - "contentHash": "NfvWJ1QeuZ1FQCkqgXTu1cOkRkbNCfxs4Tat+abXLwom6OXbULVhRGp34BTvVB4XPxj6VIAl7KfLfStXMt/Ehw==" + "resolved": "2.5.51", + "contentHash": "g/Aug7PVWaenlJ0QUyt/mEetngkQNsMCuNeRVXbcJED1nZS7JcK+GTU4kz3jcQ7bFuKfi8PF4ExXH7XSFNuSLQ==" }, "xunit.abstractions": { "type": "Transitive", @@ -347,61 +370,57 @@ }, "xunit.analyzers": { "type": "Transitive", - "resolved": "1.12.0", - "contentHash": "w23LH3aXade2WXKvXi0oA/uV15fpgUMjsPq1x91iQckzgWApgAiijNHmfFQtqNPm41wfrdbRl7nSJRd0yux/dw==" + "resolved": "1.18.0", + "contentHash": "OtFMHN8yqIcYP9wcVIgJrq01AfTxijjAqVDy/WeQVSyrDC1RzBWeQPztL49DN2syXRah8TYnfvk035s7L95EZQ==" }, "xunit.assert": { "type": "Transitive", - "resolved": "2.7.1", - "contentHash": "JqvXS4yX+PtJn5BuqoKkYav7I0g4nXcxRbGTomDwVQjFccOdyfYKpuPOHX/DqrPCcL+MIHrGVdP3bveUXlvdnA==" + "resolved": "2.9.3", + "contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA==" }, "xunit.core": { "type": "Transitive", - "resolved": "2.7.1", - "contentHash": "hcODgS+RXdjmXq0zQzmRbERQgY+bAGGx1bdH3370t/8CTGmIEU2qAc1dQAjIRpARsacR0cj6LLJDUF5BNQNKTQ==", + "resolved": "2.9.3", + "contentHash": "BiAEvqGvyme19wE0wTKdADH+NloYqikiU0mcnmiNyXaF9HyHmE6sr/3DC5vnBkgsWaE6yPyWszKSPSApWdRVeQ==", "dependencies": { - "xunit.extensibility.core": "[2.7.1]", - "xunit.extensibility.execution": "[2.7.1]" + "xunit.extensibility.core": "[2.9.3]", + "xunit.extensibility.execution": "[2.9.3]" } }, "xunit.extensibility.core": { "type": "Transitive", - "resolved": "2.7.1", - "contentHash": "yLX4XlBFkvNYCzf+DEzlNk45KsSlu9W93IJHBmtUP96qZ9XyRYDFlwMj6BCcOhDKVNrZxSM8bqu4F/Qud4ehxA==", + "resolved": "2.9.3", + "contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==", "dependencies": { "xunit.abstractions": "2.0.3" } }, "xunit.extensibility.execution": { "type": "Transitive", - "resolved": "2.7.1", - "contentHash": "ei3dKF8agj4UKkJ6KkaZ5/Gcif3if6hBsyzegIQJonZDSKZFvb0AyKtyOhDfggBaXVL5iXZExITdRkfjC95yhw==", + "resolved": "2.9.3", + "contentHash": "yMb6vMESlSrE3Wfj7V6cjQ3S4TXdXpRqYeNEI3zsX31uTsGMJjEw6oD5F5u1cHnMptjhEECnmZSsPxB6ChZHDQ==", "dependencies": { - "xunit.extensibility.core": "[2.7.1]" + "xunit.extensibility.core": "[2.9.3]" } }, "sharpcompress": { "type": "Project", "dependencies": { - "Microsoft.SourceLink.GitHub": "[8.0.0, )", - "ZstdSharp.Port": "[0.7.6, )" + "System.Buffers": "[4.6.0, )", + "ZstdSharp.Port": "[0.8.4, )" } }, - "Microsoft.SourceLink.GitHub": { + "System.Buffers": { "type": "CentralTransitive", - "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" - } + "requested": "[4.6.0, )", + "resolved": "4.6.0", + "contentHash": "lN6tZi7Q46zFzAbRYXTIvfXcyvQQgxnY7Xm6C6xQ9784dEL1amjM6S6Iw4ZpsvesAKnRVsM4scrDQaDqSClkjA==" }, "ZstdSharp.Port": { "type": "CentralTransitive", - "requested": "[0.7.6, )", - "resolved": "0.7.6", - "contentHash": "opRgjMeBTNpZQyJaZOWCHGGpQ+d9T2Eg3q8JPeBj65srJBf1F3JidO2h7sYkK2s0Mcga+skrqypQY39CF8kcfA==" + "requested": "[0.8.4, )", + "resolved": "0.8.4", + "contentHash": "eieSXq3kakCUXbgdxkKaRqWS6hF0KBJcqok9LlDCs60GOyrynLvPOcQ0pRw7shdPF7lh/VepJ9cP9n9HHc759g==" } } } diff --git a/tests/TestArchives/Archives/7Zip.LZMA2.exe b/tests/TestArchives/Archives/7Zip.LZMA2.exe new file mode 100644 index 00000000..889c8318 Binary files /dev/null and b/tests/TestArchives/Archives/7Zip.LZMA2.exe 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.implode.zip b/tests/TestArchives/Archives/Zip.implode.zip new file mode 100644 index 00000000..bd4354f0 Binary files /dev/null and b/tests/TestArchives/Archives/Zip.implode.zip differ diff --git a/tests/TestArchives/Archives/Zip.reduce1.zip b/tests/TestArchives/Archives/Zip.reduce1.zip new file mode 100644 index 00000000..61af465b Binary files /dev/null and b/tests/TestArchives/Archives/Zip.reduce1.zip differ diff --git a/tests/TestArchives/Archives/Zip.reduce2.zip b/tests/TestArchives/Archives/Zip.reduce2.zip new file mode 100644 index 00000000..c1bb8fdc Binary files /dev/null and b/tests/TestArchives/Archives/Zip.reduce2.zip differ diff --git a/tests/TestArchives/Archives/Zip.reduce3.zip b/tests/TestArchives/Archives/Zip.reduce3.zip new file mode 100644 index 00000000..41773e57 Binary files /dev/null and b/tests/TestArchives/Archives/Zip.reduce3.zip differ diff --git a/tests/TestArchives/Archives/Zip.reduce4.zip b/tests/TestArchives/Archives/Zip.reduce4.zip new file mode 100644 index 00000000..d21129af Binary files /dev/null and b/tests/TestArchives/Archives/Zip.reduce4.zip differ diff --git a/tests/TestArchives/Archives/false.positive.tar b/tests/TestArchives/Archives/false.positive.tar new file mode 100644 index 00000000..db91a2ff Binary files /dev/null and b/tests/TestArchives/Archives/false.positive.tar differ