mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-02-04 05:25:00 +00:00
Compare commits
13 Commits
copilot/su
...
adam/memor
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
daacb93902 | ||
|
|
eb2f60fb53 | ||
|
|
bd0439c424 | ||
|
|
b935bcfaef | ||
|
|
56c22cee78 | ||
|
|
5c4b83e501 | ||
|
|
80ac10a5fe | ||
|
|
a92ce90252 | ||
|
|
e519f61f0f | ||
|
|
49f2271253 | ||
|
|
5b1d11bc1d | ||
|
|
aa3a40d968 | ||
|
|
6125654b2e |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -20,3 +20,7 @@ artifacts/
|
||||
|
||||
.DS_Store
|
||||
*.snupkg
|
||||
|
||||
# BenchmarkDotNet artifacts
|
||||
BenchmarkDotNet.Artifacts/
|
||||
**/BenchmarkDotNet.Artifacts/
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<Project>
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="BenchmarkDotNet" Version="0.14.0" />
|
||||
<PackageVersion Include="Bullseye" Version="6.1.0" />
|
||||
<PackageVersion Include="AwesomeAssertions" Version="9.3.0" />
|
||||
<PackageVersion Include="Glob" Version="1.1.9" />
|
||||
|
||||
@@ -48,6 +48,20 @@ public partial class RarArchive : AbstractArchive<RarArchiveEntry, RarVolume>, I
|
||||
}
|
||||
}
|
||||
|
||||
public override async ValueTask DisposeAsync()
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
if (UnpackV1.IsValueCreated && UnpackV1.Value is IDisposable unpackV1)
|
||||
{
|
||||
unpackV1.Dispose();
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
await base.DisposeAsync();
|
||||
}
|
||||
}
|
||||
|
||||
protected override IEnumerable<RarArchiveEntry> LoadEntries(IEnumerable<RarVolume> volumes) =>
|
||||
RarArchiveEntryFactory.GetEntries(this, volumes, ReaderOptions);
|
||||
|
||||
|
||||
@@ -51,11 +51,14 @@ namespace SharpCompress.Common
|
||||
return BinaryPrimitives.ReadUInt64LittleEndian(_buffer);
|
||||
}
|
||||
|
||||
public async ValueTask<byte[]> ReadBytesAsync(int count, CancellationToken ct = default)
|
||||
public async ValueTask ReadBytesAsync(byte[] bytes, int offset, int count, CancellationToken ct = default)
|
||||
{
|
||||
var result = new byte[count];
|
||||
await _stream.ReadExactAsync(result, 0, count, ct).ConfigureAwait(false);
|
||||
return result;
|
||||
await _stream.ReadExactAsync(bytes, offset, count, ct).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async ValueTask SkipAsync(int count, CancellationToken ct = default)
|
||||
{
|
||||
await _stream.SkipAsync( count, ct).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
||||
@@ -29,7 +29,8 @@ internal class DirectoryEndHeader : ZipHeader
|
||||
DirectorySize = await reader.ReadUInt32Async();
|
||||
DirectoryStartOffsetRelativeToDisk = await reader.ReadUInt32Async();
|
||||
CommentLength = await reader.ReadUInt16Async();
|
||||
Comment = await reader.ReadBytesAsync(CommentLength);
|
||||
Comment = new byte[CommentLength];
|
||||
await reader.ReadBytesAsync(Comment, 0, CommentLength);
|
||||
}
|
||||
|
||||
public ushort VolumeNumber { get; private set; }
|
||||
|
||||
@@ -53,10 +53,12 @@ internal class DirectoryEntryHeader : ZipFileEntry
|
||||
InternalFileAttributes = await reader.ReadUInt16Async();
|
||||
ExternalFileAttributes = await reader.ReadUInt32Async();
|
||||
RelativeOffsetOfEntryHeader = await reader.ReadUInt32Async();
|
||||
|
||||
var name = await reader.ReadBytesAsync(nameLength);
|
||||
var extra = await reader.ReadBytesAsync(extraLength);
|
||||
var comment = await reader.ReadBytesAsync(commentLength);
|
||||
var name = new byte[nameLength];
|
||||
var extra = new byte[extraLength];
|
||||
var comment = new byte[commentLength];
|
||||
await reader.ReadBytesAsync(name,0 ,nameLength);
|
||||
await reader.ReadBytesAsync(extra, 0, extraLength);
|
||||
await reader.ReadBytesAsync(comment, 0, commentLength);
|
||||
|
||||
ProcessReadData(name, extra, comment);
|
||||
}
|
||||
|
||||
@@ -37,8 +37,10 @@ internal class LocalEntryHeader(IArchiveEncoding archiveEncoding)
|
||||
UncompressedSize = await reader.ReadUInt32Async();
|
||||
var nameLength = await reader.ReadUInt16Async();
|
||||
var extraLength = await reader.ReadUInt16Async();
|
||||
var name = await reader.ReadBytesAsync(nameLength);
|
||||
var extra = await reader.ReadBytesAsync(extraLength);
|
||||
var name = new byte[nameLength];
|
||||
var extra = new byte[extraLength];
|
||||
await reader.ReadBytesAsync(name,0 ,nameLength);
|
||||
await reader.ReadBytesAsync(extra, 0, extraLength);
|
||||
|
||||
ProcessReadData(name, extra);
|
||||
}
|
||||
|
||||
@@ -38,12 +38,12 @@ internal class Zip64DirectoryEndHeader : ZipHeader
|
||||
TotalNumberOfEntries = (long)await reader.ReadUInt64Async();
|
||||
DirectorySize = (long)await reader.ReadUInt64Async();
|
||||
DirectoryStartOffsetRelativeToDisk = (long)await reader.ReadUInt64Async();
|
||||
DataSector = await reader.ReadBytesAsync(
|
||||
(int)(
|
||||
SizeOfDirectoryEndRecord
|
||||
- SIZE_OF_FIXED_HEADER_DATA_EXCEPT_SIGNATURE_AND_SIZE_FIELDS
|
||||
)
|
||||
var size = (int)(
|
||||
SizeOfDirectoryEndRecord
|
||||
- SIZE_OF_FIXED_HEADER_DATA_EXCEPT_SIGNATURE_AND_SIZE_FIELDS
|
||||
);
|
||||
DataSector = new byte[size];
|
||||
await reader.ReadBytesAsync(DataSector, 0, size);
|
||||
}
|
||||
|
||||
private const int SIZE_OF_FIXED_HEADER_DATA_EXCEPT_SIGNATURE_AND_SIZE_FIELDS = 44;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
@@ -16,6 +17,8 @@ internal sealed class SeekableZipHeaderFactory : ZipHeaderFactory
|
||||
private const int MAX_SEARCH_LENGTH_FOR_EOCD = 65557;
|
||||
private bool _zip64;
|
||||
|
||||
private static readonly byte[] needle = { 0x06, 0x05, 0x4b, 0x50 };
|
||||
|
||||
internal SeekableZipHeaderFactory(string? password, IArchiveEncoding archiveEncoding)
|
||||
: base(StreamingMode.Seekable, password, archiveEncoding) { }
|
||||
|
||||
@@ -153,74 +156,8 @@ internal sealed class SeekableZipHeaderFactory : ZipHeaderFactory
|
||||
}
|
||||
}
|
||||
|
||||
internal async IAsyncEnumerable<ZipHeader> ReadSeekableHeaderAsync(Stream stream, bool useSync)
|
||||
{
|
||||
var reader = new AsyncBinaryReader(stream);
|
||||
|
||||
await SeekBackToHeaderAsync(stream, reader);
|
||||
|
||||
var eocd_location = stream.Position;
|
||||
var entry = new DirectoryEndHeader();
|
||||
await entry.Read(reader);
|
||||
|
||||
if (entry.IsZip64)
|
||||
{
|
||||
_zip64 = true;
|
||||
|
||||
// ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR should be before the EOCD
|
||||
stream.Seek(eocd_location - ZIP64_EOCD_LENGTH - 4, SeekOrigin.Begin);
|
||||
var zip64_locator = await reader.ReadUInt32Async();
|
||||
if (zip64_locator != ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR)
|
||||
{
|
||||
throw new ArchiveException("Failed to locate the Zip64 Directory Locator");
|
||||
}
|
||||
|
||||
var zip64Locator = new Zip64DirectoryEndLocatorHeader();
|
||||
await zip64Locator.Read(reader);
|
||||
|
||||
stream.Seek(zip64Locator.RelativeOffsetOfTheEndOfDirectoryRecord, SeekOrigin.Begin);
|
||||
var zip64Signature = await reader.ReadUInt32Async();
|
||||
if (zip64Signature != ZIP64_END_OF_CENTRAL_DIRECTORY)
|
||||
{
|
||||
throw new ArchiveException("Failed to locate the Zip64 Header");
|
||||
}
|
||||
|
||||
var zip64Entry = new Zip64DirectoryEndHeader();
|
||||
await zip64Entry.Read(reader);
|
||||
stream.Seek(zip64Entry.DirectoryStartOffsetRelativeToDisk, SeekOrigin.Begin);
|
||||
}
|
||||
else
|
||||
{
|
||||
stream.Seek(entry.DirectoryStartOffsetRelativeToDisk, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
var position = stream.Position;
|
||||
while (true)
|
||||
{
|
||||
stream.Position = position;
|
||||
var signature = await reader.ReadUInt32Async();
|
||||
var nextHeader = await ReadHeader(signature, reader, _zip64);
|
||||
position = stream.Position;
|
||||
|
||||
if (nextHeader is 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsMatch(byte[] haystack, int position, byte[] needle)
|
||||
private static bool IsMatch(Span<byte> haystack, int position, byte[] needle)
|
||||
{
|
||||
for (var i = 0; i < needle.Length; i++)
|
||||
{
|
||||
@@ -247,29 +184,35 @@ internal sealed class SeekableZipHeaderFactory : ZipHeaderFactory
|
||||
stream.Length < MAX_SEARCH_LENGTH_FOR_EOCD
|
||||
? (int)stream.Length
|
||||
: MAX_SEARCH_LENGTH_FOR_EOCD;
|
||||
// We search for marker in reverse to find the first occurance
|
||||
byte[] needle = { 0x06, 0x05, 0x4b, 0x50 };
|
||||
|
||||
stream.Seek(-len, SeekOrigin.End);
|
||||
var seek = ArrayPool<byte>.Shared.Rent(len);
|
||||
|
||||
var seek = await reader.ReadBytesAsync(len);
|
||||
|
||||
// Search in reverse
|
||||
Array.Reverse(seek);
|
||||
|
||||
// don't exclude the minimum eocd region, otherwise you fail to locate the header in empty zip files
|
||||
var max_search_area = len; // - MINIMUM_EOCD_LENGTH;
|
||||
|
||||
for (var pos_from_end = 0; pos_from_end < max_search_area; ++pos_from_end)
|
||||
try
|
||||
{
|
||||
if (IsMatch(seek, pos_from_end, needle))
|
||||
{
|
||||
stream.Seek(-pos_from_end, SeekOrigin.End);
|
||||
return;
|
||||
}
|
||||
}
|
||||
await reader.ReadBytesAsync(seek, 0, len, default);
|
||||
var memory = new Memory<byte>(seek, 0, len);
|
||||
var span = memory.Span;
|
||||
span.Reverse();
|
||||
|
||||
throw new ArchiveException("Failed to locate the Zip Header");
|
||||
// don't exclude the minimum eocd region, otherwise you fail to locate the header in empty zip files
|
||||
var max_search_area = len; // - MINIMUM_EOCD_LENGTH;
|
||||
|
||||
for (var pos_from_end = 0; pos_from_end < max_search_area; ++pos_from_end)
|
||||
{
|
||||
if (IsMatch(span, pos_from_end, needle))
|
||||
{
|
||||
stream.Seek(-pos_from_end, SeekOrigin.End);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw new ArchiveException("Failed to locate the Zip Header");
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(seek);
|
||||
}
|
||||
}
|
||||
|
||||
private static void SeekBackToHeader(Stream stream, BinaryReader reader)
|
||||
@@ -286,9 +229,6 @@ internal sealed class SeekableZipHeaderFactory : ZipHeaderFactory
|
||||
stream.Length < MAX_SEARCH_LENGTH_FOR_EOCD
|
||||
? (int)stream.Length
|
||||
: MAX_SEARCH_LENGTH_FOR_EOCD;
|
||||
// We search for marker in reverse to find the first occurance
|
||||
byte[] needle = { 0x06, 0x05, 0x4b, 0x50 };
|
||||
|
||||
stream.Seek(-len, SeekOrigin.End);
|
||||
|
||||
var seek = reader.ReadBytes(len);
|
||||
|
||||
@@ -79,7 +79,7 @@ internal class ZipHeaderFactory
|
||||
}
|
||||
else
|
||||
{
|
||||
await reader.ReadBytesAsync(zip64 ? 20 : 12);
|
||||
await reader.SkipAsync(zip64 ? 20 : 12);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using SharpCompress.Algorithms;
|
||||
|
||||
namespace SharpCompress.Compressors.Deflate;
|
||||
@@ -116,14 +117,14 @@ internal sealed class InflateBlocks
|
||||
internal int readAt; // window read pointer
|
||||
internal int table; // table lengths (14 bits)
|
||||
internal int[] tb = new int[1]; // bit length decoding tree
|
||||
internal byte[] window; // sliding window
|
||||
internal IMemoryOwner<byte> window; // sliding window
|
||||
internal int writeAt; // window write pointer
|
||||
|
||||
internal InflateBlocks(ZlibCodec codec, object checkfn, int w)
|
||||
{
|
||||
_codec = codec;
|
||||
hufts = new int[MANY * 3];
|
||||
window = new byte[w];
|
||||
window = MemoryPool<byte>.Shared.Rent(w);
|
||||
end = w;
|
||||
this.checkfn = checkfn;
|
||||
mode = InflateBlockMode.TYPE;
|
||||
@@ -340,7 +341,7 @@ internal sealed class InflateBlocks
|
||||
{
|
||||
t = m;
|
||||
}
|
||||
Array.Copy(_codec.InputBuffer, p, window, q, t);
|
||||
_codec.InputBuffer.AsSpan(p, t).CopyTo(window.Memory.Span.Slice(q));
|
||||
p += t;
|
||||
n -= t;
|
||||
q += t;
|
||||
@@ -715,13 +716,14 @@ internal sealed class InflateBlocks
|
||||
internal void Free()
|
||||
{
|
||||
Reset();
|
||||
window?.Dispose();
|
||||
window = null;
|
||||
hufts = null;
|
||||
}
|
||||
|
||||
internal void SetDictionary(byte[] d, int start, int n)
|
||||
{
|
||||
Array.Copy(d, start, window, 0, n);
|
||||
d.AsSpan(start, n).CopyTo(window.Memory.Span.Slice(0, n));
|
||||
readAt = writeAt = n;
|
||||
}
|
||||
|
||||
@@ -774,11 +776,11 @@ internal sealed class InflateBlocks
|
||||
// update check information
|
||||
if (checkfn != null)
|
||||
{
|
||||
_codec._adler32 = check = Adler32.Calculate(check, window.AsSpan(readAt, nBytes));
|
||||
_codec._adler32 = check = Adler32.Calculate(check, window.Memory.Span.Slice(readAt, nBytes));
|
||||
}
|
||||
|
||||
// copy as far as end of window
|
||||
Array.Copy(window, readAt, _codec.OutputBuffer, _codec.NextOut, nBytes);
|
||||
window.Memory.Span.Slice(readAt, nBytes).CopyTo(_codec.OutputBuffer.AsSpan(_codec.NextOut));
|
||||
_codec.NextOut += nBytes;
|
||||
readAt += nBytes;
|
||||
|
||||
@@ -1213,7 +1215,7 @@ internal sealed class InflateCodes
|
||||
}
|
||||
}
|
||||
|
||||
blocks.window[q++] = blocks.window[f++];
|
||||
blocks.window.Memory.Span[q++] = blocks.window.Memory.Span[f++];
|
||||
m--;
|
||||
|
||||
if (f == blocks.end)
|
||||
@@ -1259,7 +1261,7 @@ internal sealed class InflateCodes
|
||||
}
|
||||
r = ZlibConstants.Z_OK;
|
||||
|
||||
blocks.window[q++] = (byte)lit;
|
||||
blocks.window.Memory.Span[q++] = (byte)lit;
|
||||
m--;
|
||||
|
||||
mode = START;
|
||||
@@ -1396,7 +1398,7 @@ internal sealed class InflateCodes
|
||||
b >>= (tp[tp_index_t_3 + 1]);
|
||||
k -= (tp[tp_index_t_3 + 1]);
|
||||
|
||||
s.window[q++] = (byte)tp[tp_index_t_3 + 2];
|
||||
s.window.Memory.Span[q++] = (byte)tp[tp_index_t_3 + 2];
|
||||
m--;
|
||||
continue;
|
||||
}
|
||||
@@ -1461,13 +1463,13 @@ internal sealed class InflateCodes
|
||||
r = q - d;
|
||||
if (q - r > 0 && 2 > (q - r))
|
||||
{
|
||||
s.window[q++] = s.window[r++]; // minimum count is three,
|
||||
s.window[q++] = s.window[r++]; // so unroll loop a little
|
||||
s.window.Memory.Span[q++] = s.window.Memory.Span[r++]; // minimum count is three,
|
||||
s.window.Memory.Span[q++] = s.window.Memory.Span[r++]; // so unroll loop a little
|
||||
c -= 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
Array.Copy(s.window, r, s.window, q, 2);
|
||||
s.window.Memory.Span.Slice(r, 2).CopyTo(s.window.Memory.Span.Slice(q));
|
||||
q += 2;
|
||||
r += 2;
|
||||
c -= 2;
|
||||
@@ -1490,12 +1492,12 @@ internal sealed class InflateCodes
|
||||
{
|
||||
do
|
||||
{
|
||||
s.window[q++] = s.window[r++];
|
||||
s.window.Memory.Span[q++] = s.window.Memory.Span[r++];
|
||||
} while (--e != 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
Array.Copy(s.window, r, s.window, q, e);
|
||||
s.window.Memory.Span.Slice(r, e).CopyTo(s.window.Memory.Span.Slice(q));
|
||||
q += e;
|
||||
r += e;
|
||||
e = 0;
|
||||
@@ -1509,12 +1511,12 @@ internal sealed class InflateCodes
|
||||
{
|
||||
do
|
||||
{
|
||||
s.window[q++] = s.window[r++];
|
||||
s.window.Memory.Span[q++] = s.window.Memory.Span[r++];
|
||||
} while (--c != 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
Array.Copy(s.window, r, s.window, q, c);
|
||||
s.window.Memory.Span.Slice(r, c).CopyTo(s.window.Memory.Span.Slice(q));
|
||||
q += c;
|
||||
r += c;
|
||||
c = 0;
|
||||
@@ -1560,7 +1562,7 @@ internal sealed class InflateCodes
|
||||
{
|
||||
b >>= (tp[tp_index_t_3 + 1]);
|
||||
k -= (tp[tp_index_t_3 + 1]);
|
||||
s.window[q++] = (byte)tp[tp_index_t_3 + 2];
|
||||
s.window.Memory.Span[q++] = (byte)tp[tp_index_t_3 + 2];
|
||||
m--;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -87,10 +87,10 @@ internal class RarStream : Stream, IStreamStack
|
||||
#endif
|
||||
ArrayPool<byte>.Shared.Return(this.tmpBuffer);
|
||||
this.tmpBuffer = null;
|
||||
readStream.Dispose();
|
||||
}
|
||||
isDisposed = true;
|
||||
base.Dispose(disposing);
|
||||
readStream.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -126,17 +126,13 @@ internal sealed partial class Unpack : BitInput, IRarUnpack
|
||||
|
||||
private FileHeader fileHeader;
|
||||
|
||||
private void Init(byte[] window)
|
||||
|
||||
private void Init()
|
||||
{
|
||||
if (this.window is null && window is null)
|
||||
if (this.window is null)
|
||||
{
|
||||
this.window = ArrayPool<byte>.Shared.Rent(PackDef.MAXWINSIZE);
|
||||
}
|
||||
else if (window is not null)
|
||||
{
|
||||
this.window = window;
|
||||
externalWindow = true;
|
||||
}
|
||||
inAddr = 0;
|
||||
UnpInitData(false);
|
||||
}
|
||||
@@ -149,7 +145,7 @@ internal sealed partial class Unpack : BitInput, IRarUnpack
|
||||
this.writeStream = writeStream;
|
||||
if (!fileHeader.IsSolid)
|
||||
{
|
||||
Init(null);
|
||||
Init();
|
||||
}
|
||||
suspended = false;
|
||||
DoUnpack();
|
||||
@@ -168,7 +164,7 @@ internal sealed partial class Unpack : BitInput, IRarUnpack
|
||||
this.writeStream = writeStream;
|
||||
if (!fileHeader.IsSolid)
|
||||
{
|
||||
Init(null);
|
||||
Init();
|
||||
}
|
||||
suspended = false;
|
||||
await DoUnpackAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -28,13 +29,16 @@ internal class BufferedSubStream : SharpCompressStream, IStreamStack
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(BufferedSubStream));
|
||||
#endif
|
||||
if (disposing) { }
|
||||
if (disposing)
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(_cache);
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
private int _cacheOffset;
|
||||
private int _cacheLength;
|
||||
private readonly byte[] _cache = new byte[32 << 10];
|
||||
private readonly byte[] _cache = ArrayPool<byte>.Shared.Rent(32 << 10);
|
||||
private long origin;
|
||||
|
||||
private long BytesLeftToRead { get; set; }
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
86
tests/SharpCompress.Performance/ArchiveReadBenchmarks.cs
Normal file
86
tests/SharpCompress.Performance/ArchiveReadBenchmarks.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using SharpCompress.Archives;
|
||||
|
||||
namespace SharpCompress.Performance;
|
||||
|
||||
/// <summary>
|
||||
/// Benchmarks for Archive API operations across different formats.
|
||||
/// Archive API is used for random access to entries with seekable streams.
|
||||
/// </summary>
|
||||
[MemoryDiagnoser]
|
||||
public class ArchiveReadBenchmarks : BenchmarkBase
|
||||
{
|
||||
[Benchmark]
|
||||
public void ZipArchiveRead()
|
||||
{
|
||||
var path = GetTestArchivePath("Zip.deflate.zip");
|
||||
using var archive = ArchiveFactory.Open(path);
|
||||
foreach (var entry in archive.Entries.Where(e => !e.IsDirectory))
|
||||
{
|
||||
using var stream = entry.OpenEntryStream();
|
||||
stream.CopyTo(Stream.Null);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void TarArchiveRead()
|
||||
{
|
||||
var path = GetTestArchivePath("Tar.tar");
|
||||
using var archive = ArchiveFactory.Open(path);
|
||||
foreach (var entry in archive.Entries.Where(e => !e.IsDirectory))
|
||||
{
|
||||
using var stream = entry.OpenEntryStream();
|
||||
stream.CopyTo(Stream.Null);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void TarGzArchiveRead()
|
||||
{
|
||||
var path = GetTestArchivePath("Tar.tar.gz");
|
||||
using var archive = ArchiveFactory.Open(path);
|
||||
foreach (var entry in archive.Entries.Where(e => !e.IsDirectory))
|
||||
{
|
||||
using var stream = entry.OpenEntryStream();
|
||||
stream.CopyTo(Stream.Null);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void TarBz2ArchiveRead()
|
||||
{
|
||||
var path = GetTestArchivePath("Tar.tar.bz2");
|
||||
using var archive = ArchiveFactory.Open(path);
|
||||
foreach (var entry in archive.Entries.Where(e => !e.IsDirectory))
|
||||
{
|
||||
using var stream = entry.OpenEntryStream();
|
||||
stream.CopyTo(Stream.Null);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void SevenZipArchiveRead()
|
||||
{
|
||||
var path = GetTestArchivePath("7Zip.LZMA2.7z");
|
||||
using var archive = ArchiveFactory.Open(path);
|
||||
foreach (var entry in archive.Entries.Where(e => !e.IsDirectory))
|
||||
{
|
||||
using var stream = entry.OpenEntryStream();
|
||||
stream.CopyTo(Stream.Null);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void RarArchiveRead()
|
||||
{
|
||||
var path = GetTestArchivePath("Rar.rar");
|
||||
using var archive = ArchiveFactory.Open(path);
|
||||
foreach (var entry in archive.Entries.Where(e => !e.IsDirectory))
|
||||
{
|
||||
using var stream = entry.OpenEntryStream();
|
||||
stream.CopyTo(Stream.Null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using SharpCompress.Archives;
|
||||
|
||||
namespace SharpCompress.Performance;
|
||||
|
||||
/// <summary>
|
||||
/// Benchmarks comparing current code against a baseline.
|
||||
/// Use [Baseline] attribute to mark the reference benchmark.
|
||||
/// </summary>
|
||||
[MemoryDiagnoser]
|
||||
[RankColumn]
|
||||
public class BaselineComparisonBenchmarks : BenchmarkBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Baseline benchmark for Zip archive reading.
|
||||
/// This serves as the reference point for comparison.
|
||||
/// </summary>
|
||||
[Benchmark(Baseline = true)]
|
||||
public void ZipArchiveRead_Baseline()
|
||||
{
|
||||
var path = GetTestArchivePath("Zip.deflate.zip");
|
||||
using var archive = ArchiveFactory.Open(path);
|
||||
foreach (var entry in archive.Entries.Where(e => !e.IsDirectory))
|
||||
{
|
||||
using var stream = entry.OpenEntryStream();
|
||||
stream.CopyTo(Stream.Null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Current implementation benchmark for Zip archive reading.
|
||||
/// BenchmarkDotNet will compare this against the baseline.
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public void ZipArchiveRead_Current()
|
||||
{
|
||||
var path = GetTestArchivePath("Zip.deflate.zip");
|
||||
using var archive = ArchiveFactory.Open(path);
|
||||
foreach (var entry in archive.Entries.Where(e => !e.IsDirectory))
|
||||
{
|
||||
using var stream = entry.OpenEntryStream();
|
||||
stream.CopyTo(Stream.Null);
|
||||
}
|
||||
}
|
||||
}
|
||||
37
tests/SharpCompress.Performance/BenchmarkBase.cs
Normal file
37
tests/SharpCompress.Performance/BenchmarkBase.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace SharpCompress.Performance;
|
||||
|
||||
/// <summary>
|
||||
/// Base class for all benchmarks providing common setup for test archives path
|
||||
/// </summary>
|
||||
public class BenchmarkBase
|
||||
{
|
||||
protected readonly string TEST_ARCHIVES_PATH;
|
||||
|
||||
public BenchmarkBase()
|
||||
{
|
||||
var index = AppDomain.CurrentDomain.BaseDirectory.IndexOf(
|
||||
"SharpCompress.Performance",
|
||||
StringComparison.OrdinalIgnoreCase
|
||||
);
|
||||
|
||||
if (index == -1)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Could not locate SharpCompress.Performance in the base directory path"
|
||||
);
|
||||
}
|
||||
|
||||
var path = AppDomain.CurrentDomain.BaseDirectory.Substring(0, index);
|
||||
var solutionBasePath =
|
||||
Path.GetDirectoryName(path)
|
||||
?? throw new InvalidOperationException("Could not determine solution base path");
|
||||
|
||||
TEST_ARCHIVES_PATH = Path.Combine(solutionBasePath, "TestArchives", "Archives");
|
||||
}
|
||||
|
||||
protected string GetTestArchivePath(string filename) =>
|
||||
Path.Combine(TEST_ARCHIVES_PATH, filename);
|
||||
}
|
||||
@@ -1,54 +1,16 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Performance;
|
||||
using SharpCompress.Readers;
|
||||
using SharpCompress.Test;
|
||||
using BenchmarkDotNet.Configs;
|
||||
using BenchmarkDotNet.Running;
|
||||
|
||||
var index = AppDomain.CurrentDomain.BaseDirectory.IndexOf(
|
||||
"SharpCompress.Performance",
|
||||
StringComparison.OrdinalIgnoreCase
|
||||
);
|
||||
var path = AppDomain.CurrentDomain.BaseDirectory.Substring(0, index);
|
||||
var SOLUTION_BASE_PATH = Path.GetDirectoryName(path) ?? throw new ArgumentNullException();
|
||||
namespace SharpCompress.Performance;
|
||||
|
||||
var TEST_ARCHIVES_PATH = Path.Combine(SOLUTION_BASE_PATH, "TestArchives", "Archives");
|
||||
|
||||
//using var _ = JetbrainsProfiler.Memory($"/Users/adam/temp/");
|
||||
using (var __ = JetbrainsProfiler.Cpu($"/Users/adam/temp/"))
|
||||
internal class Program
|
||||
{
|
||||
var testArchives = new[]
|
||||
static void Main(string[] args)
|
||||
{
|
||||
"Rar.Audio_program.rar",
|
||||
// Run all benchmarks in the assembly
|
||||
var config = DefaultConfig.Instance;
|
||||
|
||||
//"64bitstream.zip.7z",
|
||||
//"TarWithSymlink.tar.gz"
|
||||
};
|
||||
var arcs = testArchives.Select(a => Path.Combine(TEST_ARCHIVES_PATH, a)).ToArray();
|
||||
|
||||
for (int i = 0; i < 50; i++)
|
||||
{
|
||||
using var found = ArchiveFactory.Open(arcs[0]);
|
||||
foreach (var entry in found.Entries.Where(entry => !entry.IsDirectory))
|
||||
{
|
||||
Console.WriteLine($"Extracting {entry.Key}");
|
||||
using var entryStream = entry.OpenEntryStream();
|
||||
entryStream.CopyTo(Stream.Null);
|
||||
}
|
||||
/*using var found = ReaderFactory.Open(arcs[0]);
|
||||
while (found.MoveToNextEntry())
|
||||
{
|
||||
var entry = found.Entry;
|
||||
if (entry.IsDirectory)
|
||||
continue;
|
||||
|
||||
Console.WriteLine($"Extracting {entry.Key}");
|
||||
found.WriteEntryTo(Stream.Null);
|
||||
}*/
|
||||
// BenchmarkRunner will find all classes with [Benchmark] attributes
|
||||
BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args, config);
|
||||
}
|
||||
|
||||
Console.WriteLine("Still running...");
|
||||
}
|
||||
await Task.Delay(500);
|
||||
|
||||
131
tests/SharpCompress.Performance/README.md
Normal file
131
tests/SharpCompress.Performance/README.md
Normal file
@@ -0,0 +1,131 @@
|
||||
# SharpCompress Performance Benchmarks
|
||||
|
||||
This project uses [BenchmarkDotNet](https://benchmarkdotnet.org/) to measure and track performance of SharpCompress archive operations.
|
||||
|
||||
## Running Benchmarks
|
||||
|
||||
### Run All Benchmarks
|
||||
```bash
|
||||
cd tests/SharpCompress.Performance
|
||||
dotnet run -c Release
|
||||
```
|
||||
|
||||
### Run Specific Benchmark Classes
|
||||
```bash
|
||||
# Run only Archive API benchmarks
|
||||
dotnet run -c Release -- --filter "*ArchiveReadBenchmarks*"
|
||||
|
||||
# Run only Reader API benchmarks
|
||||
dotnet run -c Release -- --filter "*ReaderBenchmarks*"
|
||||
```
|
||||
|
||||
### Run Specific Benchmark Methods
|
||||
```bash
|
||||
# Run only Zip benchmarks
|
||||
dotnet run -c Release -- --filter "*Zip*"
|
||||
|
||||
# Run a specific method
|
||||
dotnet run -c Release -- --filter "ArchiveReadBenchmarks.ZipArchiveRead"
|
||||
```
|
||||
|
||||
### Quick Dry Run (for testing)
|
||||
```bash
|
||||
dotnet run -c Release -- --job dry
|
||||
```
|
||||
|
||||
## Benchmark Categories
|
||||
|
||||
### ArchiveReadBenchmarks
|
||||
Tests the **Archive API** which provides random access to entries with seekable streams. Covers:
|
||||
- Zip (deflate compression)
|
||||
- Tar (uncompressed)
|
||||
- Tar.gz (gzip compression)
|
||||
- Tar.bz2 (bzip2 compression)
|
||||
- 7Zip (LZMA2 compression)
|
||||
- Rar
|
||||
|
||||
### ReaderBenchmarks
|
||||
Tests the **Reader API** which provides forward-only streaming for non-seekable streams. Covers:
|
||||
- Zip
|
||||
- Tar
|
||||
- Tar.gz
|
||||
- Tar.bz2
|
||||
- Rar
|
||||
|
||||
### WriteBenchmarks
|
||||
Tests the **Writer API** for creating archives using forward-only writing. Covers:
|
||||
- Zip (deflate compression)
|
||||
- Tar (uncompressed)
|
||||
- Tar.gz (gzip compression)
|
||||
|
||||
### BaselineComparisonBenchmarks
|
||||
Example benchmark showing how to compare implementations using the `[Baseline]` attribute. The baseline benchmark serves as a reference point, and BenchmarkDotNet calculates the ratio of performance between baseline and other methods.
|
||||
|
||||
## Comparing Against Previous Versions
|
||||
|
||||
### Using Baseline Attribute
|
||||
Mark one benchmark with `[Baseline = true]` and BenchmarkDotNet will show relative performance:
|
||||
|
||||
```csharp
|
||||
[Benchmark(Baseline = true)]
|
||||
public void MethodA() { /* ... */ }
|
||||
|
||||
[Benchmark]
|
||||
public void MethodB() { /* ... */ }
|
||||
```
|
||||
|
||||
Results will show ratios like "1.5x slower" or "0.8x faster" compared to the baseline.
|
||||
|
||||
### Using BenchmarkDotNet.Artifacts for Historical Comparison
|
||||
BenchmarkDotNet saves results to `BenchmarkDotNet.Artifacts/results/`. You can:
|
||||
|
||||
1. Run benchmarks and save the results
|
||||
2. Keep a snapshot of the results file
|
||||
3. Compare new runs against saved results
|
||||
|
||||
### Using Different NuGet Versions (Advanced)
|
||||
To compare against a published NuGet package:
|
||||
|
||||
1. Create a separate benchmark project referencing the NuGet package
|
||||
2. Use BenchmarkDotNet's `[SimpleJob]` attribute with different runtimes
|
||||
3. Reference both the local project and NuGet package in different jobs
|
||||
|
||||
## Interpreting Results
|
||||
|
||||
BenchmarkDotNet provides:
|
||||
- **Mean**: Average execution time
|
||||
- **Error**: Half of 99.9% confidence interval
|
||||
- **StdDev**: Standard deviation of measurements
|
||||
- **Allocated**: Memory allocated per operation
|
||||
- **Rank**: Relative ranking (when using `[RankColumn]`)
|
||||
- **Ratio**: Relative performance vs baseline (when using `[Baseline]`)
|
||||
|
||||
## Output Artifacts
|
||||
|
||||
Results are saved to `BenchmarkDotNet.Artifacts/results/`:
|
||||
- `*.csv`: Raw data for further analysis
|
||||
- `*-report.html`: HTML report with charts
|
||||
- `*-report-github.md`: Markdown report for GitHub
|
||||
- `*.log`: Detailed execution log
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Always run in Release mode**: Debug builds have significant overhead
|
||||
2. **Close other applications**: Minimize system noise during benchmarks
|
||||
3. **Run multiple times**: Look for consistency across runs
|
||||
4. **Use appropriate workload**: Ensure benchmarks run for at least 100ms
|
||||
5. **Track trends**: Compare results over time to detect regressions
|
||||
6. **Archive results**: Keep snapshots of benchmark results for historical comparison
|
||||
|
||||
## CI/CD Integration
|
||||
|
||||
Consider adding benchmarks to CI/CD to:
|
||||
- Detect performance regressions automatically
|
||||
- Track performance trends over time
|
||||
- Compare PR performance against main branch
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- [BenchmarkDotNet Documentation](https://benchmarkdotnet.org/articles/overview.html)
|
||||
- [BenchmarkDotNet Configuration](https://benchmarkdotnet.org/articles/configs/configs.html)
|
||||
- [BenchmarkDotNet Baseline](https://benchmarkdotnet.org/articles/features/baselines.html)
|
||||
88
tests/SharpCompress.Performance/ReaderBenchmarks.cs
Normal file
88
tests/SharpCompress.Performance/ReaderBenchmarks.cs
Normal file
@@ -0,0 +1,88 @@
|
||||
using System.IO;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using SharpCompress.Readers;
|
||||
|
||||
namespace SharpCompress.Performance;
|
||||
|
||||
/// <summary>
|
||||
/// Benchmarks for Reader API operations across different formats.
|
||||
/// Reader API is used for forward-only streaming with non-seekable streams.
|
||||
/// </summary>
|
||||
[MemoryDiagnoser]
|
||||
public class ReaderBenchmarks : BenchmarkBase
|
||||
{
|
||||
[Benchmark]
|
||||
public void ZipReaderRead()
|
||||
{
|
||||
var path = GetTestArchivePath("Zip.deflate.zip");
|
||||
using var stream = File.OpenRead(path);
|
||||
using var reader = ReaderFactory.Open(stream);
|
||||
while (reader.MoveToNextEntry())
|
||||
{
|
||||
if (!reader.Entry.IsDirectory)
|
||||
{
|
||||
reader.WriteEntryTo(Stream.Null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void TarReaderRead()
|
||||
{
|
||||
var path = GetTestArchivePath("Tar.tar");
|
||||
using var stream = File.OpenRead(path);
|
||||
using var reader = ReaderFactory.Open(stream);
|
||||
while (reader.MoveToNextEntry())
|
||||
{
|
||||
if (!reader.Entry.IsDirectory)
|
||||
{
|
||||
reader.WriteEntryTo(Stream.Null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void TarGzReaderRead()
|
||||
{
|
||||
var path = GetTestArchivePath("Tar.tar.gz");
|
||||
using var stream = File.OpenRead(path);
|
||||
using var reader = ReaderFactory.Open(stream);
|
||||
while (reader.MoveToNextEntry())
|
||||
{
|
||||
if (!reader.Entry.IsDirectory)
|
||||
{
|
||||
reader.WriteEntryTo(Stream.Null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void TarBz2ReaderRead()
|
||||
{
|
||||
var path = GetTestArchivePath("Tar.tar.bz2");
|
||||
using var stream = File.OpenRead(path);
|
||||
using var reader = ReaderFactory.Open(stream);
|
||||
while (reader.MoveToNextEntry())
|
||||
{
|
||||
if (!reader.Entry.IsDirectory)
|
||||
{
|
||||
reader.WriteEntryTo(Stream.Null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void RarReaderRead()
|
||||
{
|
||||
var path = GetTestArchivePath("Rar.rar");
|
||||
using var stream = File.OpenRead(path);
|
||||
using var reader = ReaderFactory.Open(stream);
|
||||
while (reader.MoveToNextEntry())
|
||||
{
|
||||
if (!reader.Entry.IsDirectory)
|
||||
{
|
||||
reader.WriteEntryTo(Stream.Null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" />
|
||||
<PackageReference Include="JetBrains.Profiler.SelfApi" />
|
||||
<ProjectReference Include="..\..\src\SharpCompress\SharpCompress.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
106
tests/SharpCompress.Performance/WriteBenchmarks.cs
Normal file
106
tests/SharpCompress.Performance/WriteBenchmarks.cs
Normal file
@@ -0,0 +1,106 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Writers;
|
||||
|
||||
namespace SharpCompress.Performance;
|
||||
|
||||
/// <summary>
|
||||
/// Benchmarks for Writer operations.
|
||||
/// Tests creating archives with different compression formats using forward-only Writer API.
|
||||
/// </summary>
|
||||
[MemoryDiagnoser]
|
||||
public class WriteBenchmarks : BenchmarkBase
|
||||
{
|
||||
private string _tempOutputPath = null!;
|
||||
private string[] _testFiles = null!;
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
{
|
||||
_tempOutputPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
|
||||
Directory.CreateDirectory(_tempOutputPath);
|
||||
|
||||
// Get some test files to compress
|
||||
var originalPath = Path.Combine(Path.GetDirectoryName(TEST_ARCHIVES_PATH)!, "Original");
|
||||
if (Directory.Exists(originalPath))
|
||||
{
|
||||
_testFiles = Directory.GetFiles(originalPath).Take(5).ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
_testFiles = [];
|
||||
}
|
||||
}
|
||||
|
||||
[IterationCleanup]
|
||||
public void IterationCleanup()
|
||||
{
|
||||
// Clean up created archives after each iteration to avoid file reuse affecting measurements
|
||||
if (Directory.Exists(_tempOutputPath))
|
||||
{
|
||||
foreach (var file in Directory.GetFiles(_tempOutputPath))
|
||||
{
|
||||
File.Delete(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[GlobalCleanup]
|
||||
public void Cleanup()
|
||||
{
|
||||
if (Directory.Exists(_tempOutputPath))
|
||||
{
|
||||
Directory.Delete(_tempOutputPath, true);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void ZipWriterWrite()
|
||||
{
|
||||
var outputFile = Path.Combine(_tempOutputPath, "test.zip");
|
||||
using var stream = File.Create(outputFile);
|
||||
using var writer = WriterFactory.Open(
|
||||
stream,
|
||||
ArchiveType.Zip,
|
||||
new WriterOptions(CompressionType.Deflate)
|
||||
);
|
||||
foreach (var file in _testFiles)
|
||||
{
|
||||
writer.Write(Path.GetFileName(file), file);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void TarWriterWrite()
|
||||
{
|
||||
var outputFile = Path.Combine(_tempOutputPath, "test.tar");
|
||||
using var stream = File.Create(outputFile);
|
||||
using var writer = WriterFactory.Open(
|
||||
stream,
|
||||
ArchiveType.Tar,
|
||||
new WriterOptions(CompressionType.None)
|
||||
);
|
||||
foreach (var file in _testFiles)
|
||||
{
|
||||
writer.Write(Path.GetFileName(file), file);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void TarGzWriterWrite()
|
||||
{
|
||||
var outputFile = Path.Combine(_tempOutputPath, "test.tar.gz");
|
||||
using var stream = File.Create(outputFile);
|
||||
using var writer = WriterFactory.Open(
|
||||
stream,
|
||||
ArchiveType.Tar,
|
||||
new WriterOptions(CompressionType.GZip)
|
||||
);
|
||||
foreach (var file in _testFiles)
|
||||
{
|
||||
writer.Write(Path.GetFileName(file), file);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,24 @@
|
||||
"version": 2,
|
||||
"dependencies": {
|
||||
"net10.0": {
|
||||
"BenchmarkDotNet": {
|
||||
"type": "Direct",
|
||||
"requested": "[0.14.0, )",
|
||||
"resolved": "0.14.0",
|
||||
"contentHash": "eIPSDKi3oni734M1rt/XJAwGQQOIf9gLjRRKKJ0HuVy3vYd7gnmAIX1bTjzI9ZbAY/nPddgqqgM/TeBYitMCIg==",
|
||||
"dependencies": {
|
||||
"BenchmarkDotNet.Annotations": "0.14.0",
|
||||
"CommandLineParser": "2.9.1",
|
||||
"Gee.External.Capstone": "2.3.0",
|
||||
"Iced": "1.17.0",
|
||||
"Microsoft.CodeAnalysis.CSharp": "4.1.0",
|
||||
"Microsoft.Diagnostics.Runtime": "2.2.332302",
|
||||
"Microsoft.Diagnostics.Tracing.TraceEvent": "3.1.8",
|
||||
"Microsoft.DotNet.PlatformAbstractions": "3.1.6",
|
||||
"Perfolizer": "[0.3.17]",
|
||||
"System.Management": "5.0.0"
|
||||
}
|
||||
},
|
||||
"JetBrains.Profiler.SelfApi": {
|
||||
"type": "Direct",
|
||||
"requested": "[2.5.15, )",
|
||||
@@ -37,6 +55,26 @@
|
||||
"resolved": "17.14.15",
|
||||
"contentHash": "mXQPJsbuUD2ydq4/ffd8h8tSOFCXec+2xJOVNCvXjuMOq/+5EKHq3D2m2MC2+nUaXeFMSt66VS/J4HdKBixgcw=="
|
||||
},
|
||||
"BenchmarkDotNet.Annotations": {
|
||||
"type": "Transitive",
|
||||
"resolved": "0.14.0",
|
||||
"contentHash": "CUDCg6bgHrDzhjnA+IOBl5gAo8Y5hZ2YSs7MBXrYMlMKpBZqrD5ez0537uDveOkcf+YWAoK+S4sMcuWPbIz8bw=="
|
||||
},
|
||||
"CommandLineParser": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.9.1",
|
||||
"contentHash": "OE0sl1/sQ37bjVsPKKtwQlWDgqaxWgtme3xZz7JssWUzg5JpMIyHgCTY9MVMxOg48fJ1AgGT3tgdH5m/kQ5xhA=="
|
||||
},
|
||||
"Gee.External.Capstone": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.3.0",
|
||||
"contentHash": "2ap/rYmjtzCOT8hxrnEW/QeiOt+paD8iRrIcdKX0cxVwWLFa1e+JDBNeECakmccXrSFeBQuu5AV8SNkipFMMMw=="
|
||||
},
|
||||
"Iced": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.17.0",
|
||||
"contentHash": "8x+HCVTl/HHTGpscH3vMBhV8sknN/muZFw9s3TsI8SA6+c43cOTCi2+jE4KsU8pNLbJ++iF2ZFcpcXHXtDglnw=="
|
||||
},
|
||||
"JetBrains.FormatRipper": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.4.0",
|
||||
@@ -63,6 +101,118 @@
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
|
||||
},
|
||||
"Microsoft.CodeAnalysis.Analyzers": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.3.3",
|
||||
"contentHash": "j/rOZtLMVJjrfLRlAMckJLPW/1rze9MT1yfWqSIbUPGRu1m1P0fuo9PmqapwsmePfGB5PJrudQLvmUOAMF0DqQ=="
|
||||
},
|
||||
"Microsoft.CodeAnalysis.Common": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.1.0",
|
||||
"contentHash": "bNzTyxP3iD5FPFHfVDl15Y6/wSoI7e3MeV0lOaj9igbIKTjgrmuw6LoVJ06jUNFA7+KaDC/OIsStWl/FQJz6sQ==",
|
||||
"dependencies": {
|
||||
"Microsoft.CodeAnalysis.Analyzers": "3.3.3"
|
||||
}
|
||||
},
|
||||
"Microsoft.CodeAnalysis.CSharp": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.1.0",
|
||||
"contentHash": "sbu6kDGzo9bfQxuqWpeEE7I9P30bSuZEnpDz9/qz20OU6pm79Z63+/BsAzO2e/R/Q97kBrpj647wokZnEVr97w==",
|
||||
"dependencies": {
|
||||
"Microsoft.CodeAnalysis.Common": "[4.1.0]"
|
||||
}
|
||||
},
|
||||
"Microsoft.Diagnostics.NETCore.Client": {
|
||||
"type": "Transitive",
|
||||
"resolved": "0.2.251802",
|
||||
"contentHash": "bqnYl6AdSeboeN4v25hSukK6Odm6/54E3Y2B8rBvgqvAW0mF8fo7XNRVE2DMOG7Rk0fiuA079QIH28+V+W1Zdg==",
|
||||
"dependencies": {
|
||||
"Microsoft.Bcl.AsyncInterfaces": "1.1.0",
|
||||
"Microsoft.Extensions.Logging": "2.1.1"
|
||||
}
|
||||
},
|
||||
"Microsoft.Diagnostics.Runtime": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.2.332302",
|
||||
"contentHash": "Hp84ivxSKIMTBzYSATxmUsm3YSXHWivcwiRRbsydGmqujMUK8BAueLN0ssAVEOkOBmh0vjUBhrq7YcroT7VCug==",
|
||||
"dependencies": {
|
||||
"Microsoft.Diagnostics.NETCore.Client": "0.2.251802"
|
||||
}
|
||||
},
|
||||
"Microsoft.Diagnostics.Tracing.TraceEvent": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.1.8",
|
||||
"contentHash": "kl3UMrZKSeSEYZ8rt/GjLUQToREjgQABqfg6PzQBmSlYHTZOKE9ePEOS2xptROQ9SVvngg3QGX51TIT11iZ0wA=="
|
||||
},
|
||||
"Microsoft.DotNet.PlatformAbstractions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.1.6",
|
||||
"contentHash": "jek4XYaQ/PGUwDKKhwR8K47Uh1189PFzMeLqO83mXrXQVIpARZCcfuDedH50YDTepBkfijCZN5U/vZi++erxtg=="
|
||||
},
|
||||
"Microsoft.Extensions.Configuration": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.1",
|
||||
"contentHash": "LjVKO6P2y52c5ZhTLX/w8zc5H4Y3J/LJsgqTBj49TtFq/hAtVNue/WA0F6/7GMY90xhD7K0MDZ4qpOeWXbLvzg==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Configuration.Abstractions": "2.1.1"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Configuration.Abstractions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.1",
|
||||
"contentHash": "VfuZJNa0WUshZ/+8BFZAhwFKiKuu/qOUCFntfdLpHj7vcRnsGHqd3G2Hse78DM+pgozczGM63lGPRLmy+uhUOA==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Primitives": "2.1.1"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Configuration.Binder": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.1",
|
||||
"contentHash": "fcLCTS03poWE4v9tSNBr3pWn0QwGgAn1vzqHXlXgvqZeOc7LvQNzaWcKRQZTdEc3+YhQKwMsOtm3VKSA2aWQ8w==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Configuration": "2.1.1"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.1",
|
||||
"contentHash": "MgYpU5cwZohUMKKg3sbPhvGG+eAZ/59E9UwPwlrUkyXU+PGzqwZg9yyQNjhxuAWmoNoFReoemeCku50prYSGzA=="
|
||||
},
|
||||
"Microsoft.Extensions.Logging": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.1",
|
||||
"contentHash": "hh+mkOAQDTp6XH80xJt3+wwYVzkbwYQl9XZRCz4Um0JjP/o7N9vHM3rZ6wwwtr+BBe/L6iBO2sz0px6OWBzqZQ==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Configuration.Binder": "2.1.1",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.1",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "2.1.1",
|
||||
"Microsoft.Extensions.Options": "2.1.1"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Logging.Abstractions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.1",
|
||||
"contentHash": "XRzK7ZF+O6FzdfWrlFTi1Rgj2080ZDsd46vzOjadHUB0Cz5kOvDG8vI7caa5YFrsHQpcfn0DxtjS4E46N4FZsA=="
|
||||
},
|
||||
"Microsoft.Extensions.Options": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.1",
|
||||
"contentHash": "V7lXCU78lAbzaulCGFKojcCyG8RTJicEbiBkPJjFqiqXwndEBBIehdXRMWEVU3UtzQ1yDvphiWUL9th6/4gJ7w==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.1",
|
||||
"Microsoft.Extensions.Primitives": "2.1.1"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Primitives": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.1",
|
||||
"contentHash": "scJ1GZNIxMmjpENh0UZ8XCQ6vzr/LzeF9WvEA51Ix2OQGAs9WPgPu8ABVUdvpKPLuor/t05gm6menJK3PwqOXg=="
|
||||
},
|
||||
"Microsoft.NETCore.Platforms": {
|
||||
"type": "Transitive",
|
||||
"resolved": "5.0.0",
|
||||
"contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ=="
|
||||
},
|
||||
"Microsoft.NETFramework.ReferenceAssemblies.net461": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.0.3",
|
||||
@@ -73,8 +223,33 @@
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
|
||||
},
|
||||
"Perfolizer": {
|
||||
"type": "Transitive",
|
||||
"resolved": "0.3.17",
|
||||
"contentHash": "FQgtCoF2HFwvzKWulAwBS5BGLlh8pgbrJtOp47jyBwh2CW16juVtacN1azOA2BqdrJXkXTNLNRMo7ZlHHiuAnA=="
|
||||
},
|
||||
"System.CodeDom": {
|
||||
"type": "Transitive",
|
||||
"resolved": "5.0.0",
|
||||
"contentHash": "JPJArwA1kdj8qDAkY2XGjSWoYnqiM7q/3yRNkt6n28Mnn95MuEGkZXUbPBf7qc3IjwrGY5ttQon7yqHZyQJmOQ=="
|
||||
},
|
||||
"System.Management": {
|
||||
"type": "Transitive",
|
||||
"resolved": "5.0.0",
|
||||
"contentHash": "MF1CHaRcC+MLFdnDthv4/bKWBZnlnSpkGqa87pKukQefgEdwtb9zFW6zs0GjPp73qtpYYg4q6PEKbzJbxCpKfw==",
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.Platforms": "5.0.0",
|
||||
"System.CodeDom": "5.0.0"
|
||||
}
|
||||
},
|
||||
"sharpcompress": {
|
||||
"type": "Project"
|
||||
},
|
||||
"Microsoft.Bcl.AsyncInterfaces": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[10.0.0, )",
|
||||
"resolved": "1.1.0",
|
||||
"contentHash": "1Am6l4Vpn3/K32daEqZI+FFr96OlZkgwK2LcT3pZ2zWubR5zTPW3/FkO1Rat9kb7oQOa4rxgl9LJHc5tspCWfg=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user