mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-02-09 13:34:58 +00:00
Compare commits
29 Commits
adam/aweso
...
adam/multi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2e301becb4 | ||
|
|
2e7d4eb74b | ||
|
|
5b2030bb98 | ||
|
|
c1169539ea | ||
|
|
8d2463f575 | ||
|
|
af7e270b2d | ||
|
|
1984da6997 | ||
|
|
9433e06b93 | ||
|
|
a92aaa51d5 | ||
|
|
d41908adeb | ||
|
|
81ca15b567 | ||
|
|
b81d0fd730 | ||
|
|
3a1bb187e8 | ||
|
|
3fee14a070 | ||
|
|
5bf789ac65 | ||
|
|
be06049db3 | ||
|
|
a0435f6a60 | ||
|
|
2321e2c90b | ||
|
|
97e98d8629 | ||
|
|
d96e7362d2 | ||
|
|
7dd46fe5ed | ||
|
|
04c044cb2b | ||
|
|
cc10a12fbc | ||
|
|
8b0a1c699f | ||
|
|
0fe48c647e | ||
|
|
434ce05416 | ||
|
|
51237a34eb | ||
|
|
4c61628078 | ||
|
|
4536fddec2 |
@@ -3,7 +3,7 @@
|
||||
"isRoot": true,
|
||||
"tools": {
|
||||
"csharpier": {
|
||||
"version": "1.1.2",
|
||||
"version": "1.2.1",
|
||||
"commands": [
|
||||
"csharpier"
|
||||
],
|
||||
|
||||
2
.github/workflows/dotnetcore.yml
vendored
2
.github/workflows/dotnetcore.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
os: [windows-latest, ubuntu-latest]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-dotnet@v5
|
||||
with:
|
||||
dotnet-version: 8.0.x
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project>
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="Bullseye" Version="6.0.0" />
|
||||
<PackageVersion Include="AwesomeAssertions" Version="9.2.1" />
|
||||
<PackageVersion Include="AwesomeAssertions" Version="9.3.0" />
|
||||
<PackageVersion Include="Glob" Version="1.1.9" />
|
||||
<PackageVersion Include="JetBrains.Profiler.SelfApi" Version="2.5.14" />
|
||||
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="8.0.0" />
|
||||
|
||||
@@ -172,4 +172,9 @@ public abstract class AbstractArchive<TEntry, TVolume> : IArchive, IArchiveExtra
|
||||
return Entries.All(x => x.IsComplete);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool IsMultiVolume =>
|
||||
_sourceStream?.Files.Count > 1 || _sourceStream?.Streams.Count > 1;
|
||||
|
||||
public virtual bool SupportsMultiThreading => false;
|
||||
}
|
||||
|
||||
@@ -45,4 +45,14 @@ public interface IArchive : IDisposable
|
||||
/// The total size of the files as uncompressed in the archive.
|
||||
/// </summary>
|
||||
long TotalUncompressSize { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Is the archive part of a multi-volume set.
|
||||
/// </summary>
|
||||
bool IsMultiVolume { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Does the archive support multi-threaded extraction.
|
||||
/// </summary>
|
||||
bool SupportsMultiThreading { get; }
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ public static class IArchiveEntryExtensions
|
||||
entry,
|
||||
destinationDirectory,
|
||||
options,
|
||||
(x, opt) => entry.WriteToFileAsync(x, opt, cancellationToken),
|
||||
entry.WriteToFileAsync,
|
||||
cancellationToken
|
||||
);
|
||||
|
||||
@@ -124,10 +124,11 @@ public static class IArchiveEntryExtensions
|
||||
entry,
|
||||
destinationFileName,
|
||||
options,
|
||||
async (x, fm) =>
|
||||
async (x, fm, ct) =>
|
||||
{
|
||||
using var fs = File.Open(destinationFileName, fm);
|
||||
await entry.WriteToAsync(fs, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
await entry.WriteToAsync(fs, ct).ConfigureAwait(false);
|
||||
},
|
||||
cancellationToken
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using SharpCompress.Common.Rar;
|
||||
using SharpCompress.Common.Rar.Headers;
|
||||
using SharpCompress.IO;
|
||||
using SharpCompress.Readers;
|
||||
|
||||
namespace SharpCompress.Archives.Rar;
|
||||
|
||||
/// <summary>
|
||||
/// A rar part based on a FileInfo object
|
||||
/// </summary>
|
||||
internal class FileInfoRarArchiveVolume : RarVolume
|
||||
{
|
||||
internal FileInfoRarArchiveVolume(FileInfo fileInfo, ReaderOptions options, int index)
|
||||
: base(StreamingMode.Seekable, fileInfo.OpenRead(), FixOptions(options), index)
|
||||
{
|
||||
FileInfo = fileInfo;
|
||||
FileParts = GetVolumeFileParts().ToArray().ToReadOnly();
|
||||
}
|
||||
|
||||
private static ReaderOptions FixOptions(ReaderOptions options)
|
||||
{
|
||||
//make sure we're closing streams with fileinfo
|
||||
options.LeaveStreamOpen = false;
|
||||
return options;
|
||||
}
|
||||
|
||||
internal ReadOnlyCollection<RarFilePart> FileParts { get; }
|
||||
|
||||
internal FileInfo FileInfo { get; }
|
||||
|
||||
internal override RarFilePart CreateFilePart(MarkHeader markHeader, FileHeader fileHeader) =>
|
||||
new FileInfoRarFilePart(this, ReaderOptions.Password, markHeader, fileHeader, FileInfo);
|
||||
|
||||
internal override IEnumerable<RarFilePart> ReadFileParts() => FileParts;
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
using System.IO;
|
||||
using SharpCompress.Common.Rar.Headers;
|
||||
|
||||
namespace SharpCompress.Archives.Rar;
|
||||
|
||||
internal sealed class FileInfoRarFilePart : SeekableFilePart
|
||||
{
|
||||
internal FileInfoRarFilePart(
|
||||
FileInfoRarArchiveVolume volume,
|
||||
string? password,
|
||||
MarkHeader mh,
|
||||
FileHeader fh,
|
||||
FileInfo fi
|
||||
)
|
||||
: base(mh, fh, volume.Index, volume.Stream, password) => FileInfo = fi;
|
||||
|
||||
internal FileInfo FileInfo { get; }
|
||||
|
||||
internal override string FilePartName =>
|
||||
"Rar File: " + FileInfo.FullName + " File Entry: " + FileHeader.FileName;
|
||||
}
|
||||
@@ -47,9 +47,9 @@ public class RarArchive : AbstractArchive<RarArchiveEntry, RarVolume>
|
||||
{
|
||||
sourceStream.LoadAllParts(); //request all streams
|
||||
var streams = sourceStream.Streams.ToArray();
|
||||
var i = 0;
|
||||
if (streams.Length > 1 && IsRarFile(streams[1], ReaderOptions)) //test part 2 - true = multipart not split
|
||||
{
|
||||
var i = 0;
|
||||
sourceStream.IsVolumes = true;
|
||||
streams[1].Position = 0;
|
||||
sourceStream.Position = 0;
|
||||
@@ -57,12 +57,18 @@ public class RarArchive : AbstractArchive<RarArchiveEntry, RarVolume>
|
||||
return sourceStream.Streams.Select(a => new StreamRarArchiveVolume(
|
||||
a,
|
||||
ReaderOptions,
|
||||
i++
|
||||
i++,
|
||||
IsMultiVolume
|
||||
));
|
||||
}
|
||||
|
||||
//split mode or single file
|
||||
return new StreamRarArchiveVolume(sourceStream, ReaderOptions, i++).AsEnumerable();
|
||||
return new StreamRarArchiveVolume(
|
||||
sourceStream,
|
||||
ReaderOptions,
|
||||
0,
|
||||
IsMultiVolume
|
||||
).AsEnumerable();
|
||||
}
|
||||
|
||||
protected override IReader CreateReaderForSolidExtraction()
|
||||
@@ -83,6 +89,7 @@ public class RarArchive : AbstractArchive<RarArchiveEntry, RarVolume>
|
||||
}
|
||||
|
||||
public override bool IsSolid => Volumes.First().IsSolidArchive;
|
||||
public override bool SupportsMultiThreading => !IsMultiVolume && !IsSolid;
|
||||
|
||||
public virtual int MinVersion => Volumes.First().MinVersion;
|
||||
public virtual int MaxVersion => Volumes.First().MaxVersion;
|
||||
|
||||
@@ -134,4 +134,6 @@ public class RarArchiveEntry : RarEntry, IArchiveEntry
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool SupportsMultiThreading => Parts.Single().SupportsMultiThreading;
|
||||
}
|
||||
|
||||
@@ -1,25 +1,29 @@
|
||||
using System.IO;
|
||||
using SharpCompress.Common.Rar;
|
||||
using SharpCompress.Common.Rar.Headers;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Archives.Rar;
|
||||
|
||||
internal class SeekableFilePart : RarFilePart
|
||||
internal class SeekableRarFilePart : RarFilePart
|
||||
{
|
||||
private readonly Stream _stream;
|
||||
private readonly string? _password;
|
||||
private readonly bool _isMultiVolume;
|
||||
|
||||
internal SeekableFilePart(
|
||||
internal SeekableRarFilePart(
|
||||
MarkHeader mh,
|
||||
FileHeader fh,
|
||||
int index,
|
||||
Stream stream,
|
||||
string? password
|
||||
string? password,
|
||||
bool isMultiVolume
|
||||
)
|
||||
: base(mh, fh, index)
|
||||
{
|
||||
_stream = stream;
|
||||
_password = password;
|
||||
_isMultiVolume = isMultiVolume;
|
||||
}
|
||||
|
||||
internal override Stream GetCompressedStream()
|
||||
@@ -42,4 +46,7 @@ internal class SeekableFilePart : RarFilePart
|
||||
}
|
||||
|
||||
internal override string FilePartName => "Unknown Stream - File Entry: " + FileHeader.FileName;
|
||||
|
||||
public override bool SupportsMultiThreading =>
|
||||
!_isMultiVolume && _stream is SourceStream ss && ss.IsFileMode && ss.Files.Count == 1;
|
||||
}
|
||||
@@ -9,11 +9,28 @@ namespace SharpCompress.Archives.Rar;
|
||||
|
||||
internal class StreamRarArchiveVolume : RarVolume
|
||||
{
|
||||
internal StreamRarArchiveVolume(Stream stream, ReaderOptions options, int index)
|
||||
: base(StreamingMode.Seekable, stream, options, index) { }
|
||||
private readonly bool _isMultiVolume;
|
||||
|
||||
internal StreamRarArchiveVolume(
|
||||
Stream stream,
|
||||
ReaderOptions options,
|
||||
int index,
|
||||
bool isMultiVolume
|
||||
)
|
||||
: base(StreamingMode.Seekable, stream, options, index)
|
||||
{
|
||||
_isMultiVolume = isMultiVolume;
|
||||
}
|
||||
|
||||
internal override IEnumerable<RarFilePart> ReadFileParts() => GetVolumeFileParts();
|
||||
|
||||
internal override RarFilePart CreateFilePart(MarkHeader markHeader, FileHeader fileHeader) =>
|
||||
new SeekableFilePart(markHeader, fileHeader, Index, Stream, ReaderOptions.Password);
|
||||
new SeekableRarFilePart(
|
||||
markHeader,
|
||||
fileHeader,
|
||||
Index,
|
||||
Stream,
|
||||
ReaderOptions.Password,
|
||||
_isMultiVolume
|
||||
);
|
||||
}
|
||||
|
||||
@@ -283,7 +283,12 @@ public class ZipArchive : AbstractWritableArchive<ZipArchiveEntry, ZipVolume>
|
||||
|
||||
yield return new ZipArchiveEntry(
|
||||
this,
|
||||
new SeekableZipFilePart(headerFactory.NotNull(), deh, s)
|
||||
new SeekableZipFilePart(
|
||||
headerFactory.NotNull(),
|
||||
deh,
|
||||
s,
|
||||
IsMultiVolume
|
||||
)
|
||||
);
|
||||
}
|
||||
break;
|
||||
@@ -385,4 +390,6 @@ public class ZipArchive : AbstractWritableArchive<ZipArchiveEntry, ZipVolume>
|
||||
((IStreamStack)stream).StackSeek(0);
|
||||
return ZipReader.Open(stream, ReaderOptions, Entries);
|
||||
}
|
||||
|
||||
public override bool SupportsMultiThreading => !IsMultiVolume;
|
||||
}
|
||||
|
||||
@@ -23,5 +23,7 @@ public class ZipArchiveEntry : ZipEntry, IArchiveEntry
|
||||
|
||||
public bool IsComplete => true;
|
||||
|
||||
public override bool SupportsMultiThreading => Parts.Single().SupportsMultiThreading;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace SharpCompress.Common.Arc
|
||||
return value switch
|
||||
{
|
||||
1 or 2 => CompressionType.None,
|
||||
3 => CompressionType.RLE90,
|
||||
3 => CompressionType.Packed,
|
||||
4 => CompressionType.Squeezed,
|
||||
5 or 6 or 7 or 8 => CompressionType.Crunched,
|
||||
9 => CompressionType.Squashed,
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace SharpCompress.Common.Arc
|
||||
Header.CompressedSize
|
||||
);
|
||||
break;
|
||||
case CompressionType.RLE90:
|
||||
case CompressionType.Packed:
|
||||
compressedStream = new RunLength90Stream(
|
||||
_stream,
|
||||
(int)Header.CompressedSize
|
||||
@@ -54,6 +54,14 @@ namespace SharpCompress.Common.Arc
|
||||
compressedStream = new SqueezeStream(_stream, (int)Header.CompressedSize);
|
||||
break;
|
||||
case CompressionType.Crunched:
|
||||
if (Header.OriginalSize > 128 * 1024)
|
||||
{
|
||||
throw new NotSupportedException(
|
||||
"CompressionMethod: "
|
||||
+ Header.CompressionMethod
|
||||
+ " with size > 128KB"
|
||||
);
|
||||
}
|
||||
compressedStream = new ArcLzwStream(
|
||||
_stream,
|
||||
(int)Header.CompressedSize,
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace SharpCompress.Common.Arj
|
||||
case CompressionMethod.CompressedMost:
|
||||
case CompressionMethod.Compressed:
|
||||
case CompressionMethod.CompressedFaster:
|
||||
if (Header.CompressedSize > 128 * 1024)
|
||||
if (Header.OriginalSize > 128 * 1024)
|
||||
{
|
||||
throw new NotSupportedException(
|
||||
"CompressionMethod: "
|
||||
|
||||
@@ -23,7 +23,7 @@ public enum CompressionType
|
||||
Reduce4,
|
||||
Explode,
|
||||
Squeezed,
|
||||
RLE90,
|
||||
Packed,
|
||||
Crunched,
|
||||
Squashed,
|
||||
Crushed,
|
||||
|
||||
@@ -87,4 +87,5 @@ public abstract class Entry : IEntry
|
||||
/// Entry file attribute.
|
||||
/// </summary>
|
||||
public virtual int? Attrib => throw new NotImplementedException();
|
||||
public virtual bool SupportsMultiThreading => false;
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ internal static class ExtractionMethods
|
||||
IEntry entry,
|
||||
string destinationDirectory,
|
||||
ExtractionOptions? options,
|
||||
Func<string, ExtractionOptions?, Task> writeAsync,
|
||||
Func<string, ExtractionOptions?, CancellationToken, Task> writeAsync,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
@@ -189,7 +189,7 @@ internal static class ExtractionMethods
|
||||
"Entry is trying to write a file outside of the destination directory."
|
||||
);
|
||||
}
|
||||
await writeAsync(destinationFileName, options).ConfigureAwait(false);
|
||||
await writeAsync(destinationFileName, options, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else if (options.ExtractFullPath && !Directory.Exists(destinationFileName))
|
||||
{
|
||||
@@ -201,7 +201,7 @@ internal static class ExtractionMethods
|
||||
IEntry entry,
|
||||
string destinationFileName,
|
||||
ExtractionOptions? options,
|
||||
Func<string, FileMode, Task> openAndWriteAsync,
|
||||
Func<string, FileMode, CancellationToken, Task> openAndWriteAsync,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
@@ -225,7 +225,8 @@ internal static class ExtractionMethods
|
||||
fm = FileMode.CreateNew;
|
||||
}
|
||||
|
||||
await openAndWriteAsync(destinationFileName, fm).ConfigureAwait(false);
|
||||
await openAndWriteAsync(destinationFileName, fm, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
entry.PreserveExtractionOptions(destinationFileName, options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,4 +14,6 @@ public abstract class FilePart
|
||||
internal abstract Stream? GetCompressedStream();
|
||||
internal abstract Stream? GetRawStream();
|
||||
internal bool Skipped { get; set; }
|
||||
|
||||
public virtual bool SupportsMultiThreading => false;
|
||||
}
|
||||
|
||||
@@ -21,4 +21,5 @@ public interface IEntry
|
||||
DateTime? LastModifiedTime { get; }
|
||||
long Size { get; }
|
||||
int? Attrib { get; }
|
||||
bool SupportsMultiThreading { get; }
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.IO;
|
||||
using SharpCompress.Common.Zip.Headers;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Common.Zip;
|
||||
|
||||
@@ -7,13 +8,19 @@ internal class SeekableZipFilePart : ZipFilePart
|
||||
{
|
||||
private bool _isLocalHeaderLoaded;
|
||||
private readonly SeekableZipHeaderFactory _headerFactory;
|
||||
private readonly bool _isMultiVolume;
|
||||
|
||||
internal SeekableZipFilePart(
|
||||
SeekableZipHeaderFactory headerFactory,
|
||||
DirectoryEntryHeader header,
|
||||
Stream stream
|
||||
Stream stream,
|
||||
bool isMultiVolume
|
||||
)
|
||||
: base(header, stream) => _headerFactory = headerFactory;
|
||||
: base(header, stream)
|
||||
{
|
||||
_headerFactory = headerFactory;
|
||||
_isMultiVolume = isMultiVolume;
|
||||
}
|
||||
|
||||
internal override Stream GetCompressedStream()
|
||||
{
|
||||
@@ -30,8 +37,20 @@ internal class SeekableZipFilePart : ZipFilePart
|
||||
|
||||
protected override Stream CreateBaseStream()
|
||||
{
|
||||
if (!_isMultiVolume && BaseStream is SourceStream ss)
|
||||
{
|
||||
if (ss.IsFileMode && ss.Files.Count == 1)
|
||||
{
|
||||
var fileStream = ss.CurrentFile.OpenRead();
|
||||
fileStream.Position = Header.DataStartPosition.NotNull();
|
||||
return fileStream;
|
||||
}
|
||||
}
|
||||
BaseStream.Position = Header.DataStartPosition.NotNull();
|
||||
|
||||
return BaseStream;
|
||||
}
|
||||
|
||||
public override bool SupportsMultiThreading =>
|
||||
!_isMultiVolume && BaseStream is SourceStream ss && ss.IsFileMode && ss.Files.Count == 1;
|
||||
}
|
||||
|
||||
@@ -62,10 +62,6 @@ internal sealed class MultiVolumeReadOnlyStream : Stream, IStreamStack
|
||||
base.Dispose(disposing);
|
||||
if (disposing)
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(MultiVolumeReadOnlyStream));
|
||||
#endif
|
||||
|
||||
if (filePartEnumerator != null)
|
||||
{
|
||||
filePartEnumerator.Dispose();
|
||||
|
||||
@@ -82,9 +82,6 @@ internal class RarStream : Stream, IStreamStack
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(RarStream));
|
||||
#endif
|
||||
ArrayPool<byte>.Shared.Return(this.tmpBuffer);
|
||||
this.tmpBuffer = null;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ public class SourceStream : Stream, IStreamStack
|
||||
#endif
|
||||
int IStreamStack.DefaultBufferSize { get; set; }
|
||||
|
||||
Stream IStreamStack.BaseStream() => _streams[_stream];
|
||||
Stream IStreamStack.BaseStream() => _streams[_streamIndex];
|
||||
|
||||
int IStreamStack.BufferSize
|
||||
{
|
||||
@@ -35,7 +35,7 @@ public class SourceStream : Stream, IStreamStack
|
||||
private readonly List<Stream> _streams;
|
||||
private readonly Func<int, FileInfo?>? _getFilePart;
|
||||
private readonly Func<int, Stream?>? _getStreamPart;
|
||||
private int _stream;
|
||||
private int _streamIndex;
|
||||
|
||||
public SourceStream(FileInfo file, Func<int, FileInfo?> getPart, ReaderOptions options)
|
||||
: this(null, null, file, getPart, options) { }
|
||||
@@ -59,7 +59,7 @@ public class SourceStream : Stream, IStreamStack
|
||||
|
||||
if (!IsFileMode)
|
||||
{
|
||||
_streams.Add(stream!);
|
||||
_streams.Add(stream.NotNull("stream is null"));
|
||||
_getStreamPart = getStreamPart;
|
||||
_getFilePart = _ => null;
|
||||
if (stream is FileStream fileStream)
|
||||
@@ -69,12 +69,12 @@ public class SourceStream : Stream, IStreamStack
|
||||
}
|
||||
else
|
||||
{
|
||||
_files.Add(file!);
|
||||
_files.Add(file.NotNull("file is null"));
|
||||
_streams.Add(_files[0].OpenRead());
|
||||
_getFilePart = getFilePart;
|
||||
_getStreamPart = _ => null;
|
||||
}
|
||||
_stream = 0;
|
||||
_streamIndex = 0;
|
||||
_prevSize = 0;
|
||||
|
||||
#if DEBUG_STREAMS
|
||||
@@ -93,10 +93,12 @@ public class SourceStream : Stream, IStreamStack
|
||||
public ReaderOptions ReaderOptions { get; }
|
||||
public bool IsFileMode { get; }
|
||||
|
||||
public IEnumerable<FileInfo> Files => _files;
|
||||
public IEnumerable<Stream> Streams => _streams;
|
||||
public IReadOnlyList<FileInfo> Files => _files;
|
||||
public IReadOnlyList<Stream> Streams => _streams;
|
||||
|
||||
private Stream Current => _streams[_stream];
|
||||
private Stream Current => _streams[_streamIndex];
|
||||
|
||||
public FileInfo CurrentFile => _files[_streamIndex];
|
||||
|
||||
public bool LoadStream(int index) //ensure all parts to id are loaded
|
||||
{
|
||||
@@ -107,7 +109,7 @@ public class SourceStream : Stream, IStreamStack
|
||||
var f = _getFilePart.NotNull("GetFilePart is null")(_streams.Count);
|
||||
if (f == null)
|
||||
{
|
||||
_stream = _streams.Count - 1;
|
||||
_streamIndex = _streams.Count - 1;
|
||||
return false;
|
||||
}
|
||||
//throw new Exception($"File part {idx} not available.");
|
||||
@@ -119,7 +121,7 @@ public class SourceStream : Stream, IStreamStack
|
||||
var s = _getStreamPart.NotNull("GetStreamPart is null")(_streams.Count);
|
||||
if (s == null)
|
||||
{
|
||||
_stream = _streams.Count - 1;
|
||||
_streamIndex = _streams.Count - 1;
|
||||
return false;
|
||||
}
|
||||
//throw new Exception($"Stream part {idx} not available.");
|
||||
@@ -137,10 +139,10 @@ public class SourceStream : Stream, IStreamStack
|
||||
{
|
||||
if (LoadStream(idx))
|
||||
{
|
||||
_stream = idx;
|
||||
_streamIndex = idx;
|
||||
}
|
||||
|
||||
return _stream == idx;
|
||||
return _streamIndex == idx;
|
||||
}
|
||||
|
||||
public override bool CanRead => true;
|
||||
@@ -184,7 +186,7 @@ public class SourceStream : Stream, IStreamStack
|
||||
var length = Current.Length;
|
||||
|
||||
// Load next file if present
|
||||
if (!SetStream(_stream + 1))
|
||||
if (!SetStream(_streamIndex + 1))
|
||||
{
|
||||
break;
|
||||
}
|
||||
@@ -223,7 +225,7 @@ public class SourceStream : Stream, IStreamStack
|
||||
while (_prevSize + Current.Length < pos)
|
||||
{
|
||||
_prevSize += Current.Length;
|
||||
SetStream(_stream + 1);
|
||||
SetStream(_streamIndex + 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,7 +275,7 @@ public class SourceStream : Stream, IStreamStack
|
||||
var length = Current.Length;
|
||||
|
||||
// Load next file if present
|
||||
if (!SetStream(_stream + 1))
|
||||
if (!SetStream(_streamIndex + 1))
|
||||
{
|
||||
break;
|
||||
}
|
||||
@@ -322,7 +324,7 @@ public class SourceStream : Stream, IStreamStack
|
||||
var length = Current.Length;
|
||||
|
||||
// Load next file if present
|
||||
if (!SetStream(_stream + 1))
|
||||
if (!SetStream(_streamIndex + 1))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ public static class IReaderExtensions
|
||||
reader.Entry,
|
||||
destinationDirectory,
|
||||
options,
|
||||
(fileName, opts) => reader.WriteEntryToFileAsync(fileName, opts, cancellationToken),
|
||||
reader.WriteEntryToFileAsync,
|
||||
cancellationToken
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
@@ -101,10 +101,10 @@ public static class IReaderExtensions
|
||||
reader.Entry,
|
||||
destinationFileName,
|
||||
options,
|
||||
async (x, fm) =>
|
||||
async (x, fm, ct) =>
|
||||
{
|
||||
using var fs = File.Open(destinationFileName, fm);
|
||||
await reader.WriteEntryToAsync(fs, cancellationToken).ConfigureAwait(false);
|
||||
await reader.WriteEntryToAsync(fs, ct).ConfigureAwait(false);
|
||||
},
|
||||
cancellationToken
|
||||
)
|
||||
|
||||
@@ -48,7 +48,29 @@ internal class ZipCentralDirectoryEntry
|
||||
var decompressedvalue = zip64 ? uint.MaxValue : (uint)Decompressed;
|
||||
var headeroffsetvalue = zip64 ? uint.MaxValue : (uint)HeaderOffset;
|
||||
var extralength = zip64 ? (2 + 2 + 8 + 8 + 8 + 4) : 0;
|
||||
var version = (byte)(zip64 ? 45 : 20); // Version 20 required for deflate/encryption
|
||||
|
||||
// Determine version needed to extract:
|
||||
// - Version 63 for LZMA, PPMd, BZip2, ZStandard (advanced compression methods)
|
||||
// - Version 45 for Zip64 extensions (when Zip64HeaderOffset != 0 or actual sizes require it)
|
||||
// - Version 20 for standard Deflate/None compression
|
||||
byte version;
|
||||
if (
|
||||
compression == ZipCompressionMethod.LZMA
|
||||
|| compression == ZipCompressionMethod.PPMd
|
||||
|| compression == ZipCompressionMethod.BZip2
|
||||
|| compression == ZipCompressionMethod.ZStandard
|
||||
)
|
||||
{
|
||||
version = 63;
|
||||
}
|
||||
else if (zip64 || Zip64HeaderOffset != 0)
|
||||
{
|
||||
version = 45;
|
||||
}
|
||||
else
|
||||
{
|
||||
version = 20;
|
||||
}
|
||||
|
||||
var flags = Equals(archiveEncoding.GetEncoding(), Encoding.UTF8)
|
||||
? HeaderFlags.Efs
|
||||
|
||||
@@ -27,5 +27,22 @@ namespace SharpCompress.Test.Arc
|
||||
|
||||
[Fact]
|
||||
public void Arc_Crunched_Read() => Read("Arc.crunched.arc");
|
||||
|
||||
[Theory]
|
||||
[InlineData("Arc.crunched.largefile.arc", CompressionType.Crunched)]
|
||||
public void Arc_LargeFile_ShouldThrow(string fileName, CompressionType compressionType)
|
||||
{
|
||||
var exception = Assert.Throws<NotSupportedException>(() =>
|
||||
ReadForBufferBoundaryCheck(fileName, compressionType)
|
||||
);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("Arc.uncompressed.largefile.arc", CompressionType.None)]
|
||||
[InlineData("Arc.squeezed.largefile.arc", CompressionType.Squeezed)]
|
||||
public void Arc_LargeFileTest_Read(string fileName, CompressionType compressionType)
|
||||
{
|
||||
ReadForBufferBoundaryCheck(fileName, compressionType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,6 +134,7 @@ public class ArchiveTests : ReaderTests
|
||||
{
|
||||
foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory))
|
||||
{
|
||||
Assert.False(entry.SupportsMultiThreading);
|
||||
entry.WriteToDirectory(
|
||||
SCRATCH_FILES_PATH,
|
||||
new ExtractionOptions { ExtractFullPath = true, Overwrite = true }
|
||||
@@ -266,6 +267,31 @@ public class ArchiveTests : ReaderTests
|
||||
VerifyFiles();
|
||||
}
|
||||
|
||||
protected async Task ArchiveFileRead_Multithreaded(
|
||||
IArchiveFactory archiveFactory,
|
||||
string testArchive,
|
||||
ReaderOptions? readerOptions = null
|
||||
)
|
||||
{
|
||||
testArchive = Path.Combine(TEST_ARCHIVES_PATH, testArchive);
|
||||
var tasks = new List<Task>();
|
||||
using (var archive = archiveFactory.Open(new FileInfo(testArchive), readerOptions))
|
||||
{
|
||||
Assert.True(archive.SupportsMultiThreading);
|
||||
foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory))
|
||||
{
|
||||
Assert.True(entry.SupportsMultiThreading);
|
||||
var t = entry.WriteToDirectoryAsync(
|
||||
SCRATCH_FILES_PATH,
|
||||
new ExtractionOptions { ExtractFullPath = true, Overwrite = true }
|
||||
);
|
||||
tasks.Add(t);
|
||||
}
|
||||
}
|
||||
await Task.WhenAll(tasks);
|
||||
VerifyFiles();
|
||||
}
|
||||
|
||||
protected void ArchiveFileRead(
|
||||
IArchiveFactory archiveFactory,
|
||||
string testArchive,
|
||||
@@ -289,6 +315,11 @@ public class ArchiveTests : ReaderTests
|
||||
protected void ArchiveFileRead(string testArchive, ReaderOptions? readerOptions = null) =>
|
||||
ArchiveFileRead(ArchiveFactory.AutoFactory, testArchive, readerOptions);
|
||||
|
||||
protected Task ArchiveFileRead_Multithreaded(
|
||||
string testArchive,
|
||||
ReaderOptions? readerOptions = null
|
||||
) => ArchiveFileRead_Multithreaded(ArchiveFactory.AutoFactory, testArchive, readerOptions);
|
||||
|
||||
protected void ArchiveFileSkip(
|
||||
string testArchive,
|
||||
string fileOrder,
|
||||
|
||||
@@ -45,16 +45,14 @@ namespace SharpCompress.Test.Arj
|
||||
public void Arj_Multi_Reader()
|
||||
{
|
||||
var exception = Assert.Throws<MultiVolumeExtractionException>(() =>
|
||||
DoArj_Multi_Reader(
|
||||
[
|
||||
"Arj.store.split.arj",
|
||||
"Arj.store.split.a01",
|
||||
"Arj.store.split.a02",
|
||||
"Arj.store.split.a03",
|
||||
"Arj.store.split.a04",
|
||||
"Arj.store.split.a05",
|
||||
]
|
||||
)
|
||||
DoArj_Multi_Reader([
|
||||
"Arj.store.split.arj",
|
||||
"Arj.store.split.a01",
|
||||
"Arj.store.split.a02",
|
||||
"Arj.store.split.a03",
|
||||
"Arj.store.split.a04",
|
||||
"Arj.store.split.a05",
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
@@ -65,7 +63,7 @@ namespace SharpCompress.Test.Arj
|
||||
public void Arj_LargeFile_ShouldThrow(string fileName, CompressionType compressionType)
|
||||
{
|
||||
var exception = Assert.Throws<NotSupportedException>(() =>
|
||||
Arj_LargeFileTest_Read(fileName, compressionType)
|
||||
ReadForBufferBoundaryCheck(fileName, compressionType)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -74,24 +72,7 @@ namespace SharpCompress.Test.Arj
|
||||
[InlineData("Arj.method4.largefile.arj", CompressionType.ArjLZ77)]
|
||||
public void Arj_LargeFileTest_Read(string fileName, CompressionType compressionType)
|
||||
{
|
||||
using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, fileName)))
|
||||
using (
|
||||
var reader = ReaderFactory.Open(stream, new ReaderOptions { LookForHeader = true })
|
||||
)
|
||||
{
|
||||
while (reader.MoveToNextEntry())
|
||||
{
|
||||
Assert.Equal(compressionType, reader.Entry.CompressionType);
|
||||
reader.WriteEntryToDirectory(
|
||||
SCRATCH_FILES_PATH,
|
||||
new ExtractionOptions { ExtractFullPath = true, Overwrite = true }
|
||||
);
|
||||
}
|
||||
}
|
||||
CompareFilesByPath(
|
||||
Path.Combine(SCRATCH_FILES_PATH, "news.txt"),
|
||||
Path.Combine(MISC_TEST_FILES_PATH, "news.txt")
|
||||
);
|
||||
ReadForBufferBoundaryCheck(fileName, compressionType);
|
||||
}
|
||||
|
||||
private void DoArj_Multi_Reader(string[] archives)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Archives.Rar;
|
||||
using SharpCompress.Common;
|
||||
@@ -292,9 +293,15 @@ public class RarArchiveTests : ArchiveTests
|
||||
[Fact]
|
||||
public void Rar_ArchiveFileRead() => ArchiveFileRead("Rar.rar");
|
||||
|
||||
[Fact]
|
||||
public Task Rar_ArchiveFileRead_Multithreaded() => ArchiveFileRead_Multithreaded("Rar.rar");
|
||||
|
||||
[Fact]
|
||||
public void Rar5_ArchiveFileRead() => ArchiveFileRead("Rar5.rar");
|
||||
|
||||
[Fact]
|
||||
public Task Rar5_ArchiveFileRead_Multithreaded() => ArchiveFileRead_Multithreaded("Rar5.rar");
|
||||
|
||||
[Fact]
|
||||
public void Rar_ArchiveFileRead_HasDirectories() =>
|
||||
DoRar_ArchiveFileRead_HasDirectories("Rar.rar");
|
||||
@@ -359,6 +366,9 @@ public class RarArchiveTests : ArchiveTests
|
||||
[Fact]
|
||||
public void Rar2_ArchiveFileRead() => ArchiveFileRead("Rar2.rar");
|
||||
|
||||
[Fact]
|
||||
public Task Rar2_ArchiveFileRead_Multithreaded() => ArchiveFileRead_Multithreaded("Rar2.rar");
|
||||
|
||||
[Fact]
|
||||
public void Rar15_ArchiveFileRead()
|
||||
{
|
||||
|
||||
@@ -15,29 +15,25 @@ public class RarReaderAsyncTests : ReaderTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task Rar_Multi_Reader_Async() =>
|
||||
await DoRar_Multi_Reader_Async(
|
||||
[
|
||||
"Rar.multi.part01.rar",
|
||||
"Rar.multi.part02.rar",
|
||||
"Rar.multi.part03.rar",
|
||||
"Rar.multi.part04.rar",
|
||||
"Rar.multi.part05.rar",
|
||||
"Rar.multi.part06.rar",
|
||||
]
|
||||
);
|
||||
await DoRar_Multi_Reader_Async([
|
||||
"Rar.multi.part01.rar",
|
||||
"Rar.multi.part02.rar",
|
||||
"Rar.multi.part03.rar",
|
||||
"Rar.multi.part04.rar",
|
||||
"Rar.multi.part05.rar",
|
||||
"Rar.multi.part06.rar",
|
||||
]);
|
||||
|
||||
[Fact]
|
||||
public async Task Rar5_Multi_Reader_Async() =>
|
||||
await DoRar_Multi_Reader_Async(
|
||||
[
|
||||
"Rar5.multi.part01.rar",
|
||||
"Rar5.multi.part02.rar",
|
||||
"Rar5.multi.part03.rar",
|
||||
"Rar5.multi.part04.rar",
|
||||
"Rar5.multi.part05.rar",
|
||||
"Rar5.multi.part06.rar",
|
||||
]
|
||||
);
|
||||
await DoRar_Multi_Reader_Async([
|
||||
"Rar5.multi.part01.rar",
|
||||
"Rar5.multi.part02.rar",
|
||||
"Rar5.multi.part03.rar",
|
||||
"Rar5.multi.part04.rar",
|
||||
"Rar5.multi.part05.rar",
|
||||
"Rar5.multi.part06.rar",
|
||||
]);
|
||||
|
||||
private async Task DoRar_Multi_Reader_Async(string[] archives)
|
||||
{
|
||||
@@ -95,29 +91,25 @@ public class RarReaderAsyncTests : ReaderTests
|
||||
|
||||
[Fact]
|
||||
public async Task Rar_Multi_Reader_Delete_Files_Async() =>
|
||||
await DoRar_Multi_Reader_Delete_Files_Async(
|
||||
[
|
||||
"Rar.multi.part01.rar",
|
||||
"Rar.multi.part02.rar",
|
||||
"Rar.multi.part03.rar",
|
||||
"Rar.multi.part04.rar",
|
||||
"Rar.multi.part05.rar",
|
||||
"Rar.multi.part06.rar",
|
||||
]
|
||||
);
|
||||
await DoRar_Multi_Reader_Delete_Files_Async([
|
||||
"Rar.multi.part01.rar",
|
||||
"Rar.multi.part02.rar",
|
||||
"Rar.multi.part03.rar",
|
||||
"Rar.multi.part04.rar",
|
||||
"Rar.multi.part05.rar",
|
||||
"Rar.multi.part06.rar",
|
||||
]);
|
||||
|
||||
[Fact]
|
||||
public async Task Rar5_Multi_Reader_Delete_Files_Async() =>
|
||||
await DoRar_Multi_Reader_Delete_Files_Async(
|
||||
[
|
||||
"Rar5.multi.part01.rar",
|
||||
"Rar5.multi.part02.rar",
|
||||
"Rar5.multi.part03.rar",
|
||||
"Rar5.multi.part04.rar",
|
||||
"Rar5.multi.part05.rar",
|
||||
"Rar5.multi.part06.rar",
|
||||
]
|
||||
);
|
||||
await DoRar_Multi_Reader_Delete_Files_Async([
|
||||
"Rar5.multi.part01.rar",
|
||||
"Rar5.multi.part02.rar",
|
||||
"Rar5.multi.part03.rar",
|
||||
"Rar5.multi.part04.rar",
|
||||
"Rar5.multi.part05.rar",
|
||||
"Rar5.multi.part06.rar",
|
||||
]);
|
||||
|
||||
private async Task DoRar_Multi_Reader_Delete_Files_Async(string[] archives)
|
||||
{
|
||||
|
||||
@@ -14,29 +14,25 @@ public class RarReaderTests : ReaderTests
|
||||
{
|
||||
[Fact]
|
||||
public void Rar_Multi_Reader() =>
|
||||
DoRar_Multi_Reader(
|
||||
[
|
||||
"Rar.multi.part01.rar",
|
||||
"Rar.multi.part02.rar",
|
||||
"Rar.multi.part03.rar",
|
||||
"Rar.multi.part04.rar",
|
||||
"Rar.multi.part05.rar",
|
||||
"Rar.multi.part06.rar",
|
||||
]
|
||||
);
|
||||
DoRar_Multi_Reader([
|
||||
"Rar.multi.part01.rar",
|
||||
"Rar.multi.part02.rar",
|
||||
"Rar.multi.part03.rar",
|
||||
"Rar.multi.part04.rar",
|
||||
"Rar.multi.part05.rar",
|
||||
"Rar.multi.part06.rar",
|
||||
]);
|
||||
|
||||
[Fact]
|
||||
public void Rar5_Multi_Reader() =>
|
||||
DoRar_Multi_Reader(
|
||||
[
|
||||
"Rar5.multi.part01.rar",
|
||||
"Rar5.multi.part02.rar",
|
||||
"Rar5.multi.part03.rar",
|
||||
"Rar5.multi.part04.rar",
|
||||
"Rar5.multi.part05.rar",
|
||||
"Rar5.multi.part06.rar",
|
||||
]
|
||||
);
|
||||
DoRar_Multi_Reader([
|
||||
"Rar5.multi.part01.rar",
|
||||
"Rar5.multi.part02.rar",
|
||||
"Rar5.multi.part03.rar",
|
||||
"Rar5.multi.part04.rar",
|
||||
"Rar5.multi.part05.rar",
|
||||
"Rar5.multi.part06.rar",
|
||||
]);
|
||||
|
||||
private void DoRar_Multi_Reader(string[] archives)
|
||||
{
|
||||
@@ -61,16 +57,14 @@ public class RarReaderTests : ReaderTests
|
||||
|
||||
[Fact]
|
||||
public void Rar_Multi_Reader_Encrypted() =>
|
||||
DoRar_Multi_Reader_Encrypted(
|
||||
[
|
||||
"Rar.EncryptedParts.part01.rar",
|
||||
"Rar.EncryptedParts.part02.rar",
|
||||
"Rar.EncryptedParts.part03.rar",
|
||||
"Rar.EncryptedParts.part04.rar",
|
||||
"Rar.EncryptedParts.part05.rar",
|
||||
"Rar.EncryptedParts.part06.rar",
|
||||
]
|
||||
);
|
||||
DoRar_Multi_Reader_Encrypted([
|
||||
"Rar.EncryptedParts.part01.rar",
|
||||
"Rar.EncryptedParts.part02.rar",
|
||||
"Rar.EncryptedParts.part03.rar",
|
||||
"Rar.EncryptedParts.part04.rar",
|
||||
"Rar.EncryptedParts.part05.rar",
|
||||
"Rar.EncryptedParts.part06.rar",
|
||||
]);
|
||||
|
||||
private void DoRar_Multi_Reader_Encrypted(string[] archives) =>
|
||||
Assert.Throws<InvalidFormatException>(() =>
|
||||
@@ -97,29 +91,25 @@ public class RarReaderTests : ReaderTests
|
||||
|
||||
[Fact]
|
||||
public void Rar_Multi_Reader_Delete_Files() =>
|
||||
DoRar_Multi_Reader_Delete_Files(
|
||||
[
|
||||
"Rar.multi.part01.rar",
|
||||
"Rar.multi.part02.rar",
|
||||
"Rar.multi.part03.rar",
|
||||
"Rar.multi.part04.rar",
|
||||
"Rar.multi.part05.rar",
|
||||
"Rar.multi.part06.rar",
|
||||
]
|
||||
);
|
||||
DoRar_Multi_Reader_Delete_Files([
|
||||
"Rar.multi.part01.rar",
|
||||
"Rar.multi.part02.rar",
|
||||
"Rar.multi.part03.rar",
|
||||
"Rar.multi.part04.rar",
|
||||
"Rar.multi.part05.rar",
|
||||
"Rar.multi.part06.rar",
|
||||
]);
|
||||
|
||||
[Fact]
|
||||
public void Rar5_Multi_Reader_Delete_Files() =>
|
||||
DoRar_Multi_Reader_Delete_Files(
|
||||
[
|
||||
"Rar5.multi.part01.rar",
|
||||
"Rar5.multi.part02.rar",
|
||||
"Rar5.multi.part03.rar",
|
||||
"Rar5.multi.part04.rar",
|
||||
"Rar5.multi.part05.rar",
|
||||
"Rar5.multi.part06.rar",
|
||||
]
|
||||
);
|
||||
DoRar_Multi_Reader_Delete_Files([
|
||||
"Rar5.multi.part01.rar",
|
||||
"Rar5.multi.part02.rar",
|
||||
"Rar5.multi.part03.rar",
|
||||
"Rar5.multi.part04.rar",
|
||||
"Rar5.multi.part05.rar",
|
||||
"Rar5.multi.part06.rar",
|
||||
]);
|
||||
|
||||
private void DoRar_Multi_Reader_Delete_Files(string[] archives)
|
||||
{
|
||||
@@ -407,16 +397,14 @@ public class RarReaderTests : ReaderTests
|
||||
Path.Combine("exe", "test.exe"),
|
||||
}
|
||||
);
|
||||
using var reader = RarReader.Open(
|
||||
[
|
||||
Path.Combine(TEST_ARCHIVES_PATH, "Rar.multi.part01.rar"),
|
||||
Path.Combine(TEST_ARCHIVES_PATH, "Rar.multi.part02.rar"),
|
||||
Path.Combine(TEST_ARCHIVES_PATH, "Rar.multi.part03.rar"),
|
||||
Path.Combine(TEST_ARCHIVES_PATH, "Rar.multi.part04.rar"),
|
||||
Path.Combine(TEST_ARCHIVES_PATH, "Rar.multi.part05.rar"),
|
||||
Path.Combine(TEST_ARCHIVES_PATH, "Rar.multi.part06.rar"),
|
||||
]
|
||||
);
|
||||
using var reader = RarReader.Open([
|
||||
Path.Combine(TEST_ARCHIVES_PATH, "Rar.multi.part01.rar"),
|
||||
Path.Combine(TEST_ARCHIVES_PATH, "Rar.multi.part02.rar"),
|
||||
Path.Combine(TEST_ARCHIVES_PATH, "Rar.multi.part03.rar"),
|
||||
Path.Combine(TEST_ARCHIVES_PATH, "Rar.multi.part04.rar"),
|
||||
Path.Combine(TEST_ARCHIVES_PATH, "Rar.multi.part05.rar"),
|
||||
Path.Combine(TEST_ARCHIVES_PATH, "Rar.multi.part06.rar"),
|
||||
]);
|
||||
while (reader.MoveToNextEntry())
|
||||
{
|
||||
Assert.Equal(expectedOrder.Pop(), reader.Entry.Key);
|
||||
|
||||
@@ -176,6 +176,27 @@ public abstract class ReaderTests : TestBase
|
||||
}
|
||||
}
|
||||
|
||||
protected void ReadForBufferBoundaryCheck(string fileName, CompressionType compressionType)
|
||||
{
|
||||
using var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, fileName));
|
||||
using var reader = ReaderFactory.Open(stream, new ReaderOptions { LookForHeader = true });
|
||||
|
||||
while (reader.MoveToNextEntry())
|
||||
{
|
||||
Assert.Equal(compressionType, reader.Entry.CompressionType);
|
||||
|
||||
reader.WriteEntryToDirectory(
|
||||
SCRATCH_FILES_PATH,
|
||||
new ExtractionOptions { ExtractFullPath = true, Overwrite = true }
|
||||
);
|
||||
}
|
||||
|
||||
CompareFilesByPath(
|
||||
Path.Combine(SCRATCH_FILES_PATH, "alice29.txt"),
|
||||
Path.Combine(MISC_TEST_FILES_PATH, "alice29.txt")
|
||||
);
|
||||
}
|
||||
|
||||
protected void Iterate(
|
||||
string testArchive,
|
||||
string fileOrder,
|
||||
|
||||
@@ -74,9 +74,21 @@ public class XzIndexAsyncTests : XzTestsBase
|
||||
public async Task SkipsPaddingAsync()
|
||||
{
|
||||
// Index with 3-byte padding.
|
||||
using Stream badStream = new MemoryStream(
|
||||
[0x00, 0x01, 0x10, 0x80, 0x01, 0x00, 0x00, 0x00, 0xB1, 0x01, 0xD9, 0xC9, 0xFF]
|
||||
);
|
||||
using Stream badStream = new MemoryStream([
|
||||
0x00,
|
||||
0x01,
|
||||
0x10,
|
||||
0x80,
|
||||
0x01,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0xB1,
|
||||
0x01,
|
||||
0xD9,
|
||||
0xC9,
|
||||
0xFF,
|
||||
]);
|
||||
var br = new BinaryReader(badStream);
|
||||
var index = new XZIndex(br, false);
|
||||
await index.ProcessAsync().ConfigureAwait(false);
|
||||
|
||||
@@ -71,9 +71,21 @@ public class XzIndexTests : XzTestsBase
|
||||
public void SkipsPadding()
|
||||
{
|
||||
// Index with 3-byte padding.
|
||||
using Stream badStream = new MemoryStream(
|
||||
[0x00, 0x01, 0x10, 0x80, 0x01, 0x00, 0x00, 0x00, 0xB1, 0x01, 0xD9, 0xC9, 0xFF]
|
||||
);
|
||||
using Stream badStream = new MemoryStream([
|
||||
0x00,
|
||||
0x01,
|
||||
0x10,
|
||||
0x80,
|
||||
0x01,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0xB1,
|
||||
0x01,
|
||||
0xD9,
|
||||
0xC9,
|
||||
0xFF,
|
||||
]);
|
||||
var br = new BinaryReader(badStream);
|
||||
var index = new XZIndex(br, false);
|
||||
index.Process();
|
||||
|
||||
441
tests/SharpCompress.Test/Zip/Zip64VersionConsistencyTests.cs
Normal file
441
tests/SharpCompress.Test/Zip/Zip64VersionConsistencyTests.cs
Normal file
@@ -0,0 +1,441 @@
|
||||
using System;
|
||||
using System.Buffers.Binary;
|
||||
using System.IO;
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Archives.Zip;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Writers;
|
||||
using SharpCompress.Writers.Zip;
|
||||
using Xunit;
|
||||
|
||||
namespace SharpCompress.Test.Zip;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for verifying version consistency between Local File Header (LFH)
|
||||
/// and Central Directory File Header (CDFH) when using Zip64.
|
||||
/// </summary>
|
||||
public class Zip64VersionConsistencyTests : WriterTests
|
||||
{
|
||||
public Zip64VersionConsistencyTests()
|
||||
: base(ArchiveType.Zip) { }
|
||||
|
||||
[Fact]
|
||||
public void Zip64_Small_File_With_UseZip64_Should_Have_Matching_Versions()
|
||||
{
|
||||
// Create a zip with UseZip64=true but with a small file
|
||||
var filename = Path.Combine(SCRATCH2_FILES_PATH, "zip64_version_test.zip");
|
||||
|
||||
if (File.Exists(filename))
|
||||
{
|
||||
File.Delete(filename);
|
||||
}
|
||||
|
||||
// Create archive with UseZip64=true
|
||||
WriterOptions writerOptions = new ZipWriterOptions(CompressionType.Deflate)
|
||||
{
|
||||
LeaveStreamOpen = false,
|
||||
UseZip64 = true,
|
||||
};
|
||||
|
||||
ZipArchive zipArchive = ZipArchive.Create();
|
||||
zipArchive.AddEntry("empty", new MemoryStream());
|
||||
zipArchive.SaveTo(filename, writerOptions);
|
||||
|
||||
// Now read the raw bytes to verify version consistency
|
||||
using var fs = File.OpenRead(filename);
|
||||
using var br = new BinaryReader(fs);
|
||||
|
||||
// Read Local File Header
|
||||
var lfhSignature = br.ReadUInt32();
|
||||
Assert.Equal(0x04034b50u, lfhSignature); // Local file header signature
|
||||
|
||||
var lfhVersion = br.ReadUInt16();
|
||||
|
||||
// Skip to Central Directory
|
||||
// Find Central Directory by searching from the end
|
||||
fs.Seek(-22, SeekOrigin.End); // Min EOCD size
|
||||
var eocdSignature = br.ReadUInt32();
|
||||
|
||||
if (eocdSignature != 0x06054b50u)
|
||||
{
|
||||
// Might have Zip64 EOCD, search backwards
|
||||
fs.Seek(-100, SeekOrigin.End);
|
||||
var buffer = new byte[100];
|
||||
fs.Read(buffer, 0, 100);
|
||||
|
||||
// Find EOCD signature
|
||||
for (int i = buffer.Length - 4; i >= 0; i--)
|
||||
{
|
||||
if (BinaryPrimitives.ReadUInt32LittleEndian(buffer.AsSpan(i)) == 0x06054b50u)
|
||||
{
|
||||
fs.Seek(-100 + i, SeekOrigin.End);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Read EOCD
|
||||
fs.Seek(-22, SeekOrigin.End);
|
||||
br.ReadUInt32(); // EOCD signature
|
||||
br.ReadUInt16(); // disk number
|
||||
br.ReadUInt16(); // disk with central dir
|
||||
br.ReadUInt16(); // entries on this disk
|
||||
br.ReadUInt16(); // total entries
|
||||
br.ReadUInt32(); // central directory size (unused)
|
||||
var cdOffset = br.ReadUInt32();
|
||||
|
||||
// If Zip64, need to read from Zip64 EOCD
|
||||
if (cdOffset == 0xFFFFFFFF)
|
||||
{
|
||||
// Find Zip64 EOCD Locator
|
||||
fs.Seek(-22 - 20, SeekOrigin.End);
|
||||
var z64eocdlSig = br.ReadUInt32();
|
||||
if (z64eocdlSig == 0x07064b50u)
|
||||
{
|
||||
br.ReadUInt32(); // disk number
|
||||
var z64eocdOffset = br.ReadUInt64();
|
||||
br.ReadUInt32(); // total disks
|
||||
|
||||
// Read Zip64 EOCD
|
||||
fs.Seek((long)z64eocdOffset, SeekOrigin.Begin);
|
||||
br.ReadUInt32(); // signature
|
||||
br.ReadUInt64(); // size of EOCD64
|
||||
br.ReadUInt16(); // version made by
|
||||
br.ReadUInt16(); // version needed
|
||||
br.ReadUInt32(); // disk number
|
||||
br.ReadUInt32(); // disk with CD
|
||||
br.ReadUInt64(); // entries on disk
|
||||
br.ReadUInt64(); // total entries
|
||||
br.ReadUInt64(); // CD size
|
||||
cdOffset = (uint)br.ReadUInt64(); // CD offset
|
||||
}
|
||||
}
|
||||
|
||||
// Read Central Directory Header
|
||||
fs.Seek(cdOffset, SeekOrigin.Begin);
|
||||
var cdhSignature = br.ReadUInt32();
|
||||
Assert.Equal(0x02014b50u, cdhSignature); // Central directory header signature
|
||||
|
||||
br.ReadUInt16(); // version made by
|
||||
var cdhVersionNeeded = br.ReadUInt16();
|
||||
|
||||
// The versions should match when UseZip64 is true
|
||||
Assert.Equal(lfhVersion, cdhVersionNeeded);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Zip64_Small_File_Without_UseZip64_Should_Have_Version_20()
|
||||
{
|
||||
// Create a zip without UseZip64
|
||||
var filename = Path.Combine(SCRATCH2_FILES_PATH, "no_zip64_version_test.zip");
|
||||
|
||||
if (File.Exists(filename))
|
||||
{
|
||||
File.Delete(filename);
|
||||
}
|
||||
|
||||
// Create archive without UseZip64
|
||||
WriterOptions writerOptions = new ZipWriterOptions(CompressionType.Deflate)
|
||||
{
|
||||
LeaveStreamOpen = false,
|
||||
UseZip64 = false,
|
||||
};
|
||||
|
||||
ZipArchive zipArchive = ZipArchive.Create();
|
||||
zipArchive.AddEntry("empty", new MemoryStream());
|
||||
zipArchive.SaveTo(filename, writerOptions);
|
||||
|
||||
// Read the raw bytes
|
||||
using var fs = File.OpenRead(filename);
|
||||
using var br = new BinaryReader(fs);
|
||||
|
||||
// Read Local File Header version
|
||||
var lfhSignature = br.ReadUInt32();
|
||||
Assert.Equal(0x04034b50u, lfhSignature);
|
||||
var lfhVersion = br.ReadUInt16();
|
||||
|
||||
// Read Central Directory Header version
|
||||
fs.Seek(-22, SeekOrigin.End);
|
||||
br.ReadUInt32(); // EOCD signature
|
||||
br.ReadUInt16(); // disk number
|
||||
br.ReadUInt16(); // disk with central dir
|
||||
br.ReadUInt16(); // entries on this disk
|
||||
br.ReadUInt16(); // total entries
|
||||
br.ReadUInt32(); // CD size
|
||||
var cdOffset = br.ReadUInt32();
|
||||
|
||||
fs.Seek(cdOffset, SeekOrigin.Begin);
|
||||
var cdhSignature = br.ReadUInt32();
|
||||
Assert.Equal(0x02014b50u, cdhSignature);
|
||||
br.ReadUInt16(); // version made by
|
||||
var cdhVersionNeeded = br.ReadUInt16();
|
||||
|
||||
// Both should be version 20 (or less)
|
||||
Assert.True(lfhVersion <= 20);
|
||||
Assert.Equal(lfhVersion, cdhVersionNeeded);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LZMA_Compression_Should_Use_Version_63()
|
||||
{
|
||||
// Create a zip with LZMA compression
|
||||
var filename = Path.Combine(SCRATCH2_FILES_PATH, "lzma_version_test.zip");
|
||||
|
||||
if (File.Exists(filename))
|
||||
{
|
||||
File.Delete(filename);
|
||||
}
|
||||
|
||||
WriterOptions writerOptions = new ZipWriterOptions(CompressionType.LZMA)
|
||||
{
|
||||
LeaveStreamOpen = false,
|
||||
UseZip64 = false,
|
||||
};
|
||||
|
||||
ZipArchive zipArchive = ZipArchive.Create();
|
||||
var data = new byte[100];
|
||||
new Random(42).NextBytes(data);
|
||||
zipArchive.AddEntry("test.bin", new MemoryStream(data));
|
||||
zipArchive.SaveTo(filename, writerOptions);
|
||||
|
||||
// Read the raw bytes
|
||||
using var fs = File.OpenRead(filename);
|
||||
using var br = new BinaryReader(fs);
|
||||
|
||||
// Read Local File Header version
|
||||
var lfhSignature = br.ReadUInt32();
|
||||
Assert.Equal(0x04034b50u, lfhSignature);
|
||||
var lfhVersion = br.ReadUInt16();
|
||||
|
||||
// Read Central Directory Header version
|
||||
fs.Seek(-22, SeekOrigin.End);
|
||||
br.ReadUInt32(); // EOCD signature
|
||||
br.ReadUInt16(); // disk number
|
||||
br.ReadUInt16(); // disk with central dir
|
||||
br.ReadUInt16(); // entries on this disk
|
||||
br.ReadUInt16(); // total entries
|
||||
br.ReadUInt32(); // CD size
|
||||
var cdOffset = br.ReadUInt32();
|
||||
|
||||
fs.Seek(cdOffset, SeekOrigin.Begin);
|
||||
var cdhSignature = br.ReadUInt32();
|
||||
Assert.Equal(0x02014b50u, cdhSignature);
|
||||
br.ReadUInt16(); // version made by
|
||||
var cdhVersionNeeded = br.ReadUInt16();
|
||||
|
||||
// Both should be version 63 for LZMA
|
||||
Assert.Equal(63, lfhVersion);
|
||||
Assert.Equal(lfhVersion, cdhVersionNeeded);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PPMd_Compression_Should_Use_Version_63()
|
||||
{
|
||||
// Create a zip with PPMd compression
|
||||
var filename = Path.Combine(SCRATCH2_FILES_PATH, "ppmd_version_test.zip");
|
||||
|
||||
if (File.Exists(filename))
|
||||
{
|
||||
File.Delete(filename);
|
||||
}
|
||||
|
||||
WriterOptions writerOptions = new ZipWriterOptions(CompressionType.PPMd)
|
||||
{
|
||||
LeaveStreamOpen = false,
|
||||
UseZip64 = false,
|
||||
};
|
||||
|
||||
ZipArchive zipArchive = ZipArchive.Create();
|
||||
var data = new byte[100];
|
||||
new Random(42).NextBytes(data);
|
||||
zipArchive.AddEntry("test.bin", new MemoryStream(data));
|
||||
zipArchive.SaveTo(filename, writerOptions);
|
||||
|
||||
// Read the raw bytes
|
||||
using var fs = File.OpenRead(filename);
|
||||
using var br = new BinaryReader(fs);
|
||||
|
||||
// Read Local File Header version
|
||||
var lfhSignature = br.ReadUInt32();
|
||||
Assert.Equal(0x04034b50u, lfhSignature);
|
||||
var lfhVersion = br.ReadUInt16();
|
||||
|
||||
// Read Central Directory Header version
|
||||
fs.Seek(-22, SeekOrigin.End);
|
||||
br.ReadUInt32(); // EOCD signature
|
||||
br.ReadUInt16(); // disk number
|
||||
br.ReadUInt16(); // disk with central dir
|
||||
br.ReadUInt16(); // entries on this disk
|
||||
br.ReadUInt16(); // total entries
|
||||
br.ReadUInt32(); // CD size
|
||||
var cdOffset = br.ReadUInt32();
|
||||
|
||||
fs.Seek(cdOffset, SeekOrigin.Begin);
|
||||
var cdhSignature = br.ReadUInt32();
|
||||
Assert.Equal(0x02014b50u, cdhSignature);
|
||||
br.ReadUInt16(); // version made by
|
||||
var cdhVersionNeeded = br.ReadUInt16();
|
||||
|
||||
// Both should be version 63 for PPMd
|
||||
Assert.Equal(63, lfhVersion);
|
||||
Assert.Equal(lfhVersion, cdhVersionNeeded);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Zip64_Multiple_Small_Files_With_UseZip64_Should_Have_Matching_Versions()
|
||||
{
|
||||
// Create a zip with UseZip64=true but with multiple small files
|
||||
var filename = Path.Combine(SCRATCH2_FILES_PATH, "zip64_version_multiple_test.zip");
|
||||
|
||||
if (File.Exists(filename))
|
||||
{
|
||||
File.Delete(filename);
|
||||
}
|
||||
|
||||
WriterOptions writerOptions = new ZipWriterOptions(CompressionType.Deflate)
|
||||
{
|
||||
LeaveStreamOpen = false,
|
||||
UseZip64 = true,
|
||||
};
|
||||
|
||||
ZipArchive zipArchive = ZipArchive.Create();
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
var data = new byte[100];
|
||||
new Random(i).NextBytes(data);
|
||||
zipArchive.AddEntry($"file{i}.bin", new MemoryStream(data));
|
||||
}
|
||||
zipArchive.SaveTo(filename, writerOptions);
|
||||
|
||||
// Verify that all entries have matching versions
|
||||
using var fs = File.OpenRead(filename);
|
||||
using var br = new BinaryReader(fs);
|
||||
|
||||
// Read all LFH versions
|
||||
var lfhVersions = new System.Collections.Generic.List<ushort>();
|
||||
while (true)
|
||||
{
|
||||
var sig = br.ReadUInt32();
|
||||
if (sig == 0x04034b50u) // LFH signature
|
||||
{
|
||||
var version = br.ReadUInt16();
|
||||
lfhVersions.Add(version);
|
||||
|
||||
// Skip rest of LFH
|
||||
br.ReadUInt16(); // flags
|
||||
br.ReadUInt16(); // compression
|
||||
br.ReadUInt32(); // mod time
|
||||
br.ReadUInt32(); // crc
|
||||
br.ReadUInt32(); // compressed size
|
||||
br.ReadUInt32(); // uncompressed size
|
||||
var fnLen = br.ReadUInt16();
|
||||
var extraLen = br.ReadUInt16();
|
||||
fs.Seek(fnLen + extraLen, SeekOrigin.Current);
|
||||
|
||||
// Skip compressed data by reading compressed size from extra field if zip64
|
||||
// For simplicity in this test, we'll just find the next signature
|
||||
var found = false;
|
||||
|
||||
while (fs.Position < fs.Length - 4)
|
||||
{
|
||||
var b = br.ReadByte();
|
||||
if (b == 0x50)
|
||||
{
|
||||
var nextBytes = br.ReadBytes(3);
|
||||
if (
|
||||
(nextBytes[0] == 0x4b && nextBytes[1] == 0x03 && nextBytes[2] == 0x04)
|
||||
|| // LFH
|
||||
(nextBytes[0] == 0x4b && nextBytes[1] == 0x01 && nextBytes[2] == 0x02)
|
||||
) // CDH
|
||||
{
|
||||
fs.Seek(-4, SeekOrigin.Current);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (sig == 0x02014b50u) // CDH signature
|
||||
{
|
||||
break; // Reached central directory
|
||||
}
|
||||
else
|
||||
{
|
||||
break; // Unknown signature
|
||||
}
|
||||
}
|
||||
|
||||
// Find Central Directory
|
||||
fs.Seek(-22, SeekOrigin.End);
|
||||
br.ReadUInt32(); // EOCD signature
|
||||
br.ReadUInt16(); // disk number
|
||||
br.ReadUInt16(); // disk with central dir
|
||||
br.ReadUInt16(); // entries on this disk
|
||||
var totalEntries = br.ReadUInt16();
|
||||
br.ReadUInt32(); // CD size
|
||||
var cdOffset = br.ReadUInt32();
|
||||
|
||||
// Check if we need Zip64 EOCD
|
||||
if (cdOffset == 0xFFFFFFFF)
|
||||
{
|
||||
fs.Seek(-22 - 20, SeekOrigin.End);
|
||||
var z64eocdlSig = br.ReadUInt32();
|
||||
if (z64eocdlSig == 0x07064b50u)
|
||||
{
|
||||
br.ReadUInt32(); // disk number
|
||||
var z64eocdOffset = br.ReadUInt64();
|
||||
fs.Seek((long)z64eocdOffset, SeekOrigin.Begin);
|
||||
br.ReadUInt32(); // signature
|
||||
br.ReadUInt64(); // size
|
||||
br.ReadUInt16(); // version made by
|
||||
br.ReadUInt16(); // version needed
|
||||
br.ReadUInt32(); // disk number
|
||||
br.ReadUInt32(); // disk with CD
|
||||
br.ReadUInt64(); // entries on disk
|
||||
totalEntries = (ushort)br.ReadUInt64(); // total entries
|
||||
br.ReadUInt64(); // CD size
|
||||
cdOffset = (uint)br.ReadUInt64(); // CD offset
|
||||
}
|
||||
}
|
||||
|
||||
// Read CDH versions
|
||||
fs.Seek(cdOffset, SeekOrigin.Begin);
|
||||
var cdhVersions = new System.Collections.Generic.List<ushort>();
|
||||
for (int i = 0; i < totalEntries; i++)
|
||||
{
|
||||
var sig = br.ReadUInt32();
|
||||
Assert.Equal(0x02014b50u, sig);
|
||||
br.ReadUInt16(); // version made by
|
||||
var version = br.ReadUInt16();
|
||||
cdhVersions.Add(version);
|
||||
|
||||
// Skip rest of CDH
|
||||
br.ReadUInt16(); // flags
|
||||
br.ReadUInt16(); // compression
|
||||
br.ReadUInt32(); // mod time
|
||||
br.ReadUInt32(); // crc
|
||||
br.ReadUInt32(); // compressed size
|
||||
br.ReadUInt32(); // uncompressed size
|
||||
var fnLen = br.ReadUInt16();
|
||||
var extraLen = br.ReadUInt16();
|
||||
var commentLen = br.ReadUInt16();
|
||||
br.ReadUInt16(); // disk number start
|
||||
br.ReadUInt16(); // internal attributes
|
||||
br.ReadUInt32(); // external attributes
|
||||
br.ReadUInt32(); // LFH offset
|
||||
fs.Seek(fnLen + extraLen + commentLen, SeekOrigin.Current);
|
||||
}
|
||||
|
||||
// Verify all versions match
|
||||
Assert.Equal(lfhVersions.Count, cdhVersions.Count);
|
||||
for (int i = 0; i < lfhVersions.Count; i++)
|
||||
{
|
||||
Assert.Equal(lfhVersions[i], cdhVersions[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Archives.Zip;
|
||||
using SharpCompress.Common;
|
||||
@@ -88,6 +89,10 @@ public class ZipArchiveTests : ArchiveTests
|
||||
[Fact]
|
||||
public void Zip_Deflate_ArchiveFileRead() => ArchiveFileRead("Zip.deflate.zip");
|
||||
|
||||
[Fact]
|
||||
public Task Zip_Deflate_ArchiveFileRead_Multithreaded() =>
|
||||
ArchiveFileRead_Multithreaded("Zip.deflate.zip");
|
||||
|
||||
[Fact]
|
||||
public void Zip_Deflate_ArchiveExtractToDirectory() =>
|
||||
ArchiveExtractToDirectory("Zip.deflate.zip");
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
".NETFramework,Version=v4.8": {
|
||||
"AwesomeAssertions": {
|
||||
"type": "Direct",
|
||||
"requested": "[9.2.1, )",
|
||||
"resolved": "9.2.1",
|
||||
"contentHash": "lbwhyQNXxxEGx4oCbFqNfFy2DsywsvNhN6qoOjY4wwhMgI2L9+YrxjyF/M0io99yrvWV1Cjj12LP2QGcC43Uhw==",
|
||||
"requested": "[9.3.0, )",
|
||||
"resolved": "9.3.0",
|
||||
"contentHash": "8lGLYap2ec2gNLgjf2xKZaKLpQ7j36oJvrYzBVVpNAumqnxRdevqqhEF66qxE92f8y2+zsbQ061DeHG61ZhzaQ==",
|
||||
"dependencies": {
|
||||
"System.Threading.Tasks.Extensions": "4.5.4"
|
||||
}
|
||||
@@ -199,9 +199,9 @@
|
||||
"net8.0": {
|
||||
"AwesomeAssertions": {
|
||||
"type": "Direct",
|
||||
"requested": "[9.2.1, )",
|
||||
"resolved": "9.2.1",
|
||||
"contentHash": "lbwhyQNXxxEGx4oCbFqNfFy2DsywsvNhN6qoOjY4wwhMgI2L9+YrxjyF/M0io99yrvWV1Cjj12LP2QGcC43Uhw=="
|
||||
"requested": "[9.3.0, )",
|
||||
"resolved": "9.3.0",
|
||||
"contentHash": "8lGLYap2ec2gNLgjf2xKZaKLpQ7j36oJvrYzBVVpNAumqnxRdevqqhEF66qxE92f8y2+zsbQ061DeHG61ZhzaQ=="
|
||||
},
|
||||
"Microsoft.NET.Test.Sdk": {
|
||||
"type": "Direct",
|
||||
|
||||
BIN
tests/TestArchives/Archives/Arc.crunched.largefile.arc
Normal file
BIN
tests/TestArchives/Archives/Arc.crunched.largefile.arc
Normal file
Binary file not shown.
BIN
tests/TestArchives/Archives/Arc.squashed.largefile.arc
Normal file
BIN
tests/TestArchives/Archives/Arc.squashed.largefile.arc
Normal file
Binary file not shown.
BIN
tests/TestArchives/Archives/Arc.squeezed.largefile.arc
Normal file
BIN
tests/TestArchives/Archives/Arc.squeezed.largefile.arc
Normal file
Binary file not shown.
BIN
tests/TestArchives/Archives/Arc.uncompressed.largefile.arc
Normal file
BIN
tests/TestArchives/Archives/Arc.uncompressed.largefile.arc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
3609
tests/TestArchives/MiscTest/alice29.txt
Normal file
3609
tests/TestArchives/MiscTest/alice29.txt
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user