Compare commits

..

83 Commits

Author SHA1 Message Date
Adam Hathcock
d6a6085d75 Update docker build 2019-10-22 17:28:09 +01:00
Adam Hathcock
b5a897819d Fix pack 2019-10-22 17:26:08 +01:00
Adam Hathcock
9e842ee8ec Fix cake build 2019-10-22 17:23:58 +01:00
Adam Hathcock
a04a0a5912 Use RecyclableMemoryStreamManager 2019-10-22 17:13:44 +01:00
Adam Hathcock
9540b01bcc NET Standard 1.3 and 2.0 only (#482)
* Remove NET35, NET45 and NET Standard 1.0

* Update README and memset

* Remove NETCORE build flag

* NET 46 too?

* Update packages and usage
2019-10-10 09:24:41 +01:00
Adam Hathcock
446d6914c1 Merge pull request #483 from Bond-009/nameof
Use nameof for param names
2019-09-17 14:21:04 +01:00
Bond_009
637223aa53 Use nameof for param names 2019-09-17 13:28:44 +02:00
Adam Hathcock
17d5565120 Merge pull request #478 from Bond-009/buffers
Use System.Buffers Nuget package
2019-09-17 10:05:29 +01:00
Bond_009
4b54187b4c Fix build 2019-09-11 21:33:57 +02:00
Bond_009
cfb1421367 Use System.Buffers Nuget package 2019-09-11 20:06:50 +02:00
Adam Hathcock
5072a0f6f5 Merge pull request #471 from adamhathcock/release-024
Bump version and dependencies
2019-08-20 20:36:38 +01:00
Adam Hathcock
357dff1403 Bump version and dependencies 2019-08-20 14:29:47 -05:00
Adam Hathcock
a2bd66ded8 Merge pull request #460 from itn3000/tar-fix-garbled2
fix filename garbling in tar(#414)
2019-06-27 12:16:53 +01:00
itn3000
6bfa3c25a4 add more comments 2019-06-27 20:01:40 +09:00
itn3000
1ea9ab72c1 add comment for subtracting 2019-06-27 19:59:16 +09:00
itn3000
07c42b8725 replace magic number 2019-06-27 10:59:21 +09:00
itn3000
70392c32e2 use Buffer.BlockCopy for performance 2019-06-27 09:47:26 +09:00
itn3000
9b4b2a9f7c considering encoding in processing filename(#414)
modify test tar archive because it was not expected one.
(expected "тест.txt" in encoding 866, but actual is omitted upper byte)
2019-06-26 17:34:12 +09:00
Adam Hathcock
d3dd708b58 Merge pull request #457 from DannyBoyk/issue_456_zip_bounded_substreams_data_descriptors
Return a bounded substream when data descriptors are used in seekable zips
2019-06-04 13:42:24 +01:00
Daniel Nash
af264cdc58 Return a bounded substream when data descriptors are used in seekable zips 2019-06-04 08:31:42 -04:00
Adam Hathcock
cfd6df976f Merge pull request #455 from DannyBoyk/issue_454_zip_bad_extra_field
Handle a bad extra field in a local file header in zip files
2019-06-04 09:24:55 +01:00
Daniel Nash
b2bd20b47e Handle a bad extra field in a local file header in zip files 2019-06-03 13:02:28 -04:00
Adam Hathcock
ffea093e95 Merge pull request #453 from Lssikkes/master
Fix for clients failing on just having a 64 bit offset in ZIP64
2019-05-24 19:33:59 +01:00
Leroy Sikkes
78eb8fcf92 Fix for clients that don't support ZIP64 standard correctly in case headers are only pointed to in ZIP64 directory structure 2019-05-24 18:27:49 +02:00
Adam Hathcock
a052956881 Merge pull request #452 from Lssikkes/master
Various fixes for ZIP64 writer (zero byte entries, 32 bit where supported)
2019-05-24 16:17:48 +01:00
Lssikkes
9319ea6992 Updated ZIP64 writer to write 32 bit values to directory entries for better compatibility.
Support for zero byte files without corruption errors from WinRAR/7-zip
2019-05-24 16:14:30 +02:00
Adam Hathcock
4e5b70dbfa Merge pull request #444 from eugeny-trunin/mem-opt
Memory and speed optimization
2019-03-20 15:13:00 +00:00
evgeny
c68eaa8397 Memory and speed optimization 2019-03-20 17:46:57 +03:00
Adam Hathcock
bbb7c85ba7 Merge pull request #442 from turbolocust/master
Fix: ArchiveEncoding was ignored in TarWriterOptions
2019-03-19 08:31:31 +00:00
Matthias Fussenegger
8174359228 Fix: ArchiveEncoding was ignored in TarWriterOptions 2019-03-18 18:25:00 +01:00
Adam Hathcock
acf66c5195 Merge pull request #440 from adamhathcock/release-023
Bump release for 0.23 and update dependencies
2019-02-27 12:57:30 +00:00
Adam Hathcock
880c9fa97a Bump release and update dependencies 2019-02-27 12:55:16 +00:00
Adam Hathcock
e5c111f2be Merge pull request #436 from Numpsy/populate_zip_volume_comment
Changes to populate ZipArchive.ZipVolumne.Comment
2019-01-14 08:14:03 +00:00
Richard Webb
4e9cd064dd Unit test to show reading of a Zip volume/archive comment 2019-01-13 21:05:55 +00:00
Richard Webb
12a6d3977e Return the DirectoryEndHeader from SeekableZipHeaderFactory.ReadSeekable so that it can be used by ZipArchive 2018-12-14 22:44:44 +00:00
Adam Hathcock
a95bbaf820 Merge pull request #423 from markfinal/tarsymlink
Tar symlink support
2018-11-05 08:31:48 +00:00
Mark Final
70bafa653b Tar symlink extraction
Extended SharpCompress.Common.ExtractionOptions with a delegate to write symbolic links. If not is null, and a symbolic link is encountered, an exception is thrown.
Removed Mono.Posix.NETStandard from the library, but added to the .NET Core 2.1 test application.
Extended the test to implement the delegate.
2018-11-03 09:45:12 +00:00
Mark Final
3f4338489c Removed unnecessary code 2018-11-01 21:57:49 +00:00
Mark Final
d91e58f2cc Refs #248. Refs #132. Added a test case of a tar containing symlinks
This is a source archive of the MoltenVK project from github, which is my use-case for SharpCompress.
I added a test case in the project, which should extract the tar, and validate any symlink targets with what the tar thinks it ought to be.
2018-11-01 21:51:14 +00:00
Mark Final
192b9c1e8b Ref #248. Ref #132. Tar reader support for symlinks for .NET standard 2 and Posix platforms
Extracts linkname from the tar header, and exposes this on IEntry as the LinkTarget (string) property. If an entry is not a symlink, then that property is null.

Uses Mono.Posix.NETStandard nuget to create a symlink. However, this is only applicable to .NET standard 2.0+. So far, unable to find a nuget that works for older versions.

Also, not sure what to do on Windows.
2018-11-01 21:48:51 +00:00
Adam Hathcock
0941239454 Merge pull request #417 from KyotoFox/fix-entrystream-flush
Allow Flush on EntryStream
2018-10-04 12:48:08 +01:00
Knut Ørland
53ad00cdc4 Use soft tabs 2018-10-04 13:13:14 +02:00
Knut Ørland
6dd5da48f7 Added test that calls EntryStream.Flush() 2018-10-04 13:08:53 +02:00
Knut Ørland
efae8328a9 Don't throw an exception when flushing an EntryStream
From Microsoft docs: “In a class derived from Stream that doesn't
support writing, Flush is typically implemented as an empty method to
ensure full compatibility with other Stream types since it's valid to
flush a read-only stream.”
2018-10-04 13:05:36 +02:00
Adam Hathcock
f1facc51de Merge pull request #409 from G-Research/RecogniseEmptyTarArchives
Recognise empty tar archives.
2018-09-25 13:20:59 +01:00
Adam Hathcock
a471ca6a76 Use Cake tool on circle. Update test packages 2018-08-31 09:27:04 +01:00
Elliot Prior
83f6690576 Recognise empty tar archives.
Currently, when ArchiveFactory.Open is called on an empty tar archive, it throws due to being unable to determine the stream type. This fix allows it to recognise empty tar files by checking for whether the filename is empty, the size is empty and the entry type is defined. Add a test to try opening an empty archive.
2018-08-16 10:25:47 +01:00
Adam Hathcock
1850ea67f6 Merge pull request #408 from majoreffort/master
Test and fix for #407
2018-07-24 09:42:03 +01:00
majoreffort
2fd6178aa9 Fixed length in TarHeader#WriteStringBytes 2018-07-23 19:58:37 +02:00
majoreffort
ec044e6f42 Added Tar test for 100 char filename issue. 2018-07-23 19:48:01 +02:00
Adam Hathcock
bd96279649 Merge pull request #404 from MattKotsenas/bugfix/idisposable
Enable parallel test execution
2018-07-12 19:53:50 +01:00
Matt Kotsenas
f7ad595945 Enable test parallelization and remove garbage collection workaround
Now that the sources of file locking are fixed, enable test parallelization
and the forced garbage collection workaround.

Lastly, remove the `IsLocked` check because it doesn't work in a
parallel test running world - the file may be locked due to another test
running.
2018-07-12 10:33:19 -07:00
Matt Kotsenas
93c0b91de9 Refactor TestSharpCompressWithEmptyStream
Refactor `TestSharpCompressWithEmptyStream` so it asserts that the files
and bytes are the same.
2018-07-12 10:32:08 -07:00
Matt Kotsenas
138038b08f Move RarReaderTests over to user ReaderFactory
- Refactor `RarReaderTests` to use `ReaderFactory`
- Update `ReaderTests.Read` to support Rar tests
2018-07-12 10:32:08 -07:00
Matt Kotsenas
e9a6fed607 FIXUP decouple UseReader from VerifyFiles 2018-07-11 16:53:34 -07:00
Matt Kotsenas
87a1440382 Decouple UseReader from VerifyFiles 2018-07-11 16:49:49 -07:00
Matt Kotsenas
3c2f4ebe9b Combine ForwardOnlyStream and NonSeekableStream
Delete `NonSeekableStream` used in Zip64 tests in favor
of `ForwardOnlyStream` used in Mocks.

Additionally, delete the `ForwardOnlyStream.ReadByte` implementation
as the implementation on the base Stream is sufficient.
2018-07-11 16:42:03 -07:00
Matt Kotsenas
933ffe7828 Remove unused code from ArchiveTests 2018-07-11 16:33:46 -07:00
Matt Kotsenas
7d20ba5243 Simplify RarHeaderTests 2018-07-11 16:21:19 -07:00
Matt Kotsenas
44dc36af48 Update ReaderTests base class to validate Dispose
Update the `ReaderTests` base class to validate that `Dispose` is
called appropriately in both the close and the leave open cases.
2018-07-11 16:21:19 -07:00
Matt Kotsenas
98558c5ba9 Refactor TestStream constructor
Refactor the `TestStream` constructor so by default it defers to
the underlying Stream
2018-07-11 16:21:19 -07:00
Matt Kotsenas
6c25322465 Follow best-practices for Dispose in Volume and ForwardOnlyStream
Update `Volume` and `ForwardOnlyStream` to follow the project's
general pattern and best-practices for `Dispose`
2018-07-11 16:21:19 -07:00
Matt Kotsenas
6d1d62fd32 Delete AppendingStream
`AppendingStream` is unused, so rather than refactor it, just delete it.
2018-07-11 16:21:19 -07:00
Matt Kotsenas
ee4ae661d7 Refactor ListeningStream
Refactor `ListeningStream`:

- Override of `WriteByte` was redundant and removed
- Make `Dispose` delegate to base class
2018-07-11 16:21:19 -07:00
Matt Kotsenas
0473ec1626 Open test archives as read
Update `RarHeaderFactoryTests` and `GZipArchiveTests` to open the test
readers as `FileAccess.Read` and `FileShare.Read` to prevent issues with
multiple test from trying to open exclusive access to files.
2018-07-11 16:21:19 -07:00
Matt Kotsenas
c6cf0d40ee Simplify ReaderTests
The `IEnumerable<string>` version of `ReaderTests` is unused, so delete
it to simplify the code.
2018-07-11 16:21:19 -07:00
Matt Kotsenas
4cd80e96f3 Simplify GZip bad compression test 2018-07-11 16:21:19 -07:00
Matt Kotsenas
16524717ba Fix Stream leak in ArchiveFactory
`ArchiveFactory.Open` has two overloads that take `string` or
`FileInfo` (string delegates to FileInfo). Both of these implementations
open a `Stream` with the default `ReaderOptions`, which leaves the
stream open, resulting in a leak.

The fix is to set `LeaveOpen` to `false` if no options were provided.
Note that if a user was provding options and `LeaveOpen` was set to
`true`, the code did and will still leak.
2018-07-11 16:21:19 -07:00
Matt Kotsenas
cab1ce3d0c Update sub-streams to uniformly inherit from NonDisposingStream
Update the sub-stream classes to all inherit from `NonDisposingStream`.
This allows them to correctly implement the `Dispose` pattern, and delegate
the actual disposal to `NonDisposingStream`.

In doing so, we need to remove some redundant overrides from
`NonDisposingStream`, otherwise `BufferedSubStream` would use the
overrides inherited from `NonDisposingStream` instead of the ones
inherited from `Stream` (i.e. delegate `ReadByte` to `Read`).
2018-07-11 16:17:49 -07:00
Matt Kotsenas
6c2e5e1164 Cleanup NonDisposingStream for reuse
- Remove the duplicate `GC.SuppressFinalization` call
(called in `System.IO.Stream)
- Improve the `ThrowOnDispose` error message
2018-07-11 12:19:34 -07:00
Matt Kotsenas
c2bf540057 Close verification streams in TestBase.CompareArchivesByPath 2018-07-11 12:12:30 -07:00
Matt Kotsenas
a35c66e166 Move RewindableStreamTest.cs to the Streams/ folder 2018-07-10 12:07:33 -07:00
Matt Kotsenas
084c5e2686 Rename StreamTests.cs --> LzmaStreamTests.cs 2018-07-10 12:07:32 -07:00
Matt Kotsenas
6ae715b153 Move the TestStream and ForwardOnlyStream to Mocks folder
Move the `TestStream` and `ForwardOnlyStream` to Mocks/ to separate them
from the test classes.
2018-07-10 12:07:32 -07:00
Adam Hathcock
9c8692806a Merge pull request #403 from MattKotsenas/bugfix/parallel-tests
Fix and re-enable tests
2018-07-10 20:01:20 +01:00
Matt Kotsenas
2d87351d2a Add tests back to AppVeyor 2018-07-10 11:52:00 -07:00
Matt Kotsenas
3114afde0e Add workaround for in-use files
The `TestBase` is not always able to delete the scratch folder in
`Dispose()` because sometimes the files are still in use.

This problem appears to be leaked file handles (likely due to incorrect
handling of `IDisposable`). To avoid the problem for now, force a
garbage collection prior to deleting the scratch folder.
2018-07-10 11:49:38 -07:00
Matt Kotsenas
7b338511cc Create unique scratch path per test
Tests fail in Visual Studio because they try to reuse the same scratch
working space, and each test is responsible for resetting the space. To
simplify the test code:

1. Make `TestBase` `IDisposable` and have it create the scratch space
2. Remove `ResetScratch()` as it is now handled by the base class
3. Add a unique ID to each scrach space folder to prevent collisions
2018-07-10 11:46:44 -07:00
Adam Hathcock
09c27681e1 Merge pull request #402 from a764578566/master
file search support linq Pattern
2018-07-10 13:21:09 +01:00
zhoujr
4ebc1f82b7 file search support linq Pattern 2018-07-10 19:58:59 +08:00
Adam Hathcock
4640ca497a Merge pull request #400 from MattKotsenas/feature/avoid-exception-in-readerfactory
Avoid throwing NotSupportedException in ReaderFactory hot path
2018-07-10 08:47:13 +01:00
Matt Kotsenas
bebccaae28 Avoid throwing NotSupportedException in ReaderFactory hot path
`ReaderFactory.Open()` calls `ZipArchive.IsZipFile()` to determine if
the `Stream` is a zip archive, which calls into
`ZipHeaderFactory.ReadHeader()`, which throws a `NotSupportedException`
when the `Stream` is not a zip archive.

To be clear, this exception is caught and `IsZipFile()` returns `false`,
but when called in a hot-path, these exceptions can become expensive.

To address this issue, `ReadHeader` now returns `null` in the default
cause instead of throwing. All callsites were already checking for and
handling `null`, so no behavior changes.
2018-07-09 18:44:46 -07:00
Adam Hathcock
7ee53373c6 Remove tests as AppVeyor can’t handle them at the moment 2018-07-09 09:05:10 +01:00
111 changed files with 836 additions and 1404 deletions

View File

@@ -2,14 +2,15 @@ version: 2
jobs:
build:
docker:
- image: microsoft/dotnet:2.1.301-sdk
- image: mcr.microsoft.com/dotnet/core/sdk:2.2-alpine
steps:
- checkout
- run:
name: Install unzip
name: Install Cake
command: |
apt-get update
apt-get install -y unzip
dotnet tool install -g Cake.Tool
echo 'export PATH=$PATH:/root/.dotnet/tools' >> $BASH_ENV
source $BASH_ENV
- run:
name: Build
command: ./build.sh
command: dotnet cake build.cake

View File

@@ -1,6 +1,6 @@
# SharpCompress
SharpCompress is a compression library in pure C# for .NET 3.5, 4.5, .NET Standard 1.0, 1.3 that can unrar, un7zip, unzip, untar unbzip2 and ungzip with forward-only reading and file random access APIs. Write support for zip/tar/bzip2/gzip are implemented.
SharpCompress is a compression library in pure C# for .NET Standard 1.4 and 2.0 and .NET 4.6 that can unrar, un7zip, unzip, untar unbzip2 and ungzip with forward-only reading and file random access APIs. Write support for zip/tar/bzip2/gzip are implemented.
The major feature is support for non-seekable streams so large files can be processed on the fly (i.e. download stream).

View File

@@ -122,6 +122,7 @@
<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_002ECSharpUseContinuousIndentInsideBracesMigration/@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>

View File

@@ -11,32 +11,23 @@ Task("Build")
.IsDependentOn("Restore")
.Does(() =>
{
var settings = new DotNetCoreBuildSettings
{
Framework = "netstandard1.4",
Configuration = "Release",
NoRestore = true
};
DotNetCoreBuild("./src/SharpCompress/SharpCompress.csproj", settings);
if (IsRunningOnWindows())
{
MSBuild("./sharpcompress.sln", c =>
{
c.SetConfiguration("Release")
.SetVerbosity(Verbosity.Minimal)
.UseToolVersion(MSBuildToolVersion.VS2017);
});
}
else
{
var settings = new DotNetCoreBuildSettings
{
Framework = "netstandard1.0",
Configuration = "Release",
NoRestore = true
};
DotNetCoreBuild("./src/SharpCompress/SharpCompress.csproj", settings);
settings.Framework = "netstandard1.3";
DotNetCoreBuild("./src/SharpCompress/SharpCompress.csproj", settings);
settings.Framework = "netstandard2.0";
settings.Framework = "net46";
DotNetCoreBuild("./src/SharpCompress/SharpCompress.csproj", settings);
}
settings.Framework = "netstandard2.0";
DotNetCoreBuild("./src/SharpCompress/SharpCompress.csproj", settings);
});
Task("Test")
@@ -49,7 +40,7 @@ Task("Test")
var settings = new DotNetCoreTestSettings
{
Configuration = "Release",
Framework = "netcoreapp2.1"
Framework = "netcoreapp2.2"
};
DotNetCoreTest(file.ToString(), settings);
}
@@ -58,16 +49,17 @@ Task("Test")
Task("Pack")
.IsDependentOn("Build")
.Does(() =>
{
{
if (IsRunningOnWindows())
{
MSBuild("src/SharpCompress/SharpCompress.csproj", c => c
.SetConfiguration("Release")
.SetVerbosity(Verbosity.Minimal)
.UseToolVersion(MSBuildToolVersion.VS2017)
.WithProperty("NoBuild", "true")
.WithTarget("Pack"));
}
var settings = new DotNetCorePackSettings
{
Configuration = "Release",
NoBuild = true
};
DotNetCorePack("src/SharpCompress/SharpCompress.csproj", settings);
}
else
{
Information("Skipping Pack as this is not Windows");

View File

@@ -23,8 +23,7 @@ namespace SharpCompress.Archives
protected ReaderOptions ReaderOptions { get; }
private bool disposed;
#if !NO_FILE
internal AbstractArchive(ArchiveType type, FileInfo fileInfo, ReaderOptions readerOptions)
{
Type = type;
@@ -40,7 +39,6 @@ namespace SharpCompress.Archives
protected abstract IEnumerable<TVolume> LoadVolumes(FileInfo file);
#endif
internal AbstractArchive(ArchiveType type, IEnumerable<Stream> streams, ReaderOptions readerOptions)
{

View File

@@ -28,12 +28,10 @@ namespace SharpCompress.Archives
{
}
#if !NO_FILE
internal AbstractWritableArchive(ArchiveType type, FileInfo fileInfo, ReaderOptions readerFactoryOptions)
: base(type, fileInfo, readerFactoryOptions)
{
}
#endif
public override ICollection<TEntry> Entries
{

View File

@@ -6,7 +6,6 @@ using SharpCompress.Archives.SevenZip;
using SharpCompress.Archives.Tar;
using SharpCompress.Archives.Zip;
using SharpCompress.Common;
using SharpCompress.Compressors.LZMA;
using SharpCompress.Readers;
namespace SharpCompress.Archives
@@ -21,7 +20,7 @@ namespace SharpCompress.Archives
/// <returns></returns>
public static IArchive Open(Stream stream, ReaderOptions readerOptions = null)
{
stream.CheckNotNull("stream");
stream.CheckNotNull(nameof(stream));
if (!stream.CanRead || !stream.CanSeek)
{
throw new ArgumentException("Stream should be readable and seekable");
@@ -82,8 +81,6 @@ namespace SharpCompress.Archives
}
}
#if !NO_FILE
/// <summary>
/// Constructor expects a filepath to an existing file.
/// </summary>
@@ -91,8 +88,8 @@ namespace SharpCompress.Archives
/// <param name="options"></param>
public static IArchive Open(string filePath, ReaderOptions options = null)
{
filePath.CheckNotNullOrEmpty("filePath");
return Open(new FileInfo(filePath), options ?? new ReaderOptions());
filePath.CheckNotNullOrEmpty(nameof(filePath));
return Open(new FileInfo(filePath), options);
}
/// <summary>
@@ -102,37 +99,32 @@ namespace SharpCompress.Archives
/// <param name="options"></param>
public static IArchive Open(FileInfo fileInfo, ReaderOptions options = null)
{
fileInfo.CheckNotNull("fileInfo");
options = options ?? new ReaderOptions();
fileInfo.CheckNotNull(nameof(fileInfo));
options = options ?? new ReaderOptions { LeaveStreamOpen = false };
using (var stream = fileInfo.OpenRead())
{
if (ZipArchive.IsZipFile(stream, null))
{
stream.Dispose();
return ZipArchive.Open(fileInfo, options);
}
stream.Seek(0, SeekOrigin.Begin);
if (SevenZipArchive.IsSevenZipFile(stream))
{
stream.Dispose();
return SevenZipArchive.Open(fileInfo, options);
}
stream.Seek(0, SeekOrigin.Begin);
if (GZipArchive.IsGZipFile(stream))
{
stream.Dispose();
return GZipArchive.Open(fileInfo, options);
}
stream.Seek(0, SeekOrigin.Begin);
if (RarArchive.IsRarFile(stream, options))
{
stream.Dispose();
return RarArchive.Open(fileInfo, options);
}
stream.Seek(0, SeekOrigin.Begin);
if (TarArchive.IsTarFile(stream))
{
stream.Dispose();
return TarArchive.Open(fileInfo, options);
}
throw new InvalidOperationException("Cannot determine compressed stream type. Supported Archive Formats: Zip, GZip, Tar, Rar, 7Zip");
@@ -153,6 +145,5 @@ namespace SharpCompress.Archives
}
}
}
#endif
}
}
}

View File

@@ -13,8 +13,6 @@ namespace SharpCompress.Archives.GZip
{
public class GZipArchive : AbstractWritableArchive<GZipArchiveEntry, GZipVolume>
{
#if !NO_FILE
/// <summary>
/// Constructor expects a filepath to an existing file.
/// </summary>
@@ -22,7 +20,7 @@ namespace SharpCompress.Archives.GZip
/// <param name="readerOptions"></param>
public static GZipArchive Open(string filePath, ReaderOptions readerOptions = null)
{
filePath.CheckNotNullOrEmpty("filePath");
filePath.CheckNotNullOrEmpty(nameof(filePath));
return Open(new FileInfo(filePath), readerOptions ?? new ReaderOptions());
}
@@ -33,10 +31,9 @@ namespace SharpCompress.Archives.GZip
/// <param name="readerOptions"></param>
public static GZipArchive Open(FileInfo fileInfo, ReaderOptions readerOptions = null)
{
fileInfo.CheckNotNull("fileInfo");
fileInfo.CheckNotNull(nameof(fileInfo));
return new GZipArchive(fileInfo, readerOptions ?? new ReaderOptions());
}
#endif
/// <summary>
/// Takes a seekable Stream as a source
@@ -45,7 +42,7 @@ namespace SharpCompress.Archives.GZip
/// <param name="readerOptions"></param>
public static GZipArchive Open(Stream stream, ReaderOptions readerOptions = null)
{
stream.CheckNotNull("stream");
stream.CheckNotNull(nameof(stream));
return new GZipArchive(stream, readerOptions ?? new ReaderOptions());
}
@@ -54,8 +51,6 @@ namespace SharpCompress.Archives.GZip
return new GZipArchive();
}
#if !NO_FILE
/// <summary>
/// Constructor with a FileInfo object to an existing file.
/// </summary>
@@ -100,7 +95,6 @@ namespace SharpCompress.Archives.GZip
SaveTo(stream, new WriterOptions(CompressionType.GZip));
}
}
#endif
public static bool IsGZipFile(Stream stream)
{
@@ -185,4 +179,4 @@ namespace SharpCompress.Archives.GZip
return GZipReader.Open(stream);
}
}
}
}

View File

@@ -36,12 +36,10 @@ namespace SharpCompress.Archives
}
streamListener.FireEntryExtractionEnd(archiveEntry);
}
#if !NO_FILE
/// <summary>
/// Extract to specific directory, retaining filename
/// </summary>
/// <summary>
/// Extract to specific directory, retaining filename
/// </summary>
public static void WriteToDirectory(this IArchiveEntry entry, string destinationDirectory,
ExtractionOptions options = null)
{
@@ -65,6 +63,5 @@ namespace SharpCompress.Archives
}
});
}
#endif
}
}

View File

@@ -1,18 +1,13 @@
#if !NO_FILE
using System.Linq;
using System.Linq;
using SharpCompress.Common;
#endif
namespace SharpCompress.Archives
{
public static class IArchiveExtensions
{
#if !NO_FILE
/// <summary>
/// Extract to specific directory, retaining filename
/// </summary>
/// <summary>
/// Extract to specific directory, retaining filename
/// </summary>
public static void WriteToDirectory(this IArchive archive, string destinationDirectory,
ExtractionOptions options = null)
{
@@ -21,6 +16,5 @@ namespace SharpCompress.Archives
entry.WriteToDirectory(destinationDirectory, options);
}
}
#endif
}
}

View File

@@ -1,6 +1,4 @@
#if !NO_FILE
using System;
#endif
using System;
using System.IO;
using SharpCompress.Writers;
@@ -8,8 +6,6 @@ namespace SharpCompress.Archives
{
public static class IWritableArchiveExtensions
{
#if !NO_FILE
public static void AddEntry(this IWritableArchive writableArchive,
string entryPath, string filePath)
{
@@ -39,11 +35,7 @@ namespace SharpCompress.Archives
this IWritableArchive writableArchive,
string filePath, string searchPattern = "*.*", SearchOption searchOption = SearchOption.AllDirectories)
{
#if NET35
foreach (var path in Directory.GetFiles(filePath, searchPattern, searchOption))
#else
foreach (var path in Directory.EnumerateFiles(filePath, searchPattern, searchOption))
#endif
{
var fileInfo = new FileInfo(path);
writableArchive.AddEntry(path.Substring(filePath.Length), fileInfo.OpenRead(), true, fileInfo.Length,
@@ -58,6 +50,5 @@ namespace SharpCompress.Archives
}
return writableArchive.AddEntry(key, fileInfo.OpenRead(), true, fileInfo.Length, fileInfo.LastWriteTime);
}
#endif
}
}

View File

@@ -1,7 +1,6 @@
#if !NO_FILE
using System.Collections.Generic;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using SharpCompress.Common.Rar;
using SharpCompress.Common.Rar.Headers;
using SharpCompress.IO;
@@ -18,7 +17,7 @@ namespace SharpCompress.Archives.Rar
: base(StreamingMode.Seekable, fileInfo.OpenRead(), FixOptions(options))
{
FileInfo = fileInfo;
FileParts = GetVolumeFileParts().ToReadOnly();
FileParts = GetVolumeFileParts().ToArray().ToReadOnly();
}
private static ReaderOptions FixOptions(ReaderOptions options)
@@ -43,4 +42,3 @@ namespace SharpCompress.Archives.Rar
}
}
}
#endif

View File

@@ -1,6 +1,4 @@
#if !NO_FILE
using System.IO;
using System.IO;
using SharpCompress.Common.Rar.Headers;
namespace SharpCompress.Archives.Rar
@@ -25,4 +23,3 @@ namespace SharpCompress.Archives.Rar
}
}
}
#endif

View File

@@ -15,8 +15,6 @@ namespace SharpCompress.Archives.Rar
internal Lazy<IRarUnpack> UnpackV2017 { get; } = new Lazy<IRarUnpack>(() => new SharpCompress.Compressors.Rar.UnpackV2017.Unpack());
internal Lazy<IRarUnpack> UnpackV1 { get; } = new Lazy<IRarUnpack>(() => new SharpCompress.Compressors.Rar.UnpackV1.Unpack());
#if !NO_FILE
/// <summary>
/// Constructor with a FileInfo object to an existing file.
/// </summary>
@@ -31,7 +29,6 @@ namespace SharpCompress.Archives.Rar
{
return RarArchiveVolumeFactory.GetParts(file, ReaderOptions);
}
#endif
/// <summary>
/// Takes multiple seekable Streams for a multi-part archive
@@ -63,9 +60,6 @@ namespace SharpCompress.Archives.Rar
public override bool IsSolid => Volumes.First().IsSolidArchive;
#region Creation
#if !NO_FILE
/// <summary>
/// Constructor with a FileInfo object to an existing file.
/// </summary>
@@ -73,7 +67,7 @@ namespace SharpCompress.Archives.Rar
/// <param name="options"></param>
public static RarArchive Open(string filePath, ReaderOptions options = null)
{
filePath.CheckNotNullOrEmpty("filePath");
filePath.CheckNotNullOrEmpty(nameof(filePath));
return new RarArchive(new FileInfo(filePath), options ?? new ReaderOptions());
}
@@ -84,10 +78,9 @@ namespace SharpCompress.Archives.Rar
/// <param name="options"></param>
public static RarArchive Open(FileInfo fileInfo, ReaderOptions options = null)
{
fileInfo.CheckNotNull("fileInfo");
fileInfo.CheckNotNull(nameof(fileInfo));
return new RarArchive(fileInfo, options ?? new ReaderOptions());
}
#endif
/// <summary>
/// Takes a seekable Stream as a source
@@ -96,7 +89,7 @@ namespace SharpCompress.Archives.Rar
/// <param name="options"></param>
public static RarArchive Open(Stream stream, ReaderOptions options = null)
{
stream.CheckNotNull("stream");
stream.CheckNotNull(nameof(stream));
return Open(stream.AsEnumerable(), options ?? new ReaderOptions());
}
@@ -107,11 +100,10 @@ namespace SharpCompress.Archives.Rar
/// <param name="options"></param>
public static RarArchive Open(IEnumerable<Stream> streams, ReaderOptions options = null)
{
streams.CheckNotNull("streams");
streams.CheckNotNull(nameof(streams));
return new RarArchive(streams, options ?? new ReaderOptions());
}
#if !NO_FILE
public static bool IsRarFile(string filePath)
{
return IsRarFile(new FileInfo(filePath));
@@ -128,7 +120,6 @@ namespace SharpCompress.Archives.Rar
return IsRarFile(stream);
}
}
#endif
public static bool IsRarFile(Stream stream, ReaderOptions options = null)
{
@@ -145,4 +136,4 @@ namespace SharpCompress.Archives.Rar
#endregion
}
}
}

View File

@@ -3,11 +3,9 @@ using System.Collections.Generic;
using System.IO;
using SharpCompress.Common.Rar;
using SharpCompress.Readers;
#if !NO_FILE
using System.Linq;
using System.Text;
using SharpCompress.Common.Rar.Headers;
#endif
namespace SharpCompress.Archives.Rar
{
@@ -25,8 +23,7 @@ namespace SharpCompress.Archives.Rar
yield return part;
}
}
#if !NO_FILE
internal static IEnumerable<RarVolume> GetParts(FileInfo fileInfo, ReaderOptions options)
{
FileInfoRarArchiveVolume part = new FileInfoRarArchiveVolume(fileInfo, options);
@@ -141,7 +138,5 @@ namespace SharpCompress.Archives.Rar
throw new ArgumentException("Filename invalid or next archive could not be found:"
+ fileInfo.FullName);
}
#endif
}
}

View File

@@ -19,12 +19,10 @@ namespace SharpCompress.Archives.Rar
internal override Stream GetCompressedStream()
{
stream.Position = FileHeader.DataStartPosition;
#if !NO_CRYPTO
if (FileHeader.R4Salt != null)
{
return new RarCryptoWrapper(stream, password, FileHeader.R4Salt);
}
#endif
return stream;
}

View File

@@ -13,8 +13,6 @@ namespace SharpCompress.Archives.SevenZip
public class SevenZipArchive : AbstractArchive<SevenZipArchiveEntry, SevenZipVolume>
{
private ArchiveDatabase database;
#if !NO_FILE
/// <summary>
/// Constructor expects a filepath to an existing file.
/// </summary>
@@ -36,7 +34,6 @@ namespace SharpCompress.Archives.SevenZip
fileInfo.CheckNotNull("fileInfo");
return new SevenZipArchive(fileInfo, readerOptions ?? new ReaderOptions());
}
#endif
/// <summary>
/// Takes a seekable Stream as a source
/// </summary>
@@ -48,7 +45,6 @@ namespace SharpCompress.Archives.SevenZip
return new SevenZipArchive(stream, readerOptions ?? new ReaderOptions());
}
#if !NO_FILE
internal SevenZipArchive(FileInfo fileInfo, ReaderOptions readerOptions)
: base(ArchiveType.SevenZip, fileInfo, readerOptions)
{
@@ -75,7 +71,6 @@ namespace SharpCompress.Archives.SevenZip
return IsSevenZipFile(stream);
}
}
#endif
internal SevenZipArchive(Stream stream, ReaderOptions readerOptions)
: base(ArchiveType.SevenZip, stream.AsEnumerable(), readerOptions)

View File

@@ -15,8 +15,6 @@ namespace SharpCompress.Archives.Tar
{
public class TarArchive : AbstractWritableArchive<TarArchiveEntry, TarVolume>
{
#if !NO_FILE
/// <summary>
/// Constructor expects a filepath to an existing file.
/// </summary>
@@ -24,7 +22,7 @@ namespace SharpCompress.Archives.Tar
/// <param name="readerOptions"></param>
public static TarArchive Open(string filePath, ReaderOptions readerOptions = null)
{
filePath.CheckNotNullOrEmpty("filePath");
filePath.CheckNotNullOrEmpty(nameof(filePath));
return Open(new FileInfo(filePath), readerOptions ?? new ReaderOptions());
}
@@ -35,10 +33,9 @@ namespace SharpCompress.Archives.Tar
/// <param name="readerOptions"></param>
public static TarArchive Open(FileInfo fileInfo, ReaderOptions readerOptions = null)
{
fileInfo.CheckNotNull("fileInfo");
fileInfo.CheckNotNull(nameof(fileInfo));
return new TarArchive(fileInfo, readerOptions ?? new ReaderOptions());
}
#endif
/// <summary>
/// Takes a seekable Stream as a source
@@ -47,12 +44,10 @@ namespace SharpCompress.Archives.Tar
/// <param name="readerOptions"></param>
public static TarArchive Open(Stream stream, ReaderOptions readerOptions = null)
{
stream.CheckNotNull("stream");
stream.CheckNotNull(nameof(stream));
return new TarArchive(stream, readerOptions ?? new ReaderOptions());
}
#if !NO_FILE
public static bool IsTarFile(string filePath)
{
return IsTarFile(new FileInfo(filePath));
@@ -69,24 +64,22 @@ namespace SharpCompress.Archives.Tar
return IsTarFile(stream);
}
}
#endif
public static bool IsTarFile(Stream stream)
{
try
{
TarHeader tar = new TarHeader(new ArchiveEncoding());
tar.Read(new BinaryReader(stream));
return tar.Name.Length > 0 && Enum.IsDefined(typeof(EntryType), tar.EntryType);
TarHeader tarHeader = new TarHeader(new ArchiveEncoding());
bool readSucceeded = tarHeader.Read(new BinaryReader(stream));
bool isEmptyArchive = tarHeader.Name.Length == 0 && tarHeader.Size == 0 && Enum.IsDefined(typeof(EntryType), tarHeader.EntryType);
return readSucceeded || isEmptyArchive;
}
catch
{
}
return false;
}
#if !NO_FILE
/// <summary>
/// Constructor with a FileInfo object to an existing file.
/// </summary>
@@ -101,7 +94,6 @@ namespace SharpCompress.Archives.Tar
{
return new TarVolume(file.OpenRead(), ReaderOptions).AsEnumerable();
}
#endif
/// <summary>
/// Takes multiple seekable Streams for a multi-part archive
@@ -146,7 +138,7 @@ namespace SharpCompress.Archives.Tar
using (var entryStream = entry.OpenEntryStream())
{
using (var memoryStream = new MemoryStream())
using (var memoryStream = Utility.RECYCLABLE_MEMORY_STREAM_MANAGER.GetStream())
{
entryStream.TransferTo(memoryStream);
memoryStream.Position = 0;
@@ -202,4 +194,4 @@ namespace SharpCompress.Archives.Tar
return TarReader.Open(stream);
}
}
}
}

View File

@@ -22,9 +22,7 @@ namespace SharpCompress.Archives.Zip
/// if the compression method is set to deflate
/// </summary>
public CompressionLevel DeflateCompressionLevel { get; set; }
#if !NO_FILE
/// <summary>
/// Constructor expects a filepath to an existing file.
/// </summary>
@@ -32,7 +30,7 @@ namespace SharpCompress.Archives.Zip
/// <param name="readerOptions"></param>
public static ZipArchive Open(string filePath, ReaderOptions readerOptions = null)
{
filePath.CheckNotNullOrEmpty("filePath");
filePath.CheckNotNullOrEmpty(nameof(filePath));
return Open(new FileInfo(filePath), readerOptions ?? new ReaderOptions());
}
@@ -43,10 +41,9 @@ namespace SharpCompress.Archives.Zip
/// <param name="readerOptions"></param>
public static ZipArchive Open(FileInfo fileInfo, ReaderOptions readerOptions = null)
{
fileInfo.CheckNotNull("fileInfo");
fileInfo.CheckNotNull(nameof(fileInfo));
return new ZipArchive(fileInfo, readerOptions ?? new ReaderOptions());
}
#endif
/// <summary>
/// Takes a seekable Stream as a source
@@ -55,12 +52,10 @@ namespace SharpCompress.Archives.Zip
/// <param name="readerOptions"></param>
public static ZipArchive Open(Stream stream, ReaderOptions readerOptions = null)
{
stream.CheckNotNull("stream");
stream.CheckNotNull(nameof(stream));
return new ZipArchive(stream, readerOptions ?? new ReaderOptions());
}
#if !NO_FILE
public static bool IsZipFile(string filePath, string password = null)
{
return IsZipFile(new FileInfo(filePath), password);
@@ -77,7 +72,6 @@ namespace SharpCompress.Archives.Zip
return IsZipFile(stream, password);
}
}
#endif
public static bool IsZipFile(Stream stream, string password = null)
{
@@ -101,9 +95,7 @@ namespace SharpCompress.Archives.Zip
return false;
}
}
#if !NO_FILE
/// <summary>
/// Constructor with a FileInfo object to an existing file.
/// </summary>
@@ -119,7 +111,6 @@ namespace SharpCompress.Archives.Zip
{
return new ZipVolume(file.OpenRead(), ReaderOptions).AsEnumerable();
}
#endif
internal ZipArchive()
: base(ArchiveType.Zip)
@@ -211,4 +202,4 @@ namespace SharpCompress.Archives.Zip
return ZipReader.Open(stream, ReaderOptions);
}
}
}
}

View File

@@ -1,119 +0,0 @@
// 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.
#if NETCORE
using System.Runtime.CompilerServices;
using System.Threading;
namespace SharpCompress.Buffers
{
/// <summary>
/// Provides a resource pool that enables reusing instances of type <see cref="T:T[]"/>.
/// </summary>
/// <remarks>
/// <para>
/// Renting and returning buffers with an <see cref="ArrayPool{T}"/> can increase performance
/// in situations where arrays are created and destroyed frequently, resulting in significant
/// memory pressure on the garbage collector.
/// </para>
/// <para>
/// This class is thread-safe. All members may be used by multiple threads concurrently.
/// </para>
/// </remarks>
internal abstract class ArrayPool<T>
{
/// <summary>The lazily-initialized shared pool instance.</summary>
private static ArrayPool<T> s_sharedInstance = null;
/// <summary>
/// Retrieves a shared <see cref="ArrayPool{T}"/> instance.
/// </summary>
/// <remarks>
/// The shared pool provides a default implementation of <see cref="ArrayPool{T}"/>
/// that's intended for general applicability. It maintains arrays of multiple sizes, and
/// may hand back a larger array than was actually requested, but will never hand back a smaller
/// array than was requested. Renting a buffer from it with <see cref="Rent"/> will result in an
/// existing buffer being taken from the pool if an appropriate buffer is available or in a new
/// buffer being allocated if one is not available.
/// </remarks>
public static ArrayPool<T> Shared
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return Volatile.Read(ref s_sharedInstance) ?? EnsureSharedCreated(); }
}
/// <summary>Ensures that <see cref="s_sharedInstance"/> has been initialized to a pool and returns it.</summary>
[MethodImpl(MethodImplOptions.NoInlining)]
private static ArrayPool<T> EnsureSharedCreated()
{
Interlocked.CompareExchange(ref s_sharedInstance, Create(), null);
return s_sharedInstance;
}
/// <summary>
/// Creates a new <see cref="ArrayPool{T}"/> instance using default configuration options.
/// </summary>
/// <returns>A new <see cref="ArrayPool{T}"/> instance.</returns>
public static ArrayPool<T> Create()
{
return new DefaultArrayPool<T>();
}
/// <summary>
/// Creates a new <see cref="ArrayPool{T}"/> instance using custom configuration options.
/// </summary>
/// <param name="maxArrayLength">The maximum length of array instances that may be stored in the pool.</param>
/// <param name="maxArraysPerBucket">
/// The maximum number of array instances that may be stored in each bucket in the pool. The pool
/// groups arrays of similar lengths into buckets for faster access.
/// </param>
/// <returns>A new <see cref="ArrayPool{T}"/> instance with the specified configuration options.</returns>
/// <remarks>
/// The created pool will group arrays into buckets, with no more than <paramref name="maxArraysPerBucket"/>
/// in each bucket and with those arrays not exceeding <paramref name="maxArrayLength"/> in length.
/// </remarks>
public static ArrayPool<T> Create(int maxArrayLength, int maxArraysPerBucket)
{
return new DefaultArrayPool<T>(maxArrayLength, maxArraysPerBucket);
}
/// <summary>
/// Retrieves a buffer that is at least the requested length.
/// </summary>
/// <param name="minimumLength">The minimum length of the array needed.</param>
/// <returns>
/// An <see cref="T:T[]"/> that is at least <paramref name="minimumLength"/> in length.
/// </returns>
/// <remarks>
/// This buffer is loaned to the caller and should be returned to the same pool via
/// <see cref="Return"/> so that it may be reused in subsequent usage of <see cref="Rent"/>.
/// It is not a fatal error to not return a rented buffer, but failure to do so may lead to
/// decreased application performance, as the pool may need to create a new buffer to replace
/// the one lost.
/// </remarks>
public abstract T[] Rent(int minimumLength);
/// <summary>
/// Returns to the pool an array that was previously obtained via <see cref="Rent"/> on the same
/// <see cref="ArrayPool{T}"/> instance.
/// </summary>
/// <param name="array">
/// The buffer previously obtained from <see cref="Rent"/> to return to the pool.
/// </param>
/// <param name="clearArray">
/// If <c>true</c> and if the pool will store the buffer to enable subsequent reuse, <see cref="Return"/>
/// will clear <paramref name="array"/> of its contents so that a subsequent consumer via <see cref="Rent"/>
/// will not see the previous consumer's content. If <c>false</c> or if the pool will release the buffer,
/// the array's contents are left unchanged.
/// </param>
/// <remarks>
/// Once a buffer has been returned to the pool, the caller gives up all ownership of the buffer
/// and must not use it. The reference returned from a given call to <see cref="Rent"/> must only be
/// returned via <see cref="Return"/> once. The default <see cref="ArrayPool{T}"/>
/// may hold onto the returned buffer in order to rent it again, or it may release the returned buffer
/// if it's determined that the pool already has enough buffers stored.
/// </remarks>
public abstract void Return(T[] array, bool clearArray = false);
}
}
#endif

View File

@@ -1,144 +0,0 @@
// 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.
#if NETCORE
using System;
namespace SharpCompress.Buffers
{
internal sealed partial class DefaultArrayPool<T> : ArrayPool<T>
{
/// <summary>The default maximum length of each array in the pool (2^20).</summary>
private const int DefaultMaxArrayLength = 1024 * 1024;
/// <summary>The default maximum number of arrays per bucket that are available for rent.</summary>
private const int DefaultMaxNumberOfArraysPerBucket = 50;
/// <summary>Lazily-allocated empty array used when arrays of length 0 are requested.</summary>
private static T[] s_emptyArray; // we support contracts earlier than those with Array.Empty<T>()
private readonly Bucket[] _buckets;
internal DefaultArrayPool() : this(DefaultMaxArrayLength, DefaultMaxNumberOfArraysPerBucket)
{
}
internal DefaultArrayPool(int maxArrayLength, int maxArraysPerBucket)
{
if (maxArrayLength <= 0)
{
throw new ArgumentOutOfRangeException(nameof(maxArrayLength));
}
if (maxArraysPerBucket <= 0)
{
throw new ArgumentOutOfRangeException(nameof(maxArraysPerBucket));
}
// Our bucketing algorithm has a min length of 2^4 and a max length of 2^30.
// Constrain the actual max used to those values.
const int MinimumArrayLength = 0x10, MaximumArrayLength = 0x40000000;
if (maxArrayLength > MaximumArrayLength)
{
maxArrayLength = MaximumArrayLength;
}
else if (maxArrayLength < MinimumArrayLength)
{
maxArrayLength = MinimumArrayLength;
}
// Create the buckets.
int poolId = Id;
int maxBuckets = Utilities.SelectBucketIndex(maxArrayLength);
var buckets = new Bucket[maxBuckets + 1];
for (int i = 0; i < buckets.Length; i++)
{
buckets[i] = new Bucket(Utilities.GetMaxSizeForBucket(i), maxArraysPerBucket, poolId);
}
_buckets = buckets;
}
/// <summary>Gets an ID for the pool to use with events.</summary>
private int Id => GetHashCode();
public override T[] Rent(int minimumLength)
{
// Arrays can't be smaller than zero. We allow requesting zero-length arrays (even though
// pooling such an array isn't valuable) as it's a valid length array, and we want the pool
// to be usable in general instead of using `new`, even for computed lengths.
if (minimumLength < 0)
{
throw new ArgumentOutOfRangeException(nameof(minimumLength));
}
else if (minimumLength == 0)
{
// No need for events with the empty array. Our pool is effectively infinite
// and we'll never allocate for rents and never store for returns.
return s_emptyArray ?? (s_emptyArray = new T[0]);
}
T[] buffer = null;
int index = Utilities.SelectBucketIndex(minimumLength);
if (index < _buckets.Length)
{
// Search for an array starting at the 'index' bucket. If the bucket is empty, bump up to the
// next higher bucket and try that one, but only try at most a few buckets.
const int MaxBucketsToTry = 2;
int i = index;
do
{
// Attempt to rent from the bucket. If we get a buffer from it, return it.
buffer = _buckets[i].Rent();
if (buffer != null)
{
return buffer;
}
}
while (++i < _buckets.Length && i != index + MaxBucketsToTry);
// The pool was exhausted for this buffer size. Allocate a new buffer with a size corresponding
// to the appropriate bucket.
buffer = new T[_buckets[index]._bufferLength];
}
else
{
// The request was for a size too large for the pool. Allocate an array of exactly the requested length.
// When it's returned to the pool, we'll simply throw it away.
buffer = new T[minimumLength];
}
return buffer;
}
public override void Return(T[] array, bool clearArray = false)
{
if (array == null)
{
throw new ArgumentNullException(nameof(array));
}
else if (array.Length == 0)
{
// Ignore empty arrays. When a zero-length array is rented, we return a singleton
// rather than actually taking a buffer out of the lowest bucket.
return;
}
// Determine with what bucket this array length is associated
int bucket = Utilities.SelectBucketIndex(array.Length);
// If we can tell that the buffer was allocated, drop it. Otherwise, check if we have space in the pool
if (bucket < _buckets.Length)
{
// Clear the array if the user requests
if (clearArray)
{
Array.Clear(array, 0, array.Length);
}
// Return the buffer to its bucket. In the future, we might consider having Return return false
// instead of dropping a bucket, in which case we could try to return to a lower-sized bucket,
// just as how in Rent we allow renting from a higher-sized bucket.
_buckets[bucket].Return(array);
}
}
}
}
#endif

View File

@@ -1,111 +0,0 @@
// 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.
#if NETCORE
using System;
using System.Diagnostics;
using System.Threading;
namespace SharpCompress.Buffers
{
internal sealed partial class DefaultArrayPool<T> : ArrayPool<T>
{
/// <summary>Provides a thread-safe bucket containing buffers that can be Rent'd and Return'd.</summary>
private sealed class Bucket
{
internal readonly int _bufferLength;
private readonly T[][] _buffers;
private readonly int _poolId;
private SpinLock _lock; // do not make this readonly; it's a mutable struct
private int _index;
/// <summary>
/// Creates the pool with numberOfBuffers arrays where each buffer is of bufferLength length.
/// </summary>
internal Bucket(int bufferLength, int numberOfBuffers, int poolId)
{
_lock = new SpinLock(Debugger.IsAttached); // only enable thread tracking if debugger is attached; it adds non-trivial overheads to Enter/Exit
_buffers = new T[numberOfBuffers][];
_bufferLength = bufferLength;
_poolId = poolId;
}
/// <summary>Gets an ID for the bucket to use with events.</summary>
internal int Id => GetHashCode();
/// <summary>Takes an array from the bucket. If the bucket is empty, returns null.</summary>
internal T[] Rent()
{
T[][] buffers = _buffers;
T[] buffer = null;
// While holding the lock, grab whatever is at the next available index and
// update the index. We do as little work as possible while holding the spin
// lock to minimize contention with other threads. The try/finally is
// necessary to properly handle thread aborts on platforms which have them.
bool lockTaken = false, allocateBuffer = false;
try
{
_lock.Enter(ref lockTaken);
if (_index < buffers.Length)
{
buffer = buffers[_index];
buffers[_index++] = null;
allocateBuffer = buffer == null;
}
}
finally
{
if (lockTaken) _lock.Exit(false);
}
// While we were holding the lock, we grabbed whatever was at the next available index, if
// there was one. If we tried and if we got back null, that means we hadn't yet allocated
// for that slot, in which case we should do so now.
if (allocateBuffer)
{
buffer = new T[_bufferLength];
}
return buffer;
}
/// <summary>
/// Attempts to return the buffer to the bucket. If successful, the buffer will be stored
/// in the bucket and true will be returned; otherwise, the buffer won't be stored, and false
/// will be returned.
/// </summary>
internal void Return(T[] array)
{
// Check to see if the buffer is the correct size for this bucket
if (array.Length != _bufferLength)
{
throw new ArgumentException("Buffer not from pool", nameof(array));
}
// While holding the spin lock, if there's room available in the bucket,
// put the buffer into the next available slot. Otherwise, we just drop it.
// The try/finally is necessary to properly handle thread aborts on platforms
// which have them.
bool lockTaken = false;
try
{
_lock.Enter(ref lockTaken);
if (_index != 0)
{
_buffers[--_index] = array;
}
}
finally
{
if (lockTaken) _lock.Exit(false);
}
}
}
}
}
#endif

View File

@@ -1,38 +0,0 @@
// 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.
#if NETCORE
using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace SharpCompress.Buffers
{
internal static class Utilities
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static int SelectBucketIndex(int bufferSize)
{
Debug.Assert(bufferSize > 0);
uint bitsRemaining = ((uint)bufferSize - 1) >> 4;
int poolIndex = 0;
if (bitsRemaining > 0xFFFF) { bitsRemaining >>= 16; poolIndex = 16; }
if (bitsRemaining > 0xFF) { bitsRemaining >>= 8; poolIndex += 8; }
if (bitsRemaining > 0xF) { bitsRemaining >>= 4; poolIndex += 4; }
if (bitsRemaining > 0x3) { bitsRemaining >>= 2; poolIndex += 2; }
if (bitsRemaining > 0x1) { bitsRemaining >>= 1; poolIndex += 1; }
return poolIndex + (int)bitsRemaining;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static int GetMaxSizeForBucket(int binIndex)
{
int maxSize = 16 << binIndex;
Debug.Assert(maxSize >= 0);
return maxSize;
}
}
}
#endif

View File

@@ -28,21 +28,14 @@ namespace SharpCompress.Common
public ArchiveEncoding()
{
#if NETSTANDARD1_0
Default = Encoding.GetEncoding("cp437");
Password = Encoding.GetEncoding("cp437");
#else
Default = Encoding.GetEncoding(437);
Password = Encoding.GetEncoding(437);
#endif
}
#if NETSTANDARD1_3 || NETSTANDARD2_0
static ArchiveEncoding()
{
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
}
#endif
public string Decode(byte[] bytes)
{

View File

@@ -15,6 +15,11 @@ namespace SharpCompress.Common
/// </summary>
public abstract string Key { get; }
/// <summary>
/// The target of a symlink entry internal to the Archive. Will be null if not a symlink.
/// </summary>
public abstract string LinkTarget { get; }
/// <summary>
/// The compressed file size
/// </summary>

View File

@@ -47,9 +47,7 @@ namespace SharpCompress.Common
public override bool CanWrite => false;
public override void Flush()
{
throw new NotSupportedException();
public override void Flush() {
}
public override long Length => _stream.Length;

View File

@@ -1,14 +1,10 @@
#if !NO_FILE
using System;
using System;
using System.IO;
#endif
namespace SharpCompress.Common
{
internal static class ExtractionMethods
{
#if !NO_FILE
/// <summary>
/// Extract to specific directory, retaining filename
/// </summary>
@@ -68,20 +64,30 @@ namespace SharpCompress.Common
ExtractionOptions options,
Action<string, FileMode> openAndWrite)
{
FileMode fm = FileMode.Create;
options = options ?? new ExtractionOptions()
{
Overwrite = true
};
if (!options.Overwrite)
if (entry.LinkTarget != null)
{
fm = FileMode.CreateNew;
if (null == options.WriteSymbolicLink)
{
throw new ExtractionException("Entry is a symbolic link but ExtractionOptions.WriteSymbolicLink delegate is null");
}
options.WriteSymbolicLink(destinationFileName, entry.LinkTarget);
}
else
{
FileMode fm = FileMode.Create;
options = options ?? new ExtractionOptions()
{
Overwrite = true
};
openAndWrite(destinationFileName, fm);
entry.PreserveExtractionOptions(destinationFileName, options);
if (!options.Overwrite)
{
fm = FileMode.CreateNew;
}
openAndWrite(destinationFileName, fm);
entry.PreserveExtractionOptions(destinationFileName, options);
}
}
#endif
}
}

View File

@@ -21,5 +21,14 @@
/// preserve windows file attributes
/// </summary>
public bool PreserveAttributes { get; set; }
/// <summary>
/// Delegate for writing symbolic links to disk.
/// sourcePath is where the symlink is created.
/// targetPath is what the symlink refers to.
/// </summary>
public delegate void SymbolicLinkWriterDelegate(string sourcePath, string targetPath);
public SymbolicLinkWriterDelegate WriteSymbolicLink;
}
}

View File

@@ -10,11 +10,11 @@ namespace SharpCompress.Common
}
internal ArchiveEncoding ArchiveEncoding { get; }
internal abstract string FilePartName { get; }
internal abstract Stream GetCompressedStream();
internal abstract Stream GetRawStream();
internal bool Skipped { get; set; }
}
}
}

View File

@@ -20,6 +20,8 @@ namespace SharpCompress.Common.GZip
public override string Key => _filePart.FilePartName;
public override string LinkTarget => null;
public override long CompressedSize => 0;
public override long Size => 0;

View File

@@ -10,13 +10,11 @@ namespace SharpCompress.Common.GZip
{
}
#if !NO_FILE
public GZipVolume(FileInfo fileInfo, ReaderOptions options)
: base(fileInfo.OpenRead(), options)
{
options.LeaveStreamOpen = false;
}
#endif
public override bool IsFirstVolume => true;

View File

@@ -1,6 +1,4 @@
#if !NO_FILE
using System.IO;
using System.IO;
using SharpCompress.Readers;
namespace SharpCompress.Common
@@ -48,4 +46,3 @@ namespace SharpCompress.Common
}
}
}
#endif

View File

@@ -10,6 +10,7 @@ namespace SharpCompress.Common
long Crc { get; }
DateTime? CreatedTime { get; }
string Key { get; }
string LinkTarget { get; }
bool IsDirectory { get; }
bool IsEncrypted { get; }
bool IsSplitAfter { get; }

View File

@@ -1,9 +1,5 @@
using System;
#if !NO_FILE
using System.IO;
#endif
namespace SharpCompress.Common
{
public interface IVolume : IDisposable

View File

@@ -195,17 +195,12 @@ namespace SharpCompress.Common.Rar.Headers
private static string ConvertPathV5(string path)
{
#if NO_FILE
// not sure what to do here
throw new NotImplementedException("TODO");
#else
if (Path.DirectorySeparatorChar == '\\')
{
// replace embedded \\ with valid filename char
return path.Replace('\\', '-').Replace('/', '\\');
}
return path;
#endif
}
@@ -361,9 +356,6 @@ namespace SharpCompress.Common.Rar.Headers
private static string ConvertPathV4(string path)
{
#if NO_FILE
return path.Replace('\\', '/');
#else
if (Path.DirectorySeparatorChar == '/')
{
return path.Replace('\\', '/');
@@ -373,7 +365,6 @@ namespace SharpCompress.Common.Rar.Headers
return path.Replace('/', '\\');
}
return path;
#endif
}
public override string ToString()

View File

@@ -48,15 +48,11 @@ namespace SharpCompress.Common.Rar.Headers
}
else
{
#if !NO_CRYPTO
if (Options.Password == null)
{
throw new CryptographicException("Encrypted Rar archive has no password specified.");
}
reader = new RarCryptoBinaryReader(stream, Options.Password);
#else
throw new CryptographicException("Rar encryption unsupported on this platform");
#endif
}
var header = RarHeader.TryReadBase(reader, _isRar5, Options.ArchiveEncoding);
@@ -138,11 +134,7 @@ namespace SharpCompress.Common.Rar.Headers
}
else
{
#if !NO_CRYPTO
fh.PackedStream = new RarCryptoWrapper(ms, Options.Password, fh.R4Salt);
#else
throw new NotSupportedException("RarCrypto not supported");
#endif
}
}
break;

View File

@@ -1,5 +1,4 @@
#if !NO_CRYPTO
using System.Collections.Generic;
using System.Collections.Generic;
using System.IO;
namespace SharpCompress.Common.Rar
@@ -111,5 +110,4 @@ namespace SharpCompress.Common.Rar
ClearQueue();
}
}
}
#endif
}

View File

@@ -1,5 +1,3 @@
#if !NO_CRYPTO
using System;
using System.Collections.Generic;
using System.IO;
@@ -95,5 +93,4 @@ namespace SharpCompress.Common.Rar
base.Dispose(disposing);
}
}
}
#endif
}

View File

@@ -22,6 +22,8 @@ namespace SharpCompress.Common.Rar
/// </summary>
public override string Key => FileHeader.FileName;
public override string LinkTarget => null;
/// <summary>
/// The entry last modified time in the archive, if recorded
/// </summary>

View File

@@ -1,7 +1,5 @@
#if !NO_CRYPTO
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using SharpCompress.Crypto;
@@ -23,12 +21,6 @@ namespace SharpCompress.Common.Rar
_salt = salt;
}
private byte[] ComputeHash(byte[] input)
{
var sha = SHA1.Create();
return sha.ComputeHash(input);
}
private void Initialize()
{
@@ -47,28 +39,27 @@ namespace SharpCompress.Common.Rar
rawPassword[i + rawLength] = _salt[i];
}
const int noOfRounds = (1 << 18);
IList<byte> bytes = new List<byte>();
const int iblock = 3;
byte[] digest;
byte[] data = new byte[(rawPassword.Length + iblock) * noOfRounds];
//TODO slow code below, find ways to optimize
for (int i = 0; i < noOfRounds; i++)
{
bytes.AddRange(rawPassword);
rawPassword.CopyTo(data, i * (rawPassword.Length + iblock));
bytes.AddRange(new[]
data[i * (rawPassword.Length + iblock) + rawPassword.Length + 0] = (byte)i;
data[i * (rawPassword.Length + iblock) + rawPassword.Length + 1] = (byte)(i >> 8);
data[i * (rawPassword.Length + iblock) + rawPassword.Length + 2] = (byte)(i >> CRYPTO_BLOCK_SIZE);
if (i % (noOfRounds / CRYPTO_BLOCK_SIZE) == 0)
{
(byte) i, (byte) (i >> 8), (byte) (i >> CRYPTO_BLOCK_SIZE)
});
if (i%(noOfRounds/CRYPTO_BLOCK_SIZE) == 0)
{
digest = ComputeHash(bytes.ToArray());
_aesInitializationVector[i/(noOfRounds/CRYPTO_BLOCK_SIZE)] = digest[19];
digest = SHA1.Create().ComputeHash(data, 0, (i + 1) * (rawPassword.Length + iblock));
_aesInitializationVector[i / (noOfRounds / CRYPTO_BLOCK_SIZE)] = digest[19];
}
}
digest = ComputeHash(bytes.ToArray());
digest = SHA1.Create().ComputeHash(data);
//slow code ends
byte[] aesKey = new byte[CRYPTO_BLOCK_SIZE];
@@ -118,4 +109,3 @@ namespace SharpCompress.Common.Rar
}
}
}
#endif

View File

@@ -1396,7 +1396,7 @@ namespace SharpCompress.Common.SevenZip
}
else
{
_stream = new MemoryStream();
_stream = Utility.RECYCLABLE_MEMORY_STREAM_MANAGER.GetStream();
}
_rem = _db._files[index].Size;
}

View File

@@ -18,6 +18,8 @@ namespace SharpCompress.Common.SevenZip
public override string Key => FilePart.Header.Name;
public override string LinkTarget => null;
public override long CompressedSize => 0;
public override long Size => FilePart.Header.Size;

View File

@@ -15,6 +15,7 @@ namespace SharpCompress.Common.Tar.Headers
}
internal string Name { get; set; }
internal string LinkName { get; set; }
//internal int Mode { get; set; }
//internal int UserId { get; set; }
@@ -38,16 +39,17 @@ namespace SharpCompress.Common.Tar.Headers
WriteOctalBytes(0, buffer, 116, 8); // group ID
//ArchiveEncoding.UTF8.GetBytes("magic").CopyTo(buffer, 257);
if (Name.Length > 100)
var nameByteCount = ArchiveEncoding.GetEncoding().GetByteCount(Name);
if (nameByteCount > 100)
{
// Set mock filename and filetype to indicate the next block is the actual name of the file
WriteStringBytes("././@LongLink", buffer, 0, 100);
buffer[156] = (byte)EntryType.LongName;
WriteOctalBytes(Name.Length + 1, buffer, 124, 12);
WriteOctalBytes(nameByteCount + 1, buffer, 124, 12);
}
else
{
WriteStringBytes(Name, buffer, 0, 100);
WriteStringBytes(ArchiveEncoding.Encode(Name), buffer, 0, 100);
WriteOctalBytes(Size, buffer, 124, 12);
var time = (long)(LastModifiedTime.ToUniversalTime() - EPOCH).TotalSeconds;
WriteOctalBytes(time, buffer, 136, 12);
@@ -68,10 +70,17 @@ namespace SharpCompress.Common.Tar.Headers
output.Write(buffer, 0, buffer.Length);
if (Name.Length > 100)
if (nameByteCount > 100)
{
WriteLongFilenameHeader(output);
Name = Name.Substring(0, 100);
// update to short name lower than 100 - [max bytes of one character].
// subtracting bytes is needed because preventing infinite loop(example code is here).
//
// var bytes = Encoding.UTF8.GetBytes(new string(0x3042, 100));
// var truncated = Encoding.UTF8.GetBytes(Encoding.UTF8.GetString(bytes, 0, 100));
//
// and then infinite recursion is occured in WriteLongFilenameHeader because truncated.Length is 102.
Name = ArchiveEncoding.Decode(ArchiveEncoding.Encode(Name), 0, 100 - ArchiveEncoding.GetEncoding().GetMaxByteCount(1));
Write(output);
}
}
@@ -98,6 +107,12 @@ namespace SharpCompress.Common.Tar.Headers
return false;
}
// for symlinks, additionally read the linkname
if (ReadEntryType(buffer) == EntryType.SymLink)
{
LinkName = ArchiveEncoding.Decode(buffer, 157, 100).TrimNulls();
}
if (ReadEntryType(buffer) == EntryType.LongName)
{
Name = ReadLongName(reader, buffer);
@@ -177,11 +192,22 @@ namespace SharpCompress.Common.Tar.Headers
return buffer;
}
private static void WriteStringBytes(byte[] name, byte[] buffer, int offset, int length)
{
int i = Math.Min(length, name.Length);
Buffer.BlockCopy(name, 0, buffer, offset, i);
// if Span<byte>.Fill can be used, it is more efficient
for (; i < length; ++i)
{
buffer[offset + i] = 0;
}
}
private static void WriteStringBytes(string name, byte[] buffer, int offset, int length)
{
int i;
for (i = 0; i < length - 1 && i < name.Length; ++i)
for (i = 0; i < length && i < name.Length; ++i)
{
buffer[offset + i] = (byte)name[i];
}
@@ -272,4 +298,4 @@ namespace SharpCompress.Common.Tar.Headers
public string Magic { get; set; }
}
}
}

View File

@@ -23,6 +23,8 @@ namespace SharpCompress.Common.Tar
public override string Key => _filePart.Header.Name;
public override string LinkTarget => _filePart.Header.LinkName;
public override long CompressedSize => _filePart.Header.Size;
public override long Size => _filePart.Header.Size;

View File

@@ -1,16 +1,16 @@
using System;
using SharpCompress.IO;
using System;
using System.IO;
namespace SharpCompress.Common.Tar
{
internal class TarReadOnlySubStream : Stream
internal class TarReadOnlySubStream : NonDisposingStream
{
private bool _isDisposed;
private long _amountRead;
public TarReadOnlySubStream(Stream stream, long bytesToRead)
public TarReadOnlySubStream(Stream stream, long bytesToRead) : base(stream, throwOnDispose: false)
{
Stream = stream;
BytesLeftToRead = bytesToRead;
}
@@ -36,12 +36,11 @@ namespace SharpCompress.Common.Tar
var buffer = new byte[skipBytes];
Stream.ReadFully(buffer);
}
base.Dispose(disposing);
}
private long BytesLeftToRead { get; set; }
public Stream Stream { get; }
public override bool CanRead => true;
public override bool CanSeek => false;

View File

@@ -1,4 +1,5 @@
using System.IO;
using System;
using System.IO;
using SharpCompress.IO;
using SharpCompress.Readers;
@@ -33,15 +34,18 @@ namespace SharpCompress.Common
/// </summary>
public virtual bool IsMultiVolume => true;
private bool _disposed;
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_actualStream.Dispose();
}
}
public void Dispose()
{
if (!_disposed)
{
_actualStream.Dispose();
_disposed = true;
}
Dispose(true);
GC.SuppressFinalize(this);
}
}
}

View File

@@ -65,10 +65,8 @@ namespace SharpCompress.Common.Zip.Headers
return encryptionData;
}
#if !NO_CRYPTO
internal WinzipAesEncryptionData WinzipAesEncryptionData { get; set; }
#endif
internal ushort LastModifiedDate { get; set; }
@@ -87,6 +85,15 @@ namespace SharpCompress.Common.Zip.Headers
}
ushort length = DataConverter.LittleEndian.GetUInt16(extra, i + 2);
// 7zip has this same kind of check to ignore extras blocks that don't conform to the standard 2-byte ID, 2-byte length, N-byte value.
// CPP/7Zip/Zip/ZipIn.cpp: CInArchive::ReadExtra
if (length > extra.Length)
{
// bad extras block
return;
}
byte[] data = new byte[length];
Buffer.BlockCopy(extra, i + 4, data, 0, length);
Extra.Add(LocalEntryHeaderExtraFactory.Create(type, length, data));
@@ -99,4 +106,4 @@ namespace SharpCompress.Common.Zip.Headers
internal bool IsZip64 => CompressedSize == uint.MaxValue;
}
}
}

View File

@@ -1,5 +1,6 @@
using System.IO;
using SharpCompress.Common.Zip.Headers;
using SharpCompress.IO;
namespace SharpCompress.Common.Zip
{
@@ -7,11 +8,13 @@ namespace SharpCompress.Common.Zip
{
private bool _isLocalHeaderLoaded;
private readonly SeekableZipHeaderFactory _headerFactory;
private readonly DirectoryEntryHeader _directoryEntryHeader;
internal SeekableZipFilePart(SeekableZipHeaderFactory headerFactory, DirectoryEntryHeader header, Stream stream)
: base(header, stream)
{
_headerFactory = headerFactory;
_directoryEntryHeader = header;
}
internal override Stream GetCompressedStream()
@@ -36,6 +39,15 @@ namespace SharpCompress.Common.Zip
protected override Stream CreateBaseStream()
{
BaseStream.Position = Header.DataStartPosition.Value;
if ((Header.CompressedSize == 0)
&& FlagUtility.HasFlag(Header.Flags, HeaderFlags.UsePostDataDescriptor)
&& (_directoryEntryHeader?.HasData == true)
&& (_directoryEntryHeader?.CompressedSize != 0))
{
return new ReadOnlySubStream(BaseStream, _directoryEntryHeader.CompressedSize);
}
return BaseStream;
}
}

View File

@@ -17,7 +17,7 @@ namespace SharpCompress.Common.Zip
{
}
internal IEnumerable<DirectoryEntryHeader> ReadSeekableHeader(Stream stream)
internal IEnumerable<ZipHeader> ReadSeekableHeader(Stream stream)
{
var reader = new BinaryReader(stream);
@@ -51,16 +51,22 @@ namespace SharpCompress.Common.Zip
{
stream.Position = position;
uint signature = reader.ReadUInt32();
var directoryEntryHeader = ReadHeader(signature, reader, _zip64) as DirectoryEntryHeader;
var nextHeader = ReadHeader(signature, reader, _zip64);
position = stream.Position;
if (directoryEntryHeader == null)
{
yield break;
}
//entry could be zero bytes so we need to know that.
directoryEntryHeader.HasData = directoryEntryHeader.CompressedSize != 0;
yield return directoryEntryHeader;
if (nextHeader == null)
yield break;
if (nextHeader is DirectoryEntryHeader entryHeader)
{
//entry could be zero bytes so we need to know that.
entryHeader.HasData = entryHeader.CompressedSize != 0;
yield return entryHeader;
}
else if (nextHeader is DirectoryEndHeader endHeader)
{
yield return endHeader;
}
}
}

View File

@@ -49,6 +49,7 @@ namespace SharpCompress.Common.Zip
_lastEntryHeader = null;
uint headerBytes = reader.ReadUInt32();
header = ReadHeader(headerBytes, reader);
if (header == null) { yield break; }
//entry could be zero bytes so we need to know that.
if (header.ZipHeaderType == ZipHeaderType.LocalEntry)

View File

@@ -1,5 +1,3 @@
#if !NO_CRYPTO
using System;
using System.IO;
using System.Security.Cryptography;
@@ -181,4 +179,3 @@ namespace SharpCompress.Common.Zip
}
}
}
#endif

View File

@@ -1,5 +1,3 @@
#if !NO_CRYPTO
using System;
using System.Security.Cryptography;
using SharpCompress.Converters;
@@ -76,4 +74,3 @@ namespace SharpCompress.Common.Zip
}
}
}
#endif

View File

@@ -60,6 +60,8 @@ namespace SharpCompress.Common.Zip
public override string Key => _filePart.Header.Name;
public override string LinkTarget => null;
public override long CompressedSize => _filePart.Header.CompressedSize;
public override long Size => _filePart.Header.UncompressedSize;

View File

@@ -165,12 +165,10 @@ namespace SharpCompress.Common.Zip
case ZipCompressionMethod.WinzipAes:
{
#if !NO_FILE
if (Header.WinzipAesEncryptionData != null)
{
return new WinzipAesCryptoStream(plainStream, Header.WinzipAesEncryptionData, Header.CompressedSize - 10);
}
#endif
return plainStream;
}

View File

@@ -1,8 +1,6 @@
using System;
using System.IO;
#if !NO_CRYPTO
using System.Linq;
#endif
using SharpCompress.Common.Zip.Headers;
using SharpCompress.IO;
using System.Text;
@@ -91,7 +89,7 @@ namespace SharpCompress.Common.Zip
return entry;
}
default:
throw new NotSupportedException("Unknown header: " + headerBytes);
return null;
}
}
@@ -132,10 +130,6 @@ namespace SharpCompress.Common.Zip
if (entryHeader.CompressionMethod == ZipCompressionMethod.WinzipAes)
{
#if NO_CRYPTO
throw new NotSupportedException("Cannot decrypt Winzip AES with Silverlight or WP7.");
#else
ExtraData data = entryHeader.Extra.SingleOrDefault(x => x.Type == ExtraDataType.WinZipAes);
if (data != null)
{
@@ -150,7 +144,6 @@ namespace SharpCompress.Common.Zip
entryHeader.CompressedSize -= (uint)(salt.Length + 2);
}
#endif
}
}

View File

@@ -142,7 +142,7 @@ namespace SharpCompress.Compressors.ADC
position += chunkSize + 1;
break;
case TWO_BYTE:
tempMs = new MemoryStream();
tempMs = Utility.RECYCLABLE_MEMORY_STREAM_MANAGER.GetStream();
chunkSize = GetChunkSize((byte)readByte);
tempMs.WriteByte((byte)readByte);
tempMs.WriteByte((byte)input.ReadByte());
@@ -173,7 +173,7 @@ namespace SharpCompress.Compressors.ADC
}
break;
case THREE_BYTE:
tempMs = new MemoryStream();
tempMs = Utility.RECYCLABLE_MEMORY_STREAM_MANAGER.GetStream();
chunkSize = GetChunkSize((byte)readByte);
tempMs.WriteByte((byte)readByte);
tempMs.WriteByte((byte)input.ReadByte());

View File

@@ -1,6 +1,4 @@
#if !NO_CRYPTO
using System;
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
@@ -195,7 +193,7 @@ namespace SharpCompress.Compressors.LZMA
}
else
{
#if NETSTANDARD1_3
#if NETSTANDARD1_4 || NETSTANDARD2_0
using (IncrementalHash sha = IncrementalHash.CreateHash(HashAlgorithmName.SHA256))
{
byte[] counter = new byte[8];
@@ -262,5 +260,3 @@ namespace SharpCompress.Compressors.LZMA
#endregion
}
}
#endif

View File

@@ -34,34 +34,26 @@ namespace SharpCompress.Compressors.LZMA
if (NEEDS_INDENT)
{
NEEDS_INDENT = false;
#if !NO_FILE
Debug.Write(INDENT.Peek());
#endif
}
}
public static void Write(object value)
{
EnsureIndent();
#if !NO_FILE
Debug.Write(value);
#endif
}
public static void Write(string text)
{
EnsureIndent();
#if !NO_FILE
Debug.Write(text);
#endif
}
public static void Write(string format, params object[] args)
{
EnsureIndent();
#if !NO_FILE
Debug.Write(string.Format(format, args));
#endif
}
public static void WriteLine()

View File

@@ -36,10 +36,8 @@ namespace SharpCompress.Compressors.LZMA
case K_LZMA:
case K_LZMA2:
return new LzmaStream(info, inStreams.Single(), -1, limit);
#if !NO_CRYPTO
case CMethodId.K_AES_ID:
return new AesDecoderStream(inStreams.Single(), info, pass, limit);
#endif
case K_BCJ:
return new BCJFilter(false, inStreams.Single());
case K_BCJ2:

View File

@@ -78,7 +78,10 @@ namespace SharpCompress.Compressors.PPMd
{
if (_compress)
{
_model.EncodeBlock(_stream, new MemoryStream(), true);
using (var stream = Utility.RECYCLABLE_MEMORY_STREAM_MANAGER.GetStream())
{
_model.EncodeBlock(_stream, stream, true);
}
}
}
base.Dispose(isDisposing);

View File

@@ -1,18 +0,0 @@
#if NET35
using System;
namespace SharpCompress
{
internal static class EnumExtensions
{
public static bool HasFlag(this Enum enumRef, Enum flag)
{
long value = Convert.ToInt64(enumRef);
long flagVal = Convert.ToInt64(flag);
return (value & flagVal) == flagVal;
}
}
}
#endif

View File

@@ -1,74 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
namespace SharpCompress.IO
{
public class ReadOnlyAppendingStream : Stream
{
private readonly Queue<Stream> streams;
private Stream current;
public ReadOnlyAppendingStream(IEnumerable<Stream> streams)
{
this.streams = new Queue<Stream>(streams);
}
public override bool CanRead => true;
public override bool CanSeek => false;
public override bool CanWrite => false;
public override void Flush()
{
throw new NotImplementedException();
}
public override long Length => throw new NotImplementedException();
public override long Position { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public override int Read(byte[] buffer, int offset, int count)
{
if (current == null && streams.Count == 0)
{
return -1;
}
if (current == null)
{
current = streams.Dequeue();
}
int totalRead = 0;
while (totalRead < count)
{
int read = current.Read(buffer, offset + totalRead, count - totalRead);
if (read <= 0)
{
if (streams.Count == 0)
{
return totalRead;
}
current = streams.Dequeue();
}
totalRead += read;
}
return totalRead;
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotImplementedException();
}
public override void SetLength(long value)
{
throw new NotImplementedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotImplementedException();
}
}
}

View File

@@ -3,33 +3,22 @@ using System.IO;
namespace SharpCompress.IO
{
internal class BufferedSubStream : Stream
internal class BufferedSubStream : NonDisposingStream
{
private long position;
private int cacheOffset;
private int cacheLength;
private readonly byte[] cache;
public BufferedSubStream(Stream stream, long origin, long bytesToRead)
public BufferedSubStream(Stream stream, long origin, long bytesToRead) : base(stream, throwOnDispose: false)
{
Stream = stream;
position = origin;
BytesLeftToRead = bytesToRead;
cache = new byte[32 << 10];
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
//Stream.Dispose();
}
}
private long BytesLeftToRead { get; set; }
public Stream Stream { get; }
public override bool CanRead => true;
public override bool CanSeek => false;

View File

@@ -3,13 +3,10 @@ using System.IO;
namespace SharpCompress.IO
{
internal class CountingWritableSubStream : Stream
internal class CountingWritableSubStream : NonDisposingStream
{
private readonly Stream writableStream;
internal CountingWritableSubStream(Stream stream)
internal CountingWritableSubStream(Stream stream) : base(stream, throwOnDispose: false)
{
writableStream = stream;
}
public ulong Count { get; private set; }
@@ -22,7 +19,7 @@ namespace SharpCompress.IO
public override void Flush()
{
writableStream.Flush();
Stream.Flush();
}
public override long Length => throw new NotSupportedException();
@@ -46,13 +43,13 @@ namespace SharpCompress.IO
public override void Write(byte[] buffer, int offset, int count)
{
writableStream.Write(buffer, offset, count);
Stream.Write(buffer, offset, count);
Count += (uint)count;
}
public override void WriteByte(byte value)
{
writableStream.WriteByte(value);
Stream.WriteByte(value);
++Count;
}
}

View File

@@ -20,6 +20,7 @@ namespace SharpCompress.IO
{
Stream.Dispose();
}
base.Dispose(disposing);
}
public Stream Stream { get; }
@@ -74,10 +75,5 @@ namespace SharpCompress.IO
{
Stream.Write(buffer, offset, count);
}
public override void WriteByte(byte value)
{
Stream.WriteByte(value);
}
}
}

View File

@@ -74,13 +74,6 @@ namespace SharpCompress.IO
throw new NotSupportedException();
}
#if !SILVERLIGHT
public override decimal ReadDecimal()
{
throw new NotSupportedException();
}
#endif
public override double ReadDouble()
{
throw new NotSupportedException();

View File

@@ -15,10 +15,9 @@ namespace SharpCompress.IO
protected override void Dispose(bool disposing)
{
GC.SuppressFinalize(this);
if (ThrowOnDispose)
{
throw new InvalidOperationException();
throw new InvalidOperationException($"Attempt to dispose of a {nameof(NonDisposingStream)} when {nameof(ThrowOnDispose)} is {ThrowOnDispose}");
}
}
@@ -44,11 +43,6 @@ namespace SharpCompress.IO
return Stream.Read(buffer, offset, count);
}
public override int ReadByte()
{
return Stream.ReadByte();
}
public override long Seek(long offset, SeekOrigin origin)
{
return Stream.Seek(offset, origin);
@@ -63,10 +57,5 @@ namespace SharpCompress.IO
{
Stream.Write(buffer, offset, count);
}
public override void WriteByte(byte value)
{
Stream.WriteByte(value);
}
}
}

View File

@@ -11,7 +11,7 @@ namespace SharpCompress.IO
}
public ReadOnlySubStream(Stream stream, long? origin, long bytesToRead)
: base(stream, false)
: base(stream, throwOnDispose: false)
{
if (origin != null)
{

View File

@@ -1,5 +1,6 @@
using System;
using System.IO;
using Microsoft.IO;
using SharpCompress.Compressors.Filters;
namespace SharpCompress.IO
@@ -7,7 +8,7 @@ namespace SharpCompress.IO
internal class RewindableStream : Stream
{
private readonly Stream stream;
private MemoryStream bufferStream = new MemoryStream();
private MemoryStream bufferStream = Utility.RECYCLABLE_MEMORY_STREAM_MANAGER.GetStream();
private bool isRewound;
private bool isDisposed;
@@ -29,6 +30,7 @@ namespace SharpCompress.IO
if (disposing)
{
stream.Dispose();
bufferStream.Dispose();
}
}
@@ -51,7 +53,8 @@ namespace SharpCompress.IO
bufferStream.TransferTo(buffer);
//create new memorystream to allow proper resizing as memorystream could be a user provided buffer
//https://github.com/adamhathcock/sharpcompress/issues/306
bufferStream = new MemoryStream();
bufferStream.Dispose();
bufferStream = Utility.RECYCLABLE_MEMORY_STREAM_MANAGER.GetStream();
buffer.Position = 0;
buffer.TransferTo(bufferStream);
bufferStream.Position = 0;

View File

@@ -25,7 +25,7 @@ namespace SharpCompress.Readers.GZip
/// <returns></returns>
public static GZipReader Open(Stream stream, ReaderOptions options = null)
{
stream.CheckNotNull("stream");
stream.CheckNotNull(nameof(stream));
return new GZipReader(stream, options ?? new ReaderOptions());
}
@@ -36,4 +36,4 @@ namespace SharpCompress.Readers.GZip
return GZipEntry.GetEntries(stream, Options);
}
}
}
}

View File

@@ -1,13 +1,10 @@
#if !NO_FILE
using System.IO;
using System.IO;
using SharpCompress.Common;
#endif
namespace SharpCompress.Readers
{
public static class IReaderExtensions
{
#if !NO_FILE
public static void WriteEntryTo(this IReader reader, string filePath)
{
using (Stream stream = File.Open(filePath, FileMode.Create, FileAccess.Write))
@@ -61,6 +58,5 @@ namespace SharpCompress.Readers
}
});
}
#endif
}
}

View File

@@ -33,7 +33,7 @@ namespace SharpCompress.Readers.Rar
/// <returns></returns>
public static RarReader Open(Stream stream, ReaderOptions options = null)
{
stream.CheckNotNull("stream");
stream.CheckNotNull(nameof(stream));
return new SingleVolumeRarReader(stream, options ?? new ReaderOptions());
}
@@ -45,7 +45,7 @@ namespace SharpCompress.Readers.Rar
/// <returns></returns>
public static RarReader Open(IEnumerable<Stream> streams, ReaderOptions options = null)
{
streams.CheckNotNull("streams");
streams.CheckNotNull(nameof(streams));
return new MultiVolumeRarReader(streams, options ?? new ReaderOptions());
}
@@ -64,7 +64,7 @@ namespace SharpCompress.Readers.Rar
return Entry.Parts;
}
protected override EntryStream GetEntryStream()
protected override EntryStream GetEntryStream()
{
var stream = new MultiVolumeReadOnlyStream(CreateFilePartEnumerableForCurrentEntry().Cast<RarFilePart>(), this);
if (Entry.IsRarV3)

View File

@@ -28,7 +28,7 @@ namespace SharpCompress.Readers
/// <returns></returns>
public static IReader Open(Stream stream, ReaderOptions options = null)
{
stream.CheckNotNull("stream");
stream.CheckNotNull(nameof(stream));
options = options ?? new ReaderOptions()
{
LeaveStreamOpen = false
@@ -104,4 +104,4 @@ namespace SharpCompress.Readers
throw new InvalidOperationException("Cannot determine compressed stream type. Supported Reader Formats: Zip, GZip, BZip2, Tar, Rar, LZip, XZ");
}
}
}
}

View File

@@ -69,7 +69,7 @@ namespace SharpCompress.Readers.Tar
/// <returns></returns>
public static TarReader Open(Stream stream, ReaderOptions options = null)
{
stream.CheckNotNull("stream");
stream.CheckNotNull(nameof(stream));
options = options ?? new ReaderOptions();
RewindableStream rewindableStream = new RewindableStream(stream);
rewindableStream.StartRecording();
@@ -121,4 +121,4 @@ namespace SharpCompress.Readers.Tar
return TarEntry.GetEntries(StreamingMode.Streaming, stream, compressionType, Options.ArchiveEncoding);
}
}
}
}

View File

@@ -29,7 +29,7 @@ namespace SharpCompress.Readers.Zip
/// <returns></returns>
public static ZipReader Open(Stream stream, ReaderOptions options = null)
{
stream.CheckNotNull("stream");
stream.CheckNotNull(nameof(stream));
return new ZipReader(stream, options ?? new ReaderOptions());
}
@@ -58,4 +58,4 @@ namespace SharpCompress.Readers.Zip
}
}
}
}
}

View File

@@ -2,11 +2,11 @@
<PropertyGroup>
<AssemblyTitle>SharpCompress - Pure C# Decompression/Compression</AssemblyTitle>
<NeutralLanguage>en-US</NeutralLanguage>
<VersionPrefix>0.22.0</VersionPrefix>
<AssemblyVersion>0.22.0</AssemblyVersion>
<FileVersion>0.22.0</FileVersion>
<VersionPrefix>0.24.0</VersionPrefix>
<AssemblyVersion>0.24.0</AssemblyVersion>
<FileVersion>0.24.0</FileVersion>
<Authors>Adam Hathcock</Authors>
<TargetFrameworks Condition="'$(LibraryFrameworks)'==''">net45;net35;netstandard1.0;netstandard1.3;netstandard2.0</TargetFrameworks>
<TargetFrameworks Condition="'$(LibraryFrameworks)'==''">net46;netstandard1.4;netstandard2.0</TargetFrameworks>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<AssemblyName>SharpCompress</AssemblyName>
@@ -16,24 +16,15 @@
<PackageId>SharpCompress</PackageId>
<PackageTags>rar;unrar;zip;unzip;bzip2;gzip;tar;7zip;lzip;xz</PackageTags>
<PackageProjectUrl>https://github.com/adamhathcock/sharpcompress</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/adamhathcock/sharpcompress/blob/master/LICENSE.txt</PackageLicenseUrl>
<PackageLicense>https://github.com/adamhathcock/sharpcompress/blob/master/LICENSE.txt</PackageLicense>
<GenerateAssemblyTitleAttribute>false</GenerateAssemblyTitleAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<Description>SharpCompress is a compression library for NET Standard 1.0 that can unrar, decompress 7zip, decompress xz, zip/unzip, tar/untar lzip/unlzip, bzip2/unbzip2 and gzip/ungzip with forward-only reading and file random access APIs. Write support for zip/tar/bzip2/gzip is implemented.</Description>
<Description>SharpCompress is a compression library for NET Standard 1.3/2.0 that can unrar, decompress 7zip, decompress xz, zip/unzip, tar/untar lzip/unlzip, bzip2/unbzip2 and gzip/ungzip with forward-only reading and file random access APIs. Write support for zip/tar/bzip2/gzip is implemented.</Description>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard1.0' ">
<DefineConstants>$(DefineConstants);NO_FILE;NO_CRYPTO;SILVERLIGHT</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' ">
<DefineConstants>$(DefineConstants);NETCORE</DefineConstants>
</PropertyGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' ">
<PackageReference Include="System.Text.Encoding.CodePages" Version="4.5.0" />
<ItemGroup>
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.6.0" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="4.6.0" />
<PackageReference Include="System.Buffers" Version="4.5.0" />
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="1.3.0" />
</ItemGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
<DefineConstants>$(DefineConstants);NETCORE</DefineConstants>
</PropertyGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
<PackageReference Include="System.Text.Encoding.CodePages" Version="4.5.0" />
</ItemGroup>
</Project>
</Project>

View File

@@ -1,29 +1,26 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.InteropServices;
#if NETCORE
using SharpCompress.Buffers;
#endif
using System.Runtime.CompilerServices;
using Microsoft.IO;
using SharpCompress.Readers;
namespace SharpCompress
{
internal static class Utility
{
public static ReadOnlyCollection<T> ToReadOnly<T>(this IEnumerable<T> items)
public static readonly RecyclableMemoryStreamManager RECYCLABLE_MEMORY_STREAM_MANAGER = new RecyclableMemoryStreamManager();
public static ReadOnlyCollection<T> ToReadOnly<T>(this ICollection<T> items)
{
return new ReadOnlyCollection<T>(items.ToList());
return new ReadOnlyCollection<T>(items);
}
/// <summary>
/// Performs an unsigned bitwise right shift with the specified number
/// </summary>
/// <param name="number">Number to operate on</param>
/// <param name="bits">Ammount of bits to shift</param>
/// <param name="bits">Amount of bits to shift</param>
/// <returns>The resulting number from the shift operation</returns>
public static int URShift(int number, int bits)
{
@@ -38,7 +35,7 @@ namespace SharpCompress
/// Performs an unsigned bitwise right shift with the specified number
/// </summary>
/// <param name="number">Number to operate on</param>
/// <param name="bits">Ammount of bits to shift</param>
/// <param name="bits">Amount of bits to shift</param>
/// <returns>The resulting number from the shift operation</returns>
public static long URShift(long number, int bits)
{
@@ -75,49 +72,15 @@ namespace SharpCompress
array[index] = val;
}
}
#if NET45
// super fast memset, up to 40x faster than for loop on large arrays
// see https://stackoverflow.com/questions/1897555/what-is-the-equivalent-of-memset-in-c
private static readonly Action<IntPtr, byte, uint> MemsetDelegate = CreateMemsetDelegate();
private static Action<IntPtr, byte, uint> CreateMemsetDelegate() {
var dynamicMethod = new DynamicMethod(
"Memset",
MethodAttributes.Public | MethodAttributes.Static,
CallingConventions.Standard,
null,
new[] { typeof(IntPtr), typeof(byte), typeof(uint) },
typeof(Utility),
true);
var generator = dynamicMethod.GetILGenerator();
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldarg_1);
generator.Emit(OpCodes.Ldarg_2);
generator.Emit(OpCodes.Initblk);
generator.Emit(OpCodes.Ret);
return (Action<IntPtr, byte, uint>)dynamicMethod.CreateDelegate(typeof(Action<IntPtr, byte, uint>));
}
public static void Memset(byte[] array, byte what, int length)
{
var gcHandle = GCHandle.Alloc(array, GCHandleType.Pinned);
MemsetDelegate(gcHandle.AddrOfPinnedObject(), what, (uint)length);
gcHandle.Free();
ref byte ptr = ref array[0];
Unsafe.InitBlock(ref ptr, what, (uint)length);
}
#else
public static void Memset(byte[] array, byte what, int length)
{
for(var i = 0; i < length; i++)
{
array[i] = what;
}
}
#endif
public static void Memset<T>(T[] array, T what, int length)
{
for(var i = 0; i < length; i++)
for (var i = 0; i < length; i++)
{
array[i] = what;
}
@@ -184,7 +147,7 @@ namespace SharpCompress
action(item);
}
}
public static void Copy(Array sourceArray, long sourceIndex, Array destinationArray, long destinationIndex, long length)
{
if (sourceIndex > Int32.MaxValue || sourceIndex < Int32.MinValue)
@@ -215,7 +178,7 @@ namespace SharpCompress
obj.CheckNotNull(name);
if (obj.Length == 0)
{
throw new ArgumentException("String is empty.");
throw new ArgumentException("String is empty.", name);
}
}
@@ -254,9 +217,7 @@ namespace SharpCompress
}
finally
{
#if NETCORE
ArrayPool<byte>.Shared.Return(buffer);
#endif
}
}
@@ -272,9 +233,7 @@ namespace SharpCompress
}
finally
{
#if NETCORE
ArrayPool<byte>.Shared.Return(buffer);
#endif
}
}
@@ -359,9 +318,7 @@ namespace SharpCompress
}
finally
{
#if NETCORE
ArrayPool<byte>.Shared.Return(array);
#endif
}
}
@@ -384,9 +341,7 @@ namespace SharpCompress
}
finally
{
#if NETCORE
ArrayPool<byte>.Shared.Return(array);
#endif
}
}
@@ -397,11 +352,7 @@ namespace SharpCompress
private static byte[] GetTransferByteArray()
{
#if NETCORE
return ArrayPool<byte>.Shared.Rent(81920);
#else
return new byte[81920];
#endif
}
public static bool ReadFully(this Stream stream, byte[] buffer)
@@ -440,4 +391,4 @@ namespace SharpCompress
return true;
}
}
}
}

View File

@@ -1,7 +1,7 @@
#if !NO_FILE
using System;
#endif
using System;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
namespace SharpCompress.Writers
{
@@ -12,7 +12,6 @@ namespace SharpCompress.Writers
writer.Write(entryPath, source, null);
}
#if !NO_FILE
public static void Write(this IWriter writer, string entryPath, FileInfo source)
{
if (!source.Exists)
@@ -30,22 +29,30 @@ namespace SharpCompress.Writers
writer.Write(entryPath, new FileInfo(source));
}
public static void WriteAll(this IWriter writer, string directory, string searchPattern = "*",
public static void WriteAll(this IWriter writer, string directory, string searchPattern = "*", SearchOption option = SearchOption.TopDirectoryOnly)
{
writer.WriteAll(directory, searchPattern, null, option);
}
public static void WriteAll(this IWriter writer,
string directory,
string searchPattern = "*",
Expression<Func<string, bool>> fileSearchFunc = null,
SearchOption option = SearchOption.TopDirectoryOnly)
{
if (!Directory.Exists(directory))
{
throw new ArgumentException("Directory does not exist: " + directory);
}
#if NET35
foreach (var file in Directory.GetDirectories(directory, searchPattern, option))
#else
foreach (var file in Directory.EnumerateFiles(directory, searchPattern, option))
#endif
if (fileSearchFunc == null)
{
fileSearchFunc = n => true;
}
foreach (var file in Directory.EnumerateFiles(directory, searchPattern, option).Where(fileSearchFunc.Compile()))
{
writer.Write(file.Substring(directory.Length), file);
}
}
#endif
}
}

View File

@@ -1,4 +1,3 @@
using SharpCompress.Archives;
using SharpCompress.Common;
namespace SharpCompress.Writers.Tar
@@ -18,6 +17,7 @@ namespace SharpCompress.Writers.Tar
internal TarWriterOptions(WriterOptions options) : this(options.CompressionType, true)
{
ArchiveEncoding = options.ArchiveEncoding;
}
}
}

View File

@@ -36,7 +36,8 @@ namespace SharpCompress.Writers.Zip
byte[] encodedComment = archiveEncoding.Encode(Comment);
var zip64_stream = Compressed >= uint.MaxValue || Decompressed >= uint.MaxValue;
var zip64 = zip64_stream || HeaderOffset >= uint.MaxValue || Zip64HeaderOffset != 0;
var zip64 = zip64_stream || HeaderOffset >= uint.MaxValue;
var usedCompression = compression;
var compressedvalue = zip64 ? uint.MaxValue : (uint)Compressed;
var decompressedvalue = zip64 ? uint.MaxValue : (uint)Decompressed;
@@ -56,17 +57,21 @@ namespace SharpCompress.Writers.Zip
if (!zip64_stream)
flags |= HeaderFlags.UsePostDataDescriptor;
if (compression == ZipCompressionMethod.LZMA)
if (usedCompression == ZipCompressionMethod.LZMA)
{
flags |= HeaderFlags.Bit1; // eos marker
}
}
// Support for zero byte files
if (Decompressed == 0 && Compressed == 0)
usedCompression = ZipCompressionMethod.None;
//constant sig, then version made by, then version to extract
outputStream.Write(new byte[] { 80, 75, 1, 2, version, 0, version, 0 }, 0, 8);
outputStream.Write(DataConverter.LittleEndian.GetBytes((ushort)flags), 0, 2);
outputStream.Write(DataConverter.LittleEndian.GetBytes((ushort)compression), 0, 2); // zipping method
outputStream.Write(DataConverter.LittleEndian.GetBytes((ushort)usedCompression), 0, 2); // zipping method
outputStream.Write(DataConverter.LittleEndian.GetBytes(ModificationTime.DateTimeToDosTime()), 0, 4);
// zipping date and time

View File

@@ -383,6 +383,14 @@ namespace SharpCompress.Writers.Zip
originalStream.Position = (long)(entry.HeaderOffset + 6);
originalStream.WriteByte(0);
if (counting.Count == 0 && entry.Decompressed == 0)
{
// set compression to STORED for zero byte files (no compression data)
originalStream.Position = (long)(entry.HeaderOffset + 8);
originalStream.WriteByte(0);
originalStream.WriteByte(0);
}
originalStream.Position = (long)(entry.HeaderOffset + 14);
writer.WriteFooter(entry.Crc, compressedvalue, decompressedvalue);

View File

@@ -10,7 +10,7 @@ using Xunit;
namespace SharpCompress.Test
{
public class ArchiveTests : TestBase
public class ArchiveTests : ReaderTests
{
protected void ArchiveStreamReadExtractAll(string testArchive, CompressionType compression)
{
@@ -23,14 +23,13 @@ namespace SharpCompress.Test
{
foreach (var path in testArchives)
{
ResetScratch();
using (var stream = new NonDisposingStream(File.OpenRead(path), true))
using (var archive = ArchiveFactory.Open(stream))
{
Assert.True(archive.IsSolid);
using (var reader = archive.ExtractAllEntries())
{
ReaderTests.UseReader(this, reader, compression);
UseReader(reader, compression);
}
VerifyFiles();
@@ -69,7 +68,6 @@ namespace SharpCompress.Test
{
foreach (var path in testArchives)
{
ResetScratch();
using (var stream = new NonDisposingStream(File.OpenRead(path), true))
using (var archive = ArchiveFactory.Open(stream, readerOptions))
{
@@ -106,118 +104,42 @@ namespace SharpCompress.Test
protected void ArchiveFileRead(string testArchive, ReaderOptions readerOptions = null)
{
testArchive = Path.Combine(TEST_ARCHIVES_PATH, testArchive);
ArchiveFileRead(testArchive.AsEnumerable(), readerOptions);
}
protected void ArchiveFileRead(IEnumerable<string> testArchives, ReaderOptions readerOptions = null)
{
foreach (var path in testArchives)
using (var archive = ArchiveFactory.Open(testArchive, readerOptions))
{
ResetScratch();
using (var archive = ArchiveFactory.Open(path, readerOptions))
foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory))
{
//archive.EntryExtractionBegin += archive_EntryExtractionBegin;
//archive.FilePartExtractionBegin += archive_FilePartExtractionBegin;
//archive.CompressedBytesRead += archive_CompressedBytesRead;
foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory))
{
entry.WriteToDirectory(SCRATCH_FILES_PATH,
new ExtractionOptions()
{
ExtractFullPath = true,
Overwrite = true
});
}
entry.WriteToDirectory(SCRATCH_FILES_PATH,
new ExtractionOptions()
{
ExtractFullPath = true,
Overwrite = true
});
}
VerifyFiles();
}
VerifyFiles();
}
private void archive_CompressedBytesRead(object sender, CompressedBytesReadEventArgs e)
{
Console.WriteLine("Read Compressed File Part Bytes: {0} Percentage: {1}%",
e.CurrentFilePartCompressedBytesRead, CreatePercentage(e.CurrentFilePartCompressedBytesRead, partTotal));
string percentage = entryTotal.HasValue ? CreatePercentage(e.CompressedBytesRead,
entryTotal.Value).ToString() : "Unknown";
Console.WriteLine("Read Compressed File Entry Bytes: {0} Percentage: {1}%",
e.CompressedBytesRead, percentage);
}
private void archive_FilePartExtractionBegin(object sender, FilePartExtractionBeginEventArgs e)
{
partTotal = e.Size;
Console.WriteLine("Initializing File Part Extraction: " + e.Name);
}
private void archive_EntryExtractionBegin(object sender, ArchiveExtractionEventArgs<IArchiveEntry> e)
{
entryTotal = e.Item.Size;
Console.WriteLine("Initializing File Entry Extraction: " + e.Item.Key);
}
private long? entryTotal;
private long partTotal;
private long totalSize;
/// <summary>
/// Demonstrate the ExtractionOptions.PreserveFileTime and ExtractionOptions.PreserveAttributes extract options
/// </summary>
protected void ArchiveFileReadEx(string testArchive)
{
testArchive = Path.Combine(TEST_ARCHIVES_PATH, testArchive);
ArchiveFileReadEx(testArchive.AsEnumerable());
}
/// <summary>
/// Demonstrate the TotalUncompressSize property, and the ExtractionOptions.PreserveFileTime and ExtractionOptions.PreserveAttributes extract options
/// </summary>
protected void ArchiveFileReadEx(IEnumerable<string> testArchives)
{
foreach (var path in testArchives)
using (var archive = ArchiveFactory.Open(testArchive))
{
ResetScratch();
using (var archive = ArchiveFactory.Open(path))
foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory))
{
totalSize = archive.TotalUncompressSize;
//archive.EntryExtractionBegin += Archive_EntryExtractionBeginEx;
//archive.EntryExtractionEnd += Archive_EntryExtractionEndEx;
//archive.CompressedBytesRead += Archive_CompressedBytesReadEx;
foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory))
{
entry.WriteToDirectory(SCRATCH_FILES_PATH,
new ExtractionOptions()
{
ExtractFullPath = true,
Overwrite = true,
PreserveAttributes = true,
PreserveFileTime = true
});
}
entry.WriteToDirectory(SCRATCH_FILES_PATH,
new ExtractionOptions()
{
ExtractFullPath = true,
Overwrite = true,
PreserveAttributes = true,
PreserveFileTime = true
});
}
VerifyFilesEx();
}
}
private void Archive_EntryExtractionEndEx(object sender, ArchiveExtractionEventArgs<IArchiveEntry> e)
{
partTotal += e.Item.Size;
}
private void Archive_CompressedBytesReadEx(object sender, CompressedBytesReadEventArgs e)
{
string percentage = entryTotal.HasValue ? CreatePercentage(e.CompressedBytesRead, entryTotal.Value).ToString() : "-";
string tortalPercentage = CreatePercentage(partTotal + e.CompressedBytesRead, totalSize).ToString();
Console.WriteLine(@"Read Compressed File Progress: {0}% Total Progress {1}%", percentage, tortalPercentage);
}
private void Archive_EntryExtractionBeginEx(object sender, ArchiveExtractionEventArgs<IArchiveEntry> e)
{
entryTotal = e.Item.Size;
}
private int CreatePercentage(long n, long d)
{
return (int)(((double)n / (double)d) * 100);
VerifyFilesEx();
}
}
}

View File

@@ -17,8 +17,7 @@ namespace SharpCompress.Test.GZip
[Fact]
public void GZip_Archive_Generic()
{
ResetScratch();
using (Stream stream = File.Open(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz"), FileMode.Open))
using (Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz")))
using (var archive = ArchiveFactory.Open(stream))
{
var entry = archive.Entries.First();
@@ -31,8 +30,7 @@ namespace SharpCompress.Test.GZip
[Fact]
public void GZip_Archive()
{
ResetScratch();
using (Stream stream = File.Open(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz"), FileMode.Open))
using (Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz")))
using (var archive = GZipArchive.Open(stream))
{
var entry = archive.Entries.First();
@@ -47,8 +45,7 @@ namespace SharpCompress.Test.GZip
public void GZip_Archive_NoAdd()
{
string jpg = Path.Combine(ORIGINAL_FILES_PATH, "jpg", "test.jpg");
ResetScratch();
using (Stream stream = File.Open(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz"), FileMode.Open))
using (Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz")))
using (var archive = GZipArchive.Open(stream))
{
Assert.Throws<InvalidOperationException>(() => archive.AddEntry("jpg\\test.jpg", jpg));
@@ -60,9 +57,8 @@ namespace SharpCompress.Test.GZip
[Fact]
public void GZip_Archive_Multiple_Reads()
{
ResetScratch();
var inputStream = new MemoryStream();
using (var fileStream = File.Open(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz"), FileMode.Open))
using (var fileStream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz")))
{
fileStream.CopyTo(inputStream);
inputStream.Position = 0;

View File

@@ -17,7 +17,6 @@ namespace SharpCompress.Test.GZip
[Fact]
public void GZip_Writer_Generic()
{
ResetScratch();
using (Stream stream = File.Open(Path.Combine(SCRATCH_FILES_PATH, "Tar.tar.gz"), FileMode.OpenOrCreate, FileAccess.Write))
using (var writer = WriterFactory.Open(stream, ArchiveType.GZip, CompressionType.GZip))
{
@@ -30,7 +29,6 @@ namespace SharpCompress.Test.GZip
[Fact]
public void GZip_Writer()
{
ResetScratch();
using (Stream stream = File.Open(Path.Combine(SCRATCH_FILES_PATH, "Tar.tar.gz"), FileMode.OpenOrCreate, FileAccess.Write))
using (var writer = new GZipWriter(stream))
{
@@ -44,17 +42,12 @@ namespace SharpCompress.Test.GZip
public void GZip_Writer_Generic_Bad_Compression()
{
Assert.Throws<InvalidFormatException>(() =>
{
ResetScratch();
using (Stream stream = File.OpenWrite(Path.Combine(SCRATCH_FILES_PATH, "Tar.tar.gz")))
using (var writer = WriterFactory.Open(stream, ArchiveType.GZip, CompressionType.BZip2))
{
writer.Write("Tar.tar", Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar"));
}
CompareArchivesByPath(Path.Combine(SCRATCH_FILES_PATH, "Tar.tar.gz"),
Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz"));
});
{
using (Stream stream = File.OpenWrite(Path.Combine(SCRATCH_FILES_PATH, "Tar.tar.gz")))
using (var writer = WriterFactory.Open(stream, ArchiveType.GZip, CompressionType.BZip2))
{
}
});
}
}
}

View File

@@ -0,0 +1,57 @@
using System;
using System.IO;
namespace SharpCompress.Test.Mocks
{
// This is a simplified version of CryptoStream that always flushes the inner stream on Dispose to trigger an error in EntryStream
// CryptoStream doesn't always trigger the Flush, so this class is used instead
// See https://referencesource.microsoft.com/#mscorlib/system/security/cryptography/cryptostream.cs,141
public class FlushOnDisposeStream : Stream, IDisposable
{
private Stream inner;
public FlushOnDisposeStream(Stream innerStream) {
this.inner = innerStream;
}
public override bool CanRead => this.inner.CanRead;
public override bool CanSeek => false;
public override bool CanWrite => false;
public override long Length => this.inner.Length;
public override long Position { get => this.inner.Position; set => this.inner.Position = value; }
public override void Flush() {
throw new NotImplementedException();
}
public override int Read(byte[] buffer, int offset, int count) {
return this.inner.Read(buffer, offset, count);
}
public override long Seek(long offset, SeekOrigin origin) {
throw new NotImplementedException();
}
public override void SetLength(long value) {
throw new NotImplementedException();
}
public override void Write(byte[] buffer, int offset, int count) {
throw new NotImplementedException();
}
protected override void Dispose(bool disposing) {
if(disposing) {
this.inner.Flush();
this.inner.Close();
}
base.Dispose(disposing);
}
}
}

View File

@@ -1,7 +1,7 @@
using System;
using System.IO;
namespace SharpCompress.Test
namespace SharpCompress.Test.Mocks
{
public class ForwardOnlyStream : Stream
{
@@ -16,9 +16,15 @@ namespace SharpCompress.Test
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
stream.Dispose();
IsDisposed = true;
if (!IsDisposed)
{
if (disposing)
{
stream.Dispose();
IsDisposed = true;
base.Dispose(disposing);
}
}
}
public override bool CanRead => true;
@@ -44,11 +50,6 @@ namespace SharpCompress.Test
return stream.Read(buffer, offset, count);
}
public override int ReadByte()
{
return stream.ReadByte();
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException();

View File

@@ -0,0 +1,69 @@
using System.IO;
namespace SharpCompress.Test.Mocks
{
public class TestStream : Stream
{
private readonly Stream stream;
public TestStream(Stream stream) : this(stream, stream.CanRead, stream.CanWrite, stream.CanSeek)
{
}
public bool IsDisposed { get; private set; }
public TestStream(Stream stream, bool read, bool write, bool seek)
{
this.stream = stream;
CanRead = read;
CanWrite = write;
CanSeek = seek;
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
stream.Dispose();
IsDisposed = true;
}
public override bool CanRead { get; }
public override bool CanSeek { get; }
public override bool CanWrite { get; }
public override void Flush()
{
stream.Flush();
}
public override long Length => stream.Length;
public override long Position
{
get => stream.Position;
set => stream.Position = value;
}
public override int Read(byte[] buffer, int offset, int count)
{
return stream.Read(buffer, offset, count);
}
public override long Seek(long offset, SeekOrigin origin)
{
return stream.Seek(offset, origin);
}
public override void SetLength(long value)
{
stream.SetLength(value);
}
public override void Write(byte[] buffer, int offset, int count)
{
stream.Write(buffer, offset, count);
}
}
}

View File

@@ -48,7 +48,6 @@ namespace SharpCompress.Test.Rar
private void ReadRarPassword(string testArchive, string password)
{
ResetScratch();
using (Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, testArchive)))
using (var archive = RarArchive.Open(stream, new ReaderOptions()
{
@@ -80,7 +79,6 @@ namespace SharpCompress.Test.Rar
protected void ArchiveFileReadPassword(string archiveName, string password)
{
ResetScratch();
using (var archive = RarArchive.Open(Path.Combine(TEST_ARCHIVES_PATH, archiveName), new ReaderOptions()
{
Password = password,
@@ -131,7 +129,6 @@ namespace SharpCompress.Test.Rar
private void DoRar_test_invalid_exttime_ArchiveStreamRead(string filename)
{
ResetScratch();
using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, filename)))
{
using (var archive = ArchiveFactory.Open(stream))
@@ -151,7 +148,6 @@ namespace SharpCompress.Test.Rar
[Fact]
public void Rar_Jpg_ArchiveStreamRead()
{
ResetScratch();
using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Rar.jpeg.jpg")))
{
using (var archive = RarArchive.Open(stream, new ReaderOptions()
@@ -185,7 +181,6 @@ namespace SharpCompress.Test.Rar
private void DoRar_IsSolidArchiveCheck(string filename)
{
ResetScratch();
using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, filename)))
{
using (var archive = RarArchive.Open(stream))
@@ -253,7 +248,6 @@ namespace SharpCompress.Test.Rar
private void DoRar_Multi_ArchiveStreamRead(string[] archives)
{
ResetScratch();
using (var archive = RarArchive.Open(archives.Select(s => Path.Combine(TEST_ARCHIVES_PATH, s))
.Select(File.OpenRead)))
{
@@ -305,7 +299,6 @@ namespace SharpCompress.Test.Rar
private void DoRar_ArchiveFileRead_HasDirectories(string filename)
{
ResetScratch();
using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, filename)))
{
using (var archive = RarArchive.Open(stream))
@@ -319,7 +312,6 @@ namespace SharpCompress.Test.Rar
[Fact]
public void Rar_Jpg_ArchiveFileRead()
{
ResetScratch();
using (var archive = RarArchive.Open(Path.Combine(TEST_ARCHIVES_PATH, "Rar.jpeg.jpg"), new ReaderOptions()
{
LookForHeader = true

View File

@@ -15,7 +15,6 @@ namespace SharpCompress.Test.Rar
public RarHeaderFactoryTest()
{
ResetScratch();
rarHeaderFactory = new RarHeaderFactory(
StreamingMode.Seekable,
new ReaderOptions { LeaveStreamOpen = true });
@@ -47,7 +46,7 @@ namespace SharpCompress.Test.Rar
private void ReadEncryptedFlag(string testArchive, bool isEncrypted)
{
using (var stream = GetReaderStream(testArchive))
using (var stream = new FileStream(Path.Combine(TEST_ARCHIVES_PATH, testArchive), FileMode.Open, FileAccess.Read))
{
foreach (var header in rarHeaderFactory.ReadHeaders(stream))
{
@@ -59,10 +58,5 @@ namespace SharpCompress.Test.Rar
}
}
}
private FileStream GetReaderStream(string testArchive)
{
return new FileStream(Path.Combine(TEST_ARCHIVES_PATH, testArchive), FileMode.Open);
}
}
}

View File

@@ -33,7 +33,6 @@ namespace SharpCompress.Test.Rar
private void DoRar_Multi_Reader(string[] archives)
{
ResetScratch();
using (var reader = RarReader.Open(archives.Select(s => Path.Combine(TEST_ARCHIVES_PATH, s))
.Select(p => File.OpenRead(p))))
{
@@ -64,7 +63,6 @@ namespace SharpCompress.Test.Rar
{
Assert.Throws<InvalidFormatException>(() =>
{
ResetScratch();
using (var reader = RarReader.Open(archives.Select(s => Path.Combine(TEST_ARCHIVES_PATH, s))
.Select(p => File.OpenRead(p)),
new ReaderOptions()
@@ -110,7 +108,6 @@ namespace SharpCompress.Test.Rar
private void DoRar_Multi_Reader_Delete_Files(string[] archives)
{
ResetScratch();
foreach (var file in archives)
{
@@ -202,27 +199,7 @@ namespace SharpCompress.Test.Rar
private void ReadRar(string testArchive, string password)
{
ResetScratch();
using (Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, testArchive)))
using (var reader = RarReader.Open(stream, new ReaderOptions()
{
Password = password
}))
{
while (reader.MoveToNextEntry())
{
if (!reader.Entry.IsDirectory)
{
Assert.Equal(CompressionType.Rar, reader.Entry.CompressionType);
reader.WriteEntryToDirectory(SCRATCH_FILES_PATH, new ExtractionOptions()
{
ExtractFullPath = true,
Overwrite = true
});
}
}
}
VerifyFiles();
Read(testArchive, CompressionType.Rar, new ReaderOptions { Password = password });
}
[Fact]
@@ -237,9 +214,8 @@ namespace SharpCompress.Test.Rar
private void DoRar_Entry_Stream(string filename)
{
ResetScratch();
using (Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, filename)))
using (var reader = RarReader.Open(stream))
using (var reader = ReaderFactory.Open(stream))
{
while (reader.MoveToNextEntry())
{
@@ -271,9 +247,8 @@ namespace SharpCompress.Test.Rar
[Fact]
public void Rar_Reader_Audio_program()
{
ResetScratch();
using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Rar.Audio_program.rar")))
using (var reader = RarReader.Open(stream, new ReaderOptions()
using (var reader = ReaderFactory.Open(stream, new ReaderOptions()
{
LookForHeader = true
}))
@@ -295,7 +270,6 @@ namespace SharpCompress.Test.Rar
[Fact]
public void Rar_Jpg_Reader()
{
ResetScratch();
using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Rar.jpeg.jpg")))
using (var reader = RarReader.Open(stream, new ReaderOptions()
{
@@ -339,9 +313,8 @@ namespace SharpCompress.Test.Rar
private void DoRar_Solid_Skip_Reader(string filename)
{
ResetScratch();
using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, filename)))
using (var reader = RarReader.Open(stream, new ReaderOptions()
using (var reader = ReaderFactory.Open(stream, new ReaderOptions()
{
LookForHeader = true
}))
@@ -373,9 +346,8 @@ namespace SharpCompress.Test.Rar
private void DoRar_Reader_Skip(string filename)
{
ResetScratch();
using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, filename)))
using (var reader = RarReader.Open(stream, new ReaderOptions()
using (var reader = ReaderFactory.Open(stream, new ReaderOptions()
{
LookForHeader = true
}))

View File

@@ -1,52 +1,66 @@
using System.Collections.Generic;
using System.IO;
using System.IO;
using SharpCompress.Common;
using SharpCompress.IO;
using SharpCompress.Readers;
using SharpCompress.Test.Mocks;
using Xunit;
namespace SharpCompress.Test
{
public class ReaderTests : TestBase
public abstract class ReaderTests : TestBase
{
protected void Read(string testArchive, CompressionType expectedCompression)
protected void Read(string testArchive, CompressionType expectedCompression, ReaderOptions options = null)
{
testArchive = Path.Combine(TEST_ARCHIVES_PATH, testArchive);
Read(testArchive.AsEnumerable(), expectedCompression);
options = options ?? new ReaderOptions();
options.LeaveStreamOpen = true;
ReadImpl(testArchive, expectedCompression, options);
options.LeaveStreamOpen = false;
ReadImpl(testArchive, expectedCompression, options);
VerifyFiles();
}
protected void Read(IEnumerable<string> testArchives, CompressionType expectedCompression)
private void ReadImpl(string testArchive, CompressionType expectedCompression, ReaderOptions options)
{
foreach (var path in testArchives)
using (var file = File.OpenRead(testArchive))
{
using (var stream = new NonDisposingStream(new ForwardOnlyStream(File.OpenRead(path)), true))
using (var reader = ReaderFactory.Open(stream, new ReaderOptions()
{
LeaveStreamOpen = true
}))
using (var protectedStream = new NonDisposingStream(new ForwardOnlyStream(file), throwOnDispose: true))
{
UseReader(this, reader, expectedCompression);
stream.ThrowOnDispose = false;
using (var testStream = new TestStream(protectedStream))
{
using (var reader = ReaderFactory.Open(testStream, options))
{
UseReader(reader, expectedCompression);
protectedStream.ThrowOnDispose = false;
Assert.False(testStream.IsDisposed, "{nameof(testStream)} prematurely closed");
}
// Boolean XOR -- If the stream should be left open (true), then the stream should not be diposed (false)
// and if the stream should be closed (false), then the stream should be disposed (true)
var message = $"{nameof(options.LeaveStreamOpen)} is set to '{options.LeaveStreamOpen}', so {nameof(testStream.IsDisposed)} should be set to '{!testStream.IsDisposed}', but is set to {testStream.IsDisposed}";
Assert.True(options.LeaveStreamOpen != testStream.IsDisposed, message);
}
}
}
}
public static void UseReader(TestBase test, IReader reader, CompressionType expectedCompression)
public void UseReader(IReader reader, CompressionType expectedCompression)
{
test.ResetScratch();
while (reader.MoveToNextEntry())
{
if (!reader.Entry.IsDirectory)
{
Assert.Equal(reader.Entry.CompressionType, expectedCompression);
reader.WriteEntryToDirectory(test.SCRATCH_FILES_PATH, new ExtractionOptions()
Assert.Equal(expectedCompression, reader.Entry.CompressionType);
reader.WriteEntryToDirectory(SCRATCH_FILES_PATH, new ExtractionOptions()
{
ExtractFullPath = true,
Overwrite = true
});
}
}
test.VerifyFiles();
}
}
}

View File

@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netcoreapp2.1</TargetFrameworks>
<TargetFrameworks>netcoreapp2.2</TargetFrameworks>
<AssemblyName>SharpCompress.Test</AssemblyName>
<AssemblyOriginatorKeyFile>../../SharpCompress.snk</AssemblyOriginatorKeyFile>
<SignAssembly>true</SignAssembly>
@@ -12,9 +12,10 @@
<ProjectReference Include="..\..\src\SharpCompress\SharpCompress.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
<PackageReference Include="xunit" Version="2.3.1" />
<PackageReference Include="Xunit.SkippableFact" Version="1.3.6" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.3.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="Xunit.SkippableFact" Version="1.3.12" />
<PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" />
</ItemGroup>
</Project>
</Project>

View File

@@ -4,7 +4,7 @@ using Xunit;
namespace SharpCompress.Test.Streams
{
public class StreamTests
public class LzmaStreamTests
{
[Fact]
public void TestLzma2Decompress1Byte()

View File

@@ -2,7 +2,7 @@
using SharpCompress.IO;
using Xunit;
namespace SharpCompress.Test
namespace SharpCompress.Test.Streams
{
public class RewindableStreamTest
{

View File

@@ -5,6 +5,10 @@ using SharpCompress.Archives.Tar;
using SharpCompress.Common;
using SharpCompress.Writers;
using Xunit;
using System.Text;
using SharpCompress.Readers;
using SharpCompress.Writers.Tar;
using SharpCompress.Readers.Tar;
namespace SharpCompress.Test.Tar
{
@@ -27,6 +31,39 @@ namespace SharpCompress.Test.Tar
ArchiveFileRead("Tar.tar");
}
[Fact]
public void Tar_FileName_Exactly_100_Characters()
{
string archive = "Tar_FileName_Exactly_100_Characters.tar";
// create the 100 char filename
string filename = "filename_with_exactly_100_characters_______________________________________________________________X";
// Step 1: create a tar file containing a file with the test name
using (Stream stream = File.OpenWrite(Path.Combine(SCRATCH2_FILES_PATH, archive)))
using (var writer = WriterFactory.Open(stream, ArchiveType.Tar, CompressionType.None))
using (Stream inputStream = new MemoryStream())
{
StreamWriter sw = new StreamWriter(inputStream);
sw.Write("dummy filecontent");
sw.Flush();
inputStream.Position = 0;
writer.Write(filename, inputStream, null);
}
// Step 2: check if the written tar file can be read correctly
string unmodified = Path.Combine(SCRATCH2_FILES_PATH, archive);
using (var archive2 = TarArchive.Open(unmodified))
{
Assert.Equal(1, archive2.Entries.Count);
Assert.Contains(filename, archive2.Entries.Select(entry => entry.Key));
foreach (var entry in archive2.Entries)
Assert.Equal("dummy filecontent", new StreamReader(entry.OpenEntryStream()).ReadLine());
}
}
[Fact]
public void Tar_NonUstarArchiveWithLongNameDoesNotSkipEntriesAfterTheLongOne()
@@ -48,7 +85,6 @@ namespace SharpCompress.Test.Tar
{
string archive = "Tar_VeryLongFilepathReadback.tar";
ResetScratch();
// create a very long filename
string longFilename = "";
@@ -103,11 +139,17 @@ namespace SharpCompress.Test.Tar
string scratchPath = Path.Combine(SCRATCH_FILES_PATH, "Tar.tar");
string unmodified = Path.Combine(TEST_ARCHIVES_PATH, "Tar.noEmptyDirs.tar");
ResetScratch();
// var aropt = new Ar
using (var archive = TarArchive.Create())
{
archive.AddAllFromDirectory(ORIGINAL_FILES_PATH);
archive.SaveTo(scratchPath, CompressionType.None);
var twopt = new TarWriterOptions(CompressionType.None, true);
twopt.ArchiveEncoding = new ArchiveEncoding()
{
Default = Encoding.GetEncoding(866)
};
archive.SaveTo(scratchPath, twopt);
}
CompareArchivesByPath(unmodified, scratchPath);
}
@@ -119,7 +161,6 @@ namespace SharpCompress.Test.Tar
string unmodified = Path.Combine(TEST_ARCHIVES_PATH, "Tar.mod.tar");
string modified = Path.Combine(TEST_ARCHIVES_PATH, "Tar.noEmptyDirs.tar");
ResetScratch();
using (var archive = TarArchive.Open(unmodified))
{
archive.AddEntry("jpg\\test.jpg", jpg);
@@ -135,7 +176,6 @@ namespace SharpCompress.Test.Tar
string modified = Path.Combine(TEST_ARCHIVES_PATH, "Tar.mod.tar");
string unmodified = Path.Combine(TEST_ARCHIVES_PATH, "Tar.noEmptyDirs.tar");
ResetScratch();
using (var archive = TarArchive.Open(unmodified))
{
var entry = archive.Entries.Single(x => x.Key.EndsWith("jpg"));
@@ -155,5 +195,51 @@ namespace SharpCompress.Test.Tar
Assert.True(archive.Type == ArchiveType.Tar);
}
}
[Fact]
public void Tar_Empty_Archive()
{
string archiveFullPath = Path.Combine(TEST_ARCHIVES_PATH, "Tar.Empty.tar");
using (Stream stream = File.OpenRead(archiveFullPath))
using (IArchive archive = ArchiveFactory.Open(stream))
{
Assert.True(archive.Type == ArchiveType.Tar);
}
}
[Theory]
[InlineData(10)]
[InlineData(128)]
public void Tar_Japanese_Name(int length)
{
using (var mstm = new MemoryStream())
{
var enc = new ArchiveEncoding()
{
Default = Encoding.UTF8
};
var twopt = new TarWriterOptions(CompressionType.None, true);
twopt.ArchiveEncoding = enc;
var fname = new string((char)0x3042, length);
using (var tw = new TarWriter(mstm, twopt))
using (var input = new MemoryStream(new byte[32]))
{
tw.Write(fname, input, null);
}
using (var inputMemory = new MemoryStream(mstm.ToArray()))
{
var tropt = new ReaderOptions()
{
ArchiveEncoding = enc
};
using (var tr = TarReader.Open(inputMemory, tropt))
{
while (tr.MoveToNextEntry())
{
Assert.Equal(fname, tr.Entry.Key);
}
}
}
}
}
}
}

View File

@@ -3,6 +3,7 @@ using System.IO;
using SharpCompress.Common;
using SharpCompress.Readers;
using SharpCompress.Readers.Tar;
using SharpCompress.Test.Mocks;
using Xunit;
namespace SharpCompress.Test.Tar
@@ -26,7 +27,6 @@ namespace SharpCompress.Test.Tar
using (Stream stream = new ForwardOnlyStream(File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar"))))
using (IReader reader = ReaderFactory.Open(stream))
{
ResetScratch();
int x = 0;
while (reader.MoveToNextEntry())
{
@@ -74,7 +74,6 @@ namespace SharpCompress.Test.Tar
[Fact]
public void Tar_BZip2_Entry_Stream()
{
ResetScratch();
using (Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.bz2")))
using (var reader = TarReader.Open(stream))
{
@@ -161,5 +160,91 @@ namespace SharpCompress.Test.Tar
Assert.True(reader.ArchiveType == ArchiveType.Tar);
}
}
[Fact]
public void Tar_With_TarGz_With_Flushed_EntryStream()
{
string archiveFullPath = Path.Combine(TEST_ARCHIVES_PATH, "Tar.ContainsTarGz.tar");
using(Stream stream = File.OpenRead(archiveFullPath))
using(IReader reader = ReaderFactory.Open(stream))
{
Assert.True(reader.MoveToNextEntry());
Assert.Equal("inner.tar.gz", reader.Entry.Key);
using(var entryStream = reader.OpenEntryStream()) {
using(FlushOnDisposeStream flushingStream = new FlushOnDisposeStream(entryStream)) {
// Extract inner.tar.gz
using(var innerReader = ReaderFactory.Open(flushingStream)) {
Assert.True(innerReader.MoveToNextEntry());
Assert.Equal("test", innerReader.Entry.Key);
}
}
}
}
}
[Fact]
public void Tar_GZip_With_Symlink_Entries()
{
var isWindows = System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(
System.Runtime.InteropServices.OSPlatform.Windows);
using (Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "TarWithSymlink.tar.gz")))
using (var reader = TarReader.Open(stream))
{
List<string> names = new List<string>();
while (reader.MoveToNextEntry())
{
if (reader.Entry.IsDirectory)
{
continue;
}
reader.WriteEntryToDirectory(SCRATCH_FILES_PATH,
new ExtractionOptions()
{
ExtractFullPath = true,
Overwrite = true,
WriteSymbolicLink = (sourcePath, targetPath) =>
{
if (!isWindows)
{
var link = new Mono.Unix.UnixSymbolicLinkInfo(sourcePath);
if (System.IO.File.Exists(sourcePath))
{
link.Delete(); // equivalent to ln -s -f
}
link.CreateSymbolicLinkTo(targetPath);
}
}
});
if (!isWindows)
{
if (reader.Entry.LinkTarget != null)
{
var path = System.IO.Path.Combine(SCRATCH_FILES_PATH, reader.Entry.Key);
var link = new Mono.Unix.UnixSymbolicLinkInfo(path);
if (link.HasContents)
{
// need to convert the link to an absolute path for comparison
var target = reader.Entry.LinkTarget;
var realTarget = System.IO.Path.GetFullPath(
System.IO.Path.Combine($"{System.IO.Path.GetDirectoryName(path)}",
target)
);
Assert.Equal(realTarget, link.GetContents().ToString());
}
else
{
Assert.True(false, "Symlink has no target");
}
}
}
}
}
}
}
}

View File

@@ -3,23 +3,20 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using SharpCompress.Readers;
using Xunit;
[assembly: CollectionBehavior(DisableTestParallelization = true)]
namespace SharpCompress.Test
{
public class TestBase
public class TestBase : IDisposable
{
private string SOLUTION_BASE_PATH;
protected string TEST_ARCHIVES_PATH;
protected string ORIGINAL_FILES_PATH;
protected string MISC_TEST_FILES_PATH;
private string SCRATCH_BASE_PATH;
public string SCRATCH_FILES_PATH;
protected string SCRATCH2_FILES_PATH;
public TestBase()
{
@@ -29,23 +26,18 @@ namespace SharpCompress.Test
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");
SCRATCH_FILES_PATH = Path.Combine(SOLUTION_BASE_PATH, "TestArchives", "Scratch");
SCRATCH2_FILES_PATH = Path.Combine(SOLUTION_BASE_PATH, "TestArchives", "Scratch2");
}
public void ResetScratch()
{
if (Directory.Exists(SCRATCH_FILES_PATH))
{
Directory.Delete(SCRATCH_FILES_PATH, true);
}
Directory.CreateDirectory(SCRATCH_FILES_PATH);
if (Directory.Exists(SCRATCH2_FILES_PATH))
{
Directory.Delete(SCRATCH2_FILES_PATH, true);
}
Directory.CreateDirectory(SCRATCH2_FILES_PATH);
SCRATCH_BASE_PATH = Path.Combine(SOLUTION_BASE_PATH, "TestArchives", Guid.NewGuid().ToString());
SCRATCH_FILES_PATH = Path.Combine(SCRATCH_BASE_PATH, "Scratch");
SCRATCH2_FILES_PATH = Path.Combine(SCRATCH_BASE_PATH, "Scratch2");
Directory.CreateDirectory(SCRATCH_FILES_PATH);
Directory.CreateDirectory(SCRATCH2_FILES_PATH);
}
public void Dispose()
{
Directory.Delete(SCRATCH_BASE_PATH, true);
}
public void VerifyFiles()
@@ -169,14 +161,6 @@ namespace SharpCompress.Test
return;
}
if (IsFileLocked(new FileInfo(file1)))
{
throw new InvalidOperationException($"{file1} is not disposed");
}
if (IsFileLocked(new FileInfo(file2)))
{
throw new InvalidOperationException($"{file2} is not disposed");
}
using (var file1Stream = File.OpenRead(file1))
using (var file2Stream = File.OpenRead(file2))
{
@@ -205,8 +189,7 @@ namespace SharpCompress.Test
}
protected void CompareArchivesByPath(string file1, string file2) {
ReaderOptions readerOptions = new ReaderOptions();
ReaderOptions readerOptions = new ReaderOptions { LeaveStreamOpen = false };
readerOptions.ArchiveEncoding.Default = Encoding.GetEncoding(866);
//don't compare the order. OS X reads files from the file system in a different order therefore makes the archive ordering different
@@ -231,29 +214,5 @@ namespace SharpCompress.Test
}
}
protected bool IsFileLocked(FileInfo file)
{
FileStream stream = null;
try
{
stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.None);
}
catch (IOException)
{
//the file is unavailable because it is:
//still being written to
//or being processed by another thread
//or does not exist (has already been processed)
return true;
}
finally
{
stream?.Close();
}
//file is not locked
return false;
}
}
}

View File

@@ -1,70 +0,0 @@
using System.IO;
namespace SharpCompress.Test
{
public class TestStream : Stream
{
private readonly Stream stream;
public TestStream(Stream stream)
: this(stream, true, true, true)
{
}
public bool IsDisposed { get; private set; }
public TestStream(Stream stream, bool read, bool write, bool seek)
{
this.stream = stream;
CanRead = read;
CanWrite = write;
CanSeek = seek;
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
stream.Dispose();
IsDisposed = true;
}
public override bool CanRead { get; }
public override bool CanSeek { get; }
public override bool CanWrite { get; }
public override void Flush()
{
stream.Flush();
}
public override long Length => stream.Length;
public override long Position
{
get => stream.Position;
set => stream.Position = value;
}
public override int Read(byte[] buffer, int offset, int count)
{
return stream.Read(buffer, offset, count);
}
public override long Seek(long offset, SeekOrigin origin)
{
return stream.Seek(offset, origin);
}
public override void SetLength(long value)
{
stream.SetLength(value);
}
public override void Write(byte[] buffer, int offset, int count)
{
stream.Write(buffer, offset, count);
}
}
}

View File

@@ -19,7 +19,6 @@ namespace SharpCompress.Test
protected void Write(CompressionType compressionType, string archive, string archiveToVerifyAgainst)
{
ResetScratch();
using (Stream stream = File.OpenWrite(Path.Combine(SCRATCH2_FILES_PATH, archive))) {
WriterOptions writerOptions = new WriterOptions(compressionType)
{

View File

@@ -5,6 +5,7 @@ using SharpCompress.Archives;
using SharpCompress.Common;
using SharpCompress.Readers;
using SharpCompress.Readers.Zip;
using SharpCompress.Test.Mocks;
using SharpCompress.Writers;
using SharpCompress.Writers.Zip;
using Xunit;
@@ -100,7 +101,6 @@ namespace SharpCompress.Test.Zip
public void RunSingleTest(long files, long filesize, bool set_zip64, bool forward_only, long write_chunk_size = 1024 * 1024, string filename = "zip64-test.zip")
{
ResetScratch();
filename = Path.Combine(SCRATCH2_FILES_PATH, filename);
if (File.Exists(filename))
@@ -134,7 +134,7 @@ namespace SharpCompress.Test.Zip
var eo = new ZipWriterEntryOptions() { DeflateCompressionLevel = Compressors.Deflate.CompressionLevel.None };
using (var zip = File.OpenWrite(filename))
using(var st = forward_only ? (Stream)new NonSeekableStream(zip) : zip)
using(var st = forward_only ? (Stream)new ForwardOnlyStream(zip) : zip)
using (var zipWriter = (ZipWriter)WriterFactory.Open(st, ArchiveType.Zip, opts))
{
@@ -187,38 +187,5 @@ namespace SharpCompress.Test.Zip
);
}
}
/// <summary>
/// Helper to create non-seekable streams from filestream
/// </summary>
private class NonSeekableStream : Stream
{
private readonly Stream stream;
public NonSeekableStream(Stream s) { stream = s; }
public override bool CanRead => stream.CanRead;
public override bool CanSeek => false;
public override bool CanWrite => stream.CanWrite;
public override long Length => throw new NotImplementedException();
public override long Position { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public override void Flush() { stream.Flush(); }
public override int Read(byte[] buffer, int offset, int count)
{ return stream.Read(buffer, offset, count); }
public override int ReadByte()
{ return stream.ReadByte(); }
public override long Seek(long offset, SeekOrigin origin)
{ throw new NotImplementedException(); }
public override void SetLength(long value)
{ throw new NotImplementedException(); }
public override void Write(byte[] buffer, int offset, int count)
{ stream.Write(buffer, offset, count); }
public override void WriteByte(byte value)
{ stream.WriteByte(value); }
}
}
}

Some files were not shown because too many files have changed in this diff Show More