diff --git a/src/SharpCompress/Algorithms/Adler32.cs b/src/SharpCompress/Algorithms/Adler32.cs
new file mode 100644
index 000000000..d0b3279fd
--- /dev/null
+++ b/src/SharpCompress/Algorithms/Adler32.cs
@@ -0,0 +1,285 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the GNU Affero General Public License, Version 3.
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+#if NETCOREAPP3_1
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+#endif
+
+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
+ {
+ ///
+ /// The default initial seed value of a Adler32 checksum calculation.
+ ///
+ public const uint SeedValue = 1U;
+
+#if NETCOREAPP3_1
+ private const int MinBufferSize = 64;
+#endif
+
+ // 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;
+
+ ///
+ /// Calculates the Adler32 checksum with the bytes taken from the span.
+ ///
+ /// The readonly span of bytes.
+ /// The .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static uint Calculate(ReadOnlySpan buffer)
+ {
+ return 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 .
+ public static uint Calculate(uint adler, ReadOnlySpan buffer)
+ {
+ if (buffer.IsEmpty)
+ {
+ return SeedValue;
+ }
+
+#if NETCOREAPP3_1
+ if (Sse3.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 NETCOREAPP3_1
+ private static unsafe uint CalculateSse(uint adler, ReadOnlySpan buffer)
+ {
+ uint s1 = adler & 0xFFFF;
+ uint s2 = (adler >> 16) & 0xFFFF;
+
+ // Process the data in blocks.
+ const int BLOCK_SIZE = 1 << 5;
+
+ uint length = (uint)buffer.Length;
+ uint blocks = length / BLOCK_SIZE;
+ length -= blocks * BLOCK_SIZE;
+
+ int index = 0;
+ fixed (byte* bufferPtr = &buffer[0])
+ {
+ index += (int)blocks * BLOCK_SIZE;
+ var localBufferPtr = bufferPtr;
+
+ // _mm_setr_epi8 on x86
+ var tap1 = Vector128.Create(32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17);
+ var tap2 = Vector128.Create(16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1);
+ Vector128 zero = Vector128.Zero;
+ var ones = Vector128.Create((short)1);
+
+ while (blocks > 0)
+ {
+ uint n = NMAX / BLOCK_SIZE; /* 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).AsInt32();
+ Vector128 v_s2 = Vector128.CreateScalar(s2).AsInt32();
+ Vector128 v_s1 = Vector128.Zero;
+
+ do
+ {
+ // Load 32 input bytes.
+ Vector128 bytes1 = Sse3.LoadDquVector128(localBufferPtr);
+ Vector128 bytes2 = Sse3.LoadDquVector128(localBufferPtr + 16);
+
+ // 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).AsInt32());
+ Vector128 mad1 = Ssse3.MultiplyAddAdjacent(bytes1, tap1);
+ v_s2 = Sse2.Add(v_s2, Sse2.MultiplyAddAdjacent(mad1, ones));
+
+ v_s1 = Sse2.Add(v_s1, Sse2.SumAbsoluteDifferences(bytes2, zero).AsInt32());
+ Vector128 mad2 = Ssse3.MultiplyAddAdjacent(bytes2, tap2);
+ v_s2 = Sse2.Add(v_s2, Sse2.MultiplyAddAdjacent(mad2, ones));
+
+ localBufferPtr += BLOCK_SIZE;
+ }
+ 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, S2301));
+ v_s1 = Sse2.Add(v_s1, Sse2.Shuffle(v_s1, S1032));
+
+ s1 += (uint)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 = (uint)v_s2.ToScalar();
+
+ // Reduce.
+ s1 %= BASE;
+ s2 %= BASE;
+ }
+ }
+
+ ref byte bufferRef = ref MemoryMarshal.GetReference(buffer);
+
+ if (length > 0)
+ {
+ if (length >= 16)
+ {
+ s1 += Unsafe.Add(ref bufferRef, index++);
+ s2 += s1;
+ s1 += Unsafe.Add(ref bufferRef, index++);
+ s2 += s1;
+ s1 += Unsafe.Add(ref bufferRef, index++);
+ s2 += s1;
+ s1 += Unsafe.Add(ref bufferRef, index++);
+ s2 += s1;
+ s1 += Unsafe.Add(ref bufferRef, index++);
+ s2 += s1;
+ s1 += Unsafe.Add(ref bufferRef, index++);
+ s2 += s1;
+ s1 += Unsafe.Add(ref bufferRef, index++);
+ s2 += s1;
+ s1 += Unsafe.Add(ref bufferRef, index++);
+ s2 += s1;
+ s1 += Unsafe.Add(ref bufferRef, index++);
+ s2 += s1;
+ s1 += Unsafe.Add(ref bufferRef, index++);
+ s2 += s1;
+ s1 += Unsafe.Add(ref bufferRef, index++);
+ s2 += s1;
+ s1 += Unsafe.Add(ref bufferRef, index++);
+ s2 += s1;
+ s1 += Unsafe.Add(ref bufferRef, index++);
+ s2 += s1;
+ s1 += Unsafe.Add(ref bufferRef, index++);
+ s2 += s1;
+ s1 += Unsafe.Add(ref bufferRef, index++);
+ s2 += s1;
+ s1 += Unsafe.Add(ref bufferRef, index++);
+ s2 += s1;
+ length -= 16;
+ }
+
+ while (length-- > 0)
+ {
+ s2 += s1 += Unsafe.Add(ref bufferRef, index++);
+ }
+
+ if (s1 >= BASE)
+ {
+ s1 -= BASE;
+ }
+
+ s2 %= BASE;
+ }
+
+ return s1 | (s2 << 16);
+ }
+#endif
+
+ private static uint CalculateScalar(uint adler, ReadOnlySpan buffer)
+ {
+ uint s1 = adler & 0xFFFF;
+ uint s2 = (adler >> 16) & 0xFFFF;
+ uint k;
+
+ ref byte bufferRef = ref MemoryMarshal.GetReference(buffer);
+ uint length = (uint)buffer.Length;
+ int index = 0;
+
+ while (length > 0)
+ {
+ k = length < NMAX ? length : NMAX;
+ length -= k;
+
+ while (k >= 16)
+ {
+ s1 += Unsafe.Add(ref bufferRef, index++);
+ s2 += s1;
+ s1 += Unsafe.Add(ref bufferRef, index++);
+ s2 += s1;
+ s1 += Unsafe.Add(ref bufferRef, index++);
+ s2 += s1;
+ s1 += Unsafe.Add(ref bufferRef, index++);
+ s2 += s1;
+ s1 += Unsafe.Add(ref bufferRef, index++);
+ s2 += s1;
+ s1 += Unsafe.Add(ref bufferRef, index++);
+ s2 += s1;
+ s1 += Unsafe.Add(ref bufferRef, index++);
+ s2 += s1;
+ s1 += Unsafe.Add(ref bufferRef, index++);
+ s2 += s1;
+ s1 += Unsafe.Add(ref bufferRef, index++);
+ s2 += s1;
+ s1 += Unsafe.Add(ref bufferRef, index++);
+ s2 += s1;
+ s1 += Unsafe.Add(ref bufferRef, index++);
+ s2 += s1;
+ s1 += Unsafe.Add(ref bufferRef, index++);
+ s2 += s1;
+ s1 += Unsafe.Add(ref bufferRef, index++);
+ s2 += s1;
+ s1 += Unsafe.Add(ref bufferRef, index++);
+ s2 += s1;
+ s1 += Unsafe.Add(ref bufferRef, index++);
+ s2 += s1;
+ s1 += Unsafe.Add(ref bufferRef, index++);
+ s2 += s1;
+ k -= 16;
+ }
+
+ if (k != 0)
+ {
+ do
+ {
+ s1 += Unsafe.Add(ref bufferRef, index++);
+ s2 += s1;
+ }
+ while (--k != 0);
+ }
+
+ s1 %= BASE;
+ s2 %= BASE;
+ }
+
+ return (s2 << 16) | s1;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/SharpCompress/Common/ArchiveEncoding.cs b/src/SharpCompress/Common/ArchiveEncoding.cs
index 1f0b6effa..1bb7ac71a 100644
--- a/src/SharpCompress/Common/ArchiveEncoding.cs
+++ b/src/SharpCompress/Common/ArchiveEncoding.cs
@@ -36,7 +36,7 @@ public ArchiveEncoding(Encoding def, Encoding password)
Password = password;
}
-#if NETSTANDARD1_3 || NETSTANDARD2_0 || NETSTANDARD2_1
+#if !NET46
static ArchiveEncoding()
{
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
diff --git a/src/SharpCompress/Common/ExtractionMethods.cs b/src/SharpCompress/Common/ExtractionMethods.cs
index 801661b08..4564df917 100644
--- a/src/SharpCompress/Common/ExtractionMethods.cs
+++ b/src/SharpCompress/Common/ExtractionMethods.cs
@@ -24,10 +24,8 @@ internal static class ExtractionMethods
if (options.ExtractFullPath)
{
- string folder = Path.GetDirectoryName(entry.Key);
- string destdir = Path.GetFullPath(
- Path.Combine(fullDestinationDirectoryPath, folder)
- );
+ string folder = Path.GetDirectoryName(entry.Key)!;
+ string destdir = Path.GetFullPath(Path.Combine(fullDestinationDirectoryPath, folder!));
if (!Directory.Exists(destdir))
{
diff --git a/src/SharpCompress/Common/Rar/Headers/NewSubHeaderType.cs b/src/SharpCompress/Common/Rar/Headers/NewSubHeaderType.cs
index 84d50aee2..f3d849553 100644
--- a/src/SharpCompress/Common/Rar/Headers/NewSubHeaderType.cs
+++ b/src/SharpCompress/Common/Rar/Headers/NewSubHeaderType.cs
@@ -47,8 +47,10 @@ internal bool Equals(byte[] bytes)
return true;
}
- public bool Equals(NewSubHeaderType other)
+ public bool Equals(NewSubHeaderType? other)
{
+ if (other is null) return false;
+
return Equals(other._bytes);
}
}
diff --git a/src/SharpCompress/Common/SevenZip/CMethodId.cs b/src/SharpCompress/Common/SevenZip/CMethodId.cs
index 03ce8df66..5069b051a 100644
--- a/src/SharpCompress/Common/SevenZip/CMethodId.cs
+++ b/src/SharpCompress/Common/SevenZip/CMethodId.cs
@@ -24,7 +24,7 @@ public override int GetHashCode()
return _id.GetHashCode();
}
- public override bool Equals(object obj)
+ public override bool Equals(object? obj)
{
return obj is CMethodId other && Equals(other);
}
diff --git a/src/SharpCompress/Compressors/Deflate/DeflateManager.cs b/src/SharpCompress/Compressors/Deflate/DeflateManager.cs
index c182d5e79..99aeccb7c 100644
--- a/src/SharpCompress/Compressors/Deflate/DeflateManager.cs
+++ b/src/SharpCompress/Compressors/Deflate/DeflateManager.cs
@@ -70,6 +70,8 @@
using System;
+using SharpCompress.Algorithms;
+
namespace SharpCompress.Compressors.Deflate
{
internal sealed partial class DeflateManager
@@ -1190,7 +1192,7 @@ private void _fillWindow()
// Otherwise, window_size == 2*WSIZE so more >= 2.
// If there was sliding, more >= WSIZE. So in all cases, more >= 2.
- n = _codec.read_buf(window, strstart + lookahead, more);
+ n = _codec.ReadBuffer(window, strstart + lookahead, more);
lookahead += n;
// Initialize the hash value now that we have some input:
@@ -1685,7 +1687,7 @@ internal void Reset()
Rfc1950BytesEmitted = false;
status = (WantRfc1950HeaderBytes) ? INIT_STATE : BUSY_STATE;
- _codec._Adler32 = Adler.Adler32(0, null, 0, 0);
+ _codec._Adler32 = 1;
last_flush = (int)FlushType.None;
@@ -1763,7 +1765,7 @@ internal int SetDictionary(byte[] dictionary)
throw new ZlibException("Stream error.");
}
- _codec._Adler32 = Adler.Adler32(_codec._Adler32, dictionary, 0, dictionary.Length);
+ _codec._Adler32 = Adler32.Calculate(dictionary.AsSpan());
if (length < MIN_MATCH)
{
@@ -1855,7 +1857,7 @@ internal int Deflate(FlushType flush)
pending[pendingCount++] = (byte)((_codec._Adler32 & 0x0000FF00) >> 8);
pending[pendingCount++] = (byte)(_codec._Adler32 & 0x000000FF);
}
- _codec._Adler32 = Adler.Adler32(0, null, 0, 0);
+ _codec._Adler32 = 1;
}
// Flush as much pending output as possible
diff --git a/src/SharpCompress/Compressors/Deflate/GZipStream.cs b/src/SharpCompress/Compressors/Deflate/GZipStream.cs
index def36904b..9d045d1ff 100644
--- a/src/SharpCompress/Compressors/Deflate/GZipStream.cs
+++ b/src/SharpCompress/Compressors/Deflate/GZipStream.cs
@@ -447,7 +447,7 @@ private int EmitHeader()
// filename
if (fnLength != 0)
{
- Array.Copy(filenameBytes, 0, header, i, fnLength - 1);
+ Array.Copy(filenameBytes!, 0, header, i, fnLength - 1);
i += fnLength - 1;
header[i++] = 0; // terminate
}
@@ -455,7 +455,7 @@ private int EmitHeader()
// comment
if (cbLength != 0)
{
- Array.Copy(commentBytes, 0, header, i, cbLength - 1);
+ Array.Copy(commentBytes!, 0, header, i, cbLength - 1);
i += cbLength - 1;
header[i++] = 0; // terminate
}
diff --git a/src/SharpCompress/Compressors/Deflate/Inflate.cs b/src/SharpCompress/Compressors/Deflate/Inflate.cs
index f9b66c0cb..e2e748d53 100644
--- a/src/SharpCompress/Compressors/Deflate/Inflate.cs
+++ b/src/SharpCompress/Compressors/Deflate/Inflate.cs
@@ -65,6 +65,8 @@
using System;
+using SharpCompress.Algorithms;
+
namespace SharpCompress.Compressors.Deflate
{
internal sealed class InflateBlocks
@@ -118,8 +120,9 @@ internal uint Reset()
if (checkfn != null)
{
- _codec._Adler32 = check = Adler.Adler32(0, null, 0, 0);
+ _codec._Adler32 = check = 1;
}
+
return oldCheck;
}
@@ -739,7 +742,7 @@ internal int Flush(int r)
// update check information
if (checkfn != null)
{
- _codec._Adler32 = check = Adler.Adler32(check, window, readAt, nBytes);
+ _codec._Adler32 = check = Adler32.Calculate(check, window.AsSpan(readAt, nBytes));
}
// copy as far as end of window
@@ -1879,12 +1882,12 @@ internal int SetDictionary(byte[] dictionary)
throw new ZlibException("Stream error.");
}
- if (Adler.Adler32(1, dictionary, 0, dictionary.Length) != _codec._Adler32)
+ if (Adler32.Calculate(1, dictionary.AsSpan()) != _codec._Adler32)
{
return ZlibConstants.Z_DATA_ERROR;
}
- _codec._Adler32 = Adler.Adler32(0, null, 0, 0);
+ _codec._Adler32 = 1;
if (length >= (1 << wbits))
{
diff --git a/src/SharpCompress/Compressors/Deflate/Zlib.cs b/src/SharpCompress/Compressors/Deflate/Zlib.cs
index d33cbcc9e..6133feb44 100644
--- a/src/SharpCompress/Compressors/Deflate/Zlib.cs
+++ b/src/SharpCompress/Compressors/Deflate/Zlib.cs
@@ -405,88 +405,4 @@ static StaticTree()
BitLengths = new StaticTree(null, extra_blbits, 0, InternalConstants.BL_CODES, InternalConstants.MAX_BL_BITS);
}
}
-
- ///
- /// Computes an Adler-32 checksum.
- ///
- ///
- /// The Adler checksum is similar to a CRC checksum, but faster to compute, though less
- /// reliable. It is used in producing RFC1950 compressed streams. The Adler checksum
- /// is a required part of the "ZLIB" standard. Applications will almost never need to
- /// use this class directly.
- ///
- internal sealed class Adler
- {
- // largest prime smaller than 65536
- private static readonly uint BASE = 65521U;
-
- // NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1
- private static readonly int NMAX = 5552;
-
- internal static uint Adler32(uint adler, byte[]? buf, int index, int len)
- {
- if (buf is null)
- {
- return 1;
- }
-
- uint s1 = adler & 0xffffU;
- uint s2 = (adler >> 16) & 0xffffU;
-
- while (len > 0)
- {
- int k = len < NMAX ? len : NMAX;
- len -= k;
- while (k >= 16)
- {
- //s1 += (buf[index++] & 0xff); s2 += s1;
- s1 += buf[index++];
- s2 += s1;
- s1 += buf[index++];
- s2 += s1;
- s1 += buf[index++];
- s2 += s1;
- s1 += buf[index++];
- s2 += s1;
- s1 += buf[index++];
- s2 += s1;
- s1 += buf[index++];
- s2 += s1;
- s1 += buf[index++];
- s2 += s1;
- s1 += buf[index++];
- s2 += s1;
- s1 += buf[index++];
- s2 += s1;
- s1 += buf[index++];
- s2 += s1;
- s1 += buf[index++];
- s2 += s1;
- s1 += buf[index++];
- s2 += s1;
- s1 += buf[index++];
- s2 += s1;
- s1 += buf[index++];
- s2 += s1;
- s1 += buf[index++];
- s2 += s1;
- s1 += buf[index++];
- s2 += s1;
- k -= 16;
- }
- if (k != 0)
- {
- do
- {
- s1 += buf[index++];
- s2 += s1;
- }
- while (--k != 0);
- }
- s1 %= BASE;
- s2 %= BASE;
- }
- return (s2 << 16) | s1;
- }
- }
}
\ No newline at end of file
diff --git a/src/SharpCompress/Compressors/Deflate/ZlibCodec.cs b/src/SharpCompress/Compressors/Deflate/ZlibCodec.cs
index e7e2ecb9a..95f87dff1 100644
--- a/src/SharpCompress/Compressors/Deflate/ZlibCodec.cs
+++ b/src/SharpCompress/Compressors/Deflate/ZlibCodec.cs
@@ -67,6 +67,8 @@
using System;
+using SharpCompress.Algorithms;
+
namespace SharpCompress.Compressors.Deflate
{
///
@@ -173,7 +175,7 @@ internal sealed class ZlibCodec
///
/// The Adler32 checksum on the data transferred through the codec so far. You probably don't need to look at this.
///
- public int Adler32 => (int)_Adler32;
+ public int Adler32Checksum => (int)_Adler32;
///
/// Create a ZlibCodec.
@@ -720,7 +722,7 @@ OutputBuffer.Length < (NextOut + len))
// this function so some applications may wish to modify it to avoid
// allocating a large strm->next_in buffer and copying from it.
// (See also flush_pending()).
- internal int read_buf(byte[] buf, int start, int size)
+ internal int ReadBuffer(byte[] buf, int start, int size)
{
int len = AvailableBytesIn;
@@ -728,7 +730,8 @@ internal int read_buf(byte[] buf, int start, int size)
{
len = size;
}
- if (len == 0)
+
+ if (len > 0)
{
return 0;
}
@@ -737,8 +740,9 @@ internal int read_buf(byte[] buf, int start, int size)
if (dstate.WantRfc1950HeaderBytes)
{
- _Adler32 = Adler.Adler32(_Adler32, InputBuffer, NextIn, len);
+ _Adler32 = Adler32.Calculate(_Adler32, InputBuffer.AsSpan(NextIn, len));
}
+
Array.Copy(InputBuffer, NextIn, buf, start, len);
NextIn += len;
TotalBytesIn += len;
diff --git a/src/SharpCompress/Compressors/Xz/Filters/BlockFilter.cs b/src/SharpCompress/Compressors/Xz/Filters/BlockFilter.cs
index bf6d85e57..875d1b44c 100644
--- a/src/SharpCompress/Compressors/Xz/Filters/BlockFilter.cs
+++ b/src/SharpCompress/Compressors/Xz/Filters/BlockFilter.cs
@@ -39,7 +39,7 @@ public static BlockFilter Read(BinaryReader reader)
throw new NotImplementedException($"Filter {filterType} has not yet been implemented");
}
- var filter = (BlockFilter)Activator.CreateInstance(FilterMap[filterType]);
+ var filter = (BlockFilter)Activator.CreateInstance(FilterMap[filterType])!;
var sizeOfProperties = reader.ReadXZInteger();
if (sizeOfProperties > int.MaxValue)
diff --git a/src/SharpCompress/Polyfills/StreamExtensions.cs b/src/SharpCompress/Polyfills/StreamExtensions.cs
index be97c6391..e3285e8ff 100644
--- a/src/SharpCompress/Polyfills/StreamExtensions.cs
+++ b/src/SharpCompress/Polyfills/StreamExtensions.cs
@@ -1,4 +1,4 @@
-#if !NETSTANDARD2_1
+#if NETSTANDARD2_0 || NET46
using System.Buffers;
diff --git a/src/SharpCompress/SharpCompress.csproj b/src/SharpCompress/SharpCompress.csproj
index 992438a36..e1e9b5316 100644
--- a/src/SharpCompress/SharpCompress.csproj
+++ b/src/SharpCompress/SharpCompress.csproj
@@ -6,8 +6,8 @@
0.26.0
0.26.0
Adam Hathcock
- netstandard2.0;netstandard2.1;net46
- netstandard2.0;netstandard2.1
+ netstandard2.0;netstandard2.1;netcoreapp3.1;net46
+ netstandard2.0;netcoreapp3.1;netstandard2.1
true
false
SharpCompress
@@ -21,11 +21,12 @@
false
false
SharpCompress is a compression library for NET Standard 2.0/2.1//NET 4.6 that can unrar, decompress 7zip, decompress xz, zip/unzip, tar/untar lzip/unlzip, bzip2/unbzip2 and gzip/ungzip with forward-only reading and file random access APIs. Write support for zip/tar/bzip2/gzip is implemented.
- 8
+ 9
enable
true
true
snupkg
+ true