diff --git a/build/Program.cs b/build/Program.cs
index 358082aa..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;
diff --git a/src/SharpCompress/Algorithms/Adler32.cs b/src/SharpCompress/Algorithms/Adler32.cs
index 0d4de7fa..e32893ac 100644
--- a/src/SharpCompress/Algorithms/Adler32.cs
+++ b/src/SharpCompress/Algorithms/Adler32.cs
@@ -15,101 +15,289 @@ 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);
}
-#endif
///
- /// The default initial seed value of a Adler32 checksum calculation.
+ /// Reduces even elements of the vector into one sum.
///
- public const uint SeedValue = 1U;
+ /// 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
- // Largest prime smaller than 65536
- private const uint BASE = 65521;
+ // Vector128.ToScalar() isn't optimized pre-net5.0 https://github.com/dotnet/runtime/pull/37882
+ return Sse2.ConvertToInt32(vsum);
+ }
+ }
+#endif
- // NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1
- private const uint NMAX = 5552;
+ ///
+ /// 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;
+
+ // 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
+ };
+#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 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)
+ {
+ return adler;
+ }
+
+#if SUPPORTS_RUNTIME_INTRINSICS
+ if (Avx2.IsSupported && buffer.Length >= MinBufferSize)
+ {
+ return CalculateAvx2(adler, buffer);
+ }
+
+ if (Ssse3.IsSupported && buffer.Length >= MinBufferSize)
+ {
+ return CalculateSse(adler, buffer);
+ }
+
+ return CalculateScalar(adler, buffer);
+#else
+ return CalculateScalar(adler, buffer);
+#endif
+ }
+
+ // 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)
+ {
+ 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))
+ {
+ byte* localBufferPtr = bufferPtr;
+
+ Vector256 zero = Vector256.Zero;
+ var dot3v = Vector256.Create((short)1);
+ var dot2v = Vector256.Create(
32,
31,
30,
@@ -125,7 +313,7 @@ namespace SharpCompress.Algorithms
20,
19,
18,
- 17, // tap1
+ 17,
16,
15,
14,
@@ -141,353 +329,164 @@ namespace SharpCompress.Algorithms
4,
3,
2,
- 1 // tap2
- };
-#endif
+ 1
+ );
- ///
- /// 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);
+ // 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);
- ///
- /// 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)
+ while (length >= 32)
{
- return adler;
- }
+ int k = length < NMAX ? (int)length : (int)NMAX;
+ k -= k % 32;
+ length -= (uint)k;
-#if SUPPORTS_RUNTIME_INTRINSICS
- if (Avx2.IsSupported && buffer.Length >= MinBufferSize)
- {
- return CalculateAvx2(adler, buffer);
- }
+ Vector256 vs10 = vs1;
+ Vector256 vs3 = Vector256.Zero;
- if (Ssse3.IsSupported && buffer.Length >= MinBufferSize)
- {
- return CalculateSse(adler, buffer);
- }
-
- return CalculateScalar(adler, buffer);
-#else
- return CalculateScalar(adler, buffer);
-#endif
- }
-
- // 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)
- {
- 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))
+ while (k >= 32)
{
- byte* localBufferPtr = bufferPtr;
+ // Load 32 input bytes.
+ Vector256 block = Avx.LoadVector256(localBufferPtr);
- // _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);
+ // Sum of abs diff, resulting in 2 x int32's
+ Vector256 vs1sad = Avx2.SumAbsoluteDifferences(block, zero);
- while (blocks > 0)
- {
- uint n = NMAX / BlockSize; /* The NMAX constraint. */
- if (n > blocks)
- {
- n = blocks;
- }
+ vs1 = Avx2.Add(vs1, vs1sad.AsUInt32());
+ vs3 = Avx2.Add(vs3, vs10);
- blocks -= n;
+ // sum 32 uint8s to 16 shorts.
+ Vector256 vshortsum2 = Avx2.MultiplyAddAdjacent(block, dot2v);
- // 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;
+ // sum 16 shorts to 8 uint32s.
+ Vector256 vsum2 = Avx2.MultiplyAddAdjacent(vshortsum2, dot3v);
- do
- {
- // Load 32 input bytes.
- Vector128 bytes1 = Sse3.LoadDquVector128(localBufferPtr);
- Vector128 bytes2 = Sse3.LoadDquVector128(localBufferPtr + 0x10);
+ vs2 = Avx2.Add(vsum2.AsUInt32(), vs2);
+ vs10 = vs1;
- // 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);
+ localBufferPtr += BlockSize;
+ k -= 32;
}
+
+ // 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;
}
- // 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)
+ while (length-- > 0)
{
- uint s1 = adler & 0xFFFF;
- uint s2 = (adler >> 16) & 0xFFFF;
+ 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;
- fixed (byte* bufferPtr = &MemoryMarshal.GetReference(buffer))
+ while (length > 0)
{
- byte* localBufferPtr = bufferPtr;
+ k = length < NMAX ? length : NMAX;
+ length -= k;
- 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)
+ while (k >= 16)
{
- int k = length < NMAX ? (int)length : (int)NMAX;
- k -= k % 32;
- length -= (uint)k;
+ 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];
- Vector256 vs10 = vs1;
- Vector256 vs3 = Vector256.Zero;
-
- while (k >= 32)
- {
- // 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;
- }
-
- // 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);
+ localBufferPtr += 16;
+ k -= 16;
}
- if (length > 0)
+ while (k-- > 0)
{
- HandleLeftOver(localBufferPtr, length, ref s1, ref s2);
+ s2 += s1 += *localBufferPtr++;
}
- 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;
+ s1 %= BASE;
+ s2 %= BASE;
}
- 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;
- }
+ return (s2 << 16) | s1;
}
}
}
diff --git a/src/SharpCompress/Archives/AbstractArchive.cs b/src/SharpCompress/Archives/AbstractArchive.cs
index 16b07864..d747189e 100644
--- a/src/SharpCompress/Archives/AbstractArchive.cs
+++ b/src/SharpCompress/Archives/AbstractArchive.cs
@@ -6,180 +6,179 @@ 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;
+ void IExtractionListener.FireCompressedBytesRead(
+ long currentPartCompressedBytes,
+ long compressedReadBytes
+ )
+ {
+ CompressedBytesRead?.Invoke(
+ this,
+ new CompressedBytesReadEventArgs(
+ currentFilePartCompressedBytesRead: currentPartCompressedBytes,
+ compressedBytesRead: compressedReadBytes
+ )
+ );
+ }
- ///
- /// Returns an ReadOnlyCollection of all the RarArchiveVolumes across the one or many parts of the RarArchive.
- ///
- public ICollection Volumes => lazyVolumes;
+ void IExtractionListener.FireFilePartExtractionBegin(
+ string name,
+ long size,
+ long compressedSize
+ )
+ {
+ FilePartExtractionBegin?.Invoke(
+ this,
+ new FilePartExtractionBeginEventArgs(
+ compressedSize: compressedSize,
+ size: size,
+ name: name
+ )
+ );
+ }
- ///
- /// The total size of the files compressed in the archive.
- ///
- public virtual long TotalSize =>
- Entries.Aggregate(0L, (total, cf) => total + cf.CompressedSize);
+ ///
+ /// 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();
+ }
- ///
- /// 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 IReader CreateReaderForSolidExtraction();
- protected abstract IEnumerable LoadVolumes(SourceStream srcStream);
- protected abstract IEnumerable LoadEntries(IEnumerable volumes);
+ ///
+ /// 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;
- 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(
- currentFilePartCompressedBytesRead: currentPartCompressedBytes,
- compressedBytesRead: compressedReadBytes
- )
- );
- }
-
- 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()
+ ///
+ /// 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 fc877cbd..146c8338 100644
--- a/src/SharpCompress/Archives/AbstractWritableArchive.cs
+++ b/src/SharpCompress/Archives/AbstractWritableArchive.cs
@@ -4,193 +4,191 @@ 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)
{
- private readonly AbstractWritableArchive archive;
-
- public RebuildPauseDisposable(AbstractWritableArchive archive)
- {
- this.archive = archive;
- archive.pauseRebuilding = true;
- }
-
- public void Dispose()
- {
- archive.pauseRebuilding = false;
- archive.RebuildModifiedCollection();
- }
+ this.archive = archive;
+ archive.pauseRebuilding = true;
}
- 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
+ public void Dispose()
{
- 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()
+ {
+ 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());
+ }
}
diff --git a/src/SharpCompress/Archives/ArchiveFactory.cs b/src/SharpCompress/Archives/ArchiveFactory.cs
index f5c5cdee..a246bad9 100644
--- a/src/SharpCompress/Archives/ArchiveFactory.cs
+++ b/src/SharpCompress/Archives/ArchiveFactory.cs
@@ -2,234 +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);
+ 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");
}
- ///
- /// Extract to specific directory, retaining filename
- ///
- public static void WriteToDirectory(
- string sourceArchive,
- string destinationDirectory,
- ExtractionOptions? options = null
- )
+ var factories = Factory.Factories.OfType();
+
+ var startPosition = stream.Position;
+
+ foreach (var factory in factories)
{
- using IArchive archive = Open(sourceArchive);
- foreach (IArchiveEntry entry in archive.Entries)
- {
- entry.WriteToDirectory(destinationDirectory, options);
- }
- }
+ stream.Seek(startPosition, SeekOrigin.Begin);
- private static T FindFactory(FileInfo finfo) where T : IFactory
- {
- finfo.CheckNotNull(nameof(finfo));
- using (Stream stream = finfo.OpenRead())
- {
- return FindFactory(stream);
- }
- }
-
- 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/GZip/GZipArchive.cs b/src/SharpCompress/Archives/GZip/GZipArchive.cs
index ed1b6a6b..4100f2f2 100644
--- a/src/SharpCompress/Archives/GZip/GZipArchive.cs
+++ b/src/SharpCompress/Archives/GZip/GZipArchive.cs
@@ -10,223 +10,216 @@ 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()
+ {
+ 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();
+ var 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)
{
- 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 23316046..68032131 100644
--- a/src/SharpCompress/Archives/GZip/GZipArchiveEntry.cs
+++ b/src/SharpCompress/Archives/GZip/GZipArchiveEntry.cs
@@ -1,33 +1,32 @@
-using System.IO;
+using System.IO;
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)
{
- internal GZipArchiveEntry(GZipArchive archive, GZipFilePart part) : base(part)
- {
- Archive = archive;
- }
-
- 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
+ Archive = archive;
}
+
+ 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
}
diff --git a/src/SharpCompress/Archives/GZip/GZipWritableArchiveEntry.cs b/src/SharpCompress/Archives/GZip/GZipWritableArchiveEntry.cs
index 4f35f24d..7685ceb1 100644
--- a/src/SharpCompress/Archives/GZip/GZipWritableArchiveEntry.cs
+++ b/src/SharpCompress/Archives/GZip/GZipWritableArchiveEntry.cs
@@ -6,68 +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 acb2bf11..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; }
- }
+ ///
+ /// 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 e3351582..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; }
- }
+ ///
+ /// 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 d5eac3f9..cd49cef0 100644
--- a/src/SharpCompress/Archives/IArchiveEntryExtensions.cs
+++ b/src/SharpCompress/Archives/IArchiveEntryExtensions.cs
@@ -1,79 +1,74 @@
-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;
}
+ using (entryStream)
+ {
+ using Stream s = new ListeningStream(streamListener, entryStream);
+ s.TransferTo(streamToWriteTo);
+ }
+ streamListener.FireEntryExtractionEnd(archiveEntry);
+ }
- ///
- /// Extract to specific file
- ///
- public static void WriteToFile(
- this IArchiveEntry entry,
- string destinationFileName,
- ExtractionOptions? options = null
- )
- {
- ExtractionMethods.WriteEntryToFile(
- entry,
- destinationFileName,
- options,
- (x, fm) =>
- {
- using (FileStream fs = File.Open(destinationFileName, fm))
- {
- entry.WriteTo(fs);
- }
- }
- );
- }
+ ///
+ /// 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 c0a9e1dd..14a48dbd 100644
--- a/src/SharpCompress/Archives/IArchiveExtensions.cs
+++ b/src/SharpCompress/Archives/IArchiveExtensions.cs
@@ -1,23 +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);
}
}
}
diff --git a/src/SharpCompress/Archives/IArchiveExtractionListener.cs b/src/SharpCompress/Archives/IArchiveExtractionListener.cs
index b352691e..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);
- }
+ 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 6ba64079..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 7610b146..be2eefb8 100644
--- a/src/SharpCompress/Archives/IMultiArchiveFactory.cs
+++ b/src/SharpCompress/Archives/IMultiArchiveFactory.cs
@@ -1,43 +1,39 @@
-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 02b76f66..37b84aa0 100644
--- a/src/SharpCompress/Archives/IWritableArchive.cs
+++ b/src/SharpCompress/Archives/IWritableArchive.cs
@@ -2,26 +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();
- }
+ ///
+ /// 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 f3d5eb26..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; }
- }
+ Stream Stream { get; }
}
diff --git a/src/SharpCompress/Archives/IWritableArchiveExtensions.cs b/src/SharpCompress/Archives/IWritableArchiveExtensions.cs
index 1827e9e8..e534b306 100644
--- a/src/SharpCompress/Archives/IWritableArchiveExtensions.cs
+++ b/src/SharpCompress/Archives/IWritableArchiveExtensions.cs
@@ -1,94 +1,91 @@
-using System;
+using System;
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
- )
+ 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
+ )
+ {
+ using (writableArchive.PauseEntryRebuilding())
{
- using (var stream = fileInfo.Open(FileMode.Create, FileAccess.Write))
+ foreach (
+ var path in Directory.EnumerateFiles(filePath, searchPattern, searchOption)
+ )
{
- writableArchive.SaveTo(stream, options);
+ var fileInfo = new FileInfo(path);
+ writableArchive.AddEntry(
+ path.Substring(filePath.Length),
+ fileInfo.OpenRead(),
+ true,
+ fileInfo.Length,
+ fileInfo.LastWriteTime
+ );
}
}
-
- public static void AddAllFromDirectory(
- this IWritableArchive writableArchive,
- string filePath,
- string searchPattern = "*.*",
- SearchOption searchOption = SearchOption.AllDirectories
- )
- {
- using (writableArchive.PauseEntryRebuilding())
- {
- 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
- );
- }
- }
- }
-
- 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
- );
- }
+ }
+
+ 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 78ec29e8..0f1acd3f 100644
--- a/src/SharpCompress/Archives/Rar/FileInfoRarArchiveVolume.cs
+++ b/src/SharpCompress/Archives/Rar/FileInfoRarArchiveVolume.cs
@@ -6,45 +6,44 @@ 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();
- }
+ 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;
- }
+ private static ReaderOptions FixOptions(ReaderOptions options)
+ {
+ //make sure we're closing streams with fileinfo
+ options.LeaveStreamOpen = false;
+ return options;
+ }
- internal ReadOnlyCollection FileParts { get; }
+ internal ReadOnlyCollection FileParts { get; }
- internal FileInfo FileInfo { get; }
+ internal FileInfo FileInfo { get; }
- internal override RarFilePart CreateFilePart(MarkHeader markHeader, FileHeader fileHeader)
- {
- return new FileInfoRarFilePart(
- this,
- ReaderOptions.Password,
- markHeader,
- fileHeader,
- FileInfo
- );
- }
+ internal override RarFilePart CreateFilePart(MarkHeader markHeader, FileHeader fileHeader)
+ {
+ return new FileInfoRarFilePart(
+ this,
+ ReaderOptions.Password,
+ markHeader,
+ fileHeader,
+ FileInfo
+ );
+ }
- internal override IEnumerable ReadFileParts()
- {
- return FileParts;
- }
+ internal override IEnumerable ReadFileParts()
+ {
+ return FileParts;
}
}
diff --git a/src/SharpCompress/Archives/Rar/FileInfoRarFilePart.cs b/src/SharpCompress/Archives/Rar/FileInfoRarFilePart.cs
index 1908c8af..59a21334 100644
--- a/src/SharpCompress/Archives/Rar/FileInfoRarFilePart.cs
+++ b/src/SharpCompress/Archives/Rar/FileInfoRarFilePart.cs
@@ -1,26 +1,25 @@
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)
{
- internal FileInfoRarFilePart(
- FileInfoRarArchiveVolume volume,
- string? password,
- MarkHeader mh,
- FileHeader fh,
- FileInfo fi
- ) : base(mh, fh, volume.Index, volume.Stream, password)
- {
- FileInfo = fi;
- }
+ 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
+ {
+ get { return "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 41abe3f4..b0edf3f5 100644
--- a/src/SharpCompress/Archives/Rar/RarArchive.Extensions.cs
+++ b/src/SharpCompress/Archives/Rar/RarArchive.Extensions.cs
@@ -1,23 +1,22 @@
-using System.Linq;
+using System.Linq;
-namespace SharpCompress.Archives.Rar
+namespace SharpCompress.Archives.Rar;
+
+public static class RarArchiveExtensions
{
- 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)
{
- ///
- /// 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;
- }
+ return archive.Volumes.First().IsFirstVolume;
+ }
- ///
- /// RarArchive is part of a multi-part archive.
- ///
- public static bool IsMultipartVolume(this RarArchive archive)
- {
- return archive.Volumes.First().IsMultiVolume;
- }
+ ///
+ /// RarArchive is part of a multi-part archive.
+ ///
+ public static bool IsMultipartVolume(this RarArchive archive)
+ {
+ return archive.Volumes.First().IsMultiVolume;
}
}
diff --git a/src/SharpCompress/Archives/Rar/RarArchive.cs b/src/SharpCompress/Archives/Rar/RarArchive.cs
index 67a4d070..dad32f3c 100644
--- a/src/SharpCompress/Archives/Rar/RarArchive.cs
+++ b/src/SharpCompress/Archives/Rar/RarArchive.cs
@@ -9,179 +9,178 @@ 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)
{
- 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) { }
-
- protected override IEnumerable LoadEntries(IEnumerable volumes)
- {
- return RarArchiveEntryFactory.GetEntries(this, volumes, ReaderOptions);
- }
-
- 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
+ return RarArchiveEntryFactory.GetEntries(this, volumes, ReaderOptions);
}
+
+ protected override IEnumerable LoadVolumes(SourceStream 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;
+
+ return srcStream.Streams.Select(
+ a => new StreamRarArchiveVolume(a, ReaderOptions, idx++)
+ );
+ }
+ else //split mode or single file
+ {
+ return new StreamRarArchiveVolume(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));
+ 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)
+ {
+ 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
}
diff --git a/src/SharpCompress/Archives/Rar/RarArchiveEntry.cs b/src/SharpCompress/Archives/Rar/RarArchiveEntry.cs
index a5f4d038..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,96 +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 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
+ public override long Size
+ {
+ get
{
- get
- {
- CheckIncomplete();
- return parts.Select(fp => fp.FileHeader).Single(fh => !fh.IsSplitAfter).FileCrc;
- }
+ CheckIncomplete();
+ return parts.First().FileHeader.UncompressedSize;
}
+ }
- public override long Size
+ public override long CompressedSize
+ {
+ get
{
- get
- {
- CheckIncomplete();
- return parts.First().FileHeader.UncompressedSize;
- }
+ CheckIncomplete();
+ return parts.Aggregate(0L, (total, fp) => total + fp.FileHeader.CompressedSize);
}
+ }
- public override long CompressedSize
+ public Stream OpenEntryStream()
+ {
+ if (IsRarV3)
{
- 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)
- );
- }
-
return new RarStream(
- archive.UnpackV2017.Value,
+ archive.UnpackV1.Value,
FileHeader,
new MultiVolumeReadOnlyStream(Parts.Cast(), archive)
);
}
- public bool IsComplete
- {
- get
- {
- var headers = parts.Select(x => x.FileHeader);
- return !headers.First().IsSplitBefore && !headers.Last().IsSplitAfter;
- }
- }
+ return new RarStream(
+ archive.UnpackV2017.Value,
+ FileHeader,
+ new MultiVolumeReadOnlyStream(Parts.Cast(), archive)
+ );
+ }
- private void CheckIncomplete()
+ public bool IsComplete
+ {
+ get
{
- if (!readerOptions.DisableCheckIncomplete && !IsComplete)
- {
- throw new IncompleteArchiveException(
- "ArchiveEntry is incomplete and cannot perform this operation."
- );
- }
+ var headers = parts.Select(x => x.FileHeader);
+ return !headers.First().IsSplitBefore && !headers.Last().IsSplitAfter;
+ }
+ }
+
+ private void CheckIncomplete()
+ {
+ if (!readerOptions.DisableCheckIncomplete && !IsComplete)
+ {
+ throw new IncompleteArchiveException(
+ "ArchiveEntry is incomplete and cannot perform this operation."
+ );
}
}
}
diff --git a/src/SharpCompress/Archives/Rar/RarArchiveEntryFactory.cs b/src/SharpCompress/Archives/Rar/RarArchiveEntryFactory.cs
index e345e8d3..ec4ace7c 100644
--- a/src/SharpCompress/Archives/Rar/RarArchiveEntryFactory.cs
+++ b/src/SharpCompress/Archives/Rar/RarArchiveEntryFactory.cs
@@ -1,53 +1,52 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
using SharpCompress.Common.Rar;
using SharpCompress.Readers;
-namespace SharpCompress.Archives.Rar
+namespace SharpCompress.Archives.Rar;
+
+internal static class RarArchiveEntryFactory
{
- internal static class RarArchiveEntryFactory
+ private static IEnumerable GetFileParts(IEnumerable parts)
{
- private static IEnumerable GetFileParts(IEnumerable