mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-02-04 21:21:49 +00:00
Compare commits
19 Commits
0.19.1
...
presentati
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
56be4d8921 | ||
|
|
4eb3148c26 | ||
|
|
e95d543ecd | ||
|
|
33af3d552b | ||
|
|
359a6042cd | ||
|
|
e27d2ec660 | ||
|
|
da56bfc01f | ||
|
|
6e2c7d2857 | ||
|
|
5481609554 | ||
|
|
a62f4df0b1 | ||
|
|
f893c1272c | ||
|
|
e701f5277e | ||
|
|
f85fd1f6a4 | ||
|
|
8f7ea420b3 | ||
|
|
d8c8dabb52 | ||
|
|
9092ecf331 | ||
|
|
2fd9fe96ad | ||
|
|
02f68b793c | ||
|
|
57b9133a0f |
@@ -2,10 +2,14 @@ version: 2
|
||||
jobs:
|
||||
build:
|
||||
docker:
|
||||
- image: adamhathcock/cake-build:latest
|
||||
- image: microsoft/dotnet:2.0.5-sdk-2.1.4
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Install unzip
|
||||
command: |
|
||||
apt-get update
|
||||
apt-get install -y unzip
|
||||
- run:
|
||||
name: Build
|
||||
command: ./build.sh
|
||||
|
||||
command: ./build.sh
|
||||
@@ -11,7 +11,7 @@
|
||||
| Archive Format | Compression Format(s) | Compress/Decompress | Archive API | Reader API | Writer API |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| Rar | Rar | Decompress (1) | RarArchive | RarReader | N/A |
|
||||
| Zip (2) | None, DEFLATE, BZip2, LZMA/LZMA2, PPMd | Both | ZipArchive | ZipReader | ZipWriter |
|
||||
| Zip (2) | None, DEFLATE, Deflate64, BZip2, LZMA/LZMA2, PPMd | Both | ZipArchive | ZipReader | ZipWriter |
|
||||
| Tar | None | Both | TarArchive | TarReader | TarWriter (3) |
|
||||
| Tar.GZip | DEFLATE | Both | TarArchive | TarReader | TarWriter (3) |
|
||||
| Tar.BZip2 | BZip2 | Both | TarArchive | TarReader | TarWriter (3) |
|
||||
@@ -22,7 +22,7 @@
|
||||
| LZip (single file) (5) | LZip (LZMA) | Both | LZipArchive | LZipReader | LZipWriter |
|
||||
|
||||
1. SOLID Rars are only supported in the RarReader API.
|
||||
2. Zip format supports pkware and WinzipAES encryption. However, encrypted LZMA is not supported. Zip64 reading/writing is supported but only with seekable streams as the Zip spec doesn't support Zip64 data in post data descriptors.
|
||||
2. Zip format supports pkware and WinzipAES encryption. However, encrypted LZMA is not supported. Zip64 reading/writing is supported but only with seekable streams as the Zip spec doesn't support Zip64 data in post data descriptors. Deflate64 is only supported for reading.
|
||||
3. The Tar format requires a file size in the header. If no size is specified to the TarWriter and the stream is not seekable, then an exception will be thrown.
|
||||
4. The 7Zip format doesn't allow for reading as a forward-only stream so 7Zip is only supported through the Archive API
|
||||
5. LZip has no support for extra data like the file name or timestamp. There is a default filename used when looking at the entry Key on the archive.
|
||||
@@ -36,6 +36,7 @@ For those who want to directly compress/decompress bits. The single file format
|
||||
| BZip2Stream | Both |
|
||||
| GZipStream | Both |
|
||||
| DeflateStream | Both |
|
||||
| Deflate64Stream | Decompress |
|
||||
| LZMAStream | Both |
|
||||
| PPMdStream | Both |
|
||||
| ADCStream | Decompress |
|
||||
|
||||
247
PITCHME.md
Normal file
247
PITCHME.md
Normal file
@@ -0,0 +1,247 @@
|
||||
|
||||
#### SharpCompress - Pure C# Archival and Compression
|
||||
|
||||
---
|
||||
|
||||
#### Overview
|
||||
|
||||
* History
|
||||
* Design
|
||||
* Archival Formats
|
||||
* Usages (Code!)
|
||||
|
||||
---
|
||||
|
||||
#### Why?
|
||||
|
||||
* Bored
|
||||
* Interested in Comics and wanted to make my own cross-platform viewer
|
||||
* Wrote a viewer in Sliverlight 2 using first versions of SharpCompress.
|
||||
* Used it on OS X
|
||||
|
||||
---
|
||||
|
||||
#### Initial Version
|
||||
|
||||
* Started as NUnrar on Codeplex
|
||||
* Used Visual Studio 2003 to convert JUnrar to C#
|
||||
* Cleaned up to have a nicer API
|
||||
|
||||
---
|
||||
|
||||
### More Formats
|
||||
|
||||
* Integrated DotNetZip
|
||||
* Created Unified API
|
||||
* Added Tar
|
||||
* Contributions: 7Zip, LZip, more!
|
||||
|
||||
---
|
||||
|
||||
# Design
|
||||
|
||||
---
|
||||
|
||||
### Unified APIs
|
||||
|
||||
* Random Access
|
||||
* Archive API
|
||||
* Forward-only
|
||||
* Reader API
|
||||
* Writer API
|
||||
* Neutral Factories
|
||||
|
||||
---
|
||||
|
||||
### Neural Factories
|
||||
|
||||
* Factories
|
||||
* `ArchiveFactory`
|
||||
* `ReaderFactory`
|
||||
* `WriterFactory`
|
||||
* Strategy
|
||||
* Look for Archive Signatures
|
||||
* "Rewind" if necessary with RewindableStream
|
||||
|
||||
---
|
||||
|
||||
### Random Access
|
||||
|
||||
* Random/Seekable access on a data stream (e.g. a File)
|
||||
* Strategy
|
||||
* Read Header, Skip Data
|
||||
* Dictionary
|
||||
|
||||
---
|
||||
|
||||
### Forward-only
|
||||
|
||||
* Everything is a stream of data
|
||||
* Support NetworkStreams
|
||||
* Very large files
|
||||
* `yield return` usage
|
||||
|
||||
---
|
||||
|
||||
# Formats
|
||||
|
||||
---
|
||||
|
||||
### Zip
|
||||
|
||||
* Header-Data Format
|
||||
* Optional data trailer (forward-only writing support)
|
||||
* Trailing dictionary of entries
|
||||
* pkware spec - APPNOTE.txt
|
||||
* Supports Reader API, Writer API and Archive API
|
||||
* Compression algorthims: just about everything
|
||||
* Deflate, BZ2, LZMA 1/2, PPMd
|
||||
|
||||
---
|
||||
|
||||
### Rar
|
||||
|
||||
* Header-Data Format
|
||||
* SOLID is a stream of compressed header-data pairs for small files
|
||||
* Multi-file archive
|
||||
* Unrar open-source, rar is closed-source
|
||||
* Supports Reader API and Archive API
|
||||
* Compression looks to be a modification of PPMd
|
||||
|
||||
---
|
||||
|
||||
### 7Zip
|
||||
|
||||
* Multi-data compressed Format
|
||||
* Headers are compressed
|
||||
* Multiple compressed "streams"
|
||||
* Readable Archive API support
|
||||
* Annoying
|
||||
* Known for LZMA
|
||||
|
||||
---
|
||||
|
||||
### Tar
|
||||
|
||||
* Header-Data Format
|
||||
* Supports Reader API, Writer API and Archive API
|
||||
* Uncompressed
|
||||
* Many additions to out-grow limitations
|
||||
* UStar
|
||||
* PAX
|
||||
|
||||
---
|
||||
|
||||
### GZip, BZip, LZip, Xz
|
||||
|
||||
* Header-Data Format of a single entry
|
||||
* Supports Reader API, Writer API and Archive API
|
||||
* Used with Tar
|
||||
* Compression
|
||||
* GZip - Deflate
|
||||
* BZip2 - BZip2
|
||||
* Xz - LZMA2
|
||||
* LZip - LZMA1 (improvement on Xz)
|
||||
|
||||
---
|
||||
|
||||
# Usages
|
||||
|
||||
---
|
||||
|
||||
### Reader
|
||||
|
||||
Writing entry to directory
|
||||
|
||||
```csharp
|
||||
using (Stream stream = new NetworkStream()) // pretend
|
||||
using (IReader reader = ReaderFactory.Open(stream))
|
||||
{
|
||||
while (reader.MoveToNextEntry())
|
||||
{
|
||||
if (!reader.Entry.IsDirectory)
|
||||
{
|
||||
reader.WriteEntryToDirectory(test.SCRATCH_FILES_PATH, new ExtractionOptions()
|
||||
{
|
||||
ExtractFullPath = true,
|
||||
Overwrite = true
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Reader
|
||||
|
||||
Writing entry to a stream
|
||||
|
||||
```csharp
|
||||
using (var reader = RarReader.Open("Rar.rar"))
|
||||
{
|
||||
while (reader.MoveToNextEntry())
|
||||
{
|
||||
if (!reader.Entry.IsDirectory)
|
||||
{
|
||||
using (var entryStream = reader.OpenEntryStream())
|
||||
{
|
||||
string file = Path.GetFileName(reader.Entry.Key);
|
||||
string folder = Path.GetDirectoryName(reader.Entry.Key);
|
||||
string destdir = Path.Combine(SCRATCH_FILES_PATH, folder);
|
||||
if (!Directory.Exists(destdir))
|
||||
{
|
||||
Directory.CreateDirectory(destdir);
|
||||
}
|
||||
string destinationFileName = Path.Combine(destdir, file);
|
||||
using (FileStream fs = File.OpenWrite(destinationFileName))
|
||||
{
|
||||
entryStream.TransferTo(fs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Writer
|
||||
|
||||
Creating archive
|
||||
|
||||
```csharp
|
||||
using (Stream stream = File.OpenWrite("Test.tar.lz"))
|
||||
using (var writer = WriterFactory.Open(stream, ArchiveType.Tar, CompressionType.LZip))
|
||||
{
|
||||
writer.WriteAll("C:\", "*", SearchOption.AllDirectories);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Archive
|
||||
|
||||
---
|
||||
|
||||
### Projects
|
||||
|
||||
* Mono's Zip implementation
|
||||
* Nodatime
|
||||
* Octopus Deploy
|
||||
* Duplicati
|
||||
* Large ISO multi-file usage
|
||||
|
||||
---
|
||||
|
||||
### Open-source Notes
|
||||
|
||||
* Mostly solo effort
|
||||
* A few significant contributions
|
||||
* Russian friend did RarStream
|
||||
* Jon Skeet contributed LZip reading
|
||||
* Deflate64 recently added
|
||||
* Can always use help!
|
||||
* Multi-file zip support
|
||||
* Encryption in various formats (some support exists)
|
||||
* General clean up
|
||||
@@ -45,10 +45,14 @@
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/INDENT_ANONYMOUS_METHOD_BLOCK/@EntryValue">True</s:Boolean>
|
||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_BLANK_LINES_IN_CODE/@EntryValue">1</s:Int64>
|
||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_BLANK_LINES_IN_DECLARATIONS/@EntryValue">1</s:Int64>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_ACCESSOR_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_ACCESSORHOLDER_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_CONSTRUCTOR_INITIALIZER_ON_SAME_LINE/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_FIELD_ATTRIBUTE_ON_SAME_LINE/@EntryValue">False</s:Boolean>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_FIELD_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_SIMPLE_ACCESSORHOLDER_ON_SINGLE_LINE/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_SIMPLE_ACCESSOR_ATTRIBUTE_ON_SAME_LINE/@EntryValue">False</s:Boolean>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_SIMPLE_EMBEDDED_STATEMENT_ON_SAME_LINE/@EntryValue">NEVER</s:String>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_SIMPLE_INITIALIZER_ON_SINGLE_LINE/@EntryValue">True</s:Boolean>
|
||||
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_WHILE_ON_NEW_LINE/@EntryValue">True</s:Boolean>
|
||||
@@ -115,6 +119,10 @@
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/UserRules/=XAML_005FFIELD/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/UserRules/=XAML_005FRESOURCE/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpAttributeForSingleLineMethodUpgrade/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpRenamePlacementToArrangementMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAddAccessorOwnerDeclarationBracesMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
|
||||
13
build.cake
13
build.cake
@@ -25,7 +25,8 @@ Task("Build")
|
||||
var settings = new DotNetCoreBuildSettings
|
||||
{
|
||||
Framework = "netstandard1.0",
|
||||
Configuration = "Release"
|
||||
Configuration = "Release",
|
||||
NoRestore = true
|
||||
};
|
||||
|
||||
DotNetCoreBuild("./src/SharpCompress/SharpCompress.csproj", settings);
|
||||
@@ -50,16 +51,6 @@ Task("Test")
|
||||
Configuration = "Release",
|
||||
Framework = "netcoreapp2.0"
|
||||
};
|
||||
|
||||
DotNetCoreTest(file.ToString(), settings);
|
||||
|
||||
|
||||
settings = new DotNetCoreTestSettings
|
||||
{
|
||||
Configuration = "Release",
|
||||
Framework = "netcoreapp1.1"
|
||||
};
|
||||
|
||||
DotNetCoreTest(file.ToString(), settings);
|
||||
}
|
||||
});
|
||||
|
||||
2
build.sh
2
build.sh
@@ -8,7 +8,7 @@
|
||||
# Define directories.
|
||||
SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
|
||||
TOOLS_DIR=$SCRIPT_DIR/tools
|
||||
CAKE_VERSION=0.23.0
|
||||
CAKE_VERSION=0.26.0
|
||||
CAKE_DLL=$TOOLS_DIR/Cake.CoreCLR.$CAKE_VERSION/Cake.dll
|
||||
|
||||
# Make sure the tools folder exist.
|
||||
|
||||
@@ -182,7 +182,7 @@ namespace SharpCompress.Archives.Tar
|
||||
IEnumerable<TarArchiveEntry> oldEntries,
|
||||
IEnumerable<TarArchiveEntry> newEntries)
|
||||
{
|
||||
using (var writer = new TarWriter(stream, options))
|
||||
using (var writer = new TarWriter(stream, new TarWriterOptions(options)))
|
||||
{
|
||||
foreach (var entry in oldEntries.Concat(newEntries)
|
||||
.Where(x => !x.IsDirectory))
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace System.Buffers
|
||||
namespace SharpCompress.Buffers
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a resource pool that enables reusing instances of type <see cref="T:T[]"/>.
|
||||
@@ -20,7 +20,7 @@ namespace System.Buffers
|
||||
/// This class is thread-safe. All members may be used by multiple threads concurrently.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public abstract class ArrayPool<T>
|
||||
internal abstract class ArrayPool<T>
|
||||
{
|
||||
/// <summary>The lazily-initialized shared pool instance.</summary>
|
||||
private static ArrayPool<T> s_sharedInstance = null;
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
#if NETCORE
|
||||
namespace System.Buffers
|
||||
using System;
|
||||
|
||||
namespace SharpCompress.Buffers
|
||||
{
|
||||
internal sealed partial class DefaultArrayPool<T> : ArrayPool<T>
|
||||
{
|
||||
|
||||
@@ -3,10 +3,11 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
#if NETCORE
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
|
||||
namespace System.Buffers
|
||||
namespace SharpCompress.Buffers
|
||||
{
|
||||
internal sealed partial class DefaultArrayPool<T> : ArrayPool<T>
|
||||
{
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace System.Buffers
|
||||
namespace SharpCompress.Buffers
|
||||
{
|
||||
internal static class Utilities
|
||||
{
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
BCJ2,
|
||||
LZip,
|
||||
Xz,
|
||||
Unknown
|
||||
Unknown,
|
||||
Deflate64
|
||||
}
|
||||
}
|
||||
@@ -65,6 +65,12 @@ namespace SharpCompress.Common
|
||||
/// </summary>
|
||||
public abstract bool IsSplit { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
return this.Key;
|
||||
}
|
||||
|
||||
internal abstract IEnumerable<FilePart> Parts { get; }
|
||||
internal bool IsSolid { get; set; }
|
||||
|
||||
|
||||
@@ -32,6 +32,10 @@ namespace SharpCompress.Common.Zip
|
||||
{
|
||||
return CompressionType.Deflate;
|
||||
}
|
||||
case ZipCompressionMethod.Deflate64:
|
||||
{
|
||||
return CompressionType.Deflate64;
|
||||
}
|
||||
case ZipCompressionMethod.LZMA:
|
||||
{
|
||||
return CompressionType.LZMA;
|
||||
|
||||
@@ -5,6 +5,7 @@ using SharpCompress.Common.Zip.Headers;
|
||||
using SharpCompress.Compressors;
|
||||
using SharpCompress.Compressors.BZip2;
|
||||
using SharpCompress.Compressors.Deflate;
|
||||
using SharpCompress.Compressors.Deflate64;
|
||||
using SharpCompress.Compressors.LZMA;
|
||||
using SharpCompress.Compressors.PPMd;
|
||||
using SharpCompress.Converters;
|
||||
@@ -66,6 +67,10 @@ namespace SharpCompress.Common.Zip
|
||||
{
|
||||
return new DeflateStream(stream, CompressionMode.Decompress);
|
||||
}
|
||||
case ZipCompressionMethod.Deflate64:
|
||||
{
|
||||
return new Deflate64Stream(stream, CompressionMode.Decompress);
|
||||
}
|
||||
case ZipCompressionMethod.BZip2:
|
||||
{
|
||||
return new BZip2Stream(stream, CompressionMode.Decompress);
|
||||
|
||||
13
src/SharpCompress/Compressors/Deflate64/BlockType.cs
Normal file
13
src/SharpCompress/Compressors/Deflate64/BlockType.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace SharpCompress.Compressors.Deflate64
|
||||
{
|
||||
internal enum BlockType
|
||||
{
|
||||
Uncompressed = 0,
|
||||
Static = 1,
|
||||
Dynamic = 2
|
||||
}
|
||||
}
|
||||
257
src/SharpCompress/Compressors/Deflate64/Deflate64Stream.cs
Normal file
257
src/SharpCompress/Compressors/Deflate64/Deflate64Stream.cs
Normal file
@@ -0,0 +1,257 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using SharpCompress.Common.Zip;
|
||||
using SharpCompress.Compressors.Deflate;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace SharpCompress.Compressors.Deflate64
|
||||
{
|
||||
public sealed partial class Deflate64Stream : Stream
|
||||
{
|
||||
internal const int DefaultBufferSize = 8192;
|
||||
|
||||
private Stream _stream;
|
||||
private CompressionMode _mode;
|
||||
private bool _leaveOpen;
|
||||
private InflaterManaged _inflater;
|
||||
private byte[] _buffer;
|
||||
|
||||
public Deflate64Stream(Stream stream, CompressionMode mode,
|
||||
CompressionLevel level = CompressionLevel.Default,
|
||||
bool leaveOpen = false)
|
||||
{
|
||||
if (stream == null)
|
||||
throw new ArgumentNullException(nameof(stream));
|
||||
if (mode != CompressionMode.Decompress)
|
||||
throw new NotImplementedException("Deflate64: this implementation only supports decompression");
|
||||
if (!stream.CanRead)
|
||||
throw new ArgumentException("Deflate64: input stream is not readable", nameof(stream));
|
||||
|
||||
InitializeInflater(stream, leaveOpen, ZipCompressionMethod.Deflate64);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets up this DeflateManagedStream to be used for Inflation/Decompression
|
||||
/// </summary>
|
||||
internal void InitializeInflater(Stream stream, bool leaveOpen, ZipCompressionMethod method = ZipCompressionMethod.Deflate)
|
||||
{
|
||||
Debug.Assert(stream != null);
|
||||
Debug.Assert(method == ZipCompressionMethod.Deflate || method == ZipCompressionMethod.Deflate64);
|
||||
if (!stream.CanRead)
|
||||
throw new ArgumentException("Deflate64: input stream is not readable", nameof(stream));
|
||||
|
||||
_inflater = new InflaterManaged(method == ZipCompressionMethod.Deflate64);
|
||||
|
||||
_stream = stream;
|
||||
_mode = CompressionMode.Decompress;
|
||||
_leaveOpen = leaveOpen;
|
||||
_buffer = new byte[DefaultBufferSize];
|
||||
}
|
||||
|
||||
public override bool CanRead
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_stream == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return (_mode == CompressionMode.Decompress && _stream.CanRead);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanWrite
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_stream == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return (_mode == CompressionMode.Compress && _stream.CanWrite);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanSeek => false;
|
||||
|
||||
public override long Length
|
||||
{
|
||||
get { throw new NotSupportedException("Deflate64: not supported"); }
|
||||
}
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get { throw new NotSupportedException("Deflate64: not supported"); }
|
||||
set { throw new NotSupportedException("Deflate64: not supported"); }
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
EnsureNotDisposed();
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException("Deflate64: not supported");
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotSupportedException("Deflate64: not supported");
|
||||
}
|
||||
|
||||
public override int Read(byte[] array, int offset, int count)
|
||||
{
|
||||
EnsureDecompressionMode();
|
||||
ValidateParameters(array, offset, count);
|
||||
EnsureNotDisposed();
|
||||
|
||||
int bytesRead;
|
||||
int currentOffset = offset;
|
||||
int remainingCount = count;
|
||||
|
||||
while (true)
|
||||
{
|
||||
bytesRead = _inflater.Inflate(array, currentOffset, remainingCount);
|
||||
currentOffset += bytesRead;
|
||||
remainingCount -= bytesRead;
|
||||
|
||||
if (remainingCount == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (_inflater.Finished())
|
||||
{
|
||||
// if we finished decompressing, we can't have anything left in the outputwindow.
|
||||
Debug.Assert(_inflater.AvailableOutput == 0, "We should have copied all stuff out!");
|
||||
break;
|
||||
}
|
||||
|
||||
int bytes = _stream.Read(_buffer, 0, _buffer.Length);
|
||||
if (bytes <= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (bytes > _buffer.Length)
|
||||
{
|
||||
// The stream is either malicious or poorly implemented and returned a number of
|
||||
// bytes larger than the buffer supplied to it.
|
||||
throw new InvalidDataException("Deflate64: invalid data");
|
||||
}
|
||||
|
||||
_inflater.SetInput(_buffer, 0, bytes);
|
||||
}
|
||||
|
||||
return count - remainingCount;
|
||||
}
|
||||
|
||||
private void ValidateParameters(byte[] array, int offset, int count)
|
||||
{
|
||||
if (array == null)
|
||||
throw new ArgumentNullException(nameof(array));
|
||||
|
||||
if (offset < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(offset));
|
||||
|
||||
if (count < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(count));
|
||||
|
||||
if (array.Length - offset < count)
|
||||
throw new ArgumentException("Deflate64: invalid offset/count combination");
|
||||
}
|
||||
|
||||
private void EnsureNotDisposed()
|
||||
{
|
||||
if (_stream == null)
|
||||
ThrowStreamClosedException();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static void ThrowStreamClosedException()
|
||||
{
|
||||
throw new ObjectDisposedException(null, "Deflate64: stream has been disposed");
|
||||
}
|
||||
|
||||
private void EnsureDecompressionMode()
|
||||
{
|
||||
if (_mode != CompressionMode.Decompress)
|
||||
ThrowCannotReadFromDeflateManagedStreamException();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static void ThrowCannotReadFromDeflateManagedStreamException()
|
||||
{
|
||||
throw new InvalidOperationException("Deflate64: cannot read from this stream");
|
||||
}
|
||||
|
||||
private void EnsureCompressionMode()
|
||||
{
|
||||
if (_mode != CompressionMode.Compress)
|
||||
ThrowCannotWriteToDeflateManagedStreamException();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static void ThrowCannotWriteToDeflateManagedStreamException()
|
||||
{
|
||||
throw new InvalidOperationException("Deflate64: cannot write to this stream");
|
||||
}
|
||||
|
||||
public override void Write(byte[] array, int offset, int count)
|
||||
{
|
||||
ThrowCannotWriteToDeflateManagedStreamException();
|
||||
}
|
||||
|
||||
// This is called by Dispose:
|
||||
private void PurgeBuffers(bool disposing)
|
||||
{
|
||||
if (!disposing)
|
||||
return;
|
||||
|
||||
if (_stream == null)
|
||||
return;
|
||||
|
||||
Flush();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
try
|
||||
{
|
||||
PurgeBuffers(disposing);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Close the underlying stream even if PurgeBuffers threw.
|
||||
// Stream.Close() may throw here (may or may not be due to the same error).
|
||||
// In this case, we still need to clean up internal resources, hence the inner finally blocks.
|
||||
try
|
||||
{
|
||||
if (disposing && !_leaveOpen && _stream != null)
|
||||
_stream.Dispose();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_stream = null;
|
||||
|
||||
try
|
||||
{
|
||||
_inflater?.Dispose();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_inflater = null;
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
43
src/SharpCompress/Compressors/Deflate64/DeflateInput.cs
Normal file
43
src/SharpCompress/Compressors/Deflate64/DeflateInput.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace SharpCompress.Compressors.Deflate64
|
||||
{
|
||||
internal sealed class DeflateInput
|
||||
{
|
||||
internal byte[] Buffer { get; set; }
|
||||
internal int Count { get; set; }
|
||||
internal int StartIndex { get; set; }
|
||||
|
||||
internal void ConsumeBytes(int n)
|
||||
{
|
||||
Debug.Assert(n <= Count, "Should use more bytes than what we have in the buffer");
|
||||
StartIndex += n;
|
||||
Count -= n;
|
||||
Debug.Assert(StartIndex + Count <= Buffer.Length, "Input buffer is in invalid state!");
|
||||
}
|
||||
|
||||
internal InputState DumpState() => new InputState(Count, StartIndex);
|
||||
|
||||
internal void RestoreState(InputState state)
|
||||
{
|
||||
Count = state._count;
|
||||
StartIndex = state._startIndex;
|
||||
}
|
||||
|
||||
internal /*readonly */struct InputState
|
||||
{
|
||||
internal readonly int _count;
|
||||
internal readonly int _startIndex;
|
||||
|
||||
internal InputState(int count, int startIndex)
|
||||
{
|
||||
_count = count;
|
||||
_startIndex = startIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
245
src/SharpCompress/Compressors/Deflate64/FastEncoderStatus.cs
Normal file
245
src/SharpCompress/Compressors/Deflate64/FastEncoderStatus.cs
Normal file
@@ -0,0 +1,245 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace SharpCompress.Compressors.Deflate64
|
||||
{
|
||||
internal static class FastEncoderStatics
|
||||
{
|
||||
// static information for encoding, DO NOT MODIFY
|
||||
|
||||
internal static readonly byte[] FastEncoderTreeStructureData =
|
||||
{
|
||||
0xec,0xbd,0x07,0x60,0x1c,0x49,0x96,0x25,0x26,0x2f,0x6d,0xca,
|
||||
0x7b,0x7f,0x4a,0xf5,0x4a,0xd7,0xe0,0x74,0xa1,0x08,0x80,0x60,
|
||||
0x13,0x24,0xd8,0x90,0x40,0x10,0xec,0xc1,0x88,0xcd,0xe6,0x92,
|
||||
0xec,0x1d,0x69,0x47,0x23,0x29,0xab,0x2a,0x81,0xca,0x65,0x56,
|
||||
0x65,0x5d,0x66,0x16,0x40,0xcc,0xed,0x9d,0xbc,0xf7,0xde,0x7b,
|
||||
0xef,0xbd,0xf7,0xde,0x7b,0xef,0xbd,0xf7,0xba,0x3b,0x9d,0x4e,
|
||||
0x27,0xf7,0xdf,0xff,0x3f,0x5c,0x66,0x64,0x01,0x6c,0xf6,0xce,
|
||||
0x4a,0xda,0xc9,0x9e,0x21,0x80,0xaa,0xc8,0x1f,0x3f,0x7e,0x7c,
|
||||
0x1f,0x3f
|
||||
};
|
||||
|
||||
internal static readonly byte[] BFinalFastEncoderTreeStructureData =
|
||||
{
|
||||
0xed,0xbd,0x07,0x60,0x1c,0x49,0x96,0x25,0x26,0x2f,0x6d,0xca,
|
||||
0x7b,0x7f,0x4a,0xf5,0x4a,0xd7,0xe0,0x74,0xa1,0x08,0x80,0x60,
|
||||
0x13,0x24,0xd8,0x90,0x40,0x10,0xec,0xc1,0x88,0xcd,0xe6,0x92,
|
||||
0xec,0x1d,0x69,0x47,0x23,0x29,0xab,0x2a,0x81,0xca,0x65,0x56,
|
||||
0x65,0x5d,0x66,0x16,0x40,0xcc,0xed,0x9d,0xbc,0xf7,0xde,0x7b,
|
||||
0xef,0xbd,0xf7,0xde,0x7b,0xef,0xbd,0xf7,0xba,0x3b,0x9d,0x4e,
|
||||
0x27,0xf7,0xdf,0xff,0x3f,0x5c,0x66,0x64,0x01,0x6c,0xf6,0xce,
|
||||
0x4a,0xda,0xc9,0x9e,0x21,0x80,0xaa,0xc8,0x1f,0x3f,0x7e,0x7c,
|
||||
0x1f,0x3f
|
||||
};
|
||||
|
||||
// Output a currentMatch with length matchLen (>= MIN_MATCH) and displacement matchPos
|
||||
//
|
||||
// Optimisation: unlike the other encoders, here we have an array of codes for each currentMatch
|
||||
// length (not just each currentMatch length slot), complete with all the extra bits filled in, in
|
||||
// a single array element.
|
||||
//
|
||||
// There are many advantages to doing this:
|
||||
//
|
||||
// 1. A single array lookup on g_FastEncoderLiteralCodeInfo, instead of separate array lookups
|
||||
// on g_LengthLookup (to get the length slot), g_FastEncoderLiteralTreeLength,
|
||||
// g_FastEncoderLiteralTreeCode, g_ExtraLengthBits, and g_BitMask
|
||||
//
|
||||
// 2. The array is an array of ULONGs, so no access penalty, unlike for accessing those USHORT
|
||||
// code arrays in the other encoders (although they could be made into ULONGs with some
|
||||
// modifications to the source).
|
||||
//
|
||||
// Note, if we could guarantee that codeLen <= 16 always, then we could skip an if statement here.
|
||||
//
|
||||
// A completely different optimisation is used for the distance codes since, obviously, a table for
|
||||
// all 8192 distances combining their extra bits is not feasible. The distance codeinfo table is
|
||||
// made up of code[], len[] and # extraBits for this code.
|
||||
//
|
||||
// The advantages are similar to the above; a ULONG array instead of a USHORT and BYTE array, better
|
||||
// cache locality, fewer memory operations.
|
||||
//
|
||||
|
||||
|
||||
// Encoding information for literal and Length.
|
||||
// The least 5 significant bits are the length
|
||||
// and the rest is the code bits.
|
||||
|
||||
internal static readonly uint[] FastEncoderLiteralCodeInfo =
|
||||
{
|
||||
0x0000d7ee,0x0004d7ee,0x0002d7ee,0x0006d7ee,0x0001d7ee,0x0005d7ee,0x0003d7ee,
|
||||
0x0007d7ee,0x000037ee,0x0000c7ec,0x00000126,0x000437ee,0x000237ee,0x000637ee,
|
||||
0x000137ee,0x000537ee,0x000337ee,0x000737ee,0x0000b7ee,0x0004b7ee,0x0002b7ee,
|
||||
0x0006b7ee,0x0001b7ee,0x0005b7ee,0x0003b7ee,0x0007b7ee,0x000077ee,0x000477ee,
|
||||
0x000277ee,0x000677ee,0x000017ed,0x000177ee,0x00000526,0x000577ee,0x000023ea,
|
||||
0x0001c7ec,0x000377ee,0x000777ee,0x000217ed,0x000063ea,0x00000b68,0x00000ee9,
|
||||
0x00005beb,0x000013ea,0x00000467,0x00001b68,0x00000c67,0x00002ee9,0x00000768,
|
||||
0x00001768,0x00000f68,0x00001ee9,0x00001f68,0x00003ee9,0x000053ea,0x000001e9,
|
||||
0x000000e8,0x000021e9,0x000011e9,0x000010e8,0x000031e9,0x000033ea,0x000008e8,
|
||||
0x0000f7ee,0x0004f7ee,0x000018e8,0x000009e9,0x000004e8,0x000029e9,0x000014e8,
|
||||
0x000019e9,0x000073ea,0x0000dbeb,0x00000ce8,0x00003beb,0x0002f7ee,0x000039e9,
|
||||
0x00000bea,0x000005e9,0x00004bea,0x000025e9,0x000027ec,0x000015e9,0x000035e9,
|
||||
0x00000de9,0x00002bea,0x000127ec,0x0000bbeb,0x0006f7ee,0x0001f7ee,0x0000a7ec,
|
||||
0x00007beb,0x0005f7ee,0x0000fbeb,0x0003f7ee,0x0007f7ee,0x00000fee,0x00000326,
|
||||
0x00000267,0x00000a67,0x00000667,0x00000726,0x00001ce8,0x000002e8,0x00000e67,
|
||||
0x000000a6,0x0001a7ec,0x00002de9,0x000004a6,0x00000167,0x00000967,0x000002a6,
|
||||
0x00000567,0x000117ed,0x000006a6,0x000001a6,0x000005a6,0x00000d67,0x000012e8,
|
||||
0x00000ae8,0x00001de9,0x00001ae8,0x000007eb,0x000317ed,0x000067ec,0x000097ed,
|
||||
0x000297ed,0x00040fee,0x00020fee,0x00060fee,0x00010fee,0x00050fee,0x00030fee,
|
||||
0x00070fee,0x00008fee,0x00048fee,0x00028fee,0x00068fee,0x00018fee,0x00058fee,
|
||||
0x00038fee,0x00078fee,0x00004fee,0x00044fee,0x00024fee,0x00064fee,0x00014fee,
|
||||
0x00054fee,0x00034fee,0x00074fee,0x0000cfee,0x0004cfee,0x0002cfee,0x0006cfee,
|
||||
0x0001cfee,0x0005cfee,0x0003cfee,0x0007cfee,0x00002fee,0x00042fee,0x00022fee,
|
||||
0x00062fee,0x00012fee,0x00052fee,0x00032fee,0x00072fee,0x0000afee,0x0004afee,
|
||||
0x0002afee,0x0006afee,0x0001afee,0x0005afee,0x0003afee,0x0007afee,0x00006fee,
|
||||
0x00046fee,0x00026fee,0x00066fee,0x00016fee,0x00056fee,0x00036fee,0x00076fee,
|
||||
0x0000efee,0x0004efee,0x0002efee,0x0006efee,0x0001efee,0x0005efee,0x0003efee,
|
||||
0x0007efee,0x00001fee,0x00041fee,0x00021fee,0x00061fee,0x00011fee,0x00051fee,
|
||||
0x00031fee,0x00071fee,0x00009fee,0x00049fee,0x00029fee,0x00069fee,0x00019fee,
|
||||
0x00059fee,0x00039fee,0x00079fee,0x00005fee,0x00045fee,0x00025fee,0x00065fee,
|
||||
0x00015fee,0x00055fee,0x00035fee,0x00075fee,0x0000dfee,0x0004dfee,0x0002dfee,
|
||||
0x0006dfee,0x0001dfee,0x0005dfee,0x0003dfee,0x0007dfee,0x00003fee,0x00043fee,
|
||||
0x00023fee,0x00063fee,0x00013fee,0x00053fee,0x00033fee,0x00073fee,0x0000bfee,
|
||||
0x0004bfee,0x0002bfee,0x0006bfee,0x0001bfee,0x0005bfee,0x0003bfee,0x0007bfee,
|
||||
0x00007fee,0x00047fee,0x00027fee,0x00067fee,0x00017fee,0x000197ed,0x000397ed,
|
||||
0x000057ed,0x00057fee,0x000257ed,0x00037fee,0x000157ed,0x00077fee,0x000357ed,
|
||||
0x0000ffee,0x0004ffee,0x0002ffee,0x0006ffee,0x0001ffee,0x00000084,0x00000003,
|
||||
0x00000184,0x00000044,0x00000144,0x000000c5,0x000002c5,0x000001c5,0x000003c6,
|
||||
0x000007c6,0x00000026,0x00000426,0x000003a7,0x00000ba7,0x000007a7,0x00000fa7,
|
||||
0x00000227,0x00000627,0x00000a27,0x00000e27,0x00000068,0x00000868,0x00001068,
|
||||
0x00001868,0x00000369,0x00001369,0x00002369,0x00003369,0x000006ea,0x000026ea,
|
||||
0x000046ea,0x000066ea,0x000016eb,0x000036eb,0x000056eb,0x000076eb,0x000096eb,
|
||||
0x0000b6eb,0x0000d6eb,0x0000f6eb,0x00003dec,0x00007dec,0x0000bdec,0x0000fdec,
|
||||
0x00013dec,0x00017dec,0x0001bdec,0x0001fdec,0x00006bed,0x0000ebed,0x00016bed,
|
||||
0x0001ebed,0x00026bed,0x0002ebed,0x00036bed,0x0003ebed,0x000003ec,0x000043ec,
|
||||
0x000083ec,0x0000c3ec,0x000103ec,0x000143ec,0x000183ec,0x0001c3ec,0x00001bee,
|
||||
0x00009bee,0x00011bee,0x00019bee,0x00021bee,0x00029bee,0x00031bee,0x00039bee,
|
||||
0x00041bee,0x00049bee,0x00051bee,0x00059bee,0x00061bee,0x00069bee,0x00071bee,
|
||||
0x00079bee,0x000167f0,0x000367f0,0x000567f0,0x000767f0,0x000967f0,0x000b67f0,
|
||||
0x000d67f0,0x000f67f0,0x001167f0,0x001367f0,0x001567f0,0x001767f0,0x001967f0,
|
||||
0x001b67f0,0x001d67f0,0x001f67f0,0x000087ef,0x000187ef,0x000287ef,0x000387ef,
|
||||
0x000487ef,0x000587ef,0x000687ef,0x000787ef,0x000887ef,0x000987ef,0x000a87ef,
|
||||
0x000b87ef,0x000c87ef,0x000d87ef,0x000e87ef,0x000f87ef,0x0000e7f0,0x0002e7f0,
|
||||
0x0004e7f0,0x0006e7f0,0x0008e7f0,0x000ae7f0,0x000ce7f0,0x000ee7f0,0x0010e7f0,
|
||||
0x0012e7f0,0x0014e7f0,0x0016e7f0,0x0018e7f0,0x001ae7f0,0x001ce7f0,0x001ee7f0,
|
||||
0x0005fff3,0x000dfff3,0x0015fff3,0x001dfff3,0x0025fff3,0x002dfff3,0x0035fff3,
|
||||
0x003dfff3,0x0045fff3,0x004dfff3,0x0055fff3,0x005dfff3,0x0065fff3,0x006dfff3,
|
||||
0x0075fff3,0x007dfff3,0x0085fff3,0x008dfff3,0x0095fff3,0x009dfff3,0x00a5fff3,
|
||||
0x00adfff3,0x00b5fff3,0x00bdfff3,0x00c5fff3,0x00cdfff3,0x00d5fff3,0x00ddfff3,
|
||||
0x00e5fff3,0x00edfff3,0x00f5fff3,0x00fdfff3,0x0003fff3,0x000bfff3,0x0013fff3,
|
||||
0x001bfff3,0x0023fff3,0x002bfff3,0x0033fff3,0x003bfff3,0x0043fff3,0x004bfff3,
|
||||
0x0053fff3,0x005bfff3,0x0063fff3,0x006bfff3,0x0073fff3,0x007bfff3,0x0083fff3,
|
||||
0x008bfff3,0x0093fff3,0x009bfff3,0x00a3fff3,0x00abfff3,0x00b3fff3,0x00bbfff3,
|
||||
0x00c3fff3,0x00cbfff3,0x00d3fff3,0x00dbfff3,0x00e3fff3,0x00ebfff3,0x00f3fff3,
|
||||
0x00fbfff3,0x0007fff3,0x000ffff3,0x0017fff3,0x001ffff3,0x0027fff3,0x002ffff3,
|
||||
0x0037fff3,0x003ffff3,0x0047fff3,0x004ffff3,0x0057fff3,0x005ffff3,0x0067fff3,
|
||||
0x006ffff3,0x0077fff3,0x007ffff3,0x0087fff3,0x008ffff3,0x0097fff3,0x009ffff3,
|
||||
0x00a7fff3,0x00affff3,0x00b7fff3,0x00bffff3,0x00c7fff3,0x00cffff3,0x00d7fff3,
|
||||
0x00dffff3,0x00e7fff3,0x00effff3,0x00f7fff3,0x00fffff3,0x0001e7f1,0x0003e7f1,
|
||||
0x0005e7f1,0x0007e7f1,0x0009e7f1,0x000be7f1,0x000de7f1,0x000fe7f1,0x0011e7f1,
|
||||
0x0013e7f1,0x0015e7f1,0x0017e7f1,0x0019e7f1,0x001be7f1,0x001de7f1,0x001fe7f1,
|
||||
0x0021e7f1,0x0023e7f1,0x0025e7f1,0x0027e7f1,0x0029e7f1,0x002be7f1,0x002de7f1,
|
||||
0x002fe7f1,0x0031e7f1,0x0033e7f1,0x0035e7f1,0x0037e7f1,0x0039e7f1,0x003be7f1,
|
||||
0x003de7f1,0x000047eb
|
||||
};
|
||||
|
||||
internal static readonly uint[] FastEncoderDistanceCodeInfo =
|
||||
{
|
||||
0x00000f06,0x0001ff0a,0x0003ff0b,0x0007ff0b,0x0000ff19,0x00003f18,0x0000bf28,
|
||||
0x00007f28,0x00001f37,0x00005f37,0x00000d45,0x00002f46,0x00000054,0x00001d55,
|
||||
0x00000864,0x00000365,0x00000474,0x00001375,0x00000c84,0x00000284,0x00000a94,
|
||||
0x00000694,0x00000ea4,0x000001a4,0x000009b4,0x00000bb5,0x000005c4,0x00001bc5,
|
||||
0x000007d5,0x000017d5,0x00000000,0x00000100
|
||||
};
|
||||
|
||||
internal static readonly uint[] BitMask = { 0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191, 16383, 32767 };
|
||||
internal static readonly byte[] ExtraLengthBits = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 };
|
||||
internal static readonly byte[] ExtraDistanceBits = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 0, 0 };
|
||||
internal const int NumChars = 256;
|
||||
internal const int NumLengthBaseCodes = 29;
|
||||
internal const int NumDistBaseCodes = 30;
|
||||
|
||||
internal const uint FastEncoderPostTreeBitBuf = 0x0022;
|
||||
internal const int FastEncoderPostTreeBitCount = 9;
|
||||
|
||||
internal const uint NoCompressionHeader = 0x0;
|
||||
internal const int NoCompressionHeaderBitCount = 3;
|
||||
internal const uint BFinalNoCompressionHeader = 0x1;
|
||||
internal const int BFinalNoCompressionHeaderBitCount = 3;
|
||||
internal const int MaxCodeLen = 16;
|
||||
|
||||
private static readonly byte[] s_distLookup = CreateDistanceLookup();
|
||||
|
||||
private static byte[] CreateDistanceLookup()
|
||||
{
|
||||
byte[] result = new byte[512];
|
||||
|
||||
// Generate the global slot tables which allow us to convert a distance
|
||||
// (0..32K) to a distance slot (0..29)
|
||||
//
|
||||
// Distance table
|
||||
// Extra Extra Extra
|
||||
// Code Bits Dist Code Bits Dist Code Bits Distance
|
||||
// ---- ---- ---- ---- ---- ------ ---- ---- --------
|
||||
// 0 0 1 10 4 33-48 20 9 1025-1536
|
||||
// 1 0 2 11 4 49-64 21 9 1537-2048
|
||||
// 2 0 3 12 5 65-96 22 10 2049-3072
|
||||
// 3 0 4 13 5 97-128 23 10 3073-4096
|
||||
// 4 1 5,6 14 6 129-192 24 11 4097-6144
|
||||
// 5 1 7,8 15 6 193-256 25 11 6145-8192
|
||||
// 6 2 9-12 16 7 257-384 26 12 8193-12288
|
||||
// 7 2 13-16 17 7 385-512 27 12 12289-16384
|
||||
// 8 3 17-24 18 8 513-768 28 13 16385-24576
|
||||
// 9 3 25-32 19 8 769-1024 29 13 24577-32768
|
||||
|
||||
// Initialize the mapping length (0..255) -> length code (0..28)
|
||||
//int length = 0;
|
||||
//for (code = 0; code < FastEncoderStatics.NumLengthBaseCodes-1; code++) {
|
||||
// for (int n = 0; n < (1 << FastEncoderStatics.ExtraLengthBits[code]); n++)
|
||||
// lengthLookup[length++] = (byte) code;
|
||||
//}
|
||||
//lengthLookup[length-1] = (byte) code;
|
||||
|
||||
// Initialize the mapping dist (0..32K) -> dist code (0..29)
|
||||
int dist = 0;
|
||||
int code;
|
||||
for (code = 0; code < 16; code++)
|
||||
{
|
||||
for (int n = 0; n < (1 << ExtraDistanceBits[code]); n++)
|
||||
result[dist++] = (byte)code;
|
||||
}
|
||||
|
||||
dist >>= 7; // from now on, all distances are divided by 128
|
||||
|
||||
for (; code < NumDistBaseCodes; code++)
|
||||
{
|
||||
for (int n = 0; n < (1 << (ExtraDistanceBits[code] - 7)); n++)
|
||||
result[256 + dist++] = (byte)code;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Return the position slot (0...29) of a match offset (0...32767)
|
||||
internal static int GetSlot(int pos) =>
|
||||
s_distLookup[((pos) < 256) ? (pos) : (256 + ((pos) >> 7))];
|
||||
|
||||
// Reverse 'length' of the bits in code
|
||||
public static uint BitReverse(uint code, int length)
|
||||
{
|
||||
uint new_code = 0;
|
||||
|
||||
Debug.Assert(length > 0 && length <= 16, "Invalid len");
|
||||
do
|
||||
{
|
||||
new_code |= (code & 1);
|
||||
new_code <<= 1;
|
||||
code >>= 1;
|
||||
} while (--length > 0);
|
||||
|
||||
return new_code >> 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
311
src/SharpCompress/Compressors/Deflate64/HuffmanTree.cs
Normal file
311
src/SharpCompress/Compressors/Deflate64/HuffmanTree.cs
Normal file
@@ -0,0 +1,311 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
namespace SharpCompress.Compressors.Deflate64
|
||||
{
|
||||
// Strictly speaking this class is not a HuffmanTree, this class is
|
||||
// a lookup table combined with a HuffmanTree. The idea is to speed up
|
||||
// the lookup for short symbols (they should appear more frequently ideally.)
|
||||
// However we don't want to create a huge table since it might take longer to
|
||||
// build the table than decoding (Deflate usually generates new tables frequently.)
|
||||
//
|
||||
// Jean-loup Gailly and Mark Adler gave a very good explanation about this.
|
||||
// The full text (algorithm.txt) can be found inside
|
||||
// ftp://ftp.uu.net/pub/archiving/zip/zlib/zlib.zip.
|
||||
//
|
||||
// Following paper explains decoding in details:
|
||||
// Hirschberg and Lelewer, "Efficient decoding of prefix codes,"
|
||||
// Comm. ACM, 33,4, April 1990, pp. 449-459.
|
||||
//
|
||||
|
||||
internal sealed class HuffmanTree
|
||||
{
|
||||
internal const int MaxLiteralTreeElements = 288;
|
||||
internal const int MaxDistTreeElements = 32;
|
||||
internal const int EndOfBlockCode = 256;
|
||||
internal const int NumberOfCodeLengthTreeElements = 19;
|
||||
|
||||
private readonly int _tableBits;
|
||||
private readonly short[] _table;
|
||||
private readonly short[] _left;
|
||||
private readonly short[] _right;
|
||||
private readonly byte[] _codeLengthArray;
|
||||
#if DEBUG
|
||||
private uint[] _codeArrayDebug;
|
||||
#endif
|
||||
|
||||
private readonly int _tableMask;
|
||||
|
||||
// huffman tree for static block
|
||||
public static HuffmanTree StaticLiteralLengthTree { get; } = new HuffmanTree(GetStaticLiteralTreeLength());
|
||||
|
||||
public static HuffmanTree StaticDistanceTree { get; } = new HuffmanTree(GetStaticDistanceTreeLength());
|
||||
|
||||
public HuffmanTree(byte[] codeLengths)
|
||||
{
|
||||
Debug.Assert(
|
||||
codeLengths.Length == MaxLiteralTreeElements ||
|
||||
codeLengths.Length == MaxDistTreeElements ||
|
||||
codeLengths.Length == NumberOfCodeLengthTreeElements,
|
||||
"we only expect three kinds of Length here");
|
||||
_codeLengthArray = codeLengths;
|
||||
|
||||
if (_codeLengthArray.Length == MaxLiteralTreeElements)
|
||||
{
|
||||
// bits for Literal/Length tree table
|
||||
_tableBits = 9;
|
||||
}
|
||||
else
|
||||
{
|
||||
// bits for distance tree table and code length tree table
|
||||
_tableBits = 7;
|
||||
}
|
||||
_tableMask = (1 << _tableBits) - 1;
|
||||
|
||||
_table = new short[1 << _tableBits];
|
||||
|
||||
// I need to find proof that left and right array will always be
|
||||
// enough. I think they are.
|
||||
_left = new short[2 * _codeLengthArray.Length];
|
||||
_right = new short[2 * _codeLengthArray.Length];
|
||||
|
||||
CreateTable();
|
||||
}
|
||||
|
||||
// Generate the array contains huffman codes lengths for static huffman tree.
|
||||
// The data is in RFC 1951.
|
||||
private static byte[] GetStaticLiteralTreeLength()
|
||||
{
|
||||
byte[] literalTreeLength = new byte[MaxLiteralTreeElements];
|
||||
for (int i = 0; i <= 143; i++)
|
||||
literalTreeLength[i] = 8;
|
||||
|
||||
for (int i = 144; i <= 255; i++)
|
||||
literalTreeLength[i] = 9;
|
||||
|
||||
for (int i = 256; i <= 279; i++)
|
||||
literalTreeLength[i] = 7;
|
||||
|
||||
for (int i = 280; i <= 287; i++)
|
||||
literalTreeLength[i] = 8;
|
||||
|
||||
return literalTreeLength;
|
||||
}
|
||||
|
||||
private static byte[] GetStaticDistanceTreeLength()
|
||||
{
|
||||
byte[] staticDistanceTreeLength = new byte[MaxDistTreeElements];
|
||||
for (int i = 0; i < MaxDistTreeElements; i++)
|
||||
{
|
||||
staticDistanceTreeLength[i] = 5;
|
||||
}
|
||||
return staticDistanceTreeLength;
|
||||
}
|
||||
|
||||
// Calculate the huffman code for each character based on the code length for each character.
|
||||
// This algorithm is described in standard RFC 1951
|
||||
private uint[] CalculateHuffmanCode()
|
||||
{
|
||||
uint[] bitLengthCount = new uint[17];
|
||||
foreach (int codeLength in _codeLengthArray)
|
||||
{
|
||||
bitLengthCount[codeLength]++;
|
||||
}
|
||||
bitLengthCount[0] = 0; // clear count for length 0
|
||||
|
||||
uint[] nextCode = new uint[17];
|
||||
uint tempCode = 0;
|
||||
for (int bits = 1; bits <= 16; bits++)
|
||||
{
|
||||
tempCode = (tempCode + bitLengthCount[bits - 1]) << 1;
|
||||
nextCode[bits] = tempCode;
|
||||
}
|
||||
|
||||
uint[] code = new uint[MaxLiteralTreeElements];
|
||||
for (int i = 0; i < _codeLengthArray.Length; i++)
|
||||
{
|
||||
int len = _codeLengthArray[i];
|
||||
|
||||
if (len > 0)
|
||||
{
|
||||
code[i] = FastEncoderStatics.BitReverse(nextCode[len], len);
|
||||
nextCode[len]++;
|
||||
}
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
private void CreateTable()
|
||||
{
|
||||
uint[] codeArray = CalculateHuffmanCode();
|
||||
#if DEBUG
|
||||
_codeArrayDebug = codeArray;
|
||||
#endif
|
||||
|
||||
short avail = (short)_codeLengthArray.Length;
|
||||
|
||||
for (int ch = 0; ch < _codeLengthArray.Length; ch++)
|
||||
{
|
||||
// length of this code
|
||||
int len = _codeLengthArray[ch];
|
||||
if (len > 0)
|
||||
{
|
||||
// start value (bit reversed)
|
||||
int start = (int)codeArray[ch];
|
||||
|
||||
if (len <= _tableBits)
|
||||
{
|
||||
// If a particular symbol is shorter than nine bits,
|
||||
// then that symbol's translation is duplicated
|
||||
// in all those entries that start with that symbol's bits.
|
||||
// For example, if the symbol is four bits, then it's duplicated
|
||||
// 32 times in a nine-bit table. If a symbol is nine bits long,
|
||||
// it appears in the table once.
|
||||
//
|
||||
// Make sure that in the loop below, code is always
|
||||
// less than table_size.
|
||||
//
|
||||
// On last iteration we store at array index:
|
||||
// initial_start_at + (locs-1)*increment
|
||||
// = initial_start_at + locs*increment - increment
|
||||
// = initial_start_at + (1 << tableBits) - increment
|
||||
// = initial_start_at + table_size - increment
|
||||
//
|
||||
// Therefore we must ensure:
|
||||
// initial_start_at + table_size - increment < table_size
|
||||
// or: initial_start_at < increment
|
||||
//
|
||||
int increment = 1 << len;
|
||||
if (start >= increment)
|
||||
{
|
||||
throw new InvalidDataException("Deflate64: invalid Huffman data");
|
||||
}
|
||||
|
||||
// Note the bits in the table are reverted.
|
||||
int locs = 1 << (_tableBits - len);
|
||||
for (int j = 0; j < locs; j++)
|
||||
{
|
||||
_table[start] = (short)ch;
|
||||
start += increment;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// For any code which has length longer than num_elements,
|
||||
// build a binary tree.
|
||||
|
||||
int overflowBits = len - _tableBits; // the nodes we need to respent the data.
|
||||
int codeBitMask = 1 << _tableBits; // mask to get current bit (the bits can't fit in the table)
|
||||
|
||||
// the left, right table is used to repesent the
|
||||
// the rest bits. When we got the first part (number bits.) and look at
|
||||
// tbe table, we will need to follow the tree to find the real character.
|
||||
// This is in place to avoid bloating the table if there are
|
||||
// a few ones with long code.
|
||||
int index = start & ((1 << _tableBits) - 1);
|
||||
short[] array = _table;
|
||||
|
||||
do
|
||||
{
|
||||
short value = array[index];
|
||||
|
||||
if (value == 0)
|
||||
{
|
||||
// set up next pointer if this node is not used before.
|
||||
array[index] = (short)-avail; // use next available slot.
|
||||
value = (short)-avail;
|
||||
avail++;
|
||||
}
|
||||
|
||||
if (value > 0)
|
||||
{
|
||||
// prevent an IndexOutOfRangeException from array[index]
|
||||
throw new InvalidDataException("Deflate64: invalid Huffman data");
|
||||
}
|
||||
|
||||
Debug.Assert(value < 0, "CreateTable: Only negative numbers are used for tree pointers!");
|
||||
|
||||
if ((start & codeBitMask) == 0)
|
||||
{
|
||||
// if current bit is 0, go change the left array
|
||||
array = _left;
|
||||
}
|
||||
else
|
||||
{
|
||||
// if current bit is 1, set value in the right array
|
||||
array = _right;
|
||||
}
|
||||
index = -value; // go to next node
|
||||
|
||||
codeBitMask <<= 1;
|
||||
overflowBits--;
|
||||
} while (overflowBits != 0);
|
||||
|
||||
array[index] = (short)ch;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// This function will try to get enough bits from input and
|
||||
// try to decode the bits.
|
||||
// If there are no enought bits in the input, this function will return -1.
|
||||
//
|
||||
public int GetNextSymbol(InputBuffer input)
|
||||
{
|
||||
// Try to load 16 bits into input buffer if possible and get the bitBuffer value.
|
||||
// If there aren't 16 bits available we will return all we have in the
|
||||
// input buffer.
|
||||
uint bitBuffer = input.TryLoad16Bits();
|
||||
if (input.AvailableBits == 0)
|
||||
{ // running out of input.
|
||||
return -1;
|
||||
}
|
||||
|
||||
// decode an element
|
||||
int symbol = _table[bitBuffer & _tableMask];
|
||||
if (symbol < 0)
|
||||
{ // this will be the start of the binary tree
|
||||
// navigate the tree
|
||||
uint mask = (uint)1 << _tableBits;
|
||||
do
|
||||
{
|
||||
symbol = -symbol;
|
||||
if ((bitBuffer & mask) == 0)
|
||||
symbol = _left[symbol];
|
||||
else
|
||||
symbol = _right[symbol];
|
||||
mask <<= 1;
|
||||
} while (symbol < 0);
|
||||
}
|
||||
|
||||
int codeLength = _codeLengthArray[symbol];
|
||||
|
||||
// huffman code lengths must be at least 1 bit long
|
||||
if (codeLength <= 0)
|
||||
{
|
||||
throw new InvalidDataException("Deflate64: invalid Huffman data");
|
||||
}
|
||||
|
||||
//
|
||||
// If this code is longer than the # bits we had in the bit buffer (i.e.
|
||||
// we read only part of the code), we can hit the entry in the table or the tree
|
||||
// for another symbol. However the length of another symbol will not match the
|
||||
// available bits count.
|
||||
if (codeLength > input.AvailableBits)
|
||||
{
|
||||
// We already tried to load 16 bits and maximum length is 15,
|
||||
// so this means we are running out of input.
|
||||
return -1;
|
||||
}
|
||||
|
||||
input.SkipBits(codeLength);
|
||||
return symbol;
|
||||
}
|
||||
}
|
||||
}
|
||||
738
src/SharpCompress/Compressors/Deflate64/InflaterManaged.cs
Normal file
738
src/SharpCompress/Compressors/Deflate64/InflaterManaged.cs
Normal file
@@ -0,0 +1,738 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
//
|
||||
// zlib.h -- interface of the 'zlib' general purpose compression library
|
||||
// version 1.2.1, November 17th, 2003
|
||||
//
|
||||
// Copyright (C) 1995-2003 Jean-loup Gailly and Mark Adler
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
namespace SharpCompress.Compressors.Deflate64
|
||||
{
|
||||
internal sealed class InflaterManaged
|
||||
{
|
||||
// const tables used in decoding:
|
||||
|
||||
// Extra bits for length code 257 - 285.
|
||||
private static readonly byte[] s_extraLengthBits =
|
||||
{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,16 };
|
||||
|
||||
// The base length for length code 257 - 285.
|
||||
// The formula to get the real length for a length code is lengthBase[code - 257] + (value stored in extraBits)
|
||||
private static readonly int[] s_lengthBase =
|
||||
{ 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,3};
|
||||
|
||||
// The base distance for distance code 0 - 31
|
||||
// The real distance for a distance code is distanceBasePosition[code] + (value stored in extraBits)
|
||||
private static readonly int[] s_distanceBasePosition =
|
||||
{ 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,32769,49153 };
|
||||
|
||||
// code lengths for code length alphabet is stored in following order
|
||||
private static readonly byte[] s_codeOrder = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
|
||||
|
||||
private static readonly byte[] s_staticDistanceTreeTable =
|
||||
{
|
||||
0x00,0x10,0x08,0x18,0x04,0x14,0x0c,0x1c,0x02,0x12,0x0a,0x1a,
|
||||
0x06,0x16,0x0e,0x1e,0x01,0x11,0x09,0x19,0x05,0x15,0x0d,0x1d,
|
||||
0x03,0x13,0x0b,0x1b,0x07,0x17,0x0f,0x1f
|
||||
};
|
||||
|
||||
private readonly OutputWindow _output;
|
||||
private readonly InputBuffer _input;
|
||||
private HuffmanTree _literalLengthTree;
|
||||
private HuffmanTree _distanceTree;
|
||||
|
||||
private InflaterState _state;
|
||||
//private bool _hasFormatReader;
|
||||
private int _bfinal;
|
||||
private BlockType _blockType;
|
||||
|
||||
// uncompressed block
|
||||
private readonly byte[] _blockLengthBuffer = new byte[4];
|
||||
private int _blockLength;
|
||||
|
||||
// compressed block
|
||||
private int _length;
|
||||
private int _distanceCode;
|
||||
private int _extraBits;
|
||||
|
||||
private int _loopCounter;
|
||||
private int _literalLengthCodeCount;
|
||||
private int _distanceCodeCount;
|
||||
private int _codeLengthCodeCount;
|
||||
private int _codeArraySize;
|
||||
private int _lengthCode;
|
||||
|
||||
private readonly byte[] _codeList; // temporary array to store the code length for literal/Length and distance
|
||||
private readonly byte[] _codeLengthTreeCodeLength;
|
||||
private readonly bool _deflate64;
|
||||
private HuffmanTree _codeLengthTree;
|
||||
|
||||
//private IFileFormatReader _formatReader; // class to decode header and footer (e.g. gzip)
|
||||
|
||||
internal InflaterManaged(/*IFileFormatReader reader, */bool deflate64)
|
||||
{
|
||||
_output = new OutputWindow();
|
||||
_input = new InputBuffer();
|
||||
|
||||
_codeList = new byte[HuffmanTree.MaxLiteralTreeElements + HuffmanTree.MaxDistTreeElements];
|
||||
_codeLengthTreeCodeLength = new byte[HuffmanTree.NumberOfCodeLengthTreeElements];
|
||||
_deflate64 = deflate64;
|
||||
//if (reader != null)
|
||||
//{
|
||||
// _formatReader = reader;
|
||||
// _hasFormatReader = true;
|
||||
//}
|
||||
Reset();
|
||||
}
|
||||
|
||||
private void Reset()
|
||||
{
|
||||
_state = //_hasFormatReader ?
|
||||
//InflaterState.ReadingHeader : // start by reading Header info
|
||||
InflaterState.ReadingBFinal; // start by reading BFinal bit
|
||||
}
|
||||
|
||||
public void SetInput(byte[] inputBytes, int offset, int length) =>
|
||||
_input.SetInput(inputBytes, offset, length); // append the bytes
|
||||
|
||||
public bool Finished() => _state == InflaterState.Done || _state == InflaterState.VerifyingFooter;
|
||||
|
||||
public int AvailableOutput => _output.AvailableBytes;
|
||||
|
||||
public int Inflate(byte[] bytes, int offset, int length)
|
||||
{
|
||||
// copy bytes from output to outputbytes if we have available bytes
|
||||
// if buffer is not filled up. keep decoding until no input are available
|
||||
// if decodeBlock returns false. Throw an exception.
|
||||
int count = 0;
|
||||
do
|
||||
{
|
||||
int copied = _output.CopyTo(bytes, offset, length);
|
||||
if (copied > 0)
|
||||
{
|
||||
//if (_hasFormatReader)
|
||||
//{
|
||||
// _formatReader.UpdateWithBytesRead(bytes, offset, copied);
|
||||
//}
|
||||
|
||||
offset += copied;
|
||||
count += copied;
|
||||
length -= copied;
|
||||
}
|
||||
|
||||
if (length == 0)
|
||||
{ // filled in the bytes array
|
||||
break;
|
||||
}
|
||||
// Decode will return false when more input is needed
|
||||
} while (!Finished() && Decode());
|
||||
|
||||
if (_state == InflaterState.VerifyingFooter)
|
||||
{ // finished reading CRC
|
||||
// In this case finished is true and output window has all the data.
|
||||
// But some data in output window might not be copied out.
|
||||
if (_output.AvailableBytes == 0)
|
||||
{
|
||||
//_formatReader.Validate();
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
//Each block of compressed data begins with 3 header bits
|
||||
// containing the following data:
|
||||
// first bit BFINAL
|
||||
// next 2 bits BTYPE
|
||||
// Note that the header bits do not necessarily begin on a byte
|
||||
// boundary, since a block does not necessarily occupy an integral
|
||||
// number of bytes.
|
||||
// BFINAL is set if and only if this is the last block of the data
|
||||
// set.
|
||||
// BTYPE specifies how the data are compressed, as follows:
|
||||
// 00 - no compression
|
||||
// 01 - compressed with fixed Huffman codes
|
||||
// 10 - compressed with dynamic Huffman codes
|
||||
// 11 - reserved (error)
|
||||
// The only difference between the two compressed cases is how the
|
||||
// Huffman codes for the literal/length and distance alphabets are
|
||||
// defined.
|
||||
//
|
||||
// This function returns true for success (end of block or output window is full,)
|
||||
// false if we are short of input
|
||||
//
|
||||
private bool Decode()
|
||||
{
|
||||
bool eob = false;
|
||||
bool result = false;
|
||||
|
||||
if (Finished())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//if (_hasFormatReader)
|
||||
//{
|
||||
// if (_state == InflaterState.ReadingHeader)
|
||||
// {
|
||||
// if (!_formatReader.ReadHeader(_input))
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// _state = InflaterState.ReadingBFinal;
|
||||
// }
|
||||
// else if (_state == InflaterState.StartReadingFooter || _state == InflaterState.ReadingFooter)
|
||||
// {
|
||||
// if (!_formatReader.ReadFooter(_input))
|
||||
// return false;
|
||||
|
||||
// _state = InflaterState.VerifyingFooter;
|
||||
// return true;
|
||||
// }
|
||||
//}
|
||||
|
||||
if (_state == InflaterState.ReadingBFinal)
|
||||
{
|
||||
// reading bfinal bit
|
||||
// Need 1 bit
|
||||
if (!_input.EnsureBitsAvailable(1))
|
||||
return false;
|
||||
|
||||
_bfinal = _input.GetBits(1);
|
||||
_state = InflaterState.ReadingBType;
|
||||
}
|
||||
|
||||
if (_state == InflaterState.ReadingBType)
|
||||
{
|
||||
// Need 2 bits
|
||||
if (!_input.EnsureBitsAvailable(2))
|
||||
{
|
||||
_state = InflaterState.ReadingBType;
|
||||
return false;
|
||||
}
|
||||
|
||||
_blockType = (BlockType)_input.GetBits(2);
|
||||
if (_blockType == BlockType.Dynamic)
|
||||
{
|
||||
_state = InflaterState.ReadingNumLitCodes;
|
||||
}
|
||||
else if (_blockType == BlockType.Static)
|
||||
{
|
||||
_literalLengthTree = HuffmanTree.StaticLiteralLengthTree;
|
||||
_distanceTree = HuffmanTree.StaticDistanceTree;
|
||||
_state = InflaterState.DecodeTop;
|
||||
}
|
||||
else if (_blockType == BlockType.Uncompressed)
|
||||
{
|
||||
_state = InflaterState.UncompressedAligning;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidDataException("Deflate64: unknown block type");
|
||||
}
|
||||
}
|
||||
|
||||
if (_blockType == BlockType.Dynamic)
|
||||
{
|
||||
if (_state < InflaterState.DecodeTop)
|
||||
{
|
||||
// we are reading the header
|
||||
result = DecodeDynamicBlockHeader();
|
||||
}
|
||||
else
|
||||
{
|
||||
result = DecodeBlock(out eob); // this can returns true when output is full
|
||||
}
|
||||
}
|
||||
else if (_blockType == BlockType.Static)
|
||||
{
|
||||
result = DecodeBlock(out eob);
|
||||
}
|
||||
else if (_blockType == BlockType.Uncompressed)
|
||||
{
|
||||
result = DecodeUncompressedBlock(out eob);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidDataException("Deflate64: unknown block type");
|
||||
}
|
||||
|
||||
//
|
||||
// If we reached the end of the block and the block we were decoding had
|
||||
// bfinal=1 (final block)
|
||||
//
|
||||
if (eob && (_bfinal != 0))
|
||||
{
|
||||
//if (_hasFormatReader)
|
||||
// _state = InflaterState.StartReadingFooter;
|
||||
//else
|
||||
_state = InflaterState.Done;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// Format of Non-compressed blocks (BTYPE=00):
|
||||
//
|
||||
// Any bits of input up to the next byte boundary are ignored.
|
||||
// The rest of the block consists of the following information:
|
||||
//
|
||||
// 0 1 2 3 4...
|
||||
// +---+---+---+---+================================+
|
||||
// | LEN | NLEN |... LEN bytes of literal data...|
|
||||
// +---+---+---+---+================================+
|
||||
//
|
||||
// LEN is the number of data bytes in the block. NLEN is the
|
||||
// one's complement of LEN.
|
||||
private bool DecodeUncompressedBlock(out bool end_of_block)
|
||||
{
|
||||
end_of_block = false;
|
||||
while (true)
|
||||
{
|
||||
switch (_state)
|
||||
{
|
||||
case InflaterState.UncompressedAligning: // initial state when calling this function
|
||||
// we must skip to a byte boundary
|
||||
_input.SkipToByteBoundary();
|
||||
_state = InflaterState.UncompressedByte1;
|
||||
goto case InflaterState.UncompressedByte1;
|
||||
|
||||
case InflaterState.UncompressedByte1: // decoding block length
|
||||
case InflaterState.UncompressedByte2:
|
||||
case InflaterState.UncompressedByte3:
|
||||
case InflaterState.UncompressedByte4:
|
||||
int bits = _input.GetBits(8);
|
||||
if (bits < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_blockLengthBuffer[_state - InflaterState.UncompressedByte1] = (byte)bits;
|
||||
if (_state == InflaterState.UncompressedByte4)
|
||||
{
|
||||
_blockLength = _blockLengthBuffer[0] + ((int)_blockLengthBuffer[1]) * 256;
|
||||
int blockLengthComplement = _blockLengthBuffer[2] + ((int)_blockLengthBuffer[3]) * 256;
|
||||
|
||||
// make sure complement matches
|
||||
if ((ushort)_blockLength != (ushort)(~blockLengthComplement))
|
||||
{
|
||||
throw new InvalidDataException("Deflate64: invalid block length");
|
||||
}
|
||||
}
|
||||
|
||||
_state += 1;
|
||||
break;
|
||||
|
||||
case InflaterState.DecodingUncompressed: // copying block data
|
||||
|
||||
// Directly copy bytes from input to output.
|
||||
int bytesCopied = _output.CopyFrom(_input, _blockLength);
|
||||
_blockLength -= bytesCopied;
|
||||
|
||||
if (_blockLength == 0)
|
||||
{
|
||||
// Done with this block, need to re-init bit buffer for next block
|
||||
_state = InflaterState.ReadingBFinal;
|
||||
end_of_block = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// We can fail to copy all bytes for two reasons:
|
||||
// Running out of Input
|
||||
// running out of free space in output window
|
||||
if (_output.FreeBytes == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
default:
|
||||
Debug./*Fail*/Assert(false, "check why we are here!");
|
||||
throw new InvalidDataException("Deflate64: unknown state");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool DecodeBlock(out bool end_of_block_code_seen)
|
||||
{
|
||||
end_of_block_code_seen = false;
|
||||
|
||||
int freeBytes = _output.FreeBytes; // it is a little bit faster than frequently accessing the property
|
||||
while (freeBytes > 65536)
|
||||
{
|
||||
// With Deflate64 we can have up to a 64kb length, so we ensure at least that much space is available
|
||||
// in the OutputWindow to avoid overwriting previous unflushed output data.
|
||||
|
||||
int symbol;
|
||||
switch (_state)
|
||||
{
|
||||
case InflaterState.DecodeTop:
|
||||
// decode an element from the literal tree
|
||||
|
||||
// TODO: optimize this!!!
|
||||
symbol = _literalLengthTree.GetNextSymbol(_input);
|
||||
if (symbol < 0)
|
||||
{
|
||||
// running out of input
|
||||
return false;
|
||||
}
|
||||
|
||||
if (symbol < 256)
|
||||
{
|
||||
// literal
|
||||
_output.Write((byte)symbol);
|
||||
--freeBytes;
|
||||
}
|
||||
else if (symbol == 256)
|
||||
{
|
||||
// end of block
|
||||
end_of_block_code_seen = true;
|
||||
// Reset state
|
||||
_state = InflaterState.ReadingBFinal;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// length/distance pair
|
||||
symbol -= 257; // length code started at 257
|
||||
if (symbol < 8)
|
||||
{
|
||||
symbol += 3; // match length = 3,4,5,6,7,8,9,10
|
||||
_extraBits = 0;
|
||||
}
|
||||
else if (!_deflate64 && symbol == 28)
|
||||
{
|
||||
// extra bits for code 285 is 0
|
||||
symbol = 258; // code 285 means length 258
|
||||
_extraBits = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (symbol < 0 || symbol >= s_extraLengthBits.Length)
|
||||
{
|
||||
throw new InvalidDataException("Deflate64: invalid data");
|
||||
}
|
||||
_extraBits = s_extraLengthBits[symbol];
|
||||
Debug.Assert(_extraBits != 0, "We handle other cases separately!");
|
||||
}
|
||||
_length = symbol;
|
||||
goto case InflaterState.HaveInitialLength;
|
||||
}
|
||||
break;
|
||||
|
||||
case InflaterState.HaveInitialLength:
|
||||
if (_extraBits > 0)
|
||||
{
|
||||
_state = InflaterState.HaveInitialLength;
|
||||
int bits = _input.GetBits(_extraBits);
|
||||
if (bits < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_length < 0 || _length >= s_lengthBase.Length)
|
||||
{
|
||||
throw new InvalidDataException("Deflate64: invalid data");
|
||||
}
|
||||
_length = s_lengthBase[_length] + bits;
|
||||
}
|
||||
_state = InflaterState.HaveFullLength;
|
||||
goto case InflaterState.HaveFullLength;
|
||||
|
||||
case InflaterState.HaveFullLength:
|
||||
if (_blockType == BlockType.Dynamic)
|
||||
{
|
||||
_distanceCode = _distanceTree.GetNextSymbol(_input);
|
||||
}
|
||||
else
|
||||
{
|
||||
// get distance code directly for static block
|
||||
_distanceCode = _input.GetBits(5);
|
||||
if (_distanceCode >= 0)
|
||||
{
|
||||
_distanceCode = s_staticDistanceTreeTable[_distanceCode];
|
||||
}
|
||||
}
|
||||
|
||||
if (_distanceCode < 0)
|
||||
{
|
||||
// running out input
|
||||
return false;
|
||||
}
|
||||
|
||||
_state = InflaterState.HaveDistCode;
|
||||
goto case InflaterState.HaveDistCode;
|
||||
|
||||
case InflaterState.HaveDistCode:
|
||||
// To avoid a table lookup we note that for distanceCode > 3,
|
||||
// extra_bits = (distanceCode-2) >> 1
|
||||
int offset;
|
||||
if (_distanceCode > 3)
|
||||
{
|
||||
_extraBits = (_distanceCode - 2) >> 1;
|
||||
int bits = _input.GetBits(_extraBits);
|
||||
if (bits < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
offset = s_distanceBasePosition[_distanceCode] + bits;
|
||||
}
|
||||
else
|
||||
{
|
||||
offset = _distanceCode + 1;
|
||||
}
|
||||
|
||||
_output.WriteLengthDistance(_length, offset);
|
||||
freeBytes -= _length;
|
||||
_state = InflaterState.DecodeTop;
|
||||
break;
|
||||
|
||||
default:
|
||||
Debug./*Fail*/Assert(false, "check why we are here!");
|
||||
throw new InvalidDataException("Deflate64: unknown state");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Format of the dynamic block header:
|
||||
// 5 Bits: HLIT, # of Literal/Length codes - 257 (257 - 286)
|
||||
// 5 Bits: HDIST, # of Distance codes - 1 (1 - 32)
|
||||
// 4 Bits: HCLEN, # of Code Length codes - 4 (4 - 19)
|
||||
//
|
||||
// (HCLEN + 4) x 3 bits: code lengths for the code length
|
||||
// alphabet given just above, in the order: 16, 17, 18,
|
||||
// 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
|
||||
//
|
||||
// These code lengths are interpreted as 3-bit integers
|
||||
// (0-7); as above, a code length of 0 means the
|
||||
// corresponding symbol (literal/length or distance code
|
||||
// length) is not used.
|
||||
//
|
||||
// HLIT + 257 code lengths for the literal/length alphabet,
|
||||
// encoded using the code length Huffman code
|
||||
//
|
||||
// HDIST + 1 code lengths for the distance alphabet,
|
||||
// encoded using the code length Huffman code
|
||||
//
|
||||
// The code length repeat codes can cross from HLIT + 257 to the
|
||||
// HDIST + 1 code lengths. In other words, all code lengths form
|
||||
// a single sequence of HLIT + HDIST + 258 values.
|
||||
private bool DecodeDynamicBlockHeader()
|
||||
{
|
||||
switch (_state)
|
||||
{
|
||||
case InflaterState.ReadingNumLitCodes:
|
||||
_literalLengthCodeCount = _input.GetBits(5);
|
||||
if (_literalLengthCodeCount < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
_literalLengthCodeCount += 257;
|
||||
_state = InflaterState.ReadingNumDistCodes;
|
||||
goto case InflaterState.ReadingNumDistCodes;
|
||||
|
||||
case InflaterState.ReadingNumDistCodes:
|
||||
_distanceCodeCount = _input.GetBits(5);
|
||||
if (_distanceCodeCount < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
_distanceCodeCount += 1;
|
||||
_state = InflaterState.ReadingNumCodeLengthCodes;
|
||||
goto case InflaterState.ReadingNumCodeLengthCodes;
|
||||
|
||||
case InflaterState.ReadingNumCodeLengthCodes:
|
||||
_codeLengthCodeCount = _input.GetBits(4);
|
||||
if (_codeLengthCodeCount < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
_codeLengthCodeCount += 4;
|
||||
_loopCounter = 0;
|
||||
_state = InflaterState.ReadingCodeLengthCodes;
|
||||
goto case InflaterState.ReadingCodeLengthCodes;
|
||||
|
||||
case InflaterState.ReadingCodeLengthCodes:
|
||||
while (_loopCounter < _codeLengthCodeCount)
|
||||
{
|
||||
int bits = _input.GetBits(3);
|
||||
if (bits < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
_codeLengthTreeCodeLength[s_codeOrder[_loopCounter]] = (byte)bits;
|
||||
++_loopCounter;
|
||||
}
|
||||
|
||||
for (int i = _codeLengthCodeCount; i < s_codeOrder.Length; i++)
|
||||
{
|
||||
_codeLengthTreeCodeLength[s_codeOrder[i]] = 0;
|
||||
}
|
||||
|
||||
// create huffman tree for code length
|
||||
_codeLengthTree = new HuffmanTree(_codeLengthTreeCodeLength);
|
||||
_codeArraySize = _literalLengthCodeCount + _distanceCodeCount;
|
||||
_loopCounter = 0; // reset loop count
|
||||
|
||||
_state = InflaterState.ReadingTreeCodesBefore;
|
||||
goto case InflaterState.ReadingTreeCodesBefore;
|
||||
|
||||
case InflaterState.ReadingTreeCodesBefore:
|
||||
case InflaterState.ReadingTreeCodesAfter:
|
||||
while (_loopCounter < _codeArraySize)
|
||||
{
|
||||
if (_state == InflaterState.ReadingTreeCodesBefore)
|
||||
{
|
||||
if ((_lengthCode = _codeLengthTree.GetNextSymbol(_input)) < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// The alphabet for code lengths is as follows:
|
||||
// 0 - 15: Represent code lengths of 0 - 15
|
||||
// 16: Copy the previous code length 3 - 6 times.
|
||||
// The next 2 bits indicate repeat length
|
||||
// (0 = 3, ... , 3 = 6)
|
||||
// Example: Codes 8, 16 (+2 bits 11),
|
||||
// 16 (+2 bits 10) will expand to
|
||||
// 12 code lengths of 8 (1 + 6 + 5)
|
||||
// 17: Repeat a code length of 0 for 3 - 10 times.
|
||||
// (3 bits of length)
|
||||
// 18: Repeat a code length of 0 for 11 - 138 times
|
||||
// (7 bits of length)
|
||||
if (_lengthCode <= 15)
|
||||
{
|
||||
_codeList[_loopCounter++] = (byte)_lengthCode;
|
||||
}
|
||||
else
|
||||
{
|
||||
int repeatCount;
|
||||
if (_lengthCode == 16)
|
||||
{
|
||||
if (!_input.EnsureBitsAvailable(2))
|
||||
{
|
||||
_state = InflaterState.ReadingTreeCodesAfter;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_loopCounter == 0)
|
||||
{
|
||||
// can't have "prev code" on first code
|
||||
throw new InvalidDataException();
|
||||
}
|
||||
|
||||
byte previousCode = _codeList[_loopCounter - 1];
|
||||
repeatCount = _input.GetBits(2) + 3;
|
||||
|
||||
if (_loopCounter + repeatCount > _codeArraySize)
|
||||
{
|
||||
throw new InvalidDataException();
|
||||
}
|
||||
|
||||
for (int j = 0; j < repeatCount; j++)
|
||||
{
|
||||
_codeList[_loopCounter++] = previousCode;
|
||||
}
|
||||
}
|
||||
else if (_lengthCode == 17)
|
||||
{
|
||||
if (!_input.EnsureBitsAvailable(3))
|
||||
{
|
||||
_state = InflaterState.ReadingTreeCodesAfter;
|
||||
return false;
|
||||
}
|
||||
|
||||
repeatCount = _input.GetBits(3) + 3;
|
||||
|
||||
if (_loopCounter + repeatCount > _codeArraySize)
|
||||
{
|
||||
throw new InvalidDataException();
|
||||
}
|
||||
|
||||
for (int j = 0; j < repeatCount; j++)
|
||||
{
|
||||
_codeList[_loopCounter++] = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// code == 18
|
||||
if (!_input.EnsureBitsAvailable(7))
|
||||
{
|
||||
_state = InflaterState.ReadingTreeCodesAfter;
|
||||
return false;
|
||||
}
|
||||
|
||||
repeatCount = _input.GetBits(7) + 11;
|
||||
|
||||
if (_loopCounter + repeatCount > _codeArraySize)
|
||||
{
|
||||
throw new InvalidDataException();
|
||||
}
|
||||
|
||||
for (int j = 0; j < repeatCount; j++)
|
||||
{
|
||||
_codeList[_loopCounter++] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
_state = InflaterState.ReadingTreeCodesBefore; // we want to read the next code.
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
Debug./*Fail*/Assert(false, "check why we are here!");
|
||||
throw new InvalidDataException("Deflate64: unknown state");
|
||||
}
|
||||
|
||||
byte[] literalTreeCodeLength = new byte[HuffmanTree.MaxLiteralTreeElements];
|
||||
byte[] distanceTreeCodeLength = new byte[HuffmanTree.MaxDistTreeElements];
|
||||
|
||||
// Create literal and distance tables
|
||||
Array.Copy(_codeList, 0, literalTreeCodeLength, 0, _literalLengthCodeCount);
|
||||
Array.Copy(_codeList, _literalLengthCodeCount, distanceTreeCodeLength, 0, _distanceCodeCount);
|
||||
|
||||
// Make sure there is an end-of-block code, otherwise how could we ever end?
|
||||
if (literalTreeCodeLength[HuffmanTree.EndOfBlockCode] == 0)
|
||||
{
|
||||
throw new InvalidDataException();
|
||||
}
|
||||
|
||||
_literalLengthTree = new HuffmanTree(literalTreeCodeLength);
|
||||
_distanceTree = new HuffmanTree(distanceTreeCodeLength);
|
||||
_state = InflaterState.DecodeTop;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Dispose() { }
|
||||
}
|
||||
}
|
||||
42
src/SharpCompress/Compressors/Deflate64/InflaterState.cs
Normal file
42
src/SharpCompress/Compressors/Deflate64/InflaterState.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace SharpCompress.Compressors.Deflate64
|
||||
{
|
||||
// Do not rearrange the enum values.
|
||||
internal enum InflaterState
|
||||
{
|
||||
ReadingHeader = 0, // Only applies to GZIP
|
||||
|
||||
ReadingBFinal = 2, // About to read bfinal bit
|
||||
ReadingBType = 3, // About to read blockType bits
|
||||
|
||||
ReadingNumLitCodes = 4, // About to read # literal codes
|
||||
ReadingNumDistCodes = 5, // About to read # dist codes
|
||||
ReadingNumCodeLengthCodes = 6, // About to read # code length codes
|
||||
ReadingCodeLengthCodes = 7, // In the middle of reading the code length codes
|
||||
ReadingTreeCodesBefore = 8, // In the middle of reading tree codes (loop top)
|
||||
ReadingTreeCodesAfter = 9, // In the middle of reading tree codes (extension; code > 15)
|
||||
|
||||
DecodeTop = 10, // About to decode a literal (char/match) in a compressed block
|
||||
HaveInitialLength = 11, // Decoding a match, have the literal code (base length)
|
||||
HaveFullLength = 12, // Ditto, now have the full match length (incl. extra length bits)
|
||||
HaveDistCode = 13, // Ditto, now have the distance code also, need extra dist bits
|
||||
|
||||
/* uncompressed blocks */
|
||||
UncompressedAligning = 15,
|
||||
UncompressedByte1 = 16,
|
||||
UncompressedByte2 = 17,
|
||||
UncompressedByte3 = 18,
|
||||
UncompressedByte4 = 19,
|
||||
DecodingUncompressed = 20,
|
||||
|
||||
// These three apply only to GZIP
|
||||
StartReadingFooter = 21, // (Initialisation for reading footer)
|
||||
ReadingFooter = 22,
|
||||
VerifyingFooter = 23,
|
||||
|
||||
Done = 24 // Finished
|
||||
}
|
||||
}
|
||||
202
src/SharpCompress/Compressors/Deflate64/InputBuffer.cs
Normal file
202
src/SharpCompress/Compressors/Deflate64/InputBuffer.cs
Normal file
@@ -0,0 +1,202 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace SharpCompress.Compressors.Deflate64
|
||||
{
|
||||
// This class can be used to read bits from an byte array quickly.
|
||||
// Normally we get bits from 'bitBuffer' field and bitsInBuffer stores
|
||||
// the number of bits available in 'BitBuffer'.
|
||||
// When we used up the bits in bitBuffer, we will try to get byte from
|
||||
// the byte array and copy the byte to appropiate position in bitBuffer.
|
||||
//
|
||||
// The byte array is not reused. We will go from 'start' to 'end'.
|
||||
// When we reach the end, most read operations will return -1,
|
||||
// which means we are running out of input.
|
||||
|
||||
internal sealed class InputBuffer
|
||||
{
|
||||
private byte[] _buffer; // byte array to store input
|
||||
private int _start; // start poisition of the buffer
|
||||
private int _end; // end position of the buffer
|
||||
private uint _bitBuffer = 0; // store the bits here, we can quickly shift in this buffer
|
||||
private int _bitsInBuffer = 0; // number of bits available in bitBuffer
|
||||
|
||||
/// <summary>Total bits available in the input buffer.</summary>
|
||||
public int AvailableBits => _bitsInBuffer;
|
||||
|
||||
/// <summary>Total bytes available in the input buffer.</summary>
|
||||
public int AvailableBytes => (_end - _start) + (_bitsInBuffer / 8);
|
||||
|
||||
/// <summary>Ensure that count bits are in the bit buffer.</summary>
|
||||
/// <param name="count">Can be up to 16.</param>
|
||||
/// <returns>Returns false if input is not sufficient to make this true.</returns>
|
||||
public bool EnsureBitsAvailable(int count)
|
||||
{
|
||||
Debug.Assert(0 < count && count <= 16, "count is invalid.");
|
||||
|
||||
// manual inlining to improve perf
|
||||
if (_bitsInBuffer < count)
|
||||
{
|
||||
if (NeedsInput())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// insert a byte to bitbuffer
|
||||
_bitBuffer |= (uint)_buffer[_start++] << _bitsInBuffer;
|
||||
_bitsInBuffer += 8;
|
||||
|
||||
if (_bitsInBuffer < count)
|
||||
{
|
||||
if (NeedsInput())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// insert a byte to bitbuffer
|
||||
_bitBuffer |= (uint)_buffer[_start++] << _bitsInBuffer;
|
||||
_bitsInBuffer += 8;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This function will try to load 16 or more bits into bitBuffer.
|
||||
/// It returns whatever is contained in bitBuffer after loading.
|
||||
/// The main difference between this and GetBits is that this will
|
||||
/// never return -1. So the caller needs to check AvailableBits to
|
||||
/// see how many bits are available.
|
||||
/// </summary>
|
||||
public uint TryLoad16Bits()
|
||||
{
|
||||
if (_bitsInBuffer < 8)
|
||||
{
|
||||
if (_start < _end)
|
||||
{
|
||||
_bitBuffer |= (uint)_buffer[_start++] << _bitsInBuffer;
|
||||
_bitsInBuffer += 8;
|
||||
}
|
||||
|
||||
if (_start < _end)
|
||||
{
|
||||
_bitBuffer |= (uint)_buffer[_start++] << _bitsInBuffer;
|
||||
_bitsInBuffer += 8;
|
||||
}
|
||||
}
|
||||
else if (_bitsInBuffer < 16)
|
||||
{
|
||||
if (_start < _end)
|
||||
{
|
||||
_bitBuffer |= (uint)_buffer[_start++] << _bitsInBuffer;
|
||||
_bitsInBuffer += 8;
|
||||
}
|
||||
}
|
||||
|
||||
return _bitBuffer;
|
||||
}
|
||||
|
||||
private uint GetBitMask(int count) => ((uint)1 << count) - 1;
|
||||
|
||||
/// <summary>Gets count bits from the input buffer. Returns -1 if not enough bits available.</summary>
|
||||
public int GetBits(int count)
|
||||
{
|
||||
Debug.Assert(0 < count && count <= 16, "count is invalid.");
|
||||
|
||||
if (!EnsureBitsAvailable(count))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int result = (int)(_bitBuffer & GetBitMask(count));
|
||||
_bitBuffer >>= count;
|
||||
_bitsInBuffer -= count;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies length bytes from input buffer to output buffer starting at output[offset].
|
||||
/// You have to make sure, that the buffer is byte aligned. If not enough bytes are
|
||||
/// available, copies fewer bytes.
|
||||
/// </summary>
|
||||
/// <returns>Returns the number of bytes copied, 0 if no byte is available.</returns>
|
||||
public int CopyTo(byte[] output, int offset, int length)
|
||||
{
|
||||
Debug.Assert(output != null);
|
||||
Debug.Assert(offset >= 0);
|
||||
Debug.Assert(length >= 0);
|
||||
Debug.Assert(offset <= output.Length - length);
|
||||
Debug.Assert((_bitsInBuffer % 8) == 0);
|
||||
|
||||
// Copy the bytes in bitBuffer first.
|
||||
int bytesFromBitBuffer = 0;
|
||||
while (_bitsInBuffer > 0 && length > 0)
|
||||
{
|
||||
output[offset++] = (byte)_bitBuffer;
|
||||
_bitBuffer >>= 8;
|
||||
_bitsInBuffer -= 8;
|
||||
length--;
|
||||
bytesFromBitBuffer++;
|
||||
}
|
||||
|
||||
if (length == 0)
|
||||
{
|
||||
return bytesFromBitBuffer;
|
||||
}
|
||||
|
||||
int avail = _end - _start;
|
||||
if (length > avail)
|
||||
{
|
||||
length = avail;
|
||||
}
|
||||
|
||||
Array.Copy(_buffer, _start, output, offset, length);
|
||||
_start += length;
|
||||
return bytesFromBitBuffer + length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return true is all input bytes are used.
|
||||
/// This means the caller can call SetInput to add more input.
|
||||
/// </summary>
|
||||
public bool NeedsInput() => _start == _end;
|
||||
|
||||
/// <summary>
|
||||
/// Set the byte array to be processed.
|
||||
/// All the bits remained in bitBuffer will be processed before the new bytes.
|
||||
/// We don't clone the byte array here since it is expensive.
|
||||
/// The caller should make sure after a buffer is passed in.
|
||||
/// It will not be changed before calling this function again.
|
||||
/// </summary>
|
||||
public void SetInput(byte[] buffer, int offset, int length)
|
||||
{
|
||||
Debug.Assert(buffer != null);
|
||||
Debug.Assert(offset >= 0);
|
||||
Debug.Assert(length >= 0);
|
||||
Debug.Assert(offset <= buffer.Length - length);
|
||||
Debug.Assert(_start == _end);
|
||||
|
||||
_buffer = buffer;
|
||||
_start = offset;
|
||||
_end = offset + length;
|
||||
}
|
||||
|
||||
/// <summary>Skip n bits in the buffer.</summary>
|
||||
public void SkipBits(int n)
|
||||
{
|
||||
Debug.Assert(_bitsInBuffer >= n, "No enough bits in the buffer, Did you call EnsureBitsAvailable?");
|
||||
_bitBuffer >>= n;
|
||||
_bitsInBuffer -= n;
|
||||
}
|
||||
|
||||
/// <summary>Skips to the next byte boundary.</summary>
|
||||
public void SkipToByteBoundary()
|
||||
{
|
||||
_bitBuffer >>= (_bitsInBuffer % 8);
|
||||
_bitsInBuffer = _bitsInBuffer - (_bitsInBuffer % 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
17
src/SharpCompress/Compressors/Deflate64/Match.cs
Normal file
17
src/SharpCompress/Compressors/Deflate64/Match.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace SharpCompress.Compressors.Deflate64
|
||||
{
|
||||
/// <summary>
|
||||
/// This class represents a match in the history window.
|
||||
/// </summary>
|
||||
internal sealed class Match
|
||||
{
|
||||
internal MatchState State { get; set; }
|
||||
internal int Position { get; set; }
|
||||
internal int Length { get; set; }
|
||||
internal byte Symbol { get; set; }
|
||||
}
|
||||
}
|
||||
13
src/SharpCompress/Compressors/Deflate64/MatchState.cs
Normal file
13
src/SharpCompress/Compressors/Deflate64/MatchState.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace SharpCompress.Compressors.Deflate64
|
||||
{
|
||||
internal enum MatchState
|
||||
{
|
||||
HasSymbol = 1,
|
||||
HasMatch = 2,
|
||||
HasSymbolAndMatch = 3
|
||||
}
|
||||
}
|
||||
151
src/SharpCompress/Compressors/Deflate64/OutputWindow.cs
Normal file
151
src/SharpCompress/Compressors/Deflate64/OutputWindow.cs
Normal file
@@ -0,0 +1,151 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace SharpCompress.Compressors.Deflate64
|
||||
{
|
||||
/// <summary>
|
||||
/// This class maintains a window for decompressed output.
|
||||
/// We need to keep this because the decompressed information can be
|
||||
/// a literal or a length/distance pair. For length/distance pair,
|
||||
/// we need to look back in the output window and copy bytes from there.
|
||||
/// We use a byte array of WindowSize circularly.
|
||||
/// </summary>
|
||||
internal sealed class OutputWindow
|
||||
{
|
||||
// With Deflate64 we can have up to a 65536 length as well as up to a 65538 distance. This means we need a Window that is at
|
||||
// least 131074 bytes long so we have space to retrieve up to a full 64kb in lookback and place it in our buffer without
|
||||
// overwriting existing data. OutputWindow requires that the WindowSize be an exponent of 2, so we round up to 2^18.
|
||||
private const int WindowSize = 262144;
|
||||
private const int WindowMask = 262143;
|
||||
|
||||
private readonly byte[] _window = new byte[WindowSize]; // The window is 2^18 bytes
|
||||
private int _end; // this is the position to where we should write next byte
|
||||
private int _bytesUsed; // The number of bytes in the output window which is not consumed.
|
||||
|
||||
/// <summary>Add a byte to output window.</summary>
|
||||
public void Write(byte b)
|
||||
{
|
||||
Debug.Assert(_bytesUsed < WindowSize, "Can't add byte when window is full!");
|
||||
_window[_end++] = b;
|
||||
_end &= WindowMask;
|
||||
++_bytesUsed;
|
||||
}
|
||||
|
||||
public void WriteLengthDistance(int length, int distance)
|
||||
{
|
||||
Debug.Assert((_bytesUsed + length) <= WindowSize, "No Enough space");
|
||||
|
||||
// move backwards distance bytes in the output stream,
|
||||
// and copy length bytes from this position to the output stream.
|
||||
_bytesUsed += length;
|
||||
int copyStart = (_end - distance) & WindowMask; // start position for coping.
|
||||
|
||||
int border = WindowSize - length;
|
||||
if (copyStart <= border && _end < border)
|
||||
{
|
||||
if (length <= distance)
|
||||
{
|
||||
Array.Copy(_window, copyStart, _window, _end, length);
|
||||
_end += length;
|
||||
}
|
||||
else
|
||||
{
|
||||
// The referenced string may overlap the current
|
||||
// position; for example, if the last 2 bytes decoded have values
|
||||
// X and Y, a string reference with <length = 5, distance = 2>
|
||||
// adds X,Y,X,Y,X to the output stream.
|
||||
while (length-- > 0)
|
||||
{
|
||||
_window[_end++] = _window[copyStart++];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// copy byte by byte
|
||||
while (length-- > 0)
|
||||
{
|
||||
_window[_end++] = _window[copyStart++];
|
||||
_end &= WindowMask;
|
||||
copyStart &= WindowMask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy up to length of bytes from input directly.
|
||||
/// This is used for uncompressed block.
|
||||
/// </summary>
|
||||
public int CopyFrom(InputBuffer input, int length)
|
||||
{
|
||||
length = Math.Min(Math.Min(length, WindowSize - _bytesUsed), input.AvailableBytes);
|
||||
int copied;
|
||||
|
||||
// We might need wrap around to copy all bytes.
|
||||
int tailLen = WindowSize - _end;
|
||||
if (length > tailLen)
|
||||
{
|
||||
// copy the first part
|
||||
copied = input.CopyTo(_window, _end, tailLen);
|
||||
if (copied == tailLen)
|
||||
{
|
||||
// only try to copy the second part if we have enough bytes in input
|
||||
copied += input.CopyTo(_window, 0, length - tailLen);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// only one copy is needed if there is no wrap around.
|
||||
copied = input.CopyTo(_window, _end, length);
|
||||
}
|
||||
|
||||
_end = (_end + copied) & WindowMask;
|
||||
_bytesUsed += copied;
|
||||
return copied;
|
||||
}
|
||||
|
||||
/// <summary>Free space in output window.</summary>
|
||||
public int FreeBytes => WindowSize - _bytesUsed;
|
||||
|
||||
/// <summary>Bytes not consumed in output window.</summary>
|
||||
public int AvailableBytes => _bytesUsed;
|
||||
|
||||
/// <summary>Copy the decompressed bytes to output array.</summary>
|
||||
public int CopyTo(byte[] output, int offset, int length)
|
||||
{
|
||||
int copy_end;
|
||||
|
||||
if (length > _bytesUsed)
|
||||
{
|
||||
// we can copy all the decompressed bytes out
|
||||
copy_end = _end;
|
||||
length = _bytesUsed;
|
||||
}
|
||||
else
|
||||
{
|
||||
copy_end = (_end - _bytesUsed + length) & WindowMask; // copy length of bytes
|
||||
}
|
||||
|
||||
int copied = length;
|
||||
|
||||
int tailLen = length - copy_end;
|
||||
if (tailLen > 0)
|
||||
{
|
||||
// this means we need to copy two parts separately
|
||||
// copy tailLen bytes from the end of output window
|
||||
Array.Copy(_window, WindowSize - tailLen,
|
||||
output, offset, tailLen);
|
||||
offset += tailLen;
|
||||
length = copy_end;
|
||||
}
|
||||
Array.Copy(_window, copy_end - length, output, offset, length);
|
||||
_bytesUsed -= copied;
|
||||
Debug.Assert(_bytesUsed >= 0, "check this function and find why we copied more bytes than we have");
|
||||
return copied;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,9 @@
|
||||
<PropertyGroup>
|
||||
<AssemblyTitle>SharpCompress - Pure C# Decompression/Compression</AssemblyTitle>
|
||||
<NeutralLanguage>en-US</NeutralLanguage>
|
||||
<VersionPrefix>0.19.1</VersionPrefix>
|
||||
<AssemblyVersion>0.19.1.0</AssemblyVersion>
|
||||
<FileVersion>0.19.1.0</FileVersion>
|
||||
<VersionPrefix>0.19.2</VersionPrefix>
|
||||
<AssemblyVersion>0.19.2.0</AssemblyVersion>
|
||||
<FileVersion>0.19.2.0</FileVersion>
|
||||
<Authors>Adam Hathcock</Authors>
|
||||
<TargetFrameworks Condition="'$(LibraryFrameworks)'==''">net45;net35;netstandard1.0;netstandard1.3;netstandard2.0</TargetFrameworks>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
using System;
|
||||
#if NETCORE
|
||||
using System.Buffers;
|
||||
#endif
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
#if NETCORE
|
||||
using SharpCompress.Buffers;
|
||||
#endif
|
||||
using SharpCompress.Readers;
|
||||
|
||||
namespace SharpCompress
|
||||
@@ -141,6 +141,12 @@ namespace SharpCompress
|
||||
|
||||
public static void Skip(this Stream source, long advanceAmount)
|
||||
{
|
||||
if (source.CanSeek)
|
||||
{
|
||||
source.Position += advanceAmount;
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] buffer = GetTransferByteArray();
|
||||
try
|
||||
{
|
||||
|
||||
@@ -11,9 +11,13 @@ namespace SharpCompress.Writers.Tar
|
||||
{
|
||||
public class TarWriter : AbstractWriter
|
||||
{
|
||||
public TarWriter(Stream destination, WriterOptions options)
|
||||
private bool finalizeArchiveOnClose;
|
||||
|
||||
public TarWriter(Stream destination, TarWriterOptions options)
|
||||
: base(ArchiveType.Tar, options)
|
||||
{
|
||||
finalizeArchiveOnClose = options.FinalizeArchiveOnClose;
|
||||
|
||||
if (!destination.CanWrite)
|
||||
{
|
||||
throw new ArgumentException("Tars require writable streams.");
|
||||
@@ -97,8 +101,10 @@ namespace SharpCompress.Writers.Tar
|
||||
{
|
||||
if (isDisposing)
|
||||
{
|
||||
PadTo512(0, true);
|
||||
PadTo512(0, true);
|
||||
if (finalizeArchiveOnClose) {
|
||||
PadTo512(0, true);
|
||||
PadTo512(0, true);
|
||||
}
|
||||
switch (OutputStream)
|
||||
{
|
||||
case BZip2Stream b:
|
||||
|
||||
23
src/SharpCompress/Writers/Tar/TarWriterOptions.cs
Executable file
23
src/SharpCompress/Writers/Tar/TarWriterOptions.cs
Executable file
@@ -0,0 +1,23 @@
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Common;
|
||||
|
||||
namespace SharpCompress.Writers.Tar
|
||||
{
|
||||
public class TarWriterOptions : WriterOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates if archive should be finalized (by 2 empty blocks) on close.
|
||||
/// </summary>
|
||||
public bool FinalizeArchiveOnClose { get; }
|
||||
|
||||
public TarWriterOptions(CompressionType compressionType, bool finalizeArchiveOnClose)
|
||||
: base(compressionType)
|
||||
{
|
||||
FinalizeArchiveOnClose = finalizeArchiveOnClose;
|
||||
}
|
||||
|
||||
internal TarWriterOptions(WriterOptions options) : this(options.CompressionType, true)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,7 @@ namespace SharpCompress.Writers
|
||||
}
|
||||
case ArchiveType.Tar:
|
||||
{
|
||||
return new TarWriter(stream, writerOptions);
|
||||
return new TarWriter(stream, new TarWriterOptions(writerOptions));
|
||||
}
|
||||
default:
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netcoreapp1.1;netcoreapp2.0</TargetFrameworks>
|
||||
<TargetFrameworks>netcoreapp2.0</TargetFrameworks>
|
||||
<AssemblyName>SharpCompress.Test</AssemblyName>
|
||||
<AssemblyOriginatorKeyFile>../../SharpCompress.snk</AssemblyOriginatorKeyFile>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
@@ -16,10 +16,4 @@
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
|
||||
<PackageReference Include="xunit" Version="2.3.1" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition=" '$(TargetFramework)' != 'netstandard2.0' ">
|
||||
<PackageReference Include="Microsoft.Extensions.PlatformAbstractions" Version="1.1.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
|
||||
<PackageReference Include="NETStandard.Library" Version="2.0.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,4 +1,6 @@
|
||||
using SharpCompress.Common;
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Writers.Tar;
|
||||
using Xunit;
|
||||
|
||||
namespace SharpCompress.Test.Tar
|
||||
@@ -34,5 +36,22 @@ namespace SharpCompress.Test.Tar
|
||||
{
|
||||
Assert.Throws<InvalidFormatException>(() => Write(CompressionType.Rar, "Zip.ppmd.noEmptyDirs.zip", "Zip.ppmd.noEmptyDirs.zip"));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
public void Tar_Finalize_Archive(bool finalizeArchive)
|
||||
{
|
||||
using (MemoryStream stream = new MemoryStream())
|
||||
using (Stream content = File.OpenRead(Path.Combine(ORIGINAL_FILES_PATH, "jpg", "test.jpg"))) {
|
||||
using (TarWriter writer = new TarWriter(stream, new TarWriterOptions(CompressionType.None, finalizeArchive))) {
|
||||
writer.Write("doesn't matter", content, null);
|
||||
}
|
||||
|
||||
var paddedContentWithHeader = content.Length / 512 * 512 + 512 + 512;
|
||||
var expectedStreamLength = finalizeArchive ? paddedContentWithHeader + 512 * 2 : paddedContentWithHeader;
|
||||
Assert.Equal(expectedStreamLength, stream.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ namespace SharpCompress.Test
|
||||
yield return Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.dd-.zip");
|
||||
yield return Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.dd.zip");
|
||||
yield return Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.zip");
|
||||
yield return Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate64.zip");
|
||||
yield return Path.Combine(TEST_ARCHIVES_PATH, "Zip.lzma.dd.zip");
|
||||
yield return Path.Combine(TEST_ARCHIVES_PATH, "Zip.lzma.zip");
|
||||
yield return Path.Combine(TEST_ARCHIVES_PATH, "Zip.none.zip");
|
||||
@@ -241,13 +242,9 @@ namespace SharpCompress.Test
|
||||
{
|
||||
Monitor.Enter(lockObject);
|
||||
|
||||
#if NETSTANDARD20
|
||||
var index = AppDomain.CurrentDomain.BaseDirectory.IndexOf("SharpCompress.Test", StringComparison.OrdinalIgnoreCase);
|
||||
SOLUTION_BASE_PATH = Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory.Substring(0, index));
|
||||
#else
|
||||
var index = Microsoft.Extensions.PlatformAbstractions.PlatformServices.Default.Application.ApplicationBasePath.IndexOf("SharpCompress.Test", StringComparison.OrdinalIgnoreCase);
|
||||
SOLUTION_BASE_PATH = Path.GetDirectoryName(Microsoft.Extensions.PlatformAbstractions.PlatformServices.Default.Application.ApplicationBasePath.Substring(0, index));
|
||||
#endif
|
||||
|
||||
TEST_ARCHIVES_PATH = Path.Combine(SOLUTION_BASE_PATH, "TestArchives", "Archives");
|
||||
ORIGINAL_FILES_PATH = Path.Combine(SOLUTION_BASE_PATH, "TestArchives", "Original");
|
||||
MISC_TEST_FILES_PATH = Path.Combine(SOLUTION_BASE_PATH, "TestArchives", "MiscTest");
|
||||
|
||||
@@ -49,6 +49,11 @@ namespace SharpCompress.Test.Zip
|
||||
{
|
||||
ArchiveStreamRead("Zip.deflate.zip");
|
||||
}
|
||||
[Fact]
|
||||
public void Zip_Deflate64_ArchiveStreamRead()
|
||||
{
|
||||
ArchiveStreamRead("Zip.deflate64.zip");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Zip_LZMA_Streamed_ArchiveStreamRead()
|
||||
@@ -101,6 +106,11 @@ namespace SharpCompress.Test.Zip
|
||||
{
|
||||
ArchiveFileRead("Zip.deflate.zip");
|
||||
}
|
||||
[Fact]
|
||||
public void Zip_Deflate64_ArchiveFileRead()
|
||||
{
|
||||
ArchiveFileRead("Zip.deflate64.zip");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Zip_LZMA_Streamed_ArchiveFileRead()
|
||||
|
||||
@@ -101,6 +101,11 @@ namespace SharpCompress.Test.Zip
|
||||
{
|
||||
Read("Zip.deflate.zip", CompressionType.Deflate);
|
||||
}
|
||||
[Fact]
|
||||
public void Zip_Deflate64_Read()
|
||||
{
|
||||
Read("Zip.deflate64.zip", CompressionType.Deflate64);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Zip_LZMA_Streamed_Read()
|
||||
|
||||
BIN
tests/TestArchives/Archives/Zip.deflate64.zip
Normal file
BIN
tests/TestArchives/Archives/Zip.deflate64.zip
Normal file
Binary file not shown.
Reference in New Issue
Block a user