diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json
index 034cae45..d1ac7eef 100644
--- a/.config/dotnet-tools.json
+++ b/.config/dotnet-tools.json
@@ -2,10 +2,10 @@
"version": 1,
"isRoot": true,
"tools": {
- "dotnet-format": {
- "version": "4.1.131201",
+ "csharpier": {
+ "version": "0.21.0",
"commands": [
- "dotnet-format"
+ "dotnet-csharpier"
]
}
}
diff --git a/.editorconfig b/.editorconfig
index 28ede258..1903a97c 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -195,8 +195,6 @@ csharp_prefer_static_local_function = true:warning
csharp_prefer_simple_using_statement = true:suggestion
dotnet_diagnostic.IDE0063.severity = suggestion
-csharp_style_namespace_declarations = file_scoped
-
##########################################
# .NET Formatting Conventions
# https://docs.microsoft.com/visualstudio/ide/editorconfig-code-style-settings-reference#formatting-conventions
@@ -251,6 +249,8 @@ csharp_space_between_square_brackets = false
csharp_preserve_single_line_statements = false
csharp_preserve_single_line_blocks = true
+csharp_style_namespace_declarations = file_scoped
+
##########################################
# .NET Naming Conventions
# https://docs.microsoft.com/visualstudio/ide/editorconfig-naming-conventions
@@ -260,11 +260,13 @@ csharp_preserve_single_line_blocks = true
dotnet_diagnostic.CA1000.severity = suggestion
dotnet_diagnostic.CA1001.severity = error
dotnet_diagnostic.CA1018.severity = error
+dotnet_diagnostic.CA1036.severity = silent
dotnet_diagnostic.CA1051.severity = suggestion
dotnet_diagnostic.CA1068.severity = error
dotnet_diagnostic.CA1069.severity = error
dotnet_diagnostic.CA1304.severity = error
dotnet_diagnostic.CA1305.severity = suggestion
+dotnet_diagnostic.CA1307.severity = suggestion
dotnet_diagnostic.CA1309.severity = suggestion
dotnet_diagnostic.CA1310.severity = error
dotnet_diagnostic.CA1707.severity = suggestion
@@ -283,6 +285,7 @@ dotnet_diagnostic.CA1829.severity = suggestion
dotnet_diagnostic.CA1834.severity = error
dotnet_diagnostic.CA1845.severity = suggestion
dotnet_diagnostic.CA1848.severity = suggestion
+dotnet_diagnostic.CA1852.severity = suggestion
dotnet_diagnostic.CA2016.severity = suggestion
dotnet_diagnostic.CA2201.severity = error
dotnet_diagnostic.CA2206.severity = error
@@ -295,6 +298,7 @@ dotnet_diagnostic.CA2254.severity = suggestion
dotnet_diagnostic.CS0169.severity = error
dotnet_diagnostic.CS0219.severity = error
+dotnet_diagnostic.CS0649.severity = suggestion
dotnet_diagnostic.CS1998.severity = error
dotnet_diagnostic.CS8602.severity = error
dotnet_diagnostic.CS8604.severity = error
@@ -305,43 +309,53 @@ dotnet_diagnostic.CS4014.severity = error
dotnet_diagnostic.CS8600.severity = error
dotnet_diagnostic.CS8603.severity = error
dotnet_diagnostic.CS8625.severity = error
+dotnet_diagnostic.CS8981.severity = suggestion
dotnet_diagnostic.BL0005.severity = suggestion
dotnet_diagnostic.MVC1000.severity = suggestion
-dotnet_diagnostic.IDE0055.severity = suggestion # Fix formatting
-dotnet_diagnostic.IDE0023.severity = suggestion # use expression body for operators
-dotnet_diagnostic.IDE0025.severity = suggestion # use expression body for properties
-dotnet_diagnostic.IDE1006.severity = suggestion # Naming rule violation: These words cannot contain lower case characters
-dotnet_diagnostic.IDE0072.severity = suggestion # Populate switch - forces population of all cases even when default specified
-dotnet_diagnostic.IDE0027.severity = suggestion # Use expression body for accessors
-dotnet_diagnostic.IDE0032.severity = suggestion # Use auto property
-dotnet_diagnostic.IDE0007.severity = error # Use var
-dotnet_diagnostic.IDE0160.severity = suggestion # Use block scoped
-dotnet_diagnostic.IDE0011.severity = error # Use braces on if statements
-dotnet_diagnostic.IDE0057.severity = suggestion # substring can be simplified
+dotnet_diagnostic.RZ10012.severity = error
+
dotnet_diagnostic.IDE0004.severity = error # redundant cast
+dotnet_diagnostic.IDE0005.severity = error
+dotnet_diagnostic.IDE0007.severity = error # Use var
+dotnet_diagnostic.IDE0011.severity = error # Use braces on if statements
dotnet_diagnostic.IDE0010.severity = silent # populate switch
+dotnet_diagnostic.IDE0017.severity = suggestion # initialization can be simplified
dotnet_diagnostic.IDE0021.severity = silent # expression body for constructors
dotnet_diagnostic.IDE0022.severity = silent # expression body for methods
+dotnet_diagnostic.IDE0023.severity = suggestion # use expression body for operators
dotnet_diagnostic.IDE0024.severity = silent # expression body for operators
+dotnet_diagnostic.IDE0025.severity = suggestion # use expression body for properties
+dotnet_diagnostic.IDE0027.severity = suggestion # Use expression body for accessors
dotnet_diagnostic.IDE0028.severity = silent
+dotnet_diagnostic.IDE0032.severity = suggestion # Use auto property
dotnet_diagnostic.IDE0033.severity = error # prefer tuple name
+dotnet_diagnostic.IDE0037.severity = suggestion # simplify anonymous type
dotnet_diagnostic.IDE0040.severity = error # modifiers required
dotnet_diagnostic.IDE0041.severity = error # simplify null
dotnet_diagnostic.IDE0042.severity = error # deconstruct variable
-dotnet_diagnostic.IDE0044.severity = error # make field only when possible
+dotnet_diagnostic.IDE0044.severity = suggestion # make field only when possible
dotnet_diagnostic.IDE0047.severity = suggestion # paratemeter name
dotnet_diagnostic.IDE0051.severity = error # unused field
dotnet_diagnostic.IDE0052.severity = error # unused member
+dotnet_diagnostic.IDE0053.severity = suggestion # lambda not needed
+dotnet_diagnostic.IDE0055.severity = suggestion # Fix formatting
+dotnet_diagnostic.IDE0057.severity = suggestion # substring can be simplified
dotnet_diagnostic.IDE0060.severity = suggestion # unused parameters
+dotnet_diagnostic.IDE0061.severity = suggestion # local expression body
+dotnet_diagnostic.IDE0062.severity = suggestion # local to static
+dotnet_diagnostic.IDE0063.severity = error # simplify using
dotnet_diagnostic.IDE0066.severity = suggestion # switch expression
+dotnet_diagnostic.IDE0072.severity = suggestion # Populate switch - forces population of all cases even when default specified
dotnet_diagnostic.IDE0078.severity = suggestion # use pattern matching
dotnet_diagnostic.IDE0090.severity = suggestion # new can be simplified
-dotnet_diagnostic.IDE0130.severity = suggestion # namespace folder structure
+dotnet_diagnostic.IDE0130.severity = error # namespace folder structure
dotnet_diagnostic.IDE0160.severity = silent # Use block namespaces ARE NOT required
-dotnet_diagnostic.IDE0161.severity = suggestion # Please use file namespaces
+dotnet_diagnostic.IDE0161.severity = error # Please use file namespaces
+dotnet_diagnostic.IDE0200.severity = suggestion # lambda not needed
+dotnet_diagnostic.IDE1006.severity = suggestion # Naming rule violation: These words cannot contain lower case characters
##########################################
# Styles
diff --git a/.github/workflows/dotnetcore.yml b/.github/workflows/dotnetcore.yml
index b29f37e7..530ec766 100644
--- a/.github/workflows/dotnetcore.yml
+++ b/.github/workflows/dotnetcore.yml
@@ -1,5 +1,10 @@
name: SharpCompress
-on: [push, pull_request]
+on:
+ push:
+ branches:
+ - 'master'
+ pull_request:
+ types: [ opened, synchronize, reopened, ready_for_review ]
jobs:
build:
@@ -12,8 +17,8 @@ jobs:
- uses: actions/checkout@v3
- uses: actions/setup-dotnet@v3
with:
- dotnet-version: 6.0.x
- - run: dotnet run -p build/build.csproj
+ dotnet-version: 7.0.x
+ - run: dotnet run --project build/build.csproj
- uses: actions/upload-artifact@v3
with:
name: ${{ matrix.os }}-sharpcompress.nupkg
diff --git a/Directory.Build.props b/Directory.Build.props
new file mode 100644
index 00000000..4b343bf8
--- /dev/null
+++ b/Directory.Build.props
@@ -0,0 +1,14 @@
+
+
+ latest
+ enable
+ Recommended
+ true
+ true
+ true
+ true
+ true
+ False
+ False
+
+
diff --git a/build/Program.cs b/build/Program.cs
index f5c91cbe..000051dd 100644
--- a/build/Program.cs
+++ b/build/Program.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
@@ -7,76 +7,75 @@ using static Bullseye.Targets;
using static SimpleExec.Command;
const string Clean = "clean";
-const string Format = "format";
const string Build = "build";
const string Test = "test";
const string Publish = "publish";
-Target(Clean,
- ForEach("**/bin", "**/obj"),
- dir =>
- {
- IEnumerable GetDirectories(string d)
- {
- return Glob.Directories(".", d);
- }
+Target(
+ Clean,
+ ForEach("**/bin", "**/obj"),
+ dir =>
+ {
+ IEnumerable GetDirectories(string d)
+ {
+ return Glob.Directories(".", d);
+ }
- void RemoveDirectory(string d)
- {
- if (Directory.Exists(d))
- {
- Console.WriteLine(d);
- Directory.Delete(d, true);
- }
- }
+ void RemoveDirectory(string d)
+ {
+ if (Directory.Exists(d))
+ {
+ Console.WriteLine(d);
+ Directory.Delete(d, true);
+ }
+ }
- foreach (var d in GetDirectories(dir))
- {
- RemoveDirectory(d);
- }
- });
+ foreach (var d in GetDirectories(dir))
+ {
+ RemoveDirectory(d);
+ }
+ }
+);
-Target(Format,
- () =>
- {
- Run("dotnet", "tool restore");
- Run("dotnet", "format --check");
- });
+Target(
+ Build,
+ () =>
+ {
+ Run("dotnet", "build src/SharpCompress/SharpCompress.csproj -c Release");
+ }
+);
-Target(Build,
- DependsOn(Format),
- framework =>
- {
- Run("dotnet", "build src/SharpCompress/SharpCompress.csproj -c Release");
- });
+Target(
+ Test,
+ DependsOn(Build),
+ ForEach("net7.0", "net462"),
+ framework =>
+ {
+ IEnumerable GetFiles(string d)
+ {
+ return Glob.Files(".", d);
+ }
-Target(Test,
- DependsOn(Build),
- ForEach("net6.0", "net461"),
- framework =>
- {
- IEnumerable GetFiles(string d)
- {
- return Glob.Files(".", d);
- }
+ if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && framework == "net462")
+ {
+ return;
+ }
- if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && framework == "net461")
- {
- return;
- }
+ foreach (var file in GetFiles("**/*.Test.csproj"))
+ {
+ Run("dotnet", $"test {file} -c Release -f {framework}");
+ }
+ }
+);
- foreach (var file in GetFiles("**/*.Test.csproj"))
- {
- Run("dotnet", $"test {file} -c Release -f {framework}");
- }
- });
-
-Target(Publish,
- DependsOn(Test),
- () =>
- {
- Run("dotnet", "pack src/SharpCompress/SharpCompress.csproj -c Release -o artifacts/");
- });
+Target(
+ Publish,
+ DependsOn(Test),
+ () =>
+ {
+ Run("dotnet", "pack src/SharpCompress/SharpCompress.csproj -c Release -o artifacts/");
+ }
+);
Target("default", DependsOn(Publish), () => Console.WriteLine("Done!"));
diff --git a/build/build.csproj b/build/build.csproj
index 792624f2..66d55099 100644
--- a/build/build.csproj
+++ b/build/build.csproj
@@ -2,13 +2,13 @@
Exe
- net6.0
+ net7.0
-
+
-
+
diff --git a/global.json b/global.json
index a3bb3f5e..e6e67e4e 100644
--- a/global.json
+++ b/global.json
@@ -1,6 +1,6 @@
{
"sdk": {
- "version": "6.0.200",
+ "version": "7.0.101",
"rollForward": "latestFeature"
}
-}
\ No newline at end of file
+}
diff --git a/src/SharpCompress/Algorithms/Adler32.cs b/src/SharpCompress/Algorithms/Adler32.cs
index e98c864e..1f094c13 100644
--- a/src/SharpCompress/Algorithms/Adler32.cs
+++ b/src/SharpCompress/Algorithms/Adler32.cs
@@ -15,305 +15,256 @@ using System.Runtime.Intrinsics.X86;
#endif
#pragma warning disable IDE0007 // Use implicit type
-namespace SharpCompress.Algorithms
+namespace SharpCompress.Algorithms;
+
+///
+/// Calculates the 32 bit Adler checksum of a given buffer according to
+/// RFC 1950. ZLIB Compressed Data Format Specification version 3.3)
+///
+internal static class Adler32 // From https://github.com/SixLabors/ImageSharp/blob/main/src/ImageSharp/Compression/Zlib/Adler32.cs
{
///
- /// Calculates the 32 bit Adler checksum of a given buffer according to
- /// RFC 1950. ZLIB Compressed Data Format Specification version 3.3)
+ /// Global inlining options. Helps temporarily disable inlining for better profiler output.
///
- internal static class Adler32 // From https://github.com/SixLabors/ImageSharp/blob/main/src/ImageSharp/Compression/Zlib/Adler32.cs
+ private static class InliningOptions // From https://github.com/SixLabors/ImageSharp/blob/main/src/ImageSharp/Common/Helpers/InliningOptions.cs
{
///
- /// Global inlining options. Helps temporarily disable inlining for better profiler output.
+ /// regardless of the build conditions.
///
- private static class InliningOptions // From https://github.com/SixLabors/ImageSharp/blob/main/src/ImageSharp/Common/Helpers/InliningOptions.cs
- {
- ///
- /// regardless of the build conditions.
- ///
- public const MethodImplOptions AlwaysInline = MethodImplOptions.AggressiveInlining;
+ public const MethodImplOptions AlwaysInline = MethodImplOptions.AggressiveInlining;
#if PROFILING
- public const MethodImplOptions HotPath = MethodImplOptions.NoInlining;
- public const MethodImplOptions ShortMethod = MethodImplOptions.NoInlining;
+ public const MethodImplOptions HotPath = MethodImplOptions.NoInlining;
+ public const MethodImplOptions ShortMethod = MethodImplOptions.NoInlining;
#else
#if SUPPORTS_HOTPATH
- public const MethodImplOptions HotPath = MethodImplOptions.AggressiveOptimization;
+ public const MethodImplOptions HotPath = MethodImplOptions.AggressiveOptimization;
#else
- public const MethodImplOptions HotPath = MethodImplOptions.AggressiveInlining;
+ public const MethodImplOptions HotPath = MethodImplOptions.AggressiveInlining;
#endif
- public const MethodImplOptions ShortMethod = MethodImplOptions.AggressiveInlining;
+ public const MethodImplOptions ShortMethod = MethodImplOptions.AggressiveInlining;
#endif
- public const MethodImplOptions ColdPath = MethodImplOptions.NoInlining;
- }
+ public const MethodImplOptions ColdPath = MethodImplOptions.NoInlining;
+ }
#if SUPPORTS_RUNTIME_INTRINSICS
+ ///
+ /// Provides optimized static methods for trigonometric, logarithmic,
+ /// and other common mathematical functions.
+ ///
+ private static class Numerics // From https://github.com/SixLabors/ImageSharp/blob/main/src/ImageSharp/Common/Helpers/Numerics.cs
+ {
///
- /// Provides optimized static methods for trigonometric, logarithmic,
- /// and other common mathematical functions.
+ /// Reduces elements of the vector into one sum.
///
- private static class Numerics // From https://github.com/SixLabors/ImageSharp/blob/main/src/ImageSharp/Common/Helpers/Numerics.cs
+ /// The accumulator to reduce.
+ /// The sum of all elements.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int ReduceSum(Vector256 accumulator)
{
- ///
- /// Reduces elements of the vector into one sum.
- ///
- /// The accumulator to reduce.
- /// The sum of all elements.
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static int ReduceSum(Vector256 accumulator)
- {
- // Add upper lane to lower lane.
- Vector128 vsum = Sse2.Add(accumulator.GetLower(), accumulator.GetUpper());
+ // Add upper lane to lower lane.
+ Vector128 vsum = Sse2.Add(accumulator.GetLower(), accumulator.GetUpper());
- // Add odd to even.
- vsum = Sse2.Add(vsum, Sse2.Shuffle(vsum, 0b_11_11_01_01));
+ // Add odd to even.
+ vsum = Sse2.Add(vsum, Sse2.Shuffle(vsum, 0b_11_11_01_01));
- // Add high to low.
- vsum = Sse2.Add(vsum, Sse2.Shuffle(vsum, 0b_11_10_11_10));
+ // Add high to low.
+ vsum = Sse2.Add(vsum, Sse2.Shuffle(vsum, 0b_11_10_11_10));
- return Sse2.ConvertToInt32(vsum);
- }
-
- ///
- /// Reduces even elements of the vector into one sum.
- ///
- /// The accumulator to reduce.
- /// The sum of even elements.
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static int EvenReduceSum(Vector256 accumulator)
- {
- Vector128 vsum = Sse2.Add(accumulator.GetLower(), accumulator.GetUpper()); // add upper lane to lower lane
- vsum = Sse2.Add(vsum, Sse2.Shuffle(vsum, 0b_11_10_11_10)); // add high to low
-
- // Vector128.ToScalar() isn't optimized pre-net5.0 https://github.com/dotnet/runtime/pull/37882
- return Sse2.ConvertToInt32(vsum);
- }
+ return Sse2.ConvertToInt32(vsum);
}
+
+ ///
+ /// Reduces even elements of the vector into one sum.
+ ///
+ /// The accumulator to reduce.
+ /// The sum of even elements.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int EvenReduceSum(Vector256 accumulator)
+ {
+ Vector128 vsum = Sse2.Add(accumulator.GetLower(), accumulator.GetUpper()); // add upper lane to lower lane
+ vsum = Sse2.Add(vsum, Sse2.Shuffle(vsum, 0b_11_10_11_10)); // add high to low
+
+ // Vector128.ToScalar() isn't optimized pre-net5.0 https://github.com/dotnet/runtime/pull/37882
+ return Sse2.ConvertToInt32(vsum);
+ }
+ }
#endif
- ///
- /// The default initial seed value of a Adler32 checksum calculation.
- ///
- public const uint SeedValue = 1U;
+ ///
+ /// The default initial seed value of a Adler32 checksum calculation.
+ ///
+ public const uint SeedValue = 1U;
- // Largest prime smaller than 65536
- private const uint BASE = 65521;
+ // Largest prime smaller than 65536
+ private const uint BASE = 65521;
- // NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1
- private const uint NMAX = 5552;
+ // NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1
+ private const uint NMAX = 5552;
#if SUPPORTS_RUNTIME_INTRINSICS
- private const int MinBufferSize = 64;
+ private const int MinBufferSize = 64;
- private const int BlockSize = 1 << 5;
+ private const int BlockSize = 1 << 5;
- // The C# compiler emits this as a compile-time constant embedded in the PE file.
- private static ReadOnlySpan Tap1Tap2 => new byte[]
+ // The C# compiler emits this as a compile-time constant embedded in the PE file.
+ private static ReadOnlySpan Tap1Tap2 =>
+ new byte[]
{
- 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, // tap1
- 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 // tap2
+ 32,
+ 31,
+ 30,
+ 29,
+ 28,
+ 27,
+ 26,
+ 25,
+ 24,
+ 23,
+ 22,
+ 21,
+ 20,
+ 19,
+ 18,
+ 17, // tap1
+ 16,
+ 15,
+ 14,
+ 13,
+ 12,
+ 11,
+ 10,
+ 9,
+ 8,
+ 7,
+ 6,
+ 5,
+ 4,
+ 3,
+ 2,
+ 1 // tap2
};
#endif
- ///
- /// Calculates the Adler32 checksum with the bytes taken from the span.
- ///
- /// The readonly span of bytes.
- /// The .
- [MethodImpl(InliningOptions.ShortMethod)]
- public static uint Calculate(ReadOnlySpan buffer)
- => Calculate(SeedValue, buffer);
+ ///
+ /// Calculates the Adler32 checksum with the bytes taken from the span.
+ ///
+ /// The readonly span of bytes.
+ /// The .
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public static uint Calculate(ReadOnlySpan buffer) => Calculate(SeedValue, buffer);
- ///
- /// Calculates the Adler32 checksum with the bytes taken from the span and seed.
- ///
- /// The input Adler32 value.
- /// The readonly span of bytes.
- /// The .
- [MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)]
- public static uint Calculate(uint adler, ReadOnlySpan buffer)
+ ///
+ /// Calculates the Adler32 checksum with the bytes taken from the span and seed.
+ ///
+ /// The input Adler32 value.
+ /// The readonly span of bytes.
+ /// The .
+ [MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)]
+ public static uint Calculate(uint adler, ReadOnlySpan buffer)
+ {
+ if (buffer.IsEmpty)
{
- if (buffer.IsEmpty)
- {
- return adler;
- }
+ return adler;
+ }
#if SUPPORTS_RUNTIME_INTRINSICS
- if (Avx2.IsSupported && buffer.Length >= MinBufferSize)
- {
- return CalculateAvx2(adler, buffer);
- }
+ if (Avx2.IsSupported && buffer.Length >= MinBufferSize)
+ {
+ return CalculateAvx2(adler, buffer);
+ }
- if (Ssse3.IsSupported && buffer.Length >= MinBufferSize)
- {
- return CalculateSse(adler, buffer);
- }
+ if (Ssse3.IsSupported && buffer.Length >= MinBufferSize)
+ {
+ return CalculateSse(adler, buffer);
+ }
- return CalculateScalar(adler, buffer);
+ return CalculateScalar(adler, buffer);
#else
- return CalculateScalar(adler, buffer);
+ return CalculateScalar(adler, buffer);
#endif
- }
+ }
- // Based on https://github.com/chromium/chromium/blob/master/third_party/zlib/adler32_simd.c
+ // Based on https://github.com/chromium/chromium/blob/master/third_party/zlib/adler32_simd.c
#if SUPPORTS_RUNTIME_INTRINSICS
- [MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)]
- private static unsafe uint CalculateSse(uint adler, ReadOnlySpan buffer)
+ [MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)]
+ private static unsafe uint CalculateSse(uint adler, ReadOnlySpan buffer)
+ {
+ uint s1 = adler & 0xFFFF;
+ uint s2 = (adler >> 16) & 0xFFFF;
+
+ // Process the data in blocks.
+ uint length = (uint)buffer.Length;
+ uint blocks = length / BlockSize;
+ length -= blocks * BlockSize;
+
+ fixed (byte* bufferPtr = &MemoryMarshal.GetReference(buffer))
{
- uint s1 = adler & 0xFFFF;
- uint s2 = (adler >> 16) & 0xFFFF;
-
- // Process the data in blocks.
- uint length = (uint)buffer.Length;
- uint blocks = length / BlockSize;
- length -= blocks * BlockSize;
-
- fixed (byte* bufferPtr = &MemoryMarshal.GetReference(buffer))
- {
- fixed (byte* tapPtr = &MemoryMarshal.GetReference(Tap1Tap2))
- {
- byte* localBufferPtr = bufferPtr;
-
- // _mm_setr_epi8 on x86
- Vector128 tap1 = Sse2.LoadVector128((sbyte*)tapPtr);
- Vector128 tap2 = Sse2.LoadVector128((sbyte*)(tapPtr + 0x10));
- Vector128 zero = Vector128.Zero;
- var ones = Vector128.Create((short)1);
-
- while (blocks > 0)
- {
- uint n = NMAX / BlockSize; /* The NMAX constraint. */
- if (n > blocks)
- {
- n = blocks;
- }
-
- blocks -= n;
-
- // Process n blocks of data. At most NMAX data bytes can be
- // processed before s2 must be reduced modulo BASE.
- Vector128 v_ps = Vector128.CreateScalar(s1 * n);
- Vector128 v_s2 = Vector128.CreateScalar(s2);
- Vector128 v_s1 = Vector128.Zero;
-
- do
- {
- // Load 32 input bytes.
- Vector128 bytes1 = Sse3.LoadDquVector128(localBufferPtr);
- Vector128 bytes2 = Sse3.LoadDquVector128(localBufferPtr + 0x10);
-
- // Add previous block byte sum to v_ps.
- v_ps = Sse2.Add(v_ps, v_s1);
-
- // Horizontally add the bytes for s1, multiply-adds the
- // bytes by [ 32, 31, 30, ... ] for s2.
- v_s1 = Sse2.Add(v_s1, Sse2.SumAbsoluteDifferences(bytes1, zero).AsUInt32());
- Vector128 mad1 = Ssse3.MultiplyAddAdjacent(bytes1, tap1);
- v_s2 = Sse2.Add(v_s2, Sse2.MultiplyAddAdjacent(mad1, ones).AsUInt32());
-
- v_s1 = Sse2.Add(v_s1, Sse2.SumAbsoluteDifferences(bytes2, zero).AsUInt32());
- Vector128 mad2 = Ssse3.MultiplyAddAdjacent(bytes2, tap2);
- v_s2 = Sse2.Add(v_s2, Sse2.MultiplyAddAdjacent(mad2, ones).AsUInt32());
-
- localBufferPtr += BlockSize;
- }
- while (--n > 0);
-
- v_s2 = Sse2.Add(v_s2, Sse2.ShiftLeftLogical(v_ps, 5));
-
- // Sum epi32 ints v_s1(s2) and accumulate in s1(s2).
- const byte S2301 = 0b1011_0001; // A B C D -> B A D C
- const byte S1032 = 0b0100_1110; // A B C D -> C D A B
-
- v_s1 = Sse2.Add(v_s1, Sse2.Shuffle(v_s1, S1032));
-
- s1 += v_s1.ToScalar();
-
- v_s2 = Sse2.Add(v_s2, Sse2.Shuffle(v_s2, S2301));
- v_s2 = Sse2.Add(v_s2, Sse2.Shuffle(v_s2, S1032));
-
- s2 = v_s2.ToScalar();
-
- // Reduce.
- s1 %= BASE;
- s2 %= BASE;
- }
-
- if (length > 0)
- {
- HandleLeftOver(localBufferPtr, length, ref s1, ref s2);
- }
-
- return s1 | (s2 << 16);
- }
- }
- }
-
- // Based on: https://github.com/zlib-ng/zlib-ng/blob/develop/arch/x86/adler32_avx2.c
- [MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)]
- public static unsafe uint CalculateAvx2(uint adler, ReadOnlySpan buffer)
- {
- uint s1 = adler & 0xFFFF;
- uint s2 = (adler >> 16) & 0xFFFF;
- uint length = (uint)buffer.Length;
-
- fixed (byte* bufferPtr = &MemoryMarshal.GetReference(buffer))
+ fixed (byte* tapPtr = &MemoryMarshal.GetReference(Tap1Tap2))
{
byte* localBufferPtr = bufferPtr;
- Vector256 zero = Vector256.Zero;
- var dot3v = Vector256.Create((short)1);
- var dot2v = Vector256.Create(32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1);
+ // _mm_setr_epi8 on x86
+ Vector128 tap1 = Sse2.LoadVector128((sbyte*)tapPtr);
+ Vector128 tap2 = Sse2.LoadVector128((sbyte*)(tapPtr + 0x10));
+ Vector128 zero = Vector128.Zero;
+ var ones = Vector128.Create((short)1);
- // Process n blocks of data. At most NMAX data bytes can be
- // processed before s2 must be reduced modulo BASE.
- var vs1 = Vector256.CreateScalar(s1);
- var vs2 = Vector256.CreateScalar(s2);
-
- while (length >= 32)
+ while (blocks > 0)
{
- int k = length < NMAX ? (int)length : (int)NMAX;
- k -= k % 32;
- length -= (uint)k;
-
- Vector256 vs10 = vs1;
- Vector256 vs3 = Vector256.Zero;
-
- while (k >= 32)
+ uint n = NMAX / BlockSize; /* The NMAX constraint. */
+ if (n > blocks)
{
- // Load 32 input bytes.
- Vector256 block = Avx.LoadVector256(localBufferPtr);
-
- // Sum of abs diff, resulting in 2 x int32's
- Vector256 vs1sad = Avx2.SumAbsoluteDifferences(block, zero);
-
- vs1 = Avx2.Add(vs1, vs1sad.AsUInt32());
- vs3 = Avx2.Add(vs3, vs10);
-
- // sum 32 uint8s to 16 shorts.
- Vector256 vshortsum2 = Avx2.MultiplyAddAdjacent(block, dot2v);
-
- // sum 16 shorts to 8 uint32s.
- Vector256 vsum2 = Avx2.MultiplyAddAdjacent(vshortsum2, dot3v);
-
- vs2 = Avx2.Add(vsum2.AsUInt32(), vs2);
- vs10 = vs1;
-
- localBufferPtr += BlockSize;
- k -= 32;
+ n = blocks;
}
- // Defer the multiplication with 32 to outside of the loop.
- vs3 = Avx2.ShiftLeftLogical(vs3, 5);
- vs2 = Avx2.Add(vs2, vs3);
+ blocks -= n;
- s1 = (uint)Numerics.EvenReduceSum(vs1.AsInt32());
- s2 = (uint)Numerics.ReduceSum(vs2.AsInt32());
+ // Process n blocks of data. At most NMAX data bytes can be
+ // processed before s2 must be reduced modulo BASE.
+ Vector128 v_ps = Vector128.CreateScalar(s1 * n);
+ Vector128 v_s2 = Vector128.CreateScalar(s2);
+ Vector128 v_s1 = Vector128.Zero;
+ do
+ {
+ // Load 32 input bytes.
+ Vector128 bytes1 = Sse3.LoadDquVector128(localBufferPtr);
+ Vector128 bytes2 = Sse3.LoadDquVector128(localBufferPtr + 0x10);
+
+ // Add previous block byte sum to v_ps.
+ v_ps = Sse2.Add(v_ps, v_s1);
+
+ // Horizontally add the bytes for s1, multiply-adds the
+ // bytes by [ 32, 31, 30, ... ] for s2.
+ v_s1 = Sse2.Add(v_s1, Sse2.SumAbsoluteDifferences(bytes1, zero).AsUInt32());
+ Vector128 mad1 = Ssse3.MultiplyAddAdjacent(bytes1, tap1);
+ v_s2 = Sse2.Add(v_s2, Sse2.MultiplyAddAdjacent(mad1, ones).AsUInt32());
+
+ v_s1 = Sse2.Add(v_s1, Sse2.SumAbsoluteDifferences(bytes2, zero).AsUInt32());
+ Vector128 mad2 = Ssse3.MultiplyAddAdjacent(bytes2, tap2);
+ v_s2 = Sse2.Add(v_s2, Sse2.MultiplyAddAdjacent(mad2, ones).AsUInt32());
+
+ localBufferPtr += BlockSize;
+ } while (--n > 0);
+
+ v_s2 = Sse2.Add(v_s2, Sse2.ShiftLeftLogical(v_ps, 5));
+
+ // Sum epi32 ints v_s1(s2) and accumulate in s1(s2).
+ const byte S2301 = 0b1011_0001; // A B C D -> B A D C
+ const byte S1032 = 0b0100_1110; // A B C D -> C D A B
+
+ v_s1 = Sse2.Add(v_s1, Sse2.Shuffle(v_s1, S1032));
+
+ s1 += v_s1.ToScalar();
+
+ v_s2 = Sse2.Add(v_s2, Sse2.Shuffle(v_s2, S2301));
+ v_s2 = Sse2.Add(v_s2, Sse2.Shuffle(v_s2, S1032));
+
+ s2 = v_s2.ToScalar();
+
+ // Reduce.
s1 %= BASE;
s2 %= BASE;
-
- vs1 = Vector256.CreateScalar(s1);
- vs2 = Vector256.CreateScalar(s2);
}
if (length > 0)
@@ -324,97 +275,212 @@ namespace SharpCompress.Algorithms
return s1 | (s2 << 16);
}
}
+ }
- private static unsafe void HandleLeftOver(byte* localBufferPtr, uint length, ref uint s1, ref uint s2)
+ // Based on: https://github.com/zlib-ng/zlib-ng/blob/develop/arch/x86/adler32_avx2.c
+ [MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)]
+ public static unsafe uint CalculateAvx2(uint adler, ReadOnlySpan buffer)
+ {
+ uint s1 = adler & 0xFFFF;
+ uint s2 = (adler >> 16) & 0xFFFF;
+ uint length = (uint)buffer.Length;
+
+ fixed (byte* bufferPtr = &MemoryMarshal.GetReference(buffer))
{
- if (length >= 16)
+ byte* localBufferPtr = bufferPtr;
+
+ Vector256 zero = Vector256.Zero;
+ var dot3v = Vector256.Create((short)1);
+ var dot2v = Vector256.Create(
+ 32,
+ 31,
+ 30,
+ 29,
+ 28,
+ 27,
+ 26,
+ 25,
+ 24,
+ 23,
+ 22,
+ 21,
+ 20,
+ 19,
+ 18,
+ 17,
+ 16,
+ 15,
+ 14,
+ 13,
+ 12,
+ 11,
+ 10,
+ 9,
+ 8,
+ 7,
+ 6,
+ 5,
+ 4,
+ 3,
+ 2,
+ 1
+ );
+
+ // Process n blocks of data. At most NMAX data bytes can be
+ // processed before s2 must be reduced modulo BASE.
+ var vs1 = Vector256.CreateScalar(s1);
+ var vs2 = Vector256.CreateScalar(s2);
+
+ while (length >= 32)
{
- s2 += s1 += localBufferPtr[0];
- s2 += s1 += localBufferPtr[1];
- s2 += s1 += localBufferPtr[2];
- s2 += s1 += localBufferPtr[3];
- s2 += s1 += localBufferPtr[4];
- s2 += s1 += localBufferPtr[5];
- s2 += s1 += localBufferPtr[6];
- s2 += s1 += localBufferPtr[7];
- s2 += s1 += localBufferPtr[8];
- s2 += s1 += localBufferPtr[9];
- s2 += s1 += localBufferPtr[10];
- s2 += s1 += localBufferPtr[11];
- s2 += s1 += localBufferPtr[12];
- s2 += s1 += localBufferPtr[13];
- s2 += s1 += localBufferPtr[14];
- s2 += s1 += localBufferPtr[15];
+ int k = length < NMAX ? (int)length : (int)NMAX;
+ k -= k % 32;
+ length -= (uint)k;
- localBufferPtr += 16;
- length -= 16;
- }
+ Vector256 vs10 = vs1;
+ Vector256 vs3 = Vector256.Zero;
- while (length-- > 0)
- {
- s2 += s1 += *localBufferPtr++;
- }
-
- if (s1 >= BASE)
- {
- s1 -= BASE;
- }
-
- s2 %= BASE;
- }
-#endif
-
- [MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)]
- private static unsafe uint CalculateScalar(uint adler, ReadOnlySpan buffer)
- {
- uint s1 = adler & 0xFFFF;
- uint s2 = (adler >> 16) & 0xFFFF;
- uint k;
-
- fixed (byte* bufferPtr = buffer)
- {
- var localBufferPtr = bufferPtr;
- uint length = (uint)buffer.Length;
-
- while (length > 0)
+ while (k >= 32)
{
- k = length < NMAX ? length : NMAX;
- length -= k;
+ // Load 32 input bytes.
+ Vector256 block = Avx.LoadVector256(localBufferPtr);
- while (k >= 16)
- {
- s2 += s1 += localBufferPtr[0];
- s2 += s1 += localBufferPtr[1];
- s2 += s1 += localBufferPtr[2];
- s2 += s1 += localBufferPtr[3];
- s2 += s1 += localBufferPtr[4];
- s2 += s1 += localBufferPtr[5];
- s2 += s1 += localBufferPtr[6];
- s2 += s1 += localBufferPtr[7];
- s2 += s1 += localBufferPtr[8];
- s2 += s1 += localBufferPtr[9];
- s2 += s1 += localBufferPtr[10];
- s2 += s1 += localBufferPtr[11];
- s2 += s1 += localBufferPtr[12];
- s2 += s1 += localBufferPtr[13];
- s2 += s1 += localBufferPtr[14];
- s2 += s1 += localBufferPtr[15];
+ // Sum of abs diff, resulting in 2 x int32's
+ Vector256 vs1sad = Avx2.SumAbsoluteDifferences(block, zero);
- localBufferPtr += 16;
- k -= 16;
- }
+ vs1 = Avx2.Add(vs1, vs1sad.AsUInt32());
+ vs3 = Avx2.Add(vs3, vs10);
- while (k-- > 0)
- {
- s2 += s1 += *localBufferPtr++;
- }
+ // sum 32 uint8s to 16 shorts.
+ Vector256 vshortsum2 = Avx2.MultiplyAddAdjacent(block, dot2v);
- s1 %= BASE;
- s2 %= BASE;
+ // sum 16 shorts to 8 uint32s.
+ Vector256 vsum2 = Avx2.MultiplyAddAdjacent(vshortsum2, dot3v);
+
+ vs2 = Avx2.Add(vsum2.AsUInt32(), vs2);
+ vs10 = vs1;
+
+ localBufferPtr += BlockSize;
+ k -= 32;
}
- return (s2 << 16) | s1;
+ // Defer the multiplication with 32 to outside of the loop.
+ vs3 = Avx2.ShiftLeftLogical(vs3, 5);
+ vs2 = Avx2.Add(vs2, vs3);
+
+ s1 = (uint)Numerics.EvenReduceSum(vs1.AsInt32());
+ s2 = (uint)Numerics.ReduceSum(vs2.AsInt32());
+
+ s1 %= BASE;
+ s2 %= BASE;
+
+ vs1 = Vector256.CreateScalar(s1);
+ vs2 = Vector256.CreateScalar(s2);
}
+
+ if (length > 0)
+ {
+ HandleLeftOver(localBufferPtr, length, ref s1, ref s2);
+ }
+
+ return s1 | (s2 << 16);
+ }
+ }
+
+ private static unsafe void HandleLeftOver(
+ byte* localBufferPtr,
+ uint length,
+ ref uint s1,
+ ref uint s2
+ )
+ {
+ if (length >= 16)
+ {
+ s2 += s1 += localBufferPtr[0];
+ s2 += s1 += localBufferPtr[1];
+ s2 += s1 += localBufferPtr[2];
+ s2 += s1 += localBufferPtr[3];
+ s2 += s1 += localBufferPtr[4];
+ s2 += s1 += localBufferPtr[5];
+ s2 += s1 += localBufferPtr[6];
+ s2 += s1 += localBufferPtr[7];
+ s2 += s1 += localBufferPtr[8];
+ s2 += s1 += localBufferPtr[9];
+ s2 += s1 += localBufferPtr[10];
+ s2 += s1 += localBufferPtr[11];
+ s2 += s1 += localBufferPtr[12];
+ s2 += s1 += localBufferPtr[13];
+ s2 += s1 += localBufferPtr[14];
+ s2 += s1 += localBufferPtr[15];
+
+ localBufferPtr += 16;
+ length -= 16;
+ }
+
+ while (length-- > 0)
+ {
+ s2 += s1 += *localBufferPtr++;
+ }
+
+ if (s1 >= BASE)
+ {
+ s1 -= BASE;
+ }
+
+ s2 %= BASE;
+ }
+#endif
+
+ [MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)]
+ private static unsafe uint CalculateScalar(uint adler, ReadOnlySpan buffer)
+ {
+ uint s1 = adler & 0xFFFF;
+ uint s2 = (adler >> 16) & 0xFFFF;
+ uint k;
+
+ fixed (byte* bufferPtr = buffer)
+ {
+ var localBufferPtr = bufferPtr;
+ uint length = (uint)buffer.Length;
+
+ while (length > 0)
+ {
+ k = length < NMAX ? length : NMAX;
+ length -= k;
+
+ while (k >= 16)
+ {
+ s2 += s1 += localBufferPtr[0];
+ s2 += s1 += localBufferPtr[1];
+ s2 += s1 += localBufferPtr[2];
+ s2 += s1 += localBufferPtr[3];
+ s2 += s1 += localBufferPtr[4];
+ s2 += s1 += localBufferPtr[5];
+ s2 += s1 += localBufferPtr[6];
+ s2 += s1 += localBufferPtr[7];
+ s2 += s1 += localBufferPtr[8];
+ s2 += s1 += localBufferPtr[9];
+ s2 += s1 += localBufferPtr[10];
+ s2 += s1 += localBufferPtr[11];
+ s2 += s1 += localBufferPtr[12];
+ s2 += s1 += localBufferPtr[13];
+ s2 += s1 += localBufferPtr[14];
+ s2 += s1 += localBufferPtr[15];
+
+ localBufferPtr += 16;
+ k -= 16;
+ }
+
+ while (k-- > 0)
+ {
+ s2 += s1 += *localBufferPtr++;
+ }
+
+ s1 %= BASE;
+ s2 %= BASE;
+ }
+
+ return (s2 << 16) | s1;
}
}
}
diff --git a/src/SharpCompress/Archives/AbstractArchive.cs b/src/SharpCompress/Archives/AbstractArchive.cs
index b6c0ef76..cfac55f2 100644
--- a/src/SharpCompress/Archives/AbstractArchive.cs
+++ b/src/SharpCompress/Archives/AbstractArchive.cs
@@ -6,161 +6,168 @@ using SharpCompress.Common;
using SharpCompress.IO;
using SharpCompress.Readers;
-namespace SharpCompress.Archives
+namespace SharpCompress.Archives;
+
+public abstract class AbstractArchive : IArchive, IArchiveExtractionListener
+ where TEntry : IArchiveEntry
+ where TVolume : IVolume
{
- public abstract class AbstractArchive : IArchive, IArchiveExtractionListener
- where TEntry : IArchiveEntry
- where TVolume : IVolume
+ private readonly LazyReadOnlyCollection lazyVolumes;
+ private readonly LazyReadOnlyCollection lazyEntries;
+
+ public event EventHandler>? EntryExtractionBegin;
+ public event EventHandler>? EntryExtractionEnd;
+
+ public event EventHandler? CompressedBytesRead;
+ public event EventHandler? FilePartExtractionBegin;
+
+ protected ReaderOptions ReaderOptions { get; }
+
+ private bool disposed;
+ protected SourceStream SrcStream;
+
+ internal AbstractArchive(ArchiveType type, SourceStream srcStream)
{
- private readonly LazyReadOnlyCollection lazyVolumes;
- private readonly LazyReadOnlyCollection lazyEntries;
-
- public event EventHandler>? EntryExtractionBegin;
- public event EventHandler>? EntryExtractionEnd;
-
- public event EventHandler? CompressedBytesRead;
- public event EventHandler? FilePartExtractionBegin;
-
- protected ReaderOptions ReaderOptions { get; }
-
- private bool disposed;
- protected SourceStream SrcStream;
-
- internal AbstractArchive(ArchiveType type, SourceStream srcStream)
- {
- Type = type;
- ReaderOptions = srcStream.ReaderOptions;
- SrcStream = srcStream;
- lazyVolumes = new LazyReadOnlyCollection(LoadVolumes(SrcStream));
- lazyEntries = new LazyReadOnlyCollection(LoadEntries(Volumes));
- }
+ Type = type;
+ ReaderOptions = srcStream.ReaderOptions;
+ SrcStream = srcStream;
+ lazyVolumes = new LazyReadOnlyCollection(LoadVolumes(SrcStream));
+ lazyEntries = new LazyReadOnlyCollection(LoadEntries(Volumes));
+ }
#nullable disable
- internal AbstractArchive(ArchiveType type)
- {
- Type = type;
- lazyVolumes = new LazyReadOnlyCollection(Enumerable.Empty());
- lazyEntries = new LazyReadOnlyCollection(Enumerable.Empty());
- }
+ internal AbstractArchive(ArchiveType type)
+ {
+ Type = type;
+ lazyVolumes = new LazyReadOnlyCollection(Enumerable.Empty());
+ lazyEntries = new LazyReadOnlyCollection(Enumerable.Empty());
+ }
+
#nullable enable
- public ArchiveType Type { get; }
+ public ArchiveType Type { get; }
- void IArchiveExtractionListener.FireEntryExtractionBegin(IArchiveEntry entry)
+ void IArchiveExtractionListener.FireEntryExtractionBegin(IArchiveEntry entry) =>
+ EntryExtractionBegin?.Invoke(this, new ArchiveExtractionEventArgs(entry));
+
+ void IArchiveExtractionListener.FireEntryExtractionEnd(IArchiveEntry entry) =>
+ EntryExtractionEnd?.Invoke(this, new ArchiveExtractionEventArgs(entry));
+
+ private static Stream CheckStreams(Stream stream)
+ {
+ if (!stream.CanSeek || !stream.CanRead)
{
- EntryExtractionBegin?.Invoke(this, new ArchiveExtractionEventArgs(entry));
+ throw new ArgumentException("Archive streams must be Readable and Seekable");
}
+ return stream;
+ }
- void IArchiveExtractionListener.FireEntryExtractionEnd(IArchiveEntry entry)
+ ///
+ /// Returns an ReadOnlyCollection of all the RarArchiveEntries across the one or many parts of the RarArchive.
+ ///
+ public virtual ICollection Entries => lazyEntries;
+
+ ///
+ /// Returns an ReadOnlyCollection of all the RarArchiveVolumes across the one or many parts of the RarArchive.
+ ///
+ public ICollection Volumes => lazyVolumes;
+
+ ///
+ /// The total size of the files compressed in the archive.
+ ///
+ public virtual long TotalSize =>
+ Entries.Aggregate(0L, (total, cf) => total + cf.CompressedSize);
+
+ ///
+ /// The total size of the files as uncompressed in the archive.
+ ///
+ public virtual long TotalUncompressSize =>
+ Entries.Aggregate(0L, (total, cf) => total + cf.Size);
+
+ protected abstract IEnumerable LoadVolumes(SourceStream srcStream);
+ protected abstract IEnumerable LoadEntries(IEnumerable volumes);
+
+ IEnumerable IArchive.Entries => Entries.Cast();
+
+ IEnumerable IArchive.Volumes => lazyVolumes.Cast();
+
+ public virtual void Dispose()
+ {
+ if (!disposed)
{
- EntryExtractionEnd?.Invoke(this, new ArchiveExtractionEventArgs(entry));
+ lazyVolumes.ForEach(v => v.Dispose());
+ lazyEntries.GetLoaded().Cast().ForEach(x => x.Close());
+ SrcStream?.Dispose();
+
+ disposed = true;
}
+ }
- private static Stream CheckStreams(Stream stream)
- {
- if (!stream.CanSeek || !stream.CanRead)
- {
- throw new ArgumentException("Archive streams must be Readable and Seekable");
- }
- return stream;
- }
+ void IArchiveExtractionListener.EnsureEntriesLoaded()
+ {
+ lazyEntries.EnsureFullyLoaded();
+ lazyVolumes.EnsureFullyLoaded();
+ }
- ///
- /// Returns an ReadOnlyCollection of all the RarArchiveEntries across the one or many parts of the RarArchive.
- ///
- public virtual ICollection Entries => lazyEntries;
-
- ///
- /// Returns an ReadOnlyCollection of all the RarArchiveVolumes across the one or many parts of the RarArchive.
- ///
- public ICollection Volumes => lazyVolumes;
-
- ///
- /// The total size of the files compressed in the archive.
- ///
- public virtual long TotalSize => Entries.Aggregate(0L, (total, cf) => total + cf.CompressedSize);
-
- ///
- /// The total size of the files as uncompressed in the archive.
- ///
- public virtual long TotalUncompressSize => Entries.Aggregate(0L, (total, cf) => total + cf.Size);
-
- protected abstract IEnumerable LoadVolumes(SourceStream srcStream);
- protected abstract IEnumerable LoadEntries(IEnumerable volumes);
-
- IEnumerable IArchive.Entries => Entries.Cast();
-
- IEnumerable IArchive.Volumes => lazyVolumes.Cast();
-
- public virtual void Dispose()
- {
- if (!disposed)
- {
- lazyVolumes.ForEach(v => v.Dispose());
- lazyEntries.GetLoaded().Cast().ForEach(x => x.Close());
- if (SrcStream != null)
- SrcStream.Dispose();
- disposed = true;
- }
- }
-
- void IArchiveExtractionListener.EnsureEntriesLoaded()
- {
- lazyEntries.EnsureFullyLoaded();
- lazyVolumes.EnsureFullyLoaded();
- }
-
- void IExtractionListener.FireCompressedBytesRead(long currentPartCompressedBytes, long compressedReadBytes)
- {
- CompressedBytesRead?.Invoke(this, new CompressedBytesReadEventArgs(
+ void IExtractionListener.FireCompressedBytesRead(
+ long currentPartCompressedBytes,
+ long compressedReadBytes
+ ) =>
+ CompressedBytesRead?.Invoke(
+ this,
+ new CompressedBytesReadEventArgs(
currentFilePartCompressedBytesRead: currentPartCompressedBytes,
compressedBytesRead: compressedReadBytes
- ));
- }
+ )
+ );
- void IExtractionListener.FireFilePartExtractionBegin(string name, long size, long compressedSize)
- {
- FilePartExtractionBegin?.Invoke(this, new FilePartExtractionBeginEventArgs(
+ void IExtractionListener.FireFilePartExtractionBegin(
+ string name,
+ long size,
+ long compressedSize
+ ) =>
+ FilePartExtractionBegin?.Invoke(
+ this,
+ new FilePartExtractionBeginEventArgs(
compressedSize: compressedSize,
size: size,
name: name
- ));
- }
+ )
+ );
- ///
- /// Use this method to extract all entries in an archive in order.
- /// This is primarily for SOLID Rar Archives or 7Zip Archives as they need to be
- /// extracted sequentially for the best performance.
- ///
- /// This method will load all entry information from the archive.
- ///
- /// WARNING: this will reuse the underlying stream for the archive. Errors may
- /// occur if this is used at the same time as other extraction methods on this instance.
- ///
- ///
- public IReader ExtractAllEntries()
+ ///
+ /// Use this method to extract all entries in an archive in order.
+ /// This is primarily for SOLID Rar Archives or 7Zip Archives as they need to be
+ /// extracted sequentially for the best performance.
+ ///
+ /// This method will load all entry information from the archive.
+ ///
+ /// WARNING: this will reuse the underlying stream for the archive. Errors may
+ /// occur if this is used at the same time as other extraction methods on this instance.
+ ///
+ ///
+ public IReader ExtractAllEntries()
+ {
+ ((IArchiveExtractionListener)this).EnsureEntriesLoaded();
+ return CreateReaderForSolidExtraction();
+ }
+
+ protected abstract IReader CreateReaderForSolidExtraction();
+
+ ///
+ /// Archive is SOLID (this means the Archive saved bytes by reusing information which helps for archives containing many small files).
+ ///
+ public virtual bool IsSolid => false;
+
+ ///
+ /// The archive can find all the parts of the archive needed to fully extract the archive. This forces the parsing of the entire archive.
+ ///
+ public bool IsComplete
+ {
+ get
{
((IArchiveExtractionListener)this).EnsureEntriesLoaded();
- return CreateReaderForSolidExtraction();
- }
-
- protected abstract IReader CreateReaderForSolidExtraction();
-
- ///
- /// Archive is SOLID (this means the Archive saved bytes by reusing information which helps for archives containing many small files).
- ///
- public virtual bool IsSolid => false;
-
- ///
- /// The archive can find all the parts of the archive needed to fully extract the archive. This forces the parsing of the entire archive.
- ///
- public bool IsComplete
- {
- get
- {
- ((IArchiveExtractionListener)this).EnsureEntriesLoaded();
- return Entries.All(x => x.IsComplete);
- }
+ return Entries.All(x => x.IsComplete);
}
}
}
diff --git a/src/SharpCompress/Archives/AbstractWritableArchive.cs b/src/SharpCompress/Archives/AbstractWritableArchive.cs
index 42a8213e..cf97aff8 100644
--- a/src/SharpCompress/Archives/AbstractWritableArchive.cs
+++ b/src/SharpCompress/Archives/AbstractWritableArchive.cs
@@ -4,163 +4,175 @@ using System.IO;
using System.Linq;
using SharpCompress.Common;
using SharpCompress.IO;
-using SharpCompress.Readers;
using SharpCompress.Writers;
-namespace SharpCompress.Archives
+namespace SharpCompress.Archives;
+
+public abstract class AbstractWritableArchive
+ : AbstractArchive,
+ IWritableArchive
+ where TEntry : IArchiveEntry
+ where TVolume : IVolume
{
- public abstract class AbstractWritableArchive : AbstractArchive, IWritableArchive
- where TEntry : IArchiveEntry
- where TVolume : IVolume
+ private class RebuildPauseDisposable : IDisposable
{
- private class RebuildPauseDisposable : IDisposable
- {
- private readonly AbstractWritableArchive archive;
-
- public RebuildPauseDisposable(AbstractWritableArchive archive)
- {
- this.archive = archive;
- archive.pauseRebuilding = true;
- }
-
- public void Dispose()
- {
- archive.pauseRebuilding = false;
- archive.RebuildModifiedCollection();
- }
- }
- private readonly List newEntries = new List();
- private readonly List removedEntries = new List();
-
- private readonly List modifiedEntries = new List();
- private bool hasModifications;
- private bool pauseRebuilding;
-
- internal AbstractWritableArchive(ArchiveType type)
- : base(type)
+ private readonly AbstractWritableArchive archive;
+
+ public RebuildPauseDisposable(AbstractWritableArchive archive)
{
+ this.archive = archive;
+ archive.pauseRebuilding = true;
}
- internal AbstractWritableArchive(ArchiveType type, SourceStream srcStream)
- : base(type, srcStream)
+ public void Dispose()
{
- }
-
- public override ICollection Entries
- {
- get
- {
- if (hasModifications)
- {
- return modifiedEntries;
- }
- return base.Entries;
- }
- }
-
- public IDisposable PauseEntryRebuilding()
- {
- return new RebuildPauseDisposable(this);
- }
-
- private void RebuildModifiedCollection()
- {
- if (pauseRebuilding)
- {
- return;
- }
- hasModifications = true;
- newEntries.RemoveAll(v => removedEntries.Contains(v));
- modifiedEntries.Clear();
- modifiedEntries.AddRange(OldEntries.Concat(newEntries));
- }
-
- private IEnumerable OldEntries { get { return base.Entries.Where(x => !removedEntries.Contains(x)); } }
-
- public void RemoveEntry(TEntry entry)
- {
- if (!removedEntries.Contains(entry))
- {
- removedEntries.Add(entry);
- RebuildModifiedCollection();
- }
- }
-
- void IWritableArchive.RemoveEntry(IArchiveEntry entry)
- {
- RemoveEntry((TEntry)entry);
- }
-
- public TEntry AddEntry(string key, Stream source,
- long size = 0, DateTime? modified = null)
- {
- return AddEntry(key, source, false, size, modified);
- }
-
- IArchiveEntry IWritableArchive.AddEntry(string key, Stream source, bool closeStream, long size, DateTime? modified)
- {
- return AddEntry(key, source, closeStream, size, modified);
- }
-
- public TEntry AddEntry(string key, Stream source, bool closeStream,
- long size = 0, DateTime? modified = null)
- {
- if (key.Length > 0 && key[0] is '/' or '\\')
- {
- key = key.Substring(1);
- }
- if (DoesKeyMatchExisting(key))
- {
- throw new ArchiveException("Cannot add entry with duplicate key: " + key);
- }
- var entry = CreateEntry(key, source, size, modified, closeStream);
- newEntries.Add(entry);
- RebuildModifiedCollection();
- return entry;
- }
-
- private bool DoesKeyMatchExisting(string key)
- {
- foreach (var path in Entries.Select(x => x.Key))
- {
- var p = path.Replace('/', '\\');
- if (p.Length > 0 && p[0] == '\\')
- {
- p = p.Substring(1);
- }
- return string.Equals(p, key, StringComparison.OrdinalIgnoreCase);
- }
- return false;
- }
-
- public void SaveTo(Stream stream, WriterOptions options)
- {
- //reset streams of new entries
- newEntries.Cast().ForEach(x => x.Stream.Seek(0, SeekOrigin.Begin));
- SaveTo(stream, options, OldEntries, newEntries);
- }
-
- protected TEntry CreateEntry(string key, Stream source, long size, DateTime? modified,
- bool closeStream)
- {
- if (!source.CanRead || !source.CanSeek)
- {
- throw new ArgumentException("Streams must be readable and seekable to use the Writing Archive API");
- }
- return CreateEntryInternal(key, source, size, modified, closeStream);
- }
-
- protected abstract TEntry CreateEntryInternal(string key, Stream source, long size, DateTime? modified,
- bool closeStream);
-
- protected abstract void SaveTo(Stream stream, WriterOptions options, IEnumerable oldEntries, IEnumerable newEntries);
-
- public override void Dispose()
- {
- base.Dispose();
- newEntries.Cast().ForEach(x => x.Close());
- removedEntries.Cast().ForEach(x => x.Close());
- modifiedEntries.Cast().ForEach(x => x.Close());
+ archive.pauseRebuilding = false;
+ archive.RebuildModifiedCollection();
}
}
+
+ private readonly List newEntries = new List();
+ private readonly List removedEntries = new List();
+
+ private readonly List modifiedEntries = new List();
+ private bool hasModifications;
+ private bool pauseRebuilding;
+
+ internal AbstractWritableArchive(ArchiveType type) : base(type) { }
+
+ internal AbstractWritableArchive(ArchiveType type, SourceStream srcStream)
+ : base(type, srcStream) { }
+
+ public override ICollection Entries
+ {
+ get
+ {
+ if (hasModifications)
+ {
+ return modifiedEntries;
+ }
+ return base.Entries;
+ }
+ }
+
+ public IDisposable PauseEntryRebuilding() => new RebuildPauseDisposable(this);
+
+ private void RebuildModifiedCollection()
+ {
+ if (pauseRebuilding)
+ {
+ return;
+ }
+ hasModifications = true;
+ newEntries.RemoveAll(v => removedEntries.Contains(v));
+ modifiedEntries.Clear();
+ modifiedEntries.AddRange(OldEntries.Concat(newEntries));
+ }
+
+ private IEnumerable OldEntries => base.Entries.Where(x => !removedEntries.Contains(x));
+
+ public void RemoveEntry(TEntry entry)
+ {
+ if (!removedEntries.Contains(entry))
+ {
+ removedEntries.Add(entry);
+ RebuildModifiedCollection();
+ }
+ }
+
+ void IWritableArchive.RemoveEntry(IArchiveEntry entry) => RemoveEntry((TEntry)entry);
+
+ public TEntry AddEntry(string key, Stream source, long size = 0, DateTime? modified = null) =>
+ AddEntry(key, source, false, size, modified);
+
+ IArchiveEntry IWritableArchive.AddEntry(
+ string key,
+ Stream source,
+ bool closeStream,
+ long size,
+ DateTime? modified
+ ) => AddEntry(key, source, closeStream, size, modified);
+
+ public TEntry AddEntry(
+ string key,
+ Stream source,
+ bool closeStream,
+ long size = 0,
+ DateTime? modified = null
+ )
+ {
+ if (key.Length > 0 && key[0] is '/' or '\\')
+ {
+ key = key.Substring(1);
+ }
+ if (DoesKeyMatchExisting(key))
+ {
+ throw new ArchiveException("Cannot add entry with duplicate key: " + key);
+ }
+ var entry = CreateEntry(key, source, size, modified, closeStream);
+ newEntries.Add(entry);
+ RebuildModifiedCollection();
+ return entry;
+ }
+
+ private bool DoesKeyMatchExisting(string key)
+ {
+ foreach (var path in Entries.Select(x => x.Key))
+ {
+ var p = path.Replace('/', '\\');
+ if (p.Length > 0 && p[0] == '\\')
+ {
+ p = p.Substring(1);
+ }
+ return string.Equals(p, key, StringComparison.OrdinalIgnoreCase);
+ }
+ return false;
+ }
+
+ public void SaveTo(Stream stream, WriterOptions options)
+ {
+ //reset streams of new entries
+ newEntries.Cast().ForEach(x => x.Stream.Seek(0, SeekOrigin.Begin));
+ SaveTo(stream, options, OldEntries, newEntries);
+ }
+
+ protected TEntry CreateEntry(
+ string key,
+ Stream source,
+ long size,
+ DateTime? modified,
+ bool closeStream
+ )
+ {
+ if (!source.CanRead || !source.CanSeek)
+ {
+ throw new ArgumentException(
+ "Streams must be readable and seekable to use the Writing Archive API"
+ );
+ }
+ return CreateEntryInternal(key, source, size, modified, closeStream);
+ }
+
+ protected abstract TEntry CreateEntryInternal(
+ string key,
+ Stream source,
+ long size,
+ DateTime? modified,
+ bool closeStream
+ );
+
+ protected abstract void SaveTo(
+ Stream stream,
+ WriterOptions options,
+ IEnumerable oldEntries,
+ IEnumerable newEntries
+ );
+
+ public override void Dispose()
+ {
+ base.Dispose();
+ newEntries.Cast().ForEach(x => x.Close());
+ removedEntries.Cast().ForEach(x => x.Close());
+ modifiedEntries.Cast().ForEach(x => x.Close());
+ }
}
diff --git a/src/SharpCompress/Archives/ArchiveFactory.cs b/src/SharpCompress/Archives/ArchiveFactory.cs
index 71c0358e..6b489fa1 100644
--- a/src/SharpCompress/Archives/ArchiveFactory.cs
+++ b/src/SharpCompress/Archives/ArchiveFactory.cs
@@ -2,232 +2,238 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using SharpCompress.Archives.GZip;
-using SharpCompress.Archives.Rar;
-using SharpCompress.Archives.SevenZip;
-using SharpCompress.Archives.Tar;
-using SharpCompress.Archives.Zip;
using SharpCompress.Common;
using SharpCompress.Factories;
-using SharpCompress.IO;
using SharpCompress.Readers;
-namespace SharpCompress.Archives
+namespace SharpCompress.Archives;
+
+public static class ArchiveFactory
{
- public static class ArchiveFactory
+ ///
+ /// Opens an Archive for random access
+ ///
+ ///
+ ///
+ ///
+ public static IArchive Open(Stream stream, ReaderOptions? readerOptions = null)
{
- ///
- /// Opens an Archive for random access
- ///
- ///
- ///
- ///
- public static IArchive Open(Stream stream, ReaderOptions? readerOptions = null)
- {
- readerOptions ??= new ReaderOptions();
+ readerOptions ??= new ReaderOptions();
- return FindFactory(stream).Open(stream, readerOptions);
+ return FindFactory(stream).Open(stream, readerOptions);
+ }
+
+ public static IWritableArchive Create(ArchiveType type)
+ {
+ var factory = Factory.Factories
+ .OfType()
+ .FirstOrDefault(item => item.KnownArchiveType == type);
+
+ if (factory != null)
+ {
+ return factory.CreateWriteableArchive();
}
- public static IWritableArchive Create(ArchiveType type)
+ throw new NotSupportedException("Cannot create Archives of type: " + type);
+ }
+
+ ///
+ /// Constructor expects a filepath to an existing file.
+ ///
+ ///
+ ///
+ public static IArchive Open(string filePath, ReaderOptions? options = null)
+ {
+ filePath.CheckNotNullOrEmpty(nameof(filePath));
+ return Open(new FileInfo(filePath), options);
+ }
+
+ ///
+ /// Constructor with a FileInfo object to an existing file.
+ ///
+ ///
+ ///
+ public static IArchive Open(FileInfo fileInfo, ReaderOptions? options = null)
+ {
+ options ??= new ReaderOptions { LeaveStreamOpen = false };
+
+ return FindFactory(fileInfo).Open(fileInfo, options);
+ }
+
+ ///
+ /// Constructor with IEnumerable FileInfo objects, multi and split support.
+ ///
+ ///
+ ///
+ public static IArchive Open(IEnumerable fileInfos, ReaderOptions? options = null)
+ {
+ fileInfos.CheckNotNull(nameof(fileInfos));
+ var filesArray = fileInfos.ToArray();
+ if (filesArray.Length == 0)
{
- var factory = Factories.Factory
- .Factories
- .OfType()
- .Where(item => item.KnownArchiveType == type)
- .FirstOrDefault();
-
- if (factory != null)
- return factory.CreateWriteableArchive();
-
- throw new NotSupportedException("Cannot create Archives of type: " + type);
+ throw new InvalidOperationException("No files to open");
}
- ///
- /// Constructor expects a filepath to an existing file.
- ///
- ///
- ///
- public static IArchive Open(string filePath, ReaderOptions? options = null)
+ var fileInfo = filesArray[0];
+ if (filesArray.Length == 1)
{
- filePath.CheckNotNullOrEmpty(nameof(filePath));
- return Open(new FileInfo(filePath), options);
+ return Open(fileInfo, options);
}
- ///
- /// Constructor with a FileInfo object to an existing file.
- ///
- ///
- ///
- public static IArchive Open(FileInfo fileInfo, ReaderOptions? options = null)
- {
- options ??= new ReaderOptions { LeaveStreamOpen = false };
+ fileInfo.CheckNotNull(nameof(fileInfo));
+ options ??= new ReaderOptions { LeaveStreamOpen = false };
- return FindFactory(fileInfo).Open(fileInfo, options);
+ return FindFactory(fileInfo).Open(filesArray, options);
+ }
+
+ ///
+ /// Constructor with IEnumerable FileInfo objects, multi and split support.
+ ///
+ ///
+ ///
+ public static IArchive Open(IEnumerable streams, ReaderOptions? options = null)
+ {
+ streams.CheckNotNull(nameof(streams));
+ var streamsArray = streams.ToArray();
+ if (streamsArray.Length == 0)
+ {
+ throw new InvalidOperationException("No streams");
}
- ///
- /// Constructor with IEnumerable FileInfo objects, multi and split support.
- ///
- ///
- ///
- public static IArchive Open(IEnumerable fileInfos, ReaderOptions? options = null)
+ var firstStream = streamsArray[0];
+ if (streamsArray.Length == 1)
{
- fileInfos.CheckNotNull(nameof(fileInfos));
- FileInfo[] filesArray = fileInfos.ToArray();
- if (filesArray.Length == 0)
- throw new InvalidOperationException("No files to open");
- FileInfo fileInfo = filesArray[0];
- if (filesArray.Length == 1)
- return Open(fileInfo, options);
-
- fileInfo.CheckNotNull(nameof(fileInfo));
- options ??= new ReaderOptions { LeaveStreamOpen = false };
-
- return FindFactory(fileInfo).Open(filesArray, options);
+ return Open(firstStream, options);
}
- ///
- /// Constructor with IEnumerable FileInfo objects, multi and split support.
- ///
- ///
- ///
- public static IArchive Open(IEnumerable streams, ReaderOptions? options = null)
+ firstStream.CheckNotNull(nameof(firstStream));
+ options ??= new ReaderOptions();
+
+ return FindFactory(firstStream).Open(streamsArray, options);
+ }
+
+ ///
+ /// Extract to specific directory, retaining filename
+ ///
+ public static void WriteToDirectory(
+ string sourceArchive,
+ string destinationDirectory,
+ ExtractionOptions? options = null
+ )
+ {
+ using var archive = Open(sourceArchive);
+ foreach (var entry in archive.Entries)
{
- streams.CheckNotNull(nameof(streams));
- Stream[] streamsArray = streams.ToArray();
- if (streamsArray.Length == 0)
- throw new InvalidOperationException("No streams");
- Stream firstStream = streamsArray[0];
- if (streamsArray.Length == 1)
- return Open(firstStream, options);
+ entry.WriteToDirectory(destinationDirectory, options);
+ }
+ }
- firstStream.CheckNotNull(nameof(firstStream));
- options ??= new ReaderOptions();
+ private static T FindFactory(FileInfo finfo) where T : IFactory
+ {
+ finfo.CheckNotNull(nameof(finfo));
+ using Stream stream = finfo.OpenRead();
+ return FindFactory(stream);
+ }
- return FindFactory(firstStream).Open(streamsArray, options);
- }
-
- ///
- /// Extract to specific directory, retaining filename
- ///
- public static void WriteToDirectory(string sourceArchive, string destinationDirectory,
- ExtractionOptions? options = null)
+ private static T FindFactory(Stream stream) where T : IFactory
+ {
+ stream.CheckNotNull(nameof(stream));
+ if (!stream.CanRead || !stream.CanSeek)
{
- using IArchive archive = Open(sourceArchive);
- foreach (IArchiveEntry entry in archive.Entries)
- {
- entry.WriteToDirectory(destinationDirectory, options);
- }
+ throw new ArgumentException("Stream should be readable and seekable");
}
- private static T FindFactory(FileInfo finfo)
- where T : IFactory
+ var factories = Factory.Factories.OfType();
+
+ var startPosition = stream.Position;
+
+ foreach (var factory in factories)
{
- finfo.CheckNotNull(nameof(finfo));
- using (Stream stream = finfo.OpenRead())
- {
- return FindFactory(stream);
- }
- }
+ stream.Seek(startPosition, SeekOrigin.Begin);
- private static T FindFactory(Stream stream)
- where T: IFactory
- {
- stream.CheckNotNull(nameof(stream));
- if (!stream.CanRead || !stream.CanSeek)
- {
- throw new ArgumentException("Stream should be readable and seekable");
- }
-
- var factories = Factories.Factory.Factories.OfType();
-
- long startPosition = stream.Position;
-
- foreach (var factory in factories)
+ if (factory.IsArchive(stream))
{
stream.Seek(startPosition, SeekOrigin.Begin);
- if (factory.IsArchive(stream))
- {
- stream.Seek(startPosition, SeekOrigin.Begin);
-
- return factory;
- }
+ return factory;
}
-
- var extensions = string.Join(", ", factories.Select(item => item.Name));
-
- throw new InvalidOperationException($"Cannot determine compressed stream type. Supported Archive Formats: {extensions}");
}
- public static bool IsArchive(string filePath, out ArchiveType? type)
+ var extensions = string.Join(", ", factories.Select(item => item.Name));
+
+ throw new InvalidOperationException(
+ $"Cannot determine compressed stream type. Supported Archive Formats: {extensions}"
+ );
+ }
+
+ public static bool IsArchive(string filePath, out ArchiveType? type)
+ {
+ filePath.CheckNotNullOrEmpty(nameof(filePath));
+ using Stream s = File.OpenRead(filePath);
+ return IsArchive(s, out type);
+ }
+
+ private static bool IsArchive(Stream stream, out ArchiveType? type)
+ {
+ type = null;
+ stream.CheckNotNull(nameof(stream));
+
+ if (!stream.CanRead || !stream.CanSeek)
{
- filePath.CheckNotNullOrEmpty(nameof(filePath));
- using (Stream s = File.OpenRead(filePath))
- return IsArchive(s, out type);
+ throw new ArgumentException("Stream should be readable and seekable");
}
- private static bool IsArchive(Stream stream, out ArchiveType? type)
- {
- type = null;
- stream.CheckNotNull(nameof(stream));
+ var startPosition = stream.Position;
- if (!stream.CanRead || !stream.CanSeek)
+ foreach (var factory in Factory.Factories)
+ {
+ stream.Position = startPosition;
+
+ if (factory.IsArchive(stream, null))
{
- throw new ArgumentException("Stream should be readable and seekable");
+ type = factory.KnownArchiveType;
+ return true;
}
-
- var startPosition = stream.Position;
-
- foreach(var factory in Factories.Factory.Factories)
- {
- stream.Position = startPosition;
-
- if (factory.IsArchive(stream, null))
- {
- type = factory.KnownArchiveType;
- return true;
- }
- }
-
- return false;
}
- ///
- /// From a passed in archive (zip, rar, 7z, 001), return all parts.
- ///
- ///
- ///
- public static IEnumerable GetFileParts(string part1)
- {
- part1.CheckNotNullOrEmpty(nameof(part1));
- return GetFileParts(new FileInfo(part1)).Select(a => a.FullName);
- }
+ return false;
+ }
- ///
- /// From a passed in archive (zip, rar, 7z, 001), return all parts.
- ///
- ///
- ///
- public static IEnumerable GetFileParts(FileInfo part1)
- {
- part1.CheckNotNull(nameof(part1));
- yield return part1;
+ ///
+ /// From a passed in archive (zip, rar, 7z, 001), return all parts.
+ ///
+ ///
+ ///
+ public static IEnumerable GetFileParts(string part1)
+ {
+ part1.CheckNotNullOrEmpty(nameof(part1));
+ return GetFileParts(new FileInfo(part1)).Select(a => a.FullName);
+ }
- foreach(var factory in Factory.Factories.OfType())
+ ///
+ /// From a passed in archive (zip, rar, 7z, 001), return all parts.
+ ///
+ ///
+ ///
+ public static IEnumerable GetFileParts(FileInfo part1)
+ {
+ part1.CheckNotNull(nameof(part1));
+ yield return part1;
+
+ foreach (var factory in Factory.Factories.OfType())
+ {
+ var i = 1;
+ var part = factory.GetFilePart(i++, part1);
+
+ if (part != null)
{
- int i = 1;
- FileInfo? part = factory.GetFilePart(i++, part1);
-
- if (part != null)
+ yield return part;
+ while ((part = factory.GetFilePart(i++, part1)) != null) //tests split too
{
yield return part;
- while ((part = factory.GetFilePart(i++, part1)) != null) //tests split too
- yield return part;
-
- yield break;
}
+
+ yield break;
}
}
}
diff --git a/src/SharpCompress/Archives/ArchiveVolumeFactory.cs b/src/SharpCompress/Archives/ArchiveVolumeFactory.cs
index 09315b40..d8f5535f 100644
--- a/src/SharpCompress/Archives/ArchiveVolumeFactory.cs
+++ b/src/SharpCompress/Archives/ArchiveVolumeFactory.cs
@@ -1,29 +1,30 @@
using System;
-using System.Collections.Generic;
using System.IO;
-using SharpCompress.Readers;
-using System.Linq;
-using System.Text;
-using SharpCompress.Common.Rar.Headers;
using System.Text.RegularExpressions;
-namespace SharpCompress.Archives
+namespace SharpCompress.Archives;
+
+internal abstract class ArchiveVolumeFactory
{
- internal abstract class ArchiveVolumeFactory
+ internal static FileInfo? GetFilePart(int index, FileInfo part1) //base the name on the first part
{
- internal static FileInfo? GetFilePart(int index, FileInfo part1) //base the name on the first part
- {
- FileInfo? item = null;
+ FileInfo? item = null;
- //split 001, 002 ...
- Match m = Regex.Match(part1.Name, @"^(.*\.)([0-9]+)$", RegexOptions.IgnoreCase);
- if (m.Success)
- item = new FileInfo(Path.Combine(part1.DirectoryName!, String.Concat(m.Groups[1].Value, (index + 1).ToString().PadLeft(m.Groups[2].Value.Length, '0'))));
-
- if (item != null && item.Exists)
- return item;
- return null;
- }
+ //split 001, 002 ...
+ Match m = Regex.Match(part1.Name, @"^(.*\.)([0-9]+)$", RegexOptions.IgnoreCase);
+ if (m.Success)
+ item = new FileInfo(
+ Path.Combine(
+ part1.DirectoryName!,
+ String.Concat(
+ m.Groups[1].Value,
+ (index + 1).ToString().PadLeft(m.Groups[2].Value.Length, '0')
+ )
+ )
+ );
+ if (item != null && item.Exists)
+ return item;
+ return null;
}
}
diff --git a/src/SharpCompress/Archives/GZip/GZipArchive.cs b/src/SharpCompress/Archives/GZip/GZipArchive.cs
index 845e05fb..eea56db0 100644
--- a/src/SharpCompress/Archives/GZip/GZipArchive.cs
+++ b/src/SharpCompress/Archives/GZip/GZipArchive.cs
@@ -10,183 +10,195 @@ using SharpCompress.Readers.GZip;
using SharpCompress.Writers;
using SharpCompress.Writers.GZip;
-namespace SharpCompress.Archives.GZip
+namespace SharpCompress.Archives.GZip;
+
+public class GZipArchive : AbstractWritableArchive
{
- public class GZipArchive : AbstractWritableArchive
+ ///
+ /// Constructor expects a filepath to an existing file.
+ ///
+ ///
+ ///
+ public static GZipArchive Open(string filePath, ReaderOptions? readerOptions = null)
{
- ///
- /// Constructor expects a filepath to an existing file.
- ///
- ///
- ///
- public static GZipArchive Open(string filePath, ReaderOptions? readerOptions = null)
+ filePath.CheckNotNullOrEmpty(nameof(filePath));
+ return Open(new FileInfo(filePath), readerOptions ?? new ReaderOptions());
+ }
+
+ ///
+ /// Constructor with a FileInfo object to an existing file.
+ ///
+ ///
+ ///
+ public static GZipArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null)
+ {
+ fileInfo.CheckNotNull(nameof(fileInfo));
+ return new GZipArchive(
+ new SourceStream(
+ fileInfo,
+ i => ArchiveVolumeFactory.GetFilePart(i, fileInfo),
+ readerOptions ?? new ReaderOptions()
+ )
+ );
+ }
+
+ ///
+ /// Constructor with all file parts passed in
+ ///
+ ///
+ ///
+ public static GZipArchive Open(
+ IEnumerable fileInfos,
+ ReaderOptions? readerOptions = null
+ )
+ {
+ fileInfos.CheckNotNull(nameof(fileInfos));
+ var files = fileInfos.ToArray();
+ return new GZipArchive(
+ new SourceStream(
+ files[0],
+ i => i < files.Length ? files[i] : null,
+ readerOptions ?? new ReaderOptions()
+ )
+ );
+ }
+
+ ///
+ /// Constructor with all stream parts passed in
+ ///
+ ///
+ ///
+ public static GZipArchive Open(IEnumerable streams, ReaderOptions? readerOptions = null)
+ {
+ streams.CheckNotNull(nameof(streams));
+ var strms = streams.ToArray();
+ return new GZipArchive(
+ new SourceStream(
+ strms[0],
+ i => i < strms.Length ? strms[i] : null,
+ readerOptions ?? new ReaderOptions()
+ )
+ );
+ }
+
+ ///
+ /// Takes a seekable Stream as a source
+ ///
+ ///
+ ///
+ public static GZipArchive Open(Stream stream, ReaderOptions? readerOptions = null)
+ {
+ stream.CheckNotNull(nameof(stream));
+ return new GZipArchive(
+ new SourceStream(stream, i => null, readerOptions ?? new ReaderOptions())
+ );
+ }
+
+ public static GZipArchive Create() => new GZipArchive();
+
+ ///
+ /// Constructor with a SourceStream able to handle FileInfo and Streams.
+ ///
+ ///
+ ///
+ internal GZipArchive(SourceStream srcStream) : base(ArchiveType.Tar, srcStream) { }
+
+ protected override IEnumerable LoadVolumes(SourceStream srcStream)
+ {
+ srcStream.LoadAllParts();
+ var idx = 0;
+ return srcStream.Streams.Select(a => new GZipVolume(a, ReaderOptions, idx++));
+ }
+
+ public static bool IsGZipFile(string filePath) => IsGZipFile(new FileInfo(filePath));
+
+ public static bool IsGZipFile(FileInfo fileInfo)
+ {
+ if (!fileInfo.Exists)
{
- filePath.CheckNotNullOrEmpty(nameof(filePath));
- return Open(new FileInfo(filePath), readerOptions ?? new ReaderOptions());
+ return false;
}
- ///
- /// Constructor with a FileInfo object to an existing file.
- ///
- ///
- ///
- public static GZipArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null)
+ using Stream stream = fileInfo.OpenRead();
+ return IsGZipFile(stream);
+ }
+
+ public void SaveTo(string filePath) => SaveTo(new FileInfo(filePath));
+
+ public void SaveTo(FileInfo fileInfo)
+ {
+ using var stream = fileInfo.Open(FileMode.Create, FileAccess.Write);
+ SaveTo(stream, new WriterOptions(CompressionType.GZip));
+ }
+
+ public static bool IsGZipFile(Stream stream)
+ {
+ // read the header on the first read
+ Span header = stackalloc byte[10];
+
+ // workitem 8501: handle edge case (decompress empty stream)
+ if (!stream.ReadFully(header))
{
- fileInfo.CheckNotNull(nameof(fileInfo));
- return new GZipArchive(new SourceStream(fileInfo, i => ArchiveVolumeFactory.GetFilePart(i, fileInfo), readerOptions ?? new ReaderOptions()));
+ return false;
}
- ///
- /// Constructor with all file parts passed in
- ///
- ///
- ///
- public static GZipArchive Open(IEnumerable fileInfos, ReaderOptions? readerOptions = null)
+ if (header[0] != 0x1F || header[1] != 0x8B || header[2] != 8)
{
- fileInfos.CheckNotNull(nameof(fileInfos));
- FileInfo[] files = fileInfos.ToArray();
- return new GZipArchive(new SourceStream(files[0], i => i < files.Length ? files[i] : null, readerOptions ?? new ReaderOptions()));
+ return false;
}
- ///
- /// Constructor with all stream parts passed in
- ///
- ///
- ///
- public static GZipArchive Open(IEnumerable streams, ReaderOptions? readerOptions = null)
+ return true;
+ }
+
+ internal GZipArchive() : base(ArchiveType.GZip) { }
+
+ protected override GZipArchiveEntry CreateEntryInternal(
+ string filePath,
+ Stream source,
+ long size,
+ DateTime? modified,
+ bool closeStream
+ )
+ {
+ if (Entries.Any())
{
- streams.CheckNotNull(nameof(streams));
- Stream[] strms = streams.ToArray();
- return new GZipArchive(new SourceStream(strms[0], i => i < strms.Length ? strms[i] : null, readerOptions ?? new ReaderOptions()));
+ throw new InvalidOperationException("Only one entry is allowed in a GZip Archive");
}
+ return new GZipWritableArchiveEntry(this, source, filePath, size, modified, closeStream);
+ }
- ///
- /// Takes a seekable Stream as a source
- ///
- ///
- ///
- public static GZipArchive Open(Stream stream, ReaderOptions? readerOptions = null)
+ protected override void SaveTo(
+ Stream stream,
+ WriterOptions options,
+ IEnumerable oldEntries,
+ IEnumerable newEntries
+ )
+ {
+ if (Entries.Count > 1)
{
- stream.CheckNotNull(nameof(stream));
- return new GZipArchive(new SourceStream(stream, i => null, readerOptions ?? new ReaderOptions()));
+ throw new InvalidOperationException("Only one entry is allowed in a GZip Archive");
}
-
- public static GZipArchive Create()
+ using var writer = new GZipWriter(stream, new GZipWriterOptions(options));
+ foreach (var entry in oldEntries.Concat(newEntries).Where(x => !x.IsDirectory))
{
- return new GZipArchive();
- }
-
- ///
- /// Constructor with a SourceStream able to handle FileInfo and Streams.
- ///
- ///
- ///
- internal GZipArchive(SourceStream srcStream)
- : base(ArchiveType.Tar, srcStream)
- {
- }
-
- protected override IEnumerable LoadVolumes(SourceStream srcStream)
- {
- srcStream.LoadAllParts();
- int idx = 0;
- return srcStream.Streams.Select(a => new GZipVolume(a, ReaderOptions, idx++));
- }
- public static bool IsGZipFile(string filePath)
- {
- return IsGZipFile(new FileInfo(filePath));
- }
-
- public static bool IsGZipFile(FileInfo fileInfo)
- {
- if (!fileInfo.Exists)
- {
- return false;
- }
-
- using Stream stream = fileInfo.OpenRead();
- return IsGZipFile(stream);
- }
-
- public void SaveTo(string filePath)
- {
- SaveTo(new FileInfo(filePath));
- }
-
- public void SaveTo(FileInfo fileInfo)
- {
- using (var stream = fileInfo.Open(FileMode.Create, FileAccess.Write))
- {
- SaveTo(stream, new WriterOptions(CompressionType.GZip));
- }
- }
-
- public static bool IsGZipFile(Stream stream)
- {
- // read the header on the first read
- Span header = stackalloc byte[10];
-
- // workitem 8501: handle edge case (decompress empty stream)
- if (!stream.ReadFully(header))
- {
- return false;
- }
-
- if (header[0] != 0x1F || header[1] != 0x8B || header[2] != 8)
- {
- return false;
- }
-
- return true;
- }
-
- internal GZipArchive()
- : base(ArchiveType.GZip)
- {
- }
-
- protected override GZipArchiveEntry CreateEntryInternal(string filePath, Stream source, long size, DateTime? modified,
- bool closeStream)
- {
- if (Entries.Any())
- {
- throw new InvalidOperationException("Only one entry is allowed in a GZip Archive");
- }
- return new GZipWritableArchiveEntry(this, source, filePath, size, modified, closeStream);
- }
-
- protected override void SaveTo(Stream stream, WriterOptions options,
- IEnumerable oldEntries,
- IEnumerable newEntries)
- {
- if (Entries.Count > 1)
- {
- throw new InvalidOperationException("Only one entry is allowed in a GZip Archive");
- }
- using (var writer = new GZipWriter(stream, new GZipWriterOptions(options)))
- {
- foreach (var entry in oldEntries.Concat(newEntries)
- .Where(x => !x.IsDirectory))
- {
- using (var entryStream = entry.OpenEntryStream())
- {
- writer.Write(entry.Key, entryStream, entry.LastModifiedTime);
- }
- }
- }
- }
-
- protected override IEnumerable LoadEntries(IEnumerable volumes)
- {
- Stream stream = volumes.Single().Stream;
- yield return new GZipArchiveEntry(this, new GZipFilePart(stream, ReaderOptions.ArchiveEncoding));
- }
-
- protected override IReader CreateReaderForSolidExtraction()
- {
- var stream = Volumes.Single().Stream;
- stream.Position = 0;
- return GZipReader.Open(stream);
+ using var entryStream = entry.OpenEntryStream();
+ writer.Write(entry.Key, entryStream, entry.LastModifiedTime);
}
}
+
+ protected override IEnumerable LoadEntries(IEnumerable volumes)
+ {
+ var stream = volumes.Single().Stream;
+ yield return new GZipArchiveEntry(
+ this,
+ new GZipFilePart(stream, ReaderOptions.ArchiveEncoding)
+ );
+ }
+
+ protected override IReader CreateReaderForSolidExtraction()
+ {
+ var stream = Volumes.Single().Stream;
+ stream.Position = 0;
+ return GZipReader.Open(stream);
+ }
}
diff --git a/src/SharpCompress/Archives/GZip/GZipArchiveEntry.cs b/src/SharpCompress/Archives/GZip/GZipArchiveEntry.cs
index 237a5247..0109ad98 100644
--- a/src/SharpCompress/Archives/GZip/GZipArchiveEntry.cs
+++ b/src/SharpCompress/Archives/GZip/GZipArchiveEntry.cs
@@ -2,33 +2,29 @@
using System.Linq;
using SharpCompress.Common.GZip;
-namespace SharpCompress.Archives.GZip
+namespace SharpCompress.Archives.GZip;
+
+public class GZipArchiveEntry : GZipEntry, IArchiveEntry
{
- public class GZipArchiveEntry : GZipEntry, IArchiveEntry
+ internal GZipArchiveEntry(GZipArchive archive, GZipFilePart part) : base(part) =>
+ Archive = archive;
+
+ public virtual Stream OpenEntryStream()
{
- internal GZipArchiveEntry(GZipArchive archive, GZipFilePart part)
- : base(part)
+ //this is to reset the stream to be read multiple times
+ var part = (GZipFilePart)Parts.Single();
+ if (part.GetRawStream().Position != part.EntryStartPosition)
{
- Archive = archive;
+ part.GetRawStream().Position = part.EntryStartPosition;
}
-
- public virtual Stream OpenEntryStream()
- {
- //this is to reset the stream to be read multiple times
- var part = (GZipFilePart)Parts.Single();
- if (part.GetRawStream().Position != part.EntryStartPosition)
- {
- part.GetRawStream().Position = part.EntryStartPosition;
- }
- return Parts.Single().GetCompressedStream();
- }
-
- #region IArchiveEntry Members
-
- public IArchive Archive { get; }
-
- public bool IsComplete => true;
-
- #endregion
+ return Parts.Single().GetCompressedStream();
}
-}
\ No newline at end of file
+
+ #region IArchiveEntry Members
+
+ public IArchive Archive { get; }
+
+ public bool IsComplete => true;
+
+ #endregion
+}
diff --git a/src/SharpCompress/Archives/GZip/GZipWritableArchiveEntry.cs b/src/SharpCompress/Archives/GZip/GZipWritableArchiveEntry.cs
index 4a315571..7685ceb1 100644
--- a/src/SharpCompress/Archives/GZip/GZipWritableArchiveEntry.cs
+++ b/src/SharpCompress/Archives/GZip/GZipWritableArchiveEntry.cs
@@ -6,63 +6,67 @@ using System.IO;
using SharpCompress.Common;
using SharpCompress.IO;
-namespace SharpCompress.Archives.GZip
+namespace SharpCompress.Archives.GZip;
+
+internal sealed class GZipWritableArchiveEntry : GZipArchiveEntry, IWritableArchiveEntry
{
- internal sealed class GZipWritableArchiveEntry : GZipArchiveEntry, IWritableArchiveEntry
+ private readonly bool closeStream;
+ private readonly Stream stream;
+
+ internal GZipWritableArchiveEntry(
+ GZipArchive archive,
+ Stream stream,
+ string path,
+ long size,
+ DateTime? lastModified,
+ bool closeStream
+ ) : base(archive, null)
{
- private readonly bool closeStream;
- private readonly Stream stream;
+ this.stream = stream;
+ Key = path;
+ Size = size;
+ LastModifiedTime = lastModified;
+ this.closeStream = closeStream;
+ }
- internal GZipWritableArchiveEntry(GZipArchive archive, Stream stream,
- string path, long size, DateTime? lastModified, bool closeStream)
- : base(archive, null)
+ public override long Crc => 0;
+
+ public override string Key { get; }
+
+ public override long CompressedSize => 0;
+
+ public override long Size { get; }
+
+ public override DateTime? LastModifiedTime { get; }
+
+ public override DateTime? CreatedTime => null;
+
+ public override DateTime? LastAccessedTime => null;
+
+ public override DateTime? ArchivedTime => null;
+
+ public override bool IsEncrypted => false;
+
+ public override bool IsDirectory => false;
+
+ public override bool IsSplitAfter => false;
+
+ internal override IEnumerable Parts => throw new NotImplementedException();
+
+ Stream IWritableArchiveEntry.Stream => stream;
+
+ public override Stream OpenEntryStream()
+ {
+ //ensure new stream is at the start, this could be reset
+ stream.Seek(0, SeekOrigin.Begin);
+ return NonDisposingStream.Create(stream);
+ }
+
+ internal override void Close()
+ {
+ if (closeStream)
{
- this.stream = stream;
- Key = path;
- Size = size;
- LastModifiedTime = lastModified;
- this.closeStream = closeStream;
- }
-
- public override long Crc => 0;
-
- public override string Key { get; }
-
- public override long CompressedSize => 0;
-
- public override long Size { get; }
-
- public override DateTime? LastModifiedTime { get; }
-
- public override DateTime? CreatedTime => null;
-
- public override DateTime? LastAccessedTime => null;
-
- public override DateTime? ArchivedTime => null;
-
- public override bool IsEncrypted => false;
-
- public override bool IsDirectory => false;
-
- public override bool IsSplitAfter => false;
-
- internal override IEnumerable Parts => throw new NotImplementedException();
-
- Stream IWritableArchiveEntry.Stream => stream;
-
- public override Stream OpenEntryStream()
- {
- //ensure new stream is at the start, this could be reset
- stream.Seek(0, SeekOrigin.Begin);
- return NonDisposingStream.Create(stream);
- }
-
- internal override void Close()
- {
- if (closeStream)
- {
- stream.Dispose();
- }
+ stream.Dispose();
}
}
}
diff --git a/src/SharpCompress/Archives/IArchive.cs b/src/SharpCompress/Archives/IArchive.cs
index 2ba84a39..154529bf 100644
--- a/src/SharpCompress/Archives/IArchive.cs
+++ b/src/SharpCompress/Archives/IArchive.cs
@@ -1,49 +1,48 @@
-using System;
+using System;
using System.Collections.Generic;
using SharpCompress.Common;
using SharpCompress.Readers;
-namespace SharpCompress.Archives
+namespace SharpCompress.Archives;
+
+public interface IArchive : IDisposable
{
- public interface IArchive : IDisposable
- {
- event EventHandler> EntryExtractionBegin;
- event EventHandler> EntryExtractionEnd;
+ event EventHandler> EntryExtractionBegin;
+ event EventHandler> EntryExtractionEnd;
- event EventHandler CompressedBytesRead;
- event EventHandler FilePartExtractionBegin;
+ event EventHandler CompressedBytesRead;
+ event EventHandler FilePartExtractionBegin;
- IEnumerable Entries { get; }
- IEnumerable Volumes { get; }
+ IEnumerable Entries { get; }
+ IEnumerable Volumes { get; }
- ArchiveType Type { get; }
+ ArchiveType Type { get; }
- ///
- /// Use this method to extract all entries in an archive in order.
- /// This is primarily for SOLID Rar Archives or 7Zip Archives as they need to be
- /// extracted sequentially for the best performance.
- ///
- IReader ExtractAllEntries();
+ ///
+ /// Use this method to extract all entries in an archive in order.
+ /// This is primarily for SOLID Rar Archives or 7Zip Archives as they need to be
+ /// extracted sequentially for the best performance.
+ ///
+ IReader ExtractAllEntries();
- ///
- /// Archive is SOLID (this means the Archive saved bytes by reusing information which helps for archives containing many small files).
- /// Rar Archives can be SOLID while all 7Zip archives are considered SOLID.
- ///
- bool IsSolid { get; }
+ ///
+ /// Archive is SOLID (this means the Archive saved bytes by reusing information which helps for archives containing many small files).
+ /// Rar Archives can be SOLID while all 7Zip archives are considered SOLID.
+ ///
+ bool IsSolid { get; }
- ///
- /// This checks to see if all the known entries have IsComplete = true
- ///
- bool IsComplete { get; }
+ ///
+ /// This checks to see if all the known entries have IsComplete = true
+ ///
+ bool IsComplete { get; }
- ///
- /// The total size of the files compressed in the archive.
- ///
- long TotalSize { get; }
+ ///
+ /// The total size of the files compressed in the archive.
+ ///
+ long TotalSize { get; }
- ///
- /// The total size of the files as uncompressed in the archive.
- ///
- long TotalUncompressSize { get; }
- }
-}
\ No newline at end of file
+ ///
+ /// The total size of the files as uncompressed in the archive.
+ ///
+ long TotalUncompressSize { get; }
+}
diff --git a/src/SharpCompress/Archives/IArchiveEntry.cs b/src/SharpCompress/Archives/IArchiveEntry.cs
index 43e681b4..708753cb 100644
--- a/src/SharpCompress/Archives/IArchiveEntry.cs
+++ b/src/SharpCompress/Archives/IArchiveEntry.cs
@@ -1,24 +1,23 @@
-using System.IO;
+using System.IO;
using SharpCompress.Common;
-namespace SharpCompress.Archives
+namespace SharpCompress.Archives;
+
+public interface IArchiveEntry : IEntry
{
- public interface IArchiveEntry : IEntry
- {
- ///
- /// Opens the current entry as a stream that will decompress as it is read.
- /// Read the entire stream or use SkipEntry on EntryStream.
- ///
- Stream OpenEntryStream();
+ ///
+ /// Opens the current entry as a stream that will decompress as it is read.
+ /// Read the entire stream or use SkipEntry on EntryStream.
+ ///
+ Stream OpenEntryStream();
- ///
- /// The archive can find all the parts of the archive needed to extract this entry.
- ///
- bool IsComplete { get; }
+ ///
+ /// The archive can find all the parts of the archive needed to extract this entry.
+ ///
+ bool IsComplete { get; }
- ///
- /// The archive instance this entry belongs to
- ///
- IArchive Archive { get; }
- }
-}
\ No newline at end of file
+ ///
+ /// The archive instance this entry belongs to
+ ///
+ IArchive Archive { get; }
+}
diff --git a/src/SharpCompress/Archives/IArchiveEntryExtensions.cs b/src/SharpCompress/Archives/IArchiveEntryExtensions.cs
index 829b8bbd..0992f152 100644
--- a/src/SharpCompress/Archives/IArchiveEntryExtensions.cs
+++ b/src/SharpCompress/Archives/IArchiveEntryExtensions.cs
@@ -1,63 +1,70 @@
-using System.IO;
+using System.IO;
using SharpCompress.Common;
using SharpCompress.IO;
-namespace SharpCompress.Archives
+namespace SharpCompress.Archives;
+
+public static class IArchiveEntryExtensions
{
- public static class IArchiveEntryExtensions
+ public static void WriteTo(this IArchiveEntry archiveEntry, Stream streamToWriteTo)
{
- public static void WriteTo(this IArchiveEntry archiveEntry, Stream streamToWriteTo)
+ if (archiveEntry.IsDirectory)
{
- if (archiveEntry.IsDirectory)
- {
- throw new ExtractionException("Entry is a file directory and cannot be extracted.");
- }
-
- var streamListener = (IArchiveExtractionListener)archiveEntry.Archive;
- streamListener.EnsureEntriesLoaded();
- streamListener.FireEntryExtractionBegin(archiveEntry);
- streamListener.FireFilePartExtractionBegin(archiveEntry.Key, archiveEntry.Size, archiveEntry.CompressedSize);
- var entryStream = archiveEntry.OpenEntryStream();
- if (entryStream is null)
- {
- return;
- }
- using (entryStream)
- {
- using (Stream s = new ListeningStream(streamListener, entryStream))
- {
- s.TransferTo(streamToWriteTo);
- }
- }
- streamListener.FireEntryExtractionEnd(archiveEntry);
+ throw new ExtractionException("Entry is a file directory and cannot be extracted.");
}
- ///
- /// Extract to specific directory, retaining filename
- ///
- public static void WriteToDirectory(this IArchiveEntry entry, string destinationDirectory,
- ExtractionOptions? options = null)
+ var streamListener = (IArchiveExtractionListener)archiveEntry.Archive;
+ streamListener.EnsureEntriesLoaded();
+ streamListener.FireEntryExtractionBegin(archiveEntry);
+ streamListener.FireFilePartExtractionBegin(
+ archiveEntry.Key,
+ archiveEntry.Size,
+ archiveEntry.CompressedSize
+ );
+ var entryStream = archiveEntry.OpenEntryStream();
+ if (entryStream is null)
{
- ExtractionMethods.WriteEntryToDirectory(entry, destinationDirectory, options,
- entry.WriteToFile);
+ return;
}
-
- ///
- /// Extract to specific file
- ///
- public static void WriteToFile(this IArchiveEntry entry,
- string destinationFileName,
- ExtractionOptions? options = null)
+ using (entryStream)
{
-
- ExtractionMethods.WriteEntryToFile(entry, destinationFileName, options,
- (x, fm) =>
- {
- using (FileStream fs = File.Open(destinationFileName, fm))
- {
- entry.WriteTo(fs);
- }
- });
+ using Stream s = new ListeningStream(streamListener, entryStream);
+ s.TransferTo(streamToWriteTo);
}
+ streamListener.FireEntryExtractionEnd(archiveEntry);
}
-}
\ No newline at end of file
+
+ ///
+ /// Extract to specific directory, retaining filename
+ ///
+ public static void WriteToDirectory(
+ this IArchiveEntry entry,
+ string destinationDirectory,
+ ExtractionOptions? options = null
+ ) =>
+ ExtractionMethods.WriteEntryToDirectory(
+ entry,
+ destinationDirectory,
+ options,
+ entry.WriteToFile
+ );
+
+ ///
+ /// Extract to specific file
+ ///
+ public static void WriteToFile(
+ this IArchiveEntry entry,
+ string destinationFileName,
+ ExtractionOptions? options = null
+ ) =>
+ ExtractionMethods.WriteEntryToFile(
+ entry,
+ destinationFileName,
+ options,
+ (x, fm) =>
+ {
+ using var fs = File.Open(destinationFileName, fm);
+ entry.WriteTo(fs);
+ }
+ );
+}
diff --git a/src/SharpCompress/Archives/IArchiveExtensions.cs b/src/SharpCompress/Archives/IArchiveExtensions.cs
index 23b8a27e..14a48dbd 100644
--- a/src/SharpCompress/Archives/IArchiveExtensions.cs
+++ b/src/SharpCompress/Archives/IArchiveExtensions.cs
@@ -1,20 +1,22 @@
-using System.Linq;
+using System.Linq;
using SharpCompress.Common;
-namespace SharpCompress.Archives
+namespace SharpCompress.Archives;
+
+public static class IArchiveExtensions
{
- public static class IArchiveExtensions
+ ///
+ /// Extract to specific directory, retaining filename
+ ///
+ public static void WriteToDirectory(
+ this IArchive archive,
+ string destinationDirectory,
+ ExtractionOptions? options = null
+ )
{
- ///
- /// Extract to specific directory, retaining filename
- ///
- public static void WriteToDirectory(this IArchive archive, string destinationDirectory,
- ExtractionOptions? options = null)
+ foreach (var entry in archive.Entries.Where(x => !x.IsDirectory))
{
- foreach (IArchiveEntry entry in archive.Entries.Where(x => !x.IsDirectory))
- {
- entry.WriteToDirectory(destinationDirectory, options);
- }
+ entry.WriteToDirectory(destinationDirectory, options);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/SharpCompress/Archives/IArchiveExtractionListener.cs b/src/SharpCompress/Archives/IArchiveExtractionListener.cs
index 9ce07e8a..7bc2ef34 100644
--- a/src/SharpCompress/Archives/IArchiveExtractionListener.cs
+++ b/src/SharpCompress/Archives/IArchiveExtractionListener.cs
@@ -1,11 +1,10 @@
-using SharpCompress.Common;
+using SharpCompress.Common;
-namespace SharpCompress.Archives
+namespace SharpCompress.Archives;
+
+internal interface IArchiveExtractionListener : IExtractionListener
{
- internal interface IArchiveExtractionListener : IExtractionListener
- {
- void EnsureEntriesLoaded();
- void FireEntryExtractionBegin(IArchiveEntry entry);
- void FireEntryExtractionEnd(IArchiveEntry entry);
- }
-}
\ No newline at end of file
+ void EnsureEntriesLoaded();
+ void FireEntryExtractionBegin(IArchiveEntry entry);
+ void FireEntryExtractionEnd(IArchiveEntry entry);
+}
diff --git a/src/SharpCompress/Archives/IArchiveFactory.cs b/src/SharpCompress/Archives/IArchiveFactory.cs
index 98014688..370e5c9f 100644
--- a/src/SharpCompress/Archives/IArchiveFactory.cs
+++ b/src/SharpCompress/Archives/IArchiveFactory.cs
@@ -1,40 +1,35 @@
-using System;
-using System.Collections.Generic;
using System.IO;
-
-using SharpCompress.Common;
using SharpCompress.Factories;
using SharpCompress.Readers;
-namespace SharpCompress.Archives
+namespace SharpCompress.Archives;
+
+///
+/// Represents a factory used to identify and open archives.
+///
+///
+/// Currently implemented by:
+///
+///
+///
+///
+///
+///
+///
+///
+public interface IArchiveFactory : IFactory
{
///
- /// Represents a factory used to identify and open archives.
+ /// Opens an Archive for random access.
///
- ///
- /// Currently implemented by:
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- public interface IArchiveFactory : IFactory
- {
- ///
- /// Opens an Archive for random access.
- ///
- /// An open, readable and seekable stream.
- /// reading options.
- IArchive Open(Stream stream, ReaderOptions? readerOptions = null);
+ /// An open, readable and seekable stream.
+ /// reading options.
+ IArchive Open(Stream stream, ReaderOptions? readerOptions = null);
- ///
- /// Constructor with a FileInfo object to an existing file.
- ///
- /// the file to open.
- /// reading options.
- IArchive Open(System.IO.FileInfo fileInfo, ReaderOptions? readerOptions = null);
- }
+ ///
+ /// Constructor with a FileInfo object to an existing file.
+ ///
+ /// the file to open.
+ /// reading options.
+ IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null);
}
diff --git a/src/SharpCompress/Archives/IMultiArchiveFactory.cs b/src/SharpCompress/Archives/IMultiArchiveFactory.cs
index 6ba39280..c26b649f 100644
--- a/src/SharpCompress/Archives/IMultiArchiveFactory.cs
+++ b/src/SharpCompress/Archives/IMultiArchiveFactory.cs
@@ -1,40 +1,36 @@
-using System;
using System.Collections.Generic;
using System.IO;
-
-using SharpCompress.Common;
using SharpCompress.Factories;
using SharpCompress.Readers;
-namespace SharpCompress.Archives
+namespace SharpCompress.Archives;
+
+///
+/// Represents a factory used to identify and open archives.
+///
+///
+/// Implemented by:
+///
+///
+///
+///
+///
+///
+///
+///
+public interface IMultiArchiveFactory : IFactory
{
///
- /// Represents a factory used to identify and open archives.
+ /// Constructor with IEnumerable FileInfo objects, multi and split support.
///
- ///
- /// Implemented by:
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- public interface IMultiArchiveFactory : IFactory
- {
- ///
- /// Constructor with IEnumerable FileInfo objects, multi and split support.
- ///
- ///
- /// reading options.
- IArchive Open(IReadOnlyList streams, ReaderOptions? readerOptions = null);
+ ///
+ /// reading options.
+ IArchive Open(IReadOnlyList streams, ReaderOptions? readerOptions = null);
- ///
- /// Constructor with IEnumerable Stream objects, multi and split support.
- ///
- ///
- /// reading options.
- IArchive Open(IReadOnlyList fileInfos, ReaderOptions? readerOptions = null);
- }
+ ///
+ /// Constructor with IEnumerable Stream objects, multi and split support.
+ ///
+ ///
+ /// reading options.
+ IArchive Open(IReadOnlyList fileInfos, ReaderOptions? readerOptions = null);
}
diff --git a/src/SharpCompress/Archives/IWritableArchive.cs b/src/SharpCompress/Archives/IWritableArchive.cs
index 3b1b0328..37b84aa0 100644
--- a/src/SharpCompress/Archives/IWritableArchive.cs
+++ b/src/SharpCompress/Archives/IWritableArchive.cs
@@ -2,20 +2,25 @@ using System;
using System.IO;
using SharpCompress.Writers;
-namespace SharpCompress.Archives
+namespace SharpCompress.Archives;
+
+public interface IWritableArchive : IArchive
{
- public interface IWritableArchive : IArchive
- {
- void RemoveEntry(IArchiveEntry entry);
+ void RemoveEntry(IArchiveEntry entry);
- IArchiveEntry AddEntry(string key, Stream source, bool closeStream, long size = 0, DateTime? modified = null);
+ IArchiveEntry AddEntry(
+ string key,
+ Stream source,
+ bool closeStream,
+ long size = 0,
+ DateTime? modified = null
+ );
- void SaveTo(Stream stream, WriterOptions options);
+ void SaveTo(Stream stream, WriterOptions options);
- ///
- /// Use this to pause entry rebuilding when adding large collections of entries. Dispose when complete. A using statement is recommended.
- ///
- /// IDisposeable to resume entry rebuilding
- IDisposable PauseEntryRebuilding();
- }
-}
\ No newline at end of file
+ ///
+ /// Use this to pause entry rebuilding when adding large collections of entries. Dispose when complete. A using statement is recommended.
+ ///
+ /// IDisposeable to resume entry rebuilding
+ IDisposable PauseEntryRebuilding();
+}
diff --git a/src/SharpCompress/Archives/IWritableArchiveEntry.cs b/src/SharpCompress/Archives/IWritableArchiveEntry.cs
index 044eb1c7..b4ed22c1 100644
--- a/src/SharpCompress/Archives/IWritableArchiveEntry.cs
+++ b/src/SharpCompress/Archives/IWritableArchiveEntry.cs
@@ -1,9 +1,8 @@
-using System.IO;
+using System.IO;
-namespace SharpCompress.Archives
+namespace SharpCompress.Archives;
+
+internal interface IWritableArchiveEntry
{
- internal interface IWritableArchiveEntry
- {
- Stream Stream { get; }
- }
-}
\ No newline at end of file
+ Stream Stream { get; }
+}
diff --git a/src/SharpCompress/Archives/IWritableArchiveExtensions.cs b/src/SharpCompress/Archives/IWritableArchiveExtensions.cs
index 468622a7..8f531d41 100644
--- a/src/SharpCompress/Archives/IWritableArchiveExtensions.cs
+++ b/src/SharpCompress/Archives/IWritableArchiveExtensions.cs
@@ -2,56 +2,85 @@
using System.IO;
using SharpCompress.Writers;
-namespace SharpCompress.Archives
+namespace SharpCompress.Archives;
+
+public static class IWritableArchiveExtensions
{
- public static class IWritableArchiveExtensions
+ public static void AddEntry(
+ this IWritableArchive writableArchive,
+ string entryPath,
+ string filePath
+ )
{
- public static void AddEntry(this IWritableArchive writableArchive,
- string entryPath, string filePath)
+ var fileInfo = new FileInfo(filePath);
+ if (!fileInfo.Exists)
{
- var fileInfo = new FileInfo(filePath);
- if (!fileInfo.Exists)
- {
- throw new FileNotFoundException("Could not AddEntry: " + filePath);
- }
- writableArchive.AddEntry(entryPath, new FileInfo(filePath).OpenRead(), true, fileInfo.Length,
- fileInfo.LastWriteTime);
+ throw new FileNotFoundException("Could not AddEntry: " + filePath);
}
+ writableArchive.AddEntry(
+ entryPath,
+ new FileInfo(filePath).OpenRead(),
+ true,
+ fileInfo.Length,
+ fileInfo.LastWriteTime
+ );
+ }
- public static void SaveTo(this IWritableArchive writableArchive, string filePath, WriterOptions options)
- {
- writableArchive.SaveTo(new FileInfo(filePath), options);
- }
+ public static void SaveTo(
+ this IWritableArchive writableArchive,
+ string filePath,
+ WriterOptions options
+ ) => writableArchive.SaveTo(new FileInfo(filePath), options);
- public static void SaveTo(this IWritableArchive writableArchive, FileInfo fileInfo, WriterOptions options)
- {
- using (var stream = fileInfo.Open(FileMode.Create, FileAccess.Write))
- {
- writableArchive.SaveTo(stream, options);
- }
- }
+ public static void SaveTo(
+ this IWritableArchive writableArchive,
+ FileInfo fileInfo,
+ WriterOptions options
+ )
+ {
+ using var stream = fileInfo.Open(FileMode.Create, FileAccess.Write);
+ writableArchive.SaveTo(stream, options);
+ }
- public static void AddAllFromDirectory(
- this IWritableArchive writableArchive,
- string filePath, string searchPattern = "*.*", SearchOption searchOption = SearchOption.AllDirectories)
+ public static void AddAllFromDirectory(
+ this IWritableArchive writableArchive,
+ string filePath,
+ string searchPattern = "*.*",
+ SearchOption searchOption = SearchOption.AllDirectories
+ )
+ {
+ using (writableArchive.PauseEntryRebuilding())
{
- using (writableArchive.PauseEntryRebuilding())
+ foreach (var path in Directory.EnumerateFiles(filePath, searchPattern, searchOption))
{
- foreach (var path in Directory.EnumerateFiles(filePath, searchPattern, searchOption))
- {
- var fileInfo = new FileInfo(path);
- writableArchive.AddEntry(path.Substring(filePath.Length), fileInfo.OpenRead(), true, fileInfo.Length,
- fileInfo.LastWriteTime);
- }
+ var fileInfo = new FileInfo(path);
+ writableArchive.AddEntry(
+ path.Substring(filePath.Length),
+ fileInfo.OpenRead(),
+ true,
+ fileInfo.Length,
+ fileInfo.LastWriteTime
+ );
}
}
- public static IArchiveEntry AddEntry(this IWritableArchive writableArchive, string key, FileInfo fileInfo)
- {
- if (!fileInfo.Exists)
- {
- throw new ArgumentException("FileInfo does not exist.");
- }
- return writableArchive.AddEntry(key, fileInfo.OpenRead(), true, fileInfo.Length, fileInfo.LastWriteTime);
- }
}
-}
\ No newline at end of file
+
+ public static IArchiveEntry AddEntry(
+ this IWritableArchive writableArchive,
+ string key,
+ FileInfo fileInfo
+ )
+ {
+ if (!fileInfo.Exists)
+ {
+ throw new ArgumentException("FileInfo does not exist.");
+ }
+ return writableArchive.AddEntry(
+ key,
+ fileInfo.OpenRead(),
+ true,
+ fileInfo.Length,
+ fileInfo.LastWriteTime
+ );
+ }
+}
diff --git a/src/SharpCompress/Archives/IWriteableArchiveFactory.cs b/src/SharpCompress/Archives/IWriteableArchiveFactory.cs
index 59c58134..4fae9f55 100644
--- a/src/SharpCompress/Archives/IWriteableArchiveFactory.cs
+++ b/src/SharpCompress/Archives/IWriteableArchiveFactory.cs
@@ -1,27 +1,20 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+namespace SharpCompress.Archives;
-namespace SharpCompress.Archives
+///
+/// Decorator for used to declare an archive format as able to create writeable archives
+///
+///
+/// Implemented by:
+///
+///
+///
+///
+///
+public interface IWriteableArchiveFactory : Factories.IFactory
{
///
- /// Decorator for used to declare an archive format as able to create writeable archives
+ /// Creates a new, empty archive, ready to be written.
///
- ///
- /// Implemented by:
- ///
- ///
- ///
- ///
- ///
- public interface IWriteableArchiveFactory : Factories.IFactory
- {
- ///
- /// Creates a new, empty archive, ready to be written.
- ///
- ///
- IWritableArchive CreateWriteableArchive();
- }
+ ///
+ IWritableArchive CreateWriteableArchive();
}
diff --git a/src/SharpCompress/Archives/Rar/FileInfoRarArchiveVolume.cs b/src/SharpCompress/Archives/Rar/FileInfoRarArchiveVolume.cs
index 1afba647..3e576db3 100644
--- a/src/SharpCompress/Archives/Rar/FileInfoRarArchiveVolume.cs
+++ b/src/SharpCompress/Archives/Rar/FileInfoRarArchiveVolume.cs
@@ -6,39 +6,33 @@ using SharpCompress.Common.Rar.Headers;
using SharpCompress.IO;
using SharpCompress.Readers;
-namespace SharpCompress.Archives.Rar
+namespace SharpCompress.Archives.Rar;
+
+///
+/// A rar part based on a FileInfo object
+///
+internal class FileInfoRarArchiveVolume : RarVolume
{
- ///
- /// A rar part based on a FileInfo object
- ///
- internal class FileInfoRarArchiveVolume : RarVolume
+ internal FileInfoRarArchiveVolume(FileInfo fileInfo, ReaderOptions options, int index = 0)
+ : base(StreamingMode.Seekable, fileInfo.OpenRead(), FixOptions(options), index)
{
- internal FileInfoRarArchiveVolume(FileInfo fileInfo, ReaderOptions options, int index = 0)
- : base(StreamingMode.Seekable, fileInfo.OpenRead(), FixOptions(options), index)
- {
- FileInfo = fileInfo;
- FileParts = GetVolumeFileParts().ToArray().ToReadOnly();
- }
-
- private static ReaderOptions FixOptions(ReaderOptions options)
- {
- //make sure we're closing streams with fileinfo
- options.LeaveStreamOpen = false;
- return options;
- }
-
- internal ReadOnlyCollection FileParts { get; }
-
- internal FileInfo FileInfo { get; }
-
- internal override RarFilePart CreateFilePart(MarkHeader markHeader, FileHeader fileHeader)
- {
- return new FileInfoRarFilePart(this, ReaderOptions.Password, markHeader, fileHeader, FileInfo);
- }
-
- internal override IEnumerable ReadFileParts()
- {
- return FileParts;
- }
+ FileInfo = fileInfo;
+ FileParts = GetVolumeFileParts().ToArray().ToReadOnly();
}
+
+ private static ReaderOptions FixOptions(ReaderOptions options)
+ {
+ //make sure we're closing streams with fileinfo
+ options.LeaveStreamOpen = false;
+ return options;
+ }
+
+ internal ReadOnlyCollection FileParts { get; }
+
+ internal FileInfo FileInfo { get; }
+
+ internal override RarFilePart CreateFilePart(MarkHeader markHeader, FileHeader fileHeader) =>
+ new FileInfoRarFilePart(this, ReaderOptions.Password, markHeader, fileHeader, FileInfo);
+
+ internal override IEnumerable ReadFileParts() => FileParts;
}
diff --git a/src/SharpCompress/Archives/Rar/FileInfoRarFilePart.cs b/src/SharpCompress/Archives/Rar/FileInfoRarFilePart.cs
index b2134e6d..b5416f9e 100644
--- a/src/SharpCompress/Archives/Rar/FileInfoRarFilePart.cs
+++ b/src/SharpCompress/Archives/Rar/FileInfoRarFilePart.cs
@@ -1,25 +1,20 @@
using System.IO;
using SharpCompress.Common.Rar.Headers;
-namespace SharpCompress.Archives.Rar
+namespace SharpCompress.Archives.Rar;
+
+internal sealed class FileInfoRarFilePart : SeekableFilePart
{
- internal sealed class FileInfoRarFilePart : SeekableFilePart
- {
- internal FileInfoRarFilePart(FileInfoRarArchiveVolume volume, string? password, MarkHeader mh, FileHeader fh, FileInfo fi)
- : base(mh, fh, volume.Index, volume.Stream, password)
- {
- FileInfo = fi;
- }
+ internal FileInfoRarFilePart(
+ FileInfoRarArchiveVolume volume,
+ string? password,
+ MarkHeader mh,
+ FileHeader fh,
+ FileInfo fi
+ ) : base(mh, fh, volume.Index, volume.Stream, password) => FileInfo = fi;
- internal FileInfo FileInfo { get; }
+ internal FileInfo FileInfo { get; }
- internal override string FilePartName
- {
- get
- {
- return "Rar File: " + FileInfo.FullName
- + " File Entry: " + FileHeader.FileName;
- }
- }
- }
+ internal override string FilePartName =>
+ "Rar File: " + FileInfo.FullName + " File Entry: " + FileHeader.FileName;
}
diff --git a/src/SharpCompress/Archives/Rar/RarArchive.Extensions.cs b/src/SharpCompress/Archives/Rar/RarArchive.Extensions.cs
index 7eefef9c..bf6f0cd4 100644
--- a/src/SharpCompress/Archives/Rar/RarArchive.Extensions.cs
+++ b/src/SharpCompress/Archives/Rar/RarArchive.Extensions.cs
@@ -1,23 +1,18 @@
using System.Linq;
-namespace SharpCompress.Archives.Rar
-{
- public static class RarArchiveExtensions
- {
- ///
- /// RarArchive is the first volume of a multi-part archive. If MultipartVolume is true and IsFirstVolume is false then the first volume file must be missing.
- ///
- public static bool IsFirstVolume(this RarArchive archive)
- {
- return archive.Volumes.First().IsFirstVolume;
- }
+namespace SharpCompress.Archives.Rar;
- ///
- /// RarArchive is part of a multi-part archive.
- ///
- public static bool IsMultipartVolume(this RarArchive archive)
- {
- return archive.Volumes.First().IsMultiVolume;
- }
- }
-}
\ No newline at end of file
+public static class RarArchiveExtensions
+{
+ ///
+ /// RarArchive is the first volume of a multi-part archive. If MultipartVolume is true and IsFirstVolume is false then the first volume file must be missing.
+ ///
+ public static bool IsFirstVolume(this RarArchive archive) =>
+ archive.Volumes.First().IsFirstVolume;
+
+ ///
+ /// RarArchive is part of a multi-part archive.
+ ///
+ public static bool IsMultipartVolume(this RarArchive archive) =>
+ archive.Volumes.First().IsMultiVolume;
+}
diff --git a/src/SharpCompress/Archives/Rar/RarArchive.cs b/src/SharpCompress/Archives/Rar/RarArchive.cs
index 6d43a46b..48f4ef45 100644
--- a/src/SharpCompress/Archives/Rar/RarArchive.cs
+++ b/src/SharpCompress/Archives/Rar/RarArchive.cs
@@ -9,149 +9,168 @@ using SharpCompress.IO;
using SharpCompress.Readers;
using SharpCompress.Readers.Rar;
-namespace SharpCompress.Archives.Rar
+namespace SharpCompress.Archives.Rar;
+
+public class RarArchive : AbstractArchive
{
- public class
- RarArchive : AbstractArchive
+ internal Lazy UnpackV2017 { get; } =
+ new Lazy(() => new Compressors.Rar.UnpackV2017.Unpack());
+ internal Lazy UnpackV1 { get; } =
+ new Lazy(() => new Compressors.Rar.UnpackV1.Unpack());
+
+ ///
+ /// Constructor with a SourceStream able to handle FileInfo and Streams.
+ ///
+ ///
+ ///
+ internal RarArchive(SourceStream srcStream) : base(ArchiveType.Rar, srcStream) { }
+
+ protected override IEnumerable LoadEntries(IEnumerable volumes) =>
+ RarArchiveEntryFactory.GetEntries(this, volumes, ReaderOptions);
+
+ protected override IEnumerable LoadVolumes(SourceStream srcStream)
{
- internal Lazy UnpackV2017 { get; } = new Lazy(() => new SharpCompress.Compressors.Rar.UnpackV2017.Unpack());
- internal Lazy UnpackV1 { get; } = new Lazy(() => new SharpCompress.Compressors.Rar.UnpackV1.Unpack());
-
-
- ///
- /// Constructor with a SourceStream able to handle FileInfo and Streams.
- ///
- ///
- ///
- internal RarArchive(SourceStream srcStream)
- : base(ArchiveType.Rar, srcStream)
+ SrcStream.LoadAllParts(); //request all streams
+ var streams = SrcStream.Streams.ToArray();
+ var idx = 0;
+ if (streams.Length > 1 && IsRarFile(streams[1], ReaderOptions)) //test part 2 - true = multipart not split
{
- }
+ SrcStream.IsVolumes = true;
+ streams[1].Position = 0;
+ SrcStream.Position = 0;
- protected override IEnumerable LoadEntries(IEnumerable volumes)
+ return srcStream.Streams.Select(
+ a => new StreamRarArchiveVolume(a, ReaderOptions, idx++)
+ );
+ }
+ else //split mode or single file
{
- return RarArchiveEntryFactory.GetEntries(this, volumes, ReaderOptions);
+ return new StreamRarArchiveVolume(SrcStream, ReaderOptions, idx++).AsEnumerable();
}
-
- protected override IEnumerable LoadVolumes(SourceStream srcStream)
- {
- base.SrcStream.LoadAllParts(); //request all streams
- Stream[] streams = base.SrcStream.Streams.ToArray();
- int idx = 0;
- if (streams.Length > 1 && IsRarFile(streams[1], ReaderOptions)) //test part 2 - true = multipart not split
- {
- base.SrcStream.IsVolumes = true;
- streams[1].Position = 0;
- base.SrcStream.Position = 0;
-
- return srcStream.Streams.Select(a => new StreamRarArchiveVolume(a, ReaderOptions, idx++));
- }
- else //split mode or single file
- return new StreamRarArchiveVolume(base.SrcStream, ReaderOptions, idx++).AsEnumerable();
- }
-
- protected override IReader CreateReaderForSolidExtraction()
- {
- var stream = Volumes.First().Stream;
- stream.Position = 0;
- return RarReader.Open(stream, ReaderOptions);
- }
-
- public override bool IsSolid => Volumes.First().IsSolidArchive;
-
- public virtual int MinVersion => Volumes.First().MinVersion;
- public virtual int MaxVersion => Volumes.First().MaxVersion;
-
- #region Creation
- ///
- /// Constructor with a FileInfo object to an existing file.
- ///
- ///
- ///
- public static RarArchive Open(string filePath, ReaderOptions? options = null)
- {
- filePath.CheckNotNullOrEmpty(nameof(filePath));
- FileInfo fileInfo = new FileInfo(filePath);
- return new RarArchive(new SourceStream(fileInfo, i => RarArchiveVolumeFactory.GetFilePart(i, fileInfo), options ?? new ReaderOptions()));
- }
-
- ///
- /// Constructor with a FileInfo object to an existing file.
- ///
- ///
- ///
- public static RarArchive Open(FileInfo fileInfo, ReaderOptions? options = null)
- {
- fileInfo.CheckNotNull(nameof(fileInfo));
- return new RarArchive(new SourceStream(fileInfo, i => RarArchiveVolumeFactory.GetFilePart(i, fileInfo), options ?? new ReaderOptions()));
- }
-
- ///
- /// Takes a seekable Stream as a source
- ///
- ///
- ///
- public static RarArchive Open(Stream stream, ReaderOptions? options = null)
- {
- stream.CheckNotNull(nameof(stream));
- return new RarArchive(new SourceStream(stream, i => null, options ?? new ReaderOptions()));
- }
-
- ///
- /// Constructor with all file parts passed in
- ///
- ///
- ///
- public static RarArchive Open(IEnumerable fileInfos, ReaderOptions? readerOptions = null)
- {
- fileInfos.CheckNotNull(nameof(fileInfos));
- FileInfo[] files = fileInfos.ToArray();
- return new RarArchive(new SourceStream(files[0], i => i < files.Length ? files[i] : null, readerOptions ?? new ReaderOptions()));
- }
-
- ///
- /// Constructor with all stream parts passed in
- ///
- ///
- ///
- public static RarArchive Open(IEnumerable streams, ReaderOptions? readerOptions = null)
- {
- streams.CheckNotNull(nameof(streams));
- Stream[] strms = streams.ToArray();
- return new RarArchive(new SourceStream(strms[0], i => i < strms.Length ? strms[i] : null, readerOptions ?? new ReaderOptions()));
- }
-
-
- public static bool IsRarFile(string filePath)
- {
- return IsRarFile(new FileInfo(filePath));
- }
-
- public static bool IsRarFile(FileInfo fileInfo)
- {
- if (!fileInfo.Exists)
- {
- return false;
- }
- using (Stream stream = fileInfo.OpenRead())
- {
- return IsRarFile(stream);
- }
- }
-
- public static bool IsRarFile(Stream stream, ReaderOptions? options = null)
- {
- try
- {
- MarkHeader.Read(stream, true, false);
- return true;
- }
- catch
- {
- return false;
- }
- }
-
- #endregion
}
+
+ protected override IReader CreateReaderForSolidExtraction()
+ {
+ var stream = Volumes.First().Stream;
+ stream.Position = 0;
+ return RarReader.Open(stream, ReaderOptions);
+ }
+
+ public override bool IsSolid => Volumes.First().IsSolidArchive;
+
+ public virtual int MinVersion => Volumes.First().MinVersion;
+ public virtual int MaxVersion => Volumes.First().MaxVersion;
+
+ #region Creation
+ ///
+ /// Constructor with a FileInfo object to an existing file.
+ ///
+ ///
+ ///
+ public static RarArchive Open(string filePath, ReaderOptions? options = null)
+ {
+ filePath.CheckNotNullOrEmpty(nameof(filePath));
+ var fileInfo = new FileInfo(filePath);
+ return new RarArchive(
+ new SourceStream(
+ fileInfo,
+ i => RarArchiveVolumeFactory.GetFilePart(i, fileInfo),
+ options ?? new ReaderOptions()
+ )
+ );
+ }
+
+ ///
+ /// Constructor with a FileInfo object to an existing file.
+ ///
+ ///
+ ///
+ public static RarArchive Open(FileInfo fileInfo, ReaderOptions? options = null)
+ {
+ fileInfo.CheckNotNull(nameof(fileInfo));
+ return new RarArchive(
+ new SourceStream(
+ fileInfo,
+ i => RarArchiveVolumeFactory.GetFilePart(i, fileInfo),
+ options ?? new ReaderOptions()
+ )
+ );
+ }
+
+ ///
+ /// Takes a seekable Stream as a source
+ ///
+ ///
+ ///
+ public static RarArchive Open(Stream stream, ReaderOptions? options = null)
+ {
+ stream.CheckNotNull(nameof(stream));
+ return new RarArchive(new SourceStream(stream, i => null, options ?? new ReaderOptions()));
+ }
+
+ ///
+ /// Constructor with all file parts passed in
+ ///
+ ///
+ ///
+ public static RarArchive Open(
+ IEnumerable fileInfos,
+ ReaderOptions? readerOptions = null
+ )
+ {
+ fileInfos.CheckNotNull(nameof(fileInfos));
+ var files = fileInfos.ToArray();
+ return new RarArchive(
+ new SourceStream(
+ files[0],
+ i => i < files.Length ? files[i] : null,
+ readerOptions ?? new ReaderOptions()
+ )
+ );
+ }
+
+ ///
+ /// Constructor with all stream parts passed in
+ ///
+ ///
+ ///
+ public static RarArchive Open(IEnumerable streams, ReaderOptions? readerOptions = null)
+ {
+ streams.CheckNotNull(nameof(streams));
+ var strms = streams.ToArray();
+ return new RarArchive(
+ new SourceStream(
+ strms[0],
+ i => i < strms.Length ? strms[i] : null,
+ readerOptions ?? new ReaderOptions()
+ )
+ );
+ }
+
+ public static bool IsRarFile(string filePath) => IsRarFile(new FileInfo(filePath));
+
+ public static bool IsRarFile(FileInfo fileInfo)
+ {
+ if (!fileInfo.Exists)
+ {
+ return false;
+ }
+ using Stream stream = fileInfo.OpenRead();
+ return IsRarFile(stream);
+ }
+
+ public static bool IsRarFile(Stream stream, ReaderOptions? options = null)
+ {
+ try
+ {
+ MarkHeader.Read(stream, true, false);
+ return true;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+ #endregion
}
diff --git a/src/SharpCompress/Archives/Rar/RarArchiveEntry.cs b/src/SharpCompress/Archives/Rar/RarArchiveEntry.cs
index fb2ba0f8..5885210a 100644
--- a/src/SharpCompress/Archives/Rar/RarArchiveEntry.cs
+++ b/src/SharpCompress/Archives/Rar/RarArchiveEntry.cs
@@ -1,4 +1,3 @@
-using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -8,82 +7,95 @@ using SharpCompress.Common.Rar.Headers;
using SharpCompress.Compressors.Rar;
using SharpCompress.Readers;
-namespace SharpCompress.Archives.Rar
+namespace SharpCompress.Archives.Rar;
+
+public class RarArchiveEntry : RarEntry, IArchiveEntry
{
- public class RarArchiveEntry : RarEntry, IArchiveEntry
+ private readonly ICollection parts;
+ private readonly RarArchive archive;
+ private readonly ReaderOptions readerOptions;
+
+ internal RarArchiveEntry(
+ RarArchive archive,
+ IEnumerable parts,
+ ReaderOptions readerOptions
+ )
{
- private readonly ICollection parts;
- private readonly RarArchive archive;
- private readonly ReaderOptions readerOptions;
+ this.parts = parts.ToList();
+ this.archive = archive;
+ this.readerOptions = readerOptions;
+ IsSolid = FileHeader.IsSolid;
+ }
- internal RarArchiveEntry(RarArchive archive, IEnumerable parts, ReaderOptions readerOptions)
+ public override CompressionType CompressionType => CompressionType.Rar;
+
+ public IArchive Archive => archive;
+
+ internal override IEnumerable Parts => parts.Cast();
+
+ internal override FileHeader FileHeader => parts.First().FileHeader;
+
+ public override long Crc
+ {
+ get
{
- this.parts = parts.ToList();
- this.archive = archive;
- this.readerOptions = readerOptions;
- this.IsSolid = this.FileHeader.IsSolid;
+ CheckIncomplete();
+ return parts.Select(fp => fp.FileHeader).Single(fh => !fh.IsSplitAfter).FileCrc;
+ }
+ }
+
+ public override long Size
+ {
+ get
+ {
+ CheckIncomplete();
+ return parts.First().FileHeader.UncompressedSize;
+ }
+ }
+
+ public override long CompressedSize
+ {
+ get
+ {
+ CheckIncomplete();
+ return parts.Aggregate(0L, (total, fp) => total + fp.FileHeader.CompressedSize);
+ }
+ }
+
+ public Stream OpenEntryStream()
+ {
+ if (IsRarV3)
+ {
+ return new RarStream(
+ archive.UnpackV1.Value,
+ FileHeader,
+ new MultiVolumeReadOnlyStream(Parts.Cast(), archive)
+ );
}
- public override CompressionType CompressionType => CompressionType.Rar;
+ return new RarStream(
+ archive.UnpackV2017.Value,
+ FileHeader,
+ new MultiVolumeReadOnlyStream(Parts.Cast(), archive)
+ );
+ }
- public IArchive Archive => archive;
-
- internal override IEnumerable Parts => parts.Cast();
-
- internal override FileHeader FileHeader => parts.First().FileHeader;
-
- public override long Crc
+ public bool IsComplete
+ {
+ get
{
- get
- {
- CheckIncomplete();
- return parts.Select(fp => fp.FileHeader).Single(fh => !fh.IsSplitAfter).FileCrc;
- }
+ var headers = parts.Select(x => x.FileHeader);
+ return !headers.First().IsSplitBefore && !headers.Last().IsSplitAfter;
}
+ }
- public override long Size
+ private void CheckIncomplete()
+ {
+ if (!readerOptions.DisableCheckIncomplete && !IsComplete)
{
- get
- {
- CheckIncomplete();
- return parts.First().FileHeader.UncompressedSize;
- }
- }
-
- public override long CompressedSize
- {
- get
- {
- CheckIncomplete();
- return parts.Aggregate(0L, (total, fp) => total + fp.FileHeader.CompressedSize);
- }
- }
-
- public Stream OpenEntryStream()
- {
- if (IsRarV3)
- {
- return new RarStream(archive.UnpackV1.Value, FileHeader, new MultiVolumeReadOnlyStream(Parts.Cast