mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-02-04 05:25:00 +00:00
ZStandard tar support
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
| Tar | None | Both | TarArchive | TarReader | TarWriter (3) |
|
||||
| Tar.GZip | DEFLATE | Both | TarArchive | TarReader | TarWriter (3) |
|
||||
| Tar.BZip2 | BZip2 | Both | TarArchive | TarReader | TarWriter (3) |
|
||||
| Tar.Zstandard | ZStandard | Decompress | TarArchive | TarReader | N/A |
|
||||
| Tar.LZip | LZMA | Both | TarArchive | TarReader | TarWriter (3) |
|
||||
| Tar.XZ | LZMA2 | Decompress | TarArchive | TarReader | TarWriter (3) |
|
||||
| GZip (single file) | DEFLATE | Both | GZipArchive | GZipReader | GZipWriter |
|
||||
@@ -41,6 +42,7 @@ For those who want to directly compress/decompress bits. The single file formats
|
||||
| ADCStream | Decompress |
|
||||
| LZipStream | Both |
|
||||
| XZStream | Decompress |
|
||||
| ZStandardStream | Decompress |
|
||||
|
||||
## Archive Formats vs Compression
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# SharpCompress
|
||||
|
||||
SharpCompress is a compression library in pure C# for .NET Framework 4.62, .NET Standard 2.1, .NET 6.0 and NET 8.0 that can unrar, un7zip, unzip, untar unbzip2, ungzip, unlzip with forward-only reading and file random access APIs. Write support for zip/tar/bzip2/gzip/lzip are implemented.
|
||||
SharpCompress is a compression library in pure C# for .NET Framework 4.62, .NET Standard 2.1, .NET 6.0 and NET 8.0 that can unrar, un7zip, unzip, untar unbzip2, ungzip, unlzip, unzstd with forward-only reading and file random access APIs. Write support for zip/tar/bzip2/gzip/lzip are implemented.
|
||||
|
||||
The major feature is support for non-seekable streams so large files can be processed on the fly (i.e. download stream).
|
||||
|
||||
@@ -20,10 +20,12 @@ In general, I recommend GZip (Deflate)/BZip2 (BZip)/LZip (LZMA) as the simplicit
|
||||
|
||||
Zip is okay, but it's a very hap-hazard format and the variation in headers and implementations makes it hard to get correct. Uses Deflate by default but supports a lot of compression methods.
|
||||
|
||||
RAR is not recommended as it's a propriatory format and the compression is closed source. Use Tar/LZip for LZMA
|
||||
RAR is not recommended as it's a proprietary format and the compression is closed source. Use Tar/LZip for LZMA
|
||||
|
||||
7Zip and XZ both are overly complicated. 7Zip does not support streamable formats. XZ has known holes explained here: (http://www.nongnu.org/lzip/xz_inadequate.html) Use Tar/LZip for LZMA compression instead.
|
||||
|
||||
ZStandard is an efficient format that works well for streaming with a flexible compression level to tweak the speed/performance trade off you are looking for. We currently only implement decompression for ZStandard but as we leverage the [ZstdSharp](https://github.com/oleg-st/ZstdSharp) library one could likely add compression support without much trouble (PRs are welcome!).
|
||||
|
||||
## A Simple Request
|
||||
|
||||
Hi everyone. I hope you're using SharpCompress and finding it useful. Please give me feedback on what you'd like to see changed especially as far as usability goes. New feature suggestions are always welcome as well. I would also like to know what projects SharpCompress is being used in. I like seeing how it is used to give me ideas for future versions. Thanks!
|
||||
@@ -40,6 +42,7 @@ I'm always looking for help or ideas. Please submit code or email with ideas. Un
|
||||
* 7Zip writing
|
||||
* Zip64 (Need writing and extend Reading)
|
||||
* Multi-volume Zip support.
|
||||
* ZStandard writing
|
||||
|
||||
## Version Log
|
||||
|
||||
|
||||
65
src/SharpCompress/Compressors/ZStandard/ZStandardStream.cs
Normal file
65
src/SharpCompress/Compressors/ZStandard/ZStandardStream.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Compressors.ZStandard;
|
||||
|
||||
internal class ZStandardStream : ZstdSharp.DecompressionStream, IStreamStack
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
long IStreamStack.InstanceId { get; set; }
|
||||
#endif
|
||||
|
||||
public int DefaultBufferSize { get; set; }
|
||||
int IStreamStack.BufferSize
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
int IStreamStack.BufferPosition
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
|
||||
private readonly Stream stream;
|
||||
|
||||
Stream IStreamStack.BaseStream() => stream;
|
||||
|
||||
void IStreamStack.SetPosition(long position) { }
|
||||
|
||||
internal static bool IsZStandard(Stream stream)
|
||||
{
|
||||
var br = new BinaryReader(stream);
|
||||
var magic = br.ReadUInt32();
|
||||
if (ZstandardConstants.MAGIC != magic)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public ZStandardStream(Stream baseInputStream)
|
||||
: base(baseInputStream)
|
||||
{
|
||||
this.stream = baseInputStream;
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(ZStandardStream));
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The current position within the stream.
|
||||
/// Throws a NotSupportedException when attempting to set the position
|
||||
/// </summary>
|
||||
/// <exception cref="NotSupportedException">Attempting to set the position</exception>
|
||||
public override long Position
|
||||
{
|
||||
get { return stream.Position; }
|
||||
set { throw new NotSupportedException("InflaterInputStream Position not supported"); }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SharpCompress.Compressors.ZStandard;
|
||||
|
||||
internal class ZstandardConstants
|
||||
{
|
||||
/// <summary>
|
||||
/// Magic number found at start of ZStandard frame: 0xFD 0x2F 0xB5 0x28
|
||||
/// </summary>
|
||||
public const uint MAGIC = 0xFD2FB528;
|
||||
}
|
||||
@@ -8,6 +8,7 @@ using SharpCompress.Compressors.BZip2;
|
||||
using SharpCompress.Compressors.LZMA;
|
||||
using SharpCompress.Compressors.Lzw;
|
||||
using SharpCompress.Compressors.Xz;
|
||||
using SharpCompress.Compressors.ZStandard;
|
||||
using SharpCompress.IO;
|
||||
using SharpCompress.Readers;
|
||||
using SharpCompress.Readers.Tar;
|
||||
@@ -63,7 +64,7 @@ public class TarFactory
|
||||
yield return "taZ";
|
||||
|
||||
// zstd
|
||||
// yield return "tzst"; // unsupported
|
||||
yield return "tzst";
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -135,6 +136,21 @@ public class TarFactory
|
||||
}
|
||||
}
|
||||
|
||||
((IStreamStack)rewindableStream).StackSeek(pos);
|
||||
if (ZStandardStream.IsZStandard(rewindableStream))
|
||||
{
|
||||
((IStreamStack)rewindableStream).StackSeek(pos);
|
||||
var testStream = new ZStandardStream(
|
||||
SharpCompressStream.Create(rewindableStream, leaveOpen: true)
|
||||
);
|
||||
if (TarArchive.IsTarFile(testStream))
|
||||
{
|
||||
((IStreamStack)rewindableStream).StackSeek(pos);
|
||||
reader = new TarReader(rewindableStream, options, CompressionType.ZStandard);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
((IStreamStack)rewindableStream).StackSeek(pos);
|
||||
if (LZipStream.IsLZipFile(rewindableStream))
|
||||
{
|
||||
|
||||
28
src/SharpCompress/Factories/ZStandardFactory.cs
Normal file
28
src/SharpCompress/Factories/ZStandardFactory.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Compressors.ZStandard;
|
||||
using SharpCompress.Readers;
|
||||
|
||||
namespace SharpCompress.Factories;
|
||||
|
||||
internal class ZStandardFactory : Factory
|
||||
{
|
||||
public override string Name => "ZStandard";
|
||||
|
||||
public override IEnumerable<string> GetSupportedExtensions()
|
||||
{
|
||||
yield return "zst";
|
||||
yield return "zstd";
|
||||
}
|
||||
|
||||
public override bool IsArchive(
|
||||
Stream stream,
|
||||
string? password = null,
|
||||
int bufferSize = 65536
|
||||
) => ZStandardStream.IsZStandard(stream);
|
||||
}
|
||||
@@ -32,7 +32,7 @@ public static class ReaderFactory
|
||||
}
|
||||
|
||||
throw new InvalidFormatException(
|
||||
"Cannot determine compressed stream type. Supported Reader Formats: Arc, Zip, GZip, BZip2, Tar, Rar, LZip, XZ"
|
||||
"Cannot determine compressed stream type. Supported Reader Formats: Arc, Zip, GZip, BZip2, Tar, Rar, LZip, XZ, ZStandard"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ using SharpCompress.Compressors.Deflate;
|
||||
using SharpCompress.Compressors.LZMA;
|
||||
using SharpCompress.Compressors.Lzw;
|
||||
using SharpCompress.Compressors.Xz;
|
||||
using SharpCompress.Compressors.ZStandard;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Readers.Tar;
|
||||
@@ -35,6 +36,7 @@ public class TarReader : AbstractReader<TarEntry, TarVolume>
|
||||
{
|
||||
CompressionType.BZip2 => new BZip2Stream(stream, CompressionMode.Decompress, false),
|
||||
CompressionType.GZip => new GZipStream(stream, CompressionMode.Decompress),
|
||||
CompressionType.ZStandard => new ZStandardStream(stream),
|
||||
CompressionType.LZip => new LZipStream(stream, CompressionMode.Decompress),
|
||||
CompressionType.Xz => new XZStream(stream),
|
||||
CompressionType.Lzw => new LzwStream(stream),
|
||||
@@ -84,6 +86,18 @@ public class TarReader : AbstractReader<TarEntry, TarVolume>
|
||||
throw new InvalidFormatException("Not a tar file.");
|
||||
}
|
||||
|
||||
((IStreamStack)rewindableStream).StackSeek(pos);
|
||||
if (ZStandardStream.IsZStandard(rewindableStream))
|
||||
{
|
||||
((IStreamStack)rewindableStream).StackSeek(pos);
|
||||
var testStream = new ZStandardStream(rewindableStream);
|
||||
if (TarArchive.IsTarFile(testStream))
|
||||
{
|
||||
((IStreamStack)rewindableStream).StackSeek(pos);
|
||||
return new TarReader(rewindableStream, options, CompressionType.ZStandard);
|
||||
}
|
||||
throw new InvalidFormatException("Not a tar file.");
|
||||
}
|
||||
((IStreamStack)rewindableStream).StackSeek(pos);
|
||||
if (LZipStream.IsLZipFile(rewindableStream))
|
||||
{
|
||||
|
||||
@@ -49,6 +49,9 @@ public class TarReaderTests : ReaderTests
|
||||
[Fact]
|
||||
public void Tar_GZip_Reader() => Read("Tar.tar.gz", CompressionType.GZip);
|
||||
|
||||
[Fact]
|
||||
public void Tar_ZStandard_Reader() => Read("Tar.tar.zst", CompressionType.ZStandard);
|
||||
|
||||
[Fact]
|
||||
public void Tar_LZip_Reader() => Read("Tar.tar.lz", CompressionType.LZip);
|
||||
|
||||
|
||||
BIN
tests/TestArchives/Archives/Tar.tar.zst
Normal file
BIN
tests/TestArchives/Archives/Tar.tar.zst
Normal file
Binary file not shown.
Reference in New Issue
Block a user