From aa34c22f390a45f250631fc448f76baf1363812d Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Thu, 28 May 2026 05:39:07 +0100 Subject: [PATCH 01/11] update profiler --- SharpCompress.sln | 7 +++++++ tests/SharpCompress.Performance/Program.cs | 24 +++++++++------------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/SharpCompress.sln b/SharpCompress.sln index 3609183a4..65ac7b420 100644 --- a/SharpCompress.sln +++ b/SharpCompress.sln @@ -30,6 +30,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Config", "Config", "{CDB425 EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpCompress.Performance", "tests\SharpCompress.Performance\SharpCompress.Performance.csproj", "{5BDE6DBC-9E5F-4E21-AB71-F138A3E72B17}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpCompress.AotSmoke", "tests\SharpCompress.AotSmoke\SharpCompress.AotSmoke.csproj", "{50C6DA22-2124-4E47-9FB7-96FEC5F59566}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -52,6 +54,10 @@ Global {5BDE6DBC-9E5F-4E21-AB71-F138A3E72B17}.Debug|Any CPU.Build.0 = Debug|Any CPU {5BDE6DBC-9E5F-4E21-AB71-F138A3E72B17}.Release|Any CPU.ActiveCfg = Release|Any CPU {5BDE6DBC-9E5F-4E21-AB71-F138A3E72B17}.Release|Any CPU.Build.0 = Release|Any CPU + {50C6DA22-2124-4E47-9FB7-96FEC5F59566}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50C6DA22-2124-4E47-9FB7-96FEC5F59566}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50C6DA22-2124-4E47-9FB7-96FEC5F59566}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50C6DA22-2124-4E47-9FB7-96FEC5F59566}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -60,5 +66,6 @@ Global {FD19DDD8-72B2-4024-8665-0D1F7A2AA998} = {3C5BE746-03E5-4895-9988-0B57F162F86C} {F2B1A1EB-0FA6-40D0-8908-E13247C7226F} = {0F0901FF-E8D9-426A-B5A2-17C7F47C1529} {5BDE6DBC-9E5F-4E21-AB71-F138A3E72B17} = {0F0901FF-E8D9-426A-B5A2-17C7F47C1529} + {50C6DA22-2124-4E47-9FB7-96FEC5F59566} = {0F0901FF-E8D9-426A-B5A2-17C7F47C1529} EndGlobalSection EndGlobal diff --git a/tests/SharpCompress.Performance/Program.cs b/tests/SharpCompress.Performance/Program.cs index 35e257b8a..856a0f9fe 100644 --- a/tests/SharpCompress.Performance/Program.cs +++ b/tests/SharpCompress.Performance/Program.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; using BenchmarkDotNet.Configs; using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Running; @@ -8,12 +9,12 @@ namespace SharpCompress.Performance; public class Program { - public static void Main(string[] args) + public static async Task Main(string[] args) { // Check if profiling mode is requested if (args.Length > 0 && args[0].Equals("--profile", StringComparison.OrdinalIgnoreCase)) { - RunWithProfiler(args); + await RunWithProfiler(args); return; } @@ -29,10 +30,10 @@ public static void Main(string[] args) BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args, config); } - private static void RunWithProfiler(string[] args) + private static async Task RunWithProfiler(string[] args) { var profileType = "cpu"; // Default to CPU profiling - var outputPath = "./profiler-snapshots"; + var outputPath = "./profiler-snapshots";l // Parse arguments for (int i = 1; i < args.Length; i++) @@ -59,16 +60,11 @@ private static void RunWithProfiler(string[] args) Console.WriteLine(); // Run a sample benchmark with profiling - RunSampleBenchmarkWithProfiler(profileType, outputPath); + await RunSampleBenchmarkWithProfiler(profileType, outputPath); } - private static void RunSampleBenchmarkWithProfiler(string profileType, string outputPath) + private static async Task RunSampleBenchmarkWithProfiler(string profileType, string outputPath) { - Console.WriteLine("Running sample benchmark with profiler..."); - Console.WriteLine("Note: JetBrains profiler requires the profiler tools to be installed."); - Console.WriteLine("Install from: https://www.jetbrains.com/profiler/"); - Console.WriteLine(); - try { IDisposable? profiler = null; @@ -85,13 +81,13 @@ private static void RunSampleBenchmarkWithProfiler(string profileType, string ou using (profiler) { // Run a simple benchmark iteration - var zipBenchmark = new Benchmarks.ZipBenchmarks(); + var zipBenchmark = new Benchmarks.SevenZipBenchmarks(); zipBenchmark.Setup(); Console.WriteLine("Running benchmark iterations..."); - for (int i = 0; i < 10; i++) + for (int i = 0; i < 100; i++) { - zipBenchmark.ZipExtractArchiveApi(); + await zipBenchmark.SevenZipLzma2ExtractAsync_Reader(); if (i % 3 == 0) { Console.Write("."); From 3ddfaaa899953351fd72796bbd5313b0d356f5cb Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Thu, 28 May 2026 05:53:01 +0100 Subject: [PATCH 02/11] Reduce some allocations by pooling or better disposal --- .../Compressors/LZMA/LZ/LzBinTree.cs | 30 +++- .../Compressors/LZMA/LZ/LzInWindow.cs | 23 ++- .../Compressors/LZMA/LZipStream.Async.cs | 45 ++--- .../Compressors/LZMA/Lzma2EncoderStream.cs | 158 +++++++++++------- .../Compressors/LZMA/LzmaDecoder.cs | 5 +- .../Compressors/LZMA/LzmaEncoder.cs | 8 +- .../Compressors/LZMA/LzmaStream.Async.cs | 26 +-- .../Compressors/LZMA/LzmaStream.cs | 17 ++ .../LZMA/RangeCoder/RangeCoder.Async.cs | 15 +- .../Compressors/LZMA/RangeCoder/RangeCoder.cs | 6 +- src/SharpCompress/packages.lock.json | 12 +- tests/SharpCompress.Performance/Program.cs | 2 +- 12 files changed, 227 insertions(+), 120 deletions(-) diff --git a/src/SharpCompress/Compressors/LZMA/LZ/LzBinTree.cs b/src/SharpCompress/Compressors/LZMA/LZ/LzBinTree.cs index bc0a8a7ae..d09a1e183 100644 --- a/src/SharpCompress/Compressors/LZMA/LZ/LzBinTree.cs +++ b/src/SharpCompress/Compressors/LZMA/LZ/LzBinTree.cs @@ -1,6 +1,7 @@ #nullable disable using System; +using System.Buffers; using System.IO; namespace SharpCompress.Compressors.LZMA.LZ; @@ -108,7 +109,12 @@ uint keepAddBufferAfter var cyclicBufferSize = historySize + 1; if (_cyclicBufferSize != cyclicBufferSize) { - _son = new uint[(_cyclicBufferSize = cyclicBufferSize) * 2]; + if (_son is not null) + { + ArrayPool.Shared.Return(_son); + } + _cyclicBufferSize = cyclicBufferSize; + _son = ArrayPool.Shared.Rent(checked((int)(_cyclicBufferSize * 2))); } var hs = K_BT2_HASH_SIZE; @@ -132,7 +138,27 @@ uint keepAddBufferAfter } if (hs != _hashSizeSum) { - _hash = new uint[_hashSizeSum = hs]; + if (_hash is not null) + { + ArrayPool.Shared.Return(_hash); + } + _hashSizeSum = hs; + _hash = ArrayPool.Shared.Rent(checked((int)_hashSizeSum)); + } + } + + public override void Dispose() + { + base.Dispose(); + if (_son is not null) + { + ArrayPool.Shared.Return(_son); + _son = null; + } + if (_hash is not null) + { + ArrayPool.Shared.Return(_hash); + _hash = null; } } diff --git a/src/SharpCompress/Compressors/LZMA/LZ/LzInWindow.cs b/src/SharpCompress/Compressors/LZMA/LZ/LzInWindow.cs index 9df3c27d2..7ee8bc888 100644 --- a/src/SharpCompress/Compressors/LZMA/LZ/LzInWindow.cs +++ b/src/SharpCompress/Compressors/LZMA/LZ/LzInWindow.cs @@ -1,10 +1,12 @@ #nullable disable +using System; +using System.Buffers; using System.IO; namespace SharpCompress.Compressors.LZMA.LZ; -internal class InWindow +internal class InWindow : IDisposable { public byte[] _bufferBase; // pointer to buffer with data private Stream _stream; @@ -78,7 +80,22 @@ public virtual void ReadBlock() } } - private void Free() => _bufferBase = null; + private void Free() + { + if (_bufferBase is null) + { + return; + } + + ArrayPool.Shared.Return(_bufferBase); + _bufferBase = null; + } + + public virtual void Dispose() + { + ReleaseStream(); + Free(); + } public void Create(uint keepSizeBefore, uint keepSizeAfter, uint keepSizeReserv) { @@ -89,7 +106,7 @@ public void Create(uint keepSizeBefore, uint keepSizeAfter, uint keepSizeReserv) { Free(); _blockSize = blockSize; - _bufferBase = new byte[_blockSize]; + _bufferBase = ArrayPool.Shared.Rent(checked((int)_blockSize)); } _pointerToLastSafePosition = _blockSize - keepSizeAfter; _streamEndWasReached = false; diff --git a/src/SharpCompress/Compressors/LZMA/LZipStream.Async.cs b/src/SharpCompress/Compressors/LZMA/LZipStream.Async.cs index ae8e1074c..054d5ac74 100644 --- a/src/SharpCompress/Compressors/LZMA/LZipStream.Async.cs +++ b/src/SharpCompress/Compressors/LZMA/LZipStream.Async.cs @@ -126,31 +126,36 @@ CancellationToken cancellationToken ) { // Read the header - byte[] header = new byte[6]; - var n = await stream - .ReadAsync(header, 0, header.Length, cancellationToken) - .ConfigureAwait(false); + var header = ArrayPool.Shared.Rent(6); + try + { + var n = await stream.ReadAsync(header, 0, 6, cancellationToken).ConfigureAwait(false); - // TODO: Handle reading only part of the header? + // TODO: Handle reading only part of the header? - if (n != 6) - { - return 0; - } + if (n != 6) + { + return 0; + } - if ( - header[0] != 'L' - || header[1] != 'Z' - || header[2] != 'I' - || header[3] != 'P' - || header[4] != 1 /* version 1 */ - ) + if ( + header[0] != 'L' + || header[1] != 'Z' + || header[2] != 'I' + || header[3] != 'P' + || header[4] != 1 /* version 1 */ + ) + { + return 0; + } + var basePower = header[5] & 0x1F; + var subtractionNumerator = (header[5] & 0xE0) >> 5; + return (1 << basePower) - (subtractionNumerator * (1 << (basePower - 4))); + } + finally { - return 0; + ArrayPool.Shared.Return(header); } - var basePower = header[5] & 0x1F; - var subtractionNumerator = (header[5] & 0xE0) >> 5; - return (1 << basePower) - (subtractionNumerator * (1 << (basePower - 4))); } #if !LEGACY_DOTNET diff --git a/src/SharpCompress/Compressors/LZMA/Lzma2EncoderStream.cs b/src/SharpCompress/Compressors/LZMA/Lzma2EncoderStream.cs index f1ed808c6..73ce7b643 100644 --- a/src/SharpCompress/Compressors/LZMA/Lzma2EncoderStream.cs +++ b/src/SharpCompress/Compressors/LZMA/Lzma2EncoderStream.cs @@ -1,4 +1,5 @@ using System; +using System.Buffers; using System.IO; using System.Threading; using System.Threading.Tasks; @@ -26,11 +27,12 @@ internal sealed class Lzma2EncoderStream : Stream private readonly int _dictionarySize; private readonly int _numFastBytes; private readonly byte[] _buffer; + private readonly byte[] _properties; + private readonly Encoder _encoder; private int _bufferPosition; private bool _isFirstChunk = true; private bool _isDisposed; private byte _lzmaPropertiesByte; - private bool _lzmaPropertiesKnown; /// /// Creates a new LZMA2 encoder stream. @@ -43,14 +45,23 @@ public Lzma2EncoderStream(Stream output, int dictionarySize, int numFastBytes) _output = output; _dictionarySize = dictionarySize; _numFastBytes = numFastBytes; - _buffer = new byte[MAX_UNCOMPRESSED_CHUNK_SIZE]; + _buffer = ArrayPool.Shared.Rent(MAX_UNCOMPRESSED_CHUNK_SIZE); _bufferPosition = 0; + + var encoderProps = new LzmaEncoderProperties(eos: false, _dictionarySize, _numFastBytes); + _encoder = new Encoder(); + _encoder.SetCoderProperties(encoderProps.PropIDs, encoderProps.Properties); + + Span lzmaProperties = stackalloc byte[5]; + _encoder.WriteCoderProperties(lzmaProperties); + _lzmaPropertiesByte = lzmaProperties[0]; + _properties = [EncodeDictionarySize(_dictionarySize)]; } /// /// Gets the 1-byte LZMA2 properties (encoded dictionary size). /// - public byte[] Properties => [EncodeDictionarySize(_dictionarySize)]; + public byte[] Properties => _properties; public override bool CanRead => false; public override bool CanSeek => false; @@ -153,6 +164,8 @@ protected override void Dispose(bool disposing) // Write LZMA2 end marker _output.WriteByte(0x00); + _encoder.Dispose(); + ArrayPool.Shared.Return(_buffer); } base.Dispose(disposing); } @@ -179,6 +192,9 @@ public async ValueTask DisposeAsync() #else await _output.WriteAsync(_endMarker, 0, 1).ConfigureAwait(false); #endif + + _encoder.Dispose(); + ArrayPool.Shared.Return(_buffer); } #if !LEGACY_DOTNET || NETSTANDARD2_1 @@ -193,14 +209,15 @@ private void FlushChunk() return; } - var uncompressedData = _buffer.AsSpan(0, _bufferPosition); + var uncompressedSize = _bufferPosition; + var uncompressedData = _buffer.AsSpan(0, uncompressedSize); _bufferPosition = 0; // Try compressing the data - byte[] compressed; + (byte[] Buffer, int Length) compressed; try { - compressed = CompressBlock(uncompressedData); + compressed = CompressBlock(_buffer, uncompressedSize); } catch (Exception ex) when (ex is not OutOfMemoryException and not StackOverflowException) { @@ -215,7 +232,7 @@ private void FlushChunk() && compressed.Length < uncompressedData.Length ) { - WriteCompressedChunk(uncompressedData.Length, compressed); + WriteCompressedChunk(uncompressedData.Length, compressed.Buffer, compressed.Length); } else { @@ -230,13 +247,14 @@ private async ValueTask FlushChunkAsync(CancellationToken cancellationToken = de return; } - var uncompressedData = _buffer.AsMemory(0, _bufferPosition); + var uncompressedSize = _bufferPosition; + var uncompressedData = _buffer.AsMemory(0, uncompressedSize); _bufferPosition = 0; - byte[] compressed; + (byte[] Buffer, int Length) compressed; try { - compressed = CompressBlock(uncompressedData.Span); + compressed = CompressBlock(_buffer, uncompressedSize); } catch (Exception ex) when (ex is not OutOfMemoryException and not StackOverflowException) { @@ -250,7 +268,12 @@ await WriteUncompressedChunksAsync(uncompressedData, cancellationToken) && compressed.Length < uncompressedData.Length ) { - await WriteCompressedChunkAsync(uncompressedData.Length, compressed, cancellationToken) + await WriteCompressedChunkAsync( + uncompressedData.Length, + compressed.Buffer, + compressed.Length, + cancellationToken + ) .ConfigureAwait(false); } else @@ -260,45 +283,26 @@ await WriteUncompressedChunksAsync(uncompressedData, cancellationToken) } } - private byte[] CompressBlock(ReadOnlySpan data) + private (byte[] Buffer, int Length) CompressBlock(byte[] data, int length) { - var encoderProps = new LzmaEncoderProperties(eos: false, _dictionarySize, _numFastBytes); - - var encoder = new Encoder(); - encoder.SetCoderProperties(encoderProps.PropIDs, encoderProps.Properties); - - // Capture the LZMA properties byte (pb/lp/lc encoding) for the chunk header - if (!_lzmaPropertiesKnown) - { - var propBytes = new byte[5]; - encoder.WriteCoderProperties(propBytes); - _lzmaPropertiesByte = propBytes[0]; - _lzmaPropertiesKnown = true; - } - - using var inputMs = new MemoryStream(data.ToArray(), writable: false); + using var inputMs = new MemoryStream(data, 0, length, writable: false); using var outputMs = new PooledMemoryStream(); - encoder.Code(inputMs, outputMs, data.Length, -1, null); + _encoder.Code(inputMs, outputMs, length, -1, null); var fullCompressed = outputMs.ToArray(); // The LZMA range encoder flush writes trailing bytes the decoder doesn't consume. // Trial-decode to find the exact byte count the decoder needs, so the LZMA2 // chunk header reports a compressed size that matches what the decoder reads. - var consumed = FindConsumedBytes(fullCompressed, data.Length); - if (consumed < fullCompressed.Length) - { - return fullCompressed.AsSpan(0, consumed).ToArray(); - } - - return fullCompressed; + var consumed = FindConsumedBytes(fullCompressed, fullCompressed.Length, length); + return (fullCompressed, consumed); } - private int FindConsumedBytes(byte[] compressedData, int uncompressedSize) + private int FindConsumedBytes(byte[] compressedData, int compressedSize, int uncompressedSize) { // Build 5-byte LZMA property header: [pb/lp/lc byte] [dictSize as LE int32] - var props = new byte[5]; + Span props = stackalloc byte[5]; props[0] = _lzmaPropertiesByte; props[1] = (byte)_dictionarySize; props[2] = (byte)(_dictionarySize >> 8); @@ -308,9 +312,8 @@ private int FindConsumedBytes(byte[] compressedData, int uncompressedSize) var decoder = new Decoder(); decoder.SetDecoderProperties(props); - using var input = new MemoryStream(compressedData); - using var output = new PooledMemoryStream(); - decoder.Code(input, output, compressedData.Length, uncompressedSize, null); + using var input = new MemoryStream(compressedData, 0, compressedSize, writable: false); + decoder.Code(input, Stream.Null, compressedSize, uncompressedSize, null); return (int)input.Position; } @@ -319,10 +322,14 @@ private int FindConsumedBytes(byte[] compressedData, int uncompressedSize) /// Writes a compressed LZMA2 chunk. /// Header: [control] [uncompSize_hi] [uncompSize_lo] [compSize_hi] [compSize_lo] [props?] /// - private void WriteCompressedChunk(int uncompressedSize, byte[] compressedData) + private void WriteCompressedChunk( + int uncompressedSize, + byte[] compressedData, + int compressedSize + ) { var uncompSizeMinus1 = uncompressedSize - 1; - var compSizeMinus1 = compressedData.Length - 1; + var compSizeMinus1 = compressedSize - 1; // Each chunk is compressed independently with a fresh LZMA encoder, // so we must use 0xE0 (full reset: dictionary + state + properties) every time. @@ -341,32 +348,38 @@ private void WriteCompressedChunk(int uncompressedSize, byte[] compressedData) // 0xE0 (>= 0xC0) requires properties byte _output.WriteByte(_lzmaPropertiesByte); - _output.Write(compressedData, 0, compressedData.Length); + _output.Write(compressedData, 0, compressedSize); } private async ValueTask WriteCompressedChunkAsync( int uncompressedSize, byte[] compressedData, + int compressedSize, CancellationToken cancellationToken = default ) { var uncompSizeMinus1 = uncompressedSize - 1; - var compSizeMinus1 = compressedData.Length - 1; + var compSizeMinus1 = compressedSize - 1; var control = (byte)(0xE0 | ((uncompSizeMinus1 >> 16) & 0x1F)); _isFirstChunk = false; - var header = new[] + var header = ArrayPool.Shared.Rent(6); + try + { + header[0] = control; + header[1] = (byte)((uncompSizeMinus1 >> 8) & 0xFF); + header[2] = (byte)(uncompSizeMinus1 & 0xFF); + header[3] = (byte)((compSizeMinus1 >> 8) & 0xFF); + header[4] = (byte)(compSizeMinus1 & 0xFF); + header[5] = _lzmaPropertiesByte; + await _output.WriteAsync(header, 0, 6, cancellationToken).ConfigureAwait(false); + } + finally { - control, - (byte)((uncompSizeMinus1 >> 8) & 0xFF), - (byte)(uncompSizeMinus1 & 0xFF), - (byte)((compSizeMinus1 >> 8) & 0xFF), - (byte)(compSizeMinus1 & 0xFF), - _lzmaPropertiesByte, - }; - await _output.WriteAsync(header, 0, header.Length, cancellationToken).ConfigureAwait(false); + ArrayPool.Shared.Return(header); + } await _output - .WriteAsync(compressedData, 0, compressedData.Length, cancellationToken) + .WriteAsync(compressedData, 0, compressedSize, cancellationToken) .ConfigureAwait(false); } @@ -426,20 +439,37 @@ private async ValueTask WriteUncompressedChunksAsync( control = 0x02; } - var header = new[] + var header = ArrayPool.Shared.Rent(3); + try { - control, - (byte)((sizeMinus1 >> 8) & 0xFF), - (byte)(sizeMinus1 & 0xFF), - }; - await _output - .WriteAsync(header, 0, header.Length, cancellationToken) - .ConfigureAwait(false); + header[0] = control; + header[1] = (byte)((sizeMinus1 >> 8) & 0xFF); + header[2] = (byte)(sizeMinus1 & 0xFF); + await _output.WriteAsync(header, 0, 3, cancellationToken).ConfigureAwait(false); + } + finally + { + ArrayPool.Shared.Return(header); + } - var chunk = data.Slice(offset, chunkSize).ToArray(); +#if !LEGACY_DOTNET || NETSTANDARD2_1 await _output - .WriteAsync(chunk, 0, chunk.Length, cancellationToken) + .WriteAsync(data.Slice(offset, chunkSize), cancellationToken) .ConfigureAwait(false); +#else + var chunk = ArrayPool.Shared.Rent(chunkSize); + try + { + data.Slice(offset, chunkSize).CopyTo(chunk); + await _output + .WriteAsync(chunk, 0, chunkSize, cancellationToken) + .ConfigureAwait(false); + } + finally + { + ArrayPool.Shared.Return(chunk); + } +#endif offset += chunkSize; } } diff --git a/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs b/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs index 49a0e4c28..327aa3432 100644 --- a/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs +++ b/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs @@ -446,7 +446,10 @@ internal bool Code(int dictionarySize, OutWindow outWindow, RangeCoder.Decoder r return false; } - public void SetDecoderProperties(byte[] properties) + public void SetDecoderProperties(byte[] properties) => + SetDecoderProperties(properties.AsSpan()); + + internal void SetDecoderProperties(ReadOnlySpan properties) { if (properties.Length < 1) { diff --git a/src/SharpCompress/Compressors/LZMA/LzmaEncoder.cs b/src/SharpCompress/Compressors/LZMA/LzmaEncoder.cs index 14d947079..d2e3e0d22 100644 --- a/src/SharpCompress/Compressors/LZMA/LzmaEncoder.cs +++ b/src/SharpCompress/Compressors/LZMA/LzmaEncoder.cs @@ -10,7 +10,7 @@ namespace SharpCompress.Compressors.LZMA; -internal partial class Encoder : ICoder, ISetCoderProperties, IWriteCoderProperties +internal partial class Encoder : ICoder, ISetCoderProperties, IWriteCoderProperties, IDisposable { private enum EMatchFinderType { @@ -574,6 +574,12 @@ public Encoder() } } + public void Dispose() + { + _matchFinder?.Dispose(); + _matchFinder = null; + } + private void SetWriteEndMarkerMode(bool writeEndMarker) => _writeEndMark = writeEndMarker; private void Init() diff --git a/src/SharpCompress/Compressors/LZMA/LzmaStream.Async.cs b/src/SharpCompress/Compressors/LZMA/LzmaStream.Async.cs index 3adef73d7..4ad301270 100644 --- a/src/SharpCompress/Compressors/LZMA/LzmaStream.Async.cs +++ b/src/SharpCompress/Compressors/LZMA/LzmaStream.Async.cs @@ -83,11 +83,11 @@ Stream outputStream private async ValueTask DecodeChunkHeaderAsync(CancellationToken cancellationToken = default) { - var controlBuffer = new byte[1]; + var headerBuffer = GetAsyncHeaderBuffer(); await _inputStream! - .ReadExactAsync(controlBuffer, 0, 1, cancellationToken) + .ReadExactAsync(headerBuffer, 0, 1, cancellationToken) .ConfigureAwait(false); - var control = controlBuffer[0]; + var control = headerBuffer[0]; _inputPosition++; if (control == 0x00) @@ -112,26 +112,25 @@ await _inputStream! _uncompressedChunk = false; _availableBytes = (control & 0x1F) << 16; - var buffer = new byte[2]; await _inputStream! - .ReadExactAsync(buffer, 0, 2, cancellationToken) + .ReadExactAsync(headerBuffer, 0, 2, cancellationToken) .ConfigureAwait(false); - _availableBytes += (buffer[0] << 8) + buffer[1] + 1; + _availableBytes += (headerBuffer[0] << 8) + headerBuffer[1] + 1; _inputPosition += 2; await _inputStream! - .ReadExactAsync(buffer, 0, 2, cancellationToken) + .ReadExactAsync(headerBuffer, 0, 2, cancellationToken) .ConfigureAwait(false); - _rangeDecoderLimit = (buffer[0] << 8) + buffer[1] + 1; + _rangeDecoderLimit = (headerBuffer[0] << 8) + headerBuffer[1] + 1; _inputPosition += 2; if (control >= 0xC0) { _needProps = false; await _inputStream! - .ReadExactAsync(controlBuffer, 0, 1, cancellationToken) + .ReadExactAsync(headerBuffer, 0, 1, cancellationToken) .ConfigureAwait(false); - Properties[0] = controlBuffer[0]; + Properties[0] = headerBuffer[0]; _inputPosition++; _decoder = new Decoder(); @@ -156,11 +155,10 @@ await _inputStream! else { _uncompressedChunk = true; - var buffer = new byte[2]; await _inputStream! - .ReadExactAsync(buffer, 0, 2, cancellationToken) + .ReadExactAsync(headerBuffer, 0, 2, cancellationToken) .ConfigureAwait(false); - _availableBytes = (buffer[0] << 8) + buffer[1] + 1; + _availableBytes = (headerBuffer[0] << 8) + headerBuffer[1] + 1; _inputPosition += 2; } } @@ -436,6 +434,7 @@ public async ValueTask DisposeAsync() if (_encoder != null) { _position = await _encoder.CodeAsync(null, true).ConfigureAwait(false); + _encoder.Dispose(); } if (!_leaveOpen) @@ -450,6 +449,7 @@ public async ValueTask DisposeAsync() } } await _outWindow.DisposeAsync().ConfigureAwait(false); + ReturnAsyncHeaderBuffer(); #if !LEGACY_DOTNET || NETSTANDARD2_1 await base.DisposeAsync().ConfigureAwait(false); diff --git a/src/SharpCompress/Compressors/LZMA/LzmaStream.cs b/src/SharpCompress/Compressors/LZMA/LzmaStream.cs index 12f6884e0..38bb2eca3 100644 --- a/src/SharpCompress/Compressors/LZMA/LzmaStream.cs +++ b/src/SharpCompress/Compressors/LZMA/LzmaStream.cs @@ -1,4 +1,5 @@ using System; +using System.Buffers; using System.Buffers.Binary; using System.IO; using System.Threading; @@ -33,6 +34,7 @@ public partial class LzmaStream : Stream, IStreamStack, IAsyncDisposable private bool _needProps = true; private readonly Encoder? _encoder; + private byte[]? _asyncHeaderBuffer; private bool _isDisposed; private LzmaStream( @@ -205,16 +207,31 @@ protected override void Dispose(bool disposing) if (_encoder != null) { _position = _encoder.Code(null, true); + _encoder.Dispose(); } if (!_leaveOpen) { _inputStream?.Dispose(); } _outWindow.Dispose(); + ReturnAsyncHeaderBuffer(); } base.Dispose(disposing); } + private byte[] GetAsyncHeaderBuffer() => _asyncHeaderBuffer ??= ArrayPool.Shared.Rent(6); + + private void ReturnAsyncHeaderBuffer() + { + if (_asyncHeaderBuffer is null) + { + return; + } + + ArrayPool.Shared.Return(_asyncHeaderBuffer); + _asyncHeaderBuffer = null; + } + public override long Length => _position + _availableBytes; public override long Position diff --git a/src/SharpCompress/Compressors/LZMA/RangeCoder/RangeCoder.Async.cs b/src/SharpCompress/Compressors/LZMA/RangeCoder/RangeCoder.Async.cs index cd3fc6a50..61db6eaec 100644 --- a/src/SharpCompress/Compressors/LZMA/RangeCoder/RangeCoder.Async.cs +++ b/src/SharpCompress/Compressors/LZMA/RangeCoder/RangeCoder.Async.cs @@ -9,6 +9,8 @@ namespace SharpCompress.Compressors.LZMA.RangeCoder; internal partial class Encoder { + private byte[] SingleByteBuffer => _singleByteBuffer ??= new byte[1]; + public async ValueTask ShiftLowAsync(CancellationToken cancellationToken = default) { if ((uint)_low < 0xFF000000 || (uint)(_low >> 32) == 1) @@ -17,7 +19,8 @@ public async ValueTask ShiftLowAsync(CancellationToken cancellationToken = defau do { var b = (byte)(temp + (_low >> 32)); - var buffer = new[] { b }; + var buffer = SingleByteBuffer; + buffer[0] = b; await _stream.WriteAsync(buffer, 0, 1, cancellationToken).ConfigureAwait(false); temp = 0xFF; } while (--_cacheSize != 0); @@ -78,13 +81,15 @@ public async ValueTask FlushStreamAsync(CancellationToken cancellationToken = de internal partial class Decoder { + private byte[] SingleByteBuffer => _singleByteBuffer ??= new byte[1]; + public async ValueTask InitAsync(Stream stream, CancellationToken cancellationToken = default) { _stream = stream; _code = 0; _range = 0xFFFFFFFF; - var buffer = new byte[1]; + var buffer = SingleByteBuffer; for (var i = 0; i < 5; i++) { var read = await _stream @@ -103,7 +108,7 @@ public async ValueTask NormalizeAsync(CancellationToken cancellationToken = defa { while (_range < K_TOP_VALUE) { - var buffer = new byte[1]; + var buffer = SingleByteBuffer; var read = await _stream .ReadAsync(buffer, 0, 1, cancellationToken) .ConfigureAwait(false); @@ -121,7 +126,7 @@ public async ValueTask Normalize2Async(CancellationToken cancellationToken = def { if (_range < K_TOP_VALUE) { - var buffer = new byte[1]; + var buffer = SingleByteBuffer; var read = await _stream .ReadAsync(buffer, 0, 1, cancellationToken) .ConfigureAwait(false); @@ -143,7 +148,7 @@ public async ValueTask DecodeDirectBitsAsync( var range = _range; var code = _code; uint result = 0; - var buffer = new byte[1]; + var buffer = SingleByteBuffer; for (var i = numTotalBits; i > 0; i--) { range >>= 1; diff --git a/src/SharpCompress/Compressors/LZMA/RangeCoder/RangeCoder.cs b/src/SharpCompress/Compressors/LZMA/RangeCoder/RangeCoder.cs index e29a1c375..518fa88e8 100644 --- a/src/SharpCompress/Compressors/LZMA/RangeCoder/RangeCoder.cs +++ b/src/SharpCompress/Compressors/LZMA/RangeCoder/RangeCoder.cs @@ -15,8 +15,7 @@ internal partial class Encoder public uint _range; private uint _cacheSize; private byte _cache; - - //long StartPosition; + private byte[] _singleByteBuffer; public void SetStream(Stream stream) => _stream = stream; @@ -88,6 +87,7 @@ internal partial class Decoder public Stream _stream; public long _total; + private byte[] _singleByteBuffer; public void Init(Stream stream) { @@ -180,6 +180,4 @@ public uint DecodeBit(uint size0, int numTotalBits) } public bool IsFinished => _code == 0; - - // ulong GetProcessedSize() {return Stream.GetProcessedSize(); } } diff --git a/src/SharpCompress/packages.lock.json b/src/SharpCompress/packages.lock.json index e1e7afa51..738df5991 100644 --- a/src/SharpCompress/packages.lock.json +++ b/src/SharpCompress/packages.lock.json @@ -286,9 +286,9 @@ "net10.0": { "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[10.0.8, )", - "resolved": "10.0.8", - "contentHash": "dVbSXGIFNR5nZcv2tOLoWI+a9T4jtFd77IYjuND+QVe360qWgAF7H0WtoopYhRw/+SgpGUTyrkrh+65+ClNnfw==" + "requested": "[10.0.6, )", + "resolved": "10.0.6", + "contentHash": "QKuvS0LWX4fjFqeDkyM7Kqt8P3wYTiPD4nwU+9y59n0sCiG714fxDgbbN82vDnzq89AF/PiHl92TP2C4aFDUQA==" }, "Microsoft.NETFramework.ReferenceAssemblies": { "type": "Direct", @@ -388,9 +388,9 @@ "net8.0": { "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[8.0.27, )", - "resolved": "8.0.27", - "contentHash": "rQi9TxifHRnXP7lVRZH05DxD2/XGbJp12q0ozcbrlBlBnyyzssFTH/2vLhtKWUp2CT1qVscTrcYTFiwTyKPKRg==" + "requested": "[8.0.26, )", + "resolved": "8.0.26", + "contentHash": "o7/yVssM2r9Wyln2s9edBd5ANZXqdSdBI+g7JqXkyJmXrhs2WsJp25K5yPnYrTgdKBCjKB8bg+O2oew4sgzFaA==" }, "Microsoft.NETFramework.ReferenceAssemblies": { "type": "Direct", diff --git a/tests/SharpCompress.Performance/Program.cs b/tests/SharpCompress.Performance/Program.cs index 856a0f9fe..ab940ed66 100644 --- a/tests/SharpCompress.Performance/Program.cs +++ b/tests/SharpCompress.Performance/Program.cs @@ -33,7 +33,7 @@ public static async Task Main(string[] args) private static async Task RunWithProfiler(string[] args) { var profileType = "cpu"; // Default to CPU profiling - var outputPath = "./profiler-snapshots";l + var outputPath = "./profiler-snapshots"; // Parse arguments for (int i = 1; i < args.Length; i++) From 0518137a1ec8f2681eeecd8bf958aeb5051ecc64 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Thu, 28 May 2026 06:47:22 +0100 Subject: [PATCH 03/11] Changed: - Optimal is now a struct array, removing 4096 per-encoder object allocations. - Encoder literal models are flattened into one pooled contiguous BitEncoder[]. - Decoder literal models are flattened into one non-pooled contiguous BitDecoder[]. - Updated async decoder paths to use the flattened model base index. - Fixed LzBinTree pooled buffer nullability cleanup. --- .../Compressors/LZMA/LZ/LzBinTree.cs | 16 ++--- .../Compressors/LZMA/LzmaDecoder.Async.cs | 6 +- .../Compressors/LZMA/LzmaDecoder.cs | 21 ++++-- .../Compressors/LZMA/LzmaEncoder.cs | 66 ++++++++++++------- 4 files changed, 70 insertions(+), 39 deletions(-) diff --git a/src/SharpCompress/Compressors/LZMA/LZ/LzBinTree.cs b/src/SharpCompress/Compressors/LZMA/LZ/LzBinTree.cs index d09a1e183..85795e742 100644 --- a/src/SharpCompress/Compressors/LZMA/LZ/LzBinTree.cs +++ b/src/SharpCompress/Compressors/LZMA/LZ/LzBinTree.cs @@ -12,8 +12,8 @@ internal sealed class BinTree : InWindow private uint _cyclicBufferSize; private uint _matchMaxLen; - private uint[] _son; - private uint[] _hash; + private uint[] _son = []; + private uint[] _hash = []; private uint _cutValue = 0xFF; private uint _hashMask; @@ -109,7 +109,7 @@ uint keepAddBufferAfter var cyclicBufferSize = historySize + 1; if (_cyclicBufferSize != cyclicBufferSize) { - if (_son is not null) + if (_son.Length != 0) { ArrayPool.Shared.Return(_son); } @@ -138,7 +138,7 @@ uint keepAddBufferAfter } if (hs != _hashSizeSum) { - if (_hash is not null) + if (_hash.Length != 0) { ArrayPool.Shared.Return(_hash); } @@ -150,15 +150,15 @@ uint keepAddBufferAfter public override void Dispose() { base.Dispose(); - if (_son is not null) + if (_son.Length != 0) { ArrayPool.Shared.Return(_son); - _son = null; + _son = []; } - if (_hash is not null) + if (_hash.Length != 0) { ArrayPool.Shared.Return(_hash); - _hash = null; + _hash = []; } } diff --git a/src/SharpCompress/Compressors/LZMA/LzmaDecoder.Async.cs b/src/SharpCompress/Compressors/LZMA/LzmaDecoder.Async.cs index 18e1475f2..7b81b8925 100644 --- a/src/SharpCompress/Compressors/LZMA/LzmaDecoder.Async.cs +++ b/src/SharpCompress/Compressors/LZMA/LzmaDecoder.Async.cs @@ -72,7 +72,7 @@ public async ValueTask DecodeNormalAsync( { symbol = (symbol << 1) - | await _decoders[symbol] + | await _decoders[_baseIndex + symbol] .DecodeAsync(rangeDecoder, cancellationToken) .ConfigureAwait(false); } while (symbol < 0x100); @@ -90,7 +90,7 @@ public async ValueTask DecodeWithMatchByteAsync( { var matchBit = (uint)(matchByte >> 7) & 1; matchByte <<= 1; - var bit = await _decoders[((1 + matchBit) << 8) + symbol] + var bit = await _decoders[_baseIndex + ((1 + matchBit) << 8) + symbol] .DecodeAsync(rangeDecoder, cancellationToken) .ConfigureAwait(false); symbol = (symbol << 1) | bit; @@ -100,7 +100,7 @@ public async ValueTask DecodeWithMatchByteAsync( { symbol = (symbol << 1) - | await _decoders[symbol] + | await _decoders[_baseIndex + symbol] .DecodeAsync(rangeDecoder, cancellationToken) .ConfigureAwait(false); } diff --git a/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs b/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs index 327aa3432..e6262e0b6 100644 --- a/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs +++ b/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs @@ -73,14 +73,19 @@ private partial class LiteralDecoder private partial struct Decoder2 { private BitDecoder[] _decoders; + private int _baseIndex; - public void Create() => _decoders = new BitDecoder[0x300]; + public void Create(BitDecoder[] decoders, int baseIndex) + { + _decoders = decoders; + _baseIndex = baseIndex; + } public void Init() { for (var i = 0; i < 0x300; i++) { - _decoders[i].Init(); + _decoders[_baseIndex + i].Init(); } } @@ -89,7 +94,7 @@ public byte DecodeNormal(RangeCoder.Decoder rangeDecoder) uint symbol = 1; do { - symbol = (symbol << 1) | _decoders[symbol].Decode(rangeDecoder); + symbol = (symbol << 1) | _decoders[_baseIndex + symbol].Decode(rangeDecoder); } while (symbol < 0x100); return (byte)symbol; } @@ -101,13 +106,15 @@ public byte DecodeWithMatchByte(RangeCoder.Decoder rangeDecoder, byte matchByte) { var matchBit = (uint)(matchByte >> 7) & 1; matchByte <<= 1; - var bit = _decoders[((1 + matchBit) << 8) + symbol].Decode(rangeDecoder); + var bit = _decoders[_baseIndex + ((1 + matchBit) << 8) + symbol] + .Decode(rangeDecoder); symbol = (symbol << 1) | bit; if (matchBit != bit) { while (symbol < 0x100) { - symbol = (symbol << 1) | _decoders[symbol].Decode(rangeDecoder); + symbol = + (symbol << 1) | _decoders[_baseIndex + symbol].Decode(rangeDecoder); } break; } @@ -117,6 +124,7 @@ public byte DecodeWithMatchByte(RangeCoder.Decoder rangeDecoder, byte matchByte) } private Decoder2[] _coders; + private BitDecoder[] _models; private int _numPrevBits; private int _numPosBits; private uint _posMask; @@ -131,10 +139,11 @@ public void Create(int numPosBits, int numPrevBits) _posMask = ((uint)1 << numPosBits) - 1; _numPrevBits = numPrevBits; var numStates = (uint)1 << (_numPrevBits + _numPosBits); + _models = new BitDecoder[numStates * 0x300]; _coders = new Decoder2[numStates]; for (uint i = 0; i < numStates; i++) { - _coders[i].Create(); + _coders[i].Create(_models, checked((int)(i * 0x300))); } } diff --git a/src/SharpCompress/Compressors/LZMA/LzmaEncoder.cs b/src/SharpCompress/Compressors/LZMA/LzmaEncoder.cs index d2e3e0d22..16e9b6914 100644 --- a/src/SharpCompress/Compressors/LZMA/LzmaEncoder.cs +++ b/src/SharpCompress/Compressors/LZMA/LzmaEncoder.cs @@ -1,6 +1,7 @@ #nullable disable using System; +using System.Buffers; using System.IO; using System.Threading; using System.Threading.Tasks; @@ -86,14 +87,19 @@ private partial class LiteralEncoder public partial struct Encoder2 { private BitEncoder[] _encoders; + private int _baseIndex; - public void Create() => _encoders = new BitEncoder[0x300]; + public void Create(BitEncoder[] encoders, int baseIndex) + { + _encoders = encoders; + _baseIndex = baseIndex; + } public void Init() { for (var i = 0; i < 0x300; i++) { - _encoders[i].Init(); + _encoders[_baseIndex + i].Init(); } } @@ -103,7 +109,7 @@ public void Encode(RangeCoder.Encoder rangeEncoder, byte symbol) for (var i = 7; i >= 0; i--) { var bit = (uint)((symbol >> i) & 1); - _encoders[context].Encode(rangeEncoder, bit); + _encoders[_baseIndex + context].Encode(rangeEncoder, bit); context = (context << 1) | bit; } } @@ -118,7 +124,7 @@ public async ValueTask EncodeAsync( for (var i = 7; i >= 0; i--) { var bit = (uint)((symbol >> i) & 1); - await _encoders[context] + await _encoders[_baseIndex + context] .EncodeAsync(rangeEncoder, bit, cancellationToken) .ConfigureAwait(false); context = (context << 1) | bit; @@ -139,7 +145,7 @@ public void EncodeMatched(RangeCoder.Encoder rangeEncoder, byte matchByte, byte state += ((1 + matchBit) << 8); same = (matchBit == bit); } - _encoders[state].Encode(rangeEncoder, bit); + _encoders[_baseIndex + state].Encode(rangeEncoder, bit); context = (context << 1) | bit; } } @@ -163,7 +169,7 @@ public async ValueTask EncodeMatchedAsync( state += ((1 + matchBit) << 8); same = matchBit == bit; } - await _encoders[state] + await _encoders[_baseIndex + state] .EncodeAsync(rangeEncoder, bit, cancellationToken) .ConfigureAwait(false); context = (context << 1) | bit; @@ -181,7 +187,8 @@ public uint GetPrice(bool matchMode, byte matchByte, byte symbol) { var matchBit = (uint)(matchByte >> i) & 1; var bit = (uint)(symbol >> i) & 1; - price += _encoders[((1 + matchBit) << 8) + context].GetPrice(bit); + price += _encoders[_baseIndex + ((1 + matchBit) << 8) + context] + .GetPrice(bit); context = (context << 1) | bit; if (matchBit != bit) { @@ -193,7 +200,7 @@ public uint GetPrice(bool matchMode, byte matchByte, byte symbol) for (; i >= 0; i--) { var bit = (uint)(symbol >> i) & 1; - price += _encoders[context].GetPrice(bit); + price += _encoders[_baseIndex + context].GetPrice(bit); context = (context << 1) | bit; } return price; @@ -201,6 +208,7 @@ public uint GetPrice(bool matchMode, byte matchByte, byte symbol) } private Encoder2[] _coders; + private BitEncoder[] _models; private int _numPrevBits; private int _numPosBits; private uint _posMask; @@ -215,13 +223,30 @@ public void Create(int numPosBits, int numPrevBits) _posMask = ((uint)1 << numPosBits) - 1; _numPrevBits = numPrevBits; var numStates = (uint)1 << (_numPrevBits + _numPosBits); + if (_models is not null) + { + ArrayPool.Shared.Return(_models); + } + _models = ArrayPool.Shared.Rent(checked((int)(numStates * 0x300))); _coders = new Encoder2[numStates]; for (uint i = 0; i < numStates; i++) { - _coders[i].Create(); + _coders[i].Create(_models, checked((int)(i * 0x300))); } } + public void Dispose() + { + if (_models is null) + { + return; + } + + ArrayPool.Shared.Return(_models); + _models = null; + _coders = null; + } + public void Init() { var numStates = (uint)1 << (_numPrevBits + _numPosBits); @@ -423,7 +448,7 @@ await base.EncodeAsync(rangeEncoder, symbol, posState, cancellationToken) private const uint K_NUM_OPTS = 1 << 12; - private class Optimal + private struct Optimal { public Base.State _state; @@ -564,10 +589,6 @@ private void Create() public Encoder() { - for (var i = 0; i < K_NUM_OPTS; i++) - { - _optimum[i] = new Optimal(); - } for (var i = 0; i < Base.K_NUM_LEN_TO_POS_STATES; i++) { _posSlotEncoder[i] = new BitTreeEncoder(Base.K_NUM_POS_SLOT_BITS); @@ -576,6 +597,7 @@ public Encoder() public void Dispose() { + _literalEncoder.Dispose(); _matchFinder?.Dispose(); _matchFinder = null; } @@ -865,7 +887,7 @@ private uint GetOptimum(uint position, out uint backRes) do { var curAndLenPrice = price + _repMatchLenEncoder.GetPrice(repLen - 2, posState); - var optimum = _optimum[repLen]; + ref var optimum = ref _optimum[repLen]; if (curAndLenPrice < optimum._price) { optimum._price = curAndLenPrice; @@ -890,7 +912,7 @@ private uint GetOptimum(uint position, out uint backRes) { var distance = _matchDistances[offs + 1]; var curAndLenPrice = normalMatchPrice + GetPosLenPrice(distance, len, posState); - var optimum = _optimum[len]; + ref var optimum = ref _optimum[len]; if (curAndLenPrice < optimum._price) { optimum._price = curAndLenPrice; @@ -1045,7 +1067,7 @@ private uint GetOptimum(uint position, out uint backRes) .GetSubCoder(position, _matchFinder.GetIndexByte(0 - 2)) .GetPrice(!state.IsCharState(), matchByte, currentByte); - var nextOptimum = _optimum[cur + 1]; + ref var nextOptimum = ref _optimum[cur + 1]; var nextIsChar = false; if (curAnd1Price < nextOptimum._price) @@ -1111,7 +1133,7 @@ private uint GetOptimum(uint position, out uint backRes) } var curAndLenPrice = nextRepMatchPrice + GetRepPrice(0, lenTest2, state2, posStateNext); - var optimum = _optimum[offset]; + ref var optimum = ref _optimum[offset]; if (curAndLenPrice < optimum._price) { optimum._price = curAndLenPrice; @@ -1142,7 +1164,7 @@ private uint GetOptimum(uint position, out uint backRes) } var curAndLenPrice = repMatchPrice + GetRepPrice(repIndex, lenTest, state, posState); - var optimum = _optimum[cur + lenTest]; + ref var optimum = ref _optimum[cur + lenTest]; if (curAndLenPrice < optimum._price) { optimum._price = curAndLenPrice; @@ -1206,7 +1228,7 @@ private uint GetOptimum(uint position, out uint backRes) } var curAndLenPrice = nextRepMatchPrice + GetRepPrice(0, lenTest2, state2, posStateNext); - var optimum = _optimum[cur + offset]; + ref var optimum = ref _optimum[cur + offset]; if (curAndLenPrice < optimum._price) { optimum._price = curAndLenPrice; @@ -1255,7 +1277,7 @@ private uint GetOptimum(uint position, out uint backRes) var curBack = _matchDistances[offs + 1]; var curAndLenPrice = normalMatchPrice + GetPosLenPrice(curBack, lenTest, posState); - var optimum = _optimum[cur + lenTest]; + ref var optimum = ref _optimum[cur + lenTest]; if (curAndLenPrice < optimum._price) { optimum._price = curAndLenPrice; @@ -1314,7 +1336,7 @@ private uint GetOptimum(uint position, out uint backRes) curAndLenPrice = nextRepMatchPrice + GetRepPrice(0, lenTest2, state2, posStateNext); - optimum = _optimum[cur + offset]; + optimum = ref _optimum[cur + offset]; if (curAndLenPrice < optimum._price) { optimum._price = curAndLenPrice; From 87a90dbb4ffca9ba4db244689cd2f7f08ed05355 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Mon, 1 Jun 2026 11:51:20 +0100 Subject: [PATCH 04/11] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/SharpCompress/Compressors/LZMA/RangeCoder/RangeCoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SharpCompress/Compressors/LZMA/RangeCoder/RangeCoder.cs b/src/SharpCompress/Compressors/LZMA/RangeCoder/RangeCoder.cs index 518fa88e8..147146c44 100644 --- a/src/SharpCompress/Compressors/LZMA/RangeCoder/RangeCoder.cs +++ b/src/SharpCompress/Compressors/LZMA/RangeCoder/RangeCoder.cs @@ -15,7 +15,7 @@ internal partial class Encoder public uint _range; private uint _cacheSize; private byte _cache; - private byte[] _singleByteBuffer; + private byte[] _singleByteBuffer = new byte[1]; public void SetStream(Stream stream) => _stream = stream; From 017d545bc3a5341d9ad7b6d7e3dd626918b4c2e7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 1 Jun 2026 10:56:24 +0000 Subject: [PATCH 05/11] Optimize LZMA literal encoder buffer reuse --- .../Compressors/LZMA/LzmaEncoder.cs | 15 +++++++--- src/SharpCompress/packages.lock.json | 12 ++++---- .../SharpCompress.AotSmoke/packages.lock.json | 28 +++++++++---------- 3 files changed, 31 insertions(+), 24 deletions(-) diff --git a/src/SharpCompress/Compressors/LZMA/LzmaEncoder.cs b/src/SharpCompress/Compressors/LZMA/LzmaEncoder.cs index 16e9b6914..c8d5279ba 100644 --- a/src/SharpCompress/Compressors/LZMA/LzmaEncoder.cs +++ b/src/SharpCompress/Compressors/LZMA/LzmaEncoder.cs @@ -223,12 +223,19 @@ public void Create(int numPosBits, int numPrevBits) _posMask = ((uint)1 << numPosBits) - 1; _numPrevBits = numPrevBits; var numStates = (uint)1 << (_numPrevBits + _numPosBits); - if (_models is not null) + var requiredModelLength = checked((int)(numStates * 0x300)); + if (_models is null || _models.Length < requiredModelLength) { - ArrayPool.Shared.Return(_models); + if (_models is not null) + { + ArrayPool.Shared.Return(_models); + } + _models = ArrayPool.Shared.Rent(requiredModelLength); + } + if (_coders is null || _coders.Length != numStates) + { + _coders = new Encoder2[numStates]; } - _models = ArrayPool.Shared.Rent(checked((int)(numStates * 0x300))); - _coders = new Encoder2[numStates]; for (uint i = 0; i < numStates; i++) { _coders[i].Create(_models, checked((int)(i * 0x300))); diff --git a/src/SharpCompress/packages.lock.json b/src/SharpCompress/packages.lock.json index 738df5991..e1e7afa51 100644 --- a/src/SharpCompress/packages.lock.json +++ b/src/SharpCompress/packages.lock.json @@ -286,9 +286,9 @@ "net10.0": { "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[10.0.6, )", - "resolved": "10.0.6", - "contentHash": "QKuvS0LWX4fjFqeDkyM7Kqt8P3wYTiPD4nwU+9y59n0sCiG714fxDgbbN82vDnzq89AF/PiHl92TP2C4aFDUQA==" + "requested": "[10.0.8, )", + "resolved": "10.0.8", + "contentHash": "dVbSXGIFNR5nZcv2tOLoWI+a9T4jtFd77IYjuND+QVe360qWgAF7H0WtoopYhRw/+SgpGUTyrkrh+65+ClNnfw==" }, "Microsoft.NETFramework.ReferenceAssemblies": { "type": "Direct", @@ -388,9 +388,9 @@ "net8.0": { "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[8.0.26, )", - "resolved": "8.0.26", - "contentHash": "o7/yVssM2r9Wyln2s9edBd5ANZXqdSdBI+g7JqXkyJmXrhs2WsJp25K5yPnYrTgdKBCjKB8bg+O2oew4sgzFaA==" + "requested": "[8.0.27, )", + "resolved": "8.0.27", + "contentHash": "rQi9TxifHRnXP7lVRZH05DxD2/XGbJp12q0ozcbrlBlBnyyzssFTH/2vLhtKWUp2CT1qVscTrcYTFiwTyKPKRg==" }, "Microsoft.NETFramework.ReferenceAssemblies": { "type": "Direct", diff --git a/tests/SharpCompress.AotSmoke/packages.lock.json b/tests/SharpCompress.AotSmoke/packages.lock.json index 74a5d8021..776cf41a2 100644 --- a/tests/SharpCompress.AotSmoke/packages.lock.json +++ b/tests/SharpCompress.AotSmoke/packages.lock.json @@ -4,15 +4,15 @@ "net10.0": { "Microsoft.DotNet.ILCompiler": { "type": "Direct", - "requested": "[10.0.6, )", - "resolved": "10.0.6", - "contentHash": "nBOzxOys8OeyJ+Nsi/uYlI/5TSsvwjaM/p5m4dTL6khCLx9UuP3b2ec3HeuBw/+F7hHCAZG1yFx8VBeoRAX+EQ==" + "requested": "[10.0.8, )", + "resolved": "10.0.8", + "contentHash": "RJxitcN5CCyZDcPNXKLsecwKvACzmy8C1z8hGM9+hFcnPhv1jDysJFFIeUHIPWaZ6wDAfYtZcgKEtegvL2Nz8A==" }, "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[10.0.6, )", - "resolved": "10.0.6", - "contentHash": "QKuvS0LWX4fjFqeDkyM7Kqt8P3wYTiPD4nwU+9y59n0sCiG714fxDgbbN82vDnzq89AF/PiHl92TP2C4aFDUQA==" + "requested": "[10.0.8, )", + "resolved": "10.0.8", + "contentHash": "dVbSXGIFNR5nZcv2tOLoWI+a9T4jtFd77IYjuND+QVe360qWgAF7H0WtoopYhRw/+SgpGUTyrkrh+65+ClNnfw==" }, "Microsoft.NETFramework.ReferenceAssemblies": { "type": "Direct", @@ -64,20 +64,20 @@ "type": "Project" } }, - "net10.0/osx-arm64": { + "net10.0/linux-x64": { "Microsoft.DotNet.ILCompiler": { "type": "Direct", - "requested": "[10.0.6, )", - "resolved": "10.0.6", - "contentHash": "nBOzxOys8OeyJ+Nsi/uYlI/5TSsvwjaM/p5m4dTL6khCLx9UuP3b2ec3HeuBw/+F7hHCAZG1yFx8VBeoRAX+EQ==", + "requested": "[10.0.8, )", + "resolved": "10.0.8", + "contentHash": "RJxitcN5CCyZDcPNXKLsecwKvACzmy8C1z8hGM9+hFcnPhv1jDysJFFIeUHIPWaZ6wDAfYtZcgKEtegvL2Nz8A==", "dependencies": { - "runtime.osx-arm64.Microsoft.DotNet.ILCompiler": "10.0.6" + "runtime.linux-x64.Microsoft.DotNet.ILCompiler": "10.0.8" } }, - "runtime.osx-arm64.Microsoft.DotNet.ILCompiler": { + "runtime.linux-x64.Microsoft.DotNet.ILCompiler": { "type": "Transitive", - "resolved": "10.0.6", - "contentHash": "+yovwOAlIpfIcH+ZWmLYXWTSWYJ93wcQxF/RVk+X4MXgLASeosCJYVLqP20g0cufKjoRqvCmnklR6y9Su3ORtA==" + "resolved": "10.0.8", + "contentHash": "0jxyi69frgaqADCnEpHE+f65NoiRTAjfjvNDMOxWV77BumQ56eMDL4ECw29DcJTqwaYJQ92PqDS6y6CiLf7kgw==" } } } From 0a727e16e83427bfcc1953cecfdd01c1ad7e5b02 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Mon, 1 Jun 2026 13:21:04 +0100 Subject: [PATCH 06/11] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs b/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs index e6262e0b6..bdc189ff5 100644 --- a/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs +++ b/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs @@ -139,7 +139,7 @@ public void Create(int numPosBits, int numPrevBits) _posMask = ((uint)1 << numPosBits) - 1; _numPrevBits = numPrevBits; var numStates = (uint)1 << (_numPrevBits + _numPosBits); - _models = new BitDecoder[numStates * 0x300]; + _models = new BitDecoder[checked((int)(numStates * 0x300))]; _coders = new Decoder2[numStates]; for (uint i = 0; i < numStates; i++) { From 02c612382564df471e7bad9340769cb68d169d48 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 1 Jun 2026 12:34:27 +0000 Subject: [PATCH 07/11] Use fixed LZMA2 chunk capacity in write buffering --- .../Compressors/LZMA/Lzma2EncoderStream.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/SharpCompress/Compressors/LZMA/Lzma2EncoderStream.cs b/src/SharpCompress/Compressors/LZMA/Lzma2EncoderStream.cs index 73ce7b643..51e1b7535 100644 --- a/src/SharpCompress/Compressors/LZMA/Lzma2EncoderStream.cs +++ b/src/SharpCompress/Compressors/LZMA/Lzma2EncoderStream.cs @@ -78,13 +78,13 @@ public override void Write(byte[] buffer, int offset, int count) { while (count > 0) { - var toCopy = Math.Min(count, _buffer.Length - _bufferPosition); + var toCopy = Math.Min(count, MAX_UNCOMPRESSED_CHUNK_SIZE - _bufferPosition); Buffer.BlockCopy(buffer, offset, _buffer, _bufferPosition, toCopy); _bufferPosition += toCopy; offset += toCopy; count -= toCopy; - if (_bufferPosition == _buffer.Length) + if (_bufferPosition == MAX_UNCOMPRESSED_CHUNK_SIZE) { FlushChunk(); } @@ -102,13 +102,13 @@ CancellationToken cancellationToken { cancellationToken.ThrowIfCancellationRequested(); - var toCopy = Math.Min(count, _buffer.Length - _bufferPosition); + var toCopy = Math.Min(count, MAX_UNCOMPRESSED_CHUNK_SIZE - _bufferPosition); Buffer.BlockCopy(buffer, offset, _buffer, _bufferPosition, toCopy); _bufferPosition += toCopy; offset += toCopy; count -= toCopy; - if (_bufferPosition == _buffer.Length) + if (_bufferPosition == MAX_UNCOMPRESSED_CHUNK_SIZE) { await FlushChunkAsync(cancellationToken).ConfigureAwait(false); } @@ -127,13 +127,13 @@ public override async ValueTask WriteAsync( { cancellationToken.ThrowIfCancellationRequested(); - var toCopy = Math.Min(count, _buffer.Length - _bufferPosition); + var toCopy = Math.Min(count, MAX_UNCOMPRESSED_CHUNK_SIZE - _bufferPosition); buffer.Slice(offset, toCopy).Span.CopyTo(_buffer.AsSpan(_bufferPosition, toCopy)); _bufferPosition += toCopy; offset += toCopy; count -= toCopy; - if (_bufferPosition == _buffer.Length) + if (_bufferPosition == MAX_UNCOMPRESSED_CHUNK_SIZE) { await FlushChunkAsync(cancellationToken).ConfigureAwait(false); } From 4ed093a49245328f3e0eec16a8c81eb06534047f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 1 Jun 2026 12:37:31 +0000 Subject: [PATCH 08/11] Finalize review comment fix --- src/SharpCompress/packages.lock.json | 12 ++++---- .../SharpCompress.AotSmoke/packages.lock.json | 28 ++++--------------- 2 files changed, 12 insertions(+), 28 deletions(-) diff --git a/src/SharpCompress/packages.lock.json b/src/SharpCompress/packages.lock.json index e1e7afa51..05ccd77c7 100644 --- a/src/SharpCompress/packages.lock.json +++ b/src/SharpCompress/packages.lock.json @@ -286,9 +286,9 @@ "net10.0": { "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[10.0.8, )", - "resolved": "10.0.8", - "contentHash": "dVbSXGIFNR5nZcv2tOLoWI+a9T4jtFd77IYjuND+QVe360qWgAF7H0WtoopYhRw/+SgpGUTyrkrh+65+ClNnfw==" + "requested": "[10.0.0, )", + "resolved": "10.0.0", + "contentHash": "kICGrGYEzCNI3wPzfEXcwNHgTvlvVn9yJDhSdRK+oZQy4jvYH529u7O0xf5ocQKzOMjfS07+3z9PKRIjrFMJDA==" }, "Microsoft.NETFramework.ReferenceAssemblies": { "type": "Direct", @@ -388,9 +388,9 @@ "net8.0": { "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[8.0.27, )", - "resolved": "8.0.27", - "contentHash": "rQi9TxifHRnXP7lVRZH05DxD2/XGbJp12q0ozcbrlBlBnyyzssFTH/2vLhtKWUp2CT1qVscTrcYTFiwTyKPKRg==" + "requested": "[8.0.22, )", + "resolved": "8.0.22", + "contentHash": "MhcMithKEiyyNkD2ZfbDZPmcOdi0GheGfg8saEIIEfD/fol3iHmcV8TsZkD4ZYz5gdUuoX4YtlVySUU7Sxl9SQ==" }, "Microsoft.NETFramework.ReferenceAssemblies": { "type": "Direct", diff --git a/tests/SharpCompress.AotSmoke/packages.lock.json b/tests/SharpCompress.AotSmoke/packages.lock.json index 776cf41a2..33d459fa0 100644 --- a/tests/SharpCompress.AotSmoke/packages.lock.json +++ b/tests/SharpCompress.AotSmoke/packages.lock.json @@ -4,15 +4,15 @@ "net10.0": { "Microsoft.DotNet.ILCompiler": { "type": "Direct", - "requested": "[10.0.8, )", - "resolved": "10.0.8", - "contentHash": "RJxitcN5CCyZDcPNXKLsecwKvACzmy8C1z8hGM9+hFcnPhv1jDysJFFIeUHIPWaZ6wDAfYtZcgKEtegvL2Nz8A==" + "requested": "[10.0.0, )", + "resolved": "10.0.0", + "contentHash": "f9u8fMRROe2lS5MOOLutK6iSNTK9pC3kqd90FIn8Sk29fbZ0QDjZrBbwUkhouk/8dppC71SIEQaag0lGRTxvfA==" }, "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[10.0.8, )", - "resolved": "10.0.8", - "contentHash": "dVbSXGIFNR5nZcv2tOLoWI+a9T4jtFd77IYjuND+QVe360qWgAF7H0WtoopYhRw/+SgpGUTyrkrh+65+ClNnfw==" + "requested": "[10.0.0, )", + "resolved": "10.0.0", + "contentHash": "kICGrGYEzCNI3wPzfEXcwNHgTvlvVn9yJDhSdRK+oZQy4jvYH529u7O0xf5ocQKzOMjfS07+3z9PKRIjrFMJDA==" }, "Microsoft.NETFramework.ReferenceAssemblies": { "type": "Direct", @@ -63,22 +63,6 @@ "sharpcompress": { "type": "Project" } - }, - "net10.0/linux-x64": { - "Microsoft.DotNet.ILCompiler": { - "type": "Direct", - "requested": "[10.0.8, )", - "resolved": "10.0.8", - "contentHash": "RJxitcN5CCyZDcPNXKLsecwKvACzmy8C1z8hGM9+hFcnPhv1jDysJFFIeUHIPWaZ6wDAfYtZcgKEtegvL2Nz8A==", - "dependencies": { - "runtime.linux-x64.Microsoft.DotNet.ILCompiler": "10.0.8" - } - }, - "runtime.linux-x64.Microsoft.DotNet.ILCompiler": { - "type": "Transitive", - "resolved": "10.0.8", - "contentHash": "0jxyi69frgaqADCnEpHE+f65NoiRTAjfjvNDMOxWV77BumQ56eMDL4ECw29DcJTqwaYJQ92PqDS6y6CiLf7kgw==" - } } } } \ No newline at end of file From 07319f61bfa0bd1ef02874fa1a2f37776b7fa27c Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Mon, 1 Jun 2026 13:41:17 +0100 Subject: [PATCH 09/11] Minor changes --- src/SharpCompress/Common/Rar/RarCryptoWrapper.cs | 4 ++-- src/SharpCompress/Polyfills/AsyncEnumerableExtensions.cs | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/SharpCompress/Common/Rar/RarCryptoWrapper.cs b/src/SharpCompress/Common/Rar/RarCryptoWrapper.cs index 54ca27393..6e1ed222c 100644 --- a/src/SharpCompress/Common/Rar/RarCryptoWrapper.cs +++ b/src/SharpCompress/Common/Rar/RarCryptoWrapper.cs @@ -63,9 +63,9 @@ public override Task ReadAsync( int offset, int count, CancellationToken cancellationToken - ) => ReadAndDecryptAsync(buffer, offset, count, cancellationToken); + ) => ReadAndDecryptAsync(buffer, offset, count, cancellationToken).AsTask(); - private async Task ReadAndDecryptAsync( + private async ValueTask ReadAndDecryptAsync( byte[] buffer, int offset, int count, diff --git a/src/SharpCompress/Polyfills/AsyncEnumerableExtensions.cs b/src/SharpCompress/Polyfills/AsyncEnumerableExtensions.cs index 51b6bfc89..df716ecd9 100644 --- a/src/SharpCompress/Polyfills/AsyncEnumerableExtensions.cs +++ b/src/SharpCompress/Polyfills/AsyncEnumerableExtensions.cs @@ -20,7 +20,6 @@ public static class EnumerableExtensions { public static async IAsyncEnumerable ToAsyncEnumerable(this IEnumerable source) { - await Task.Yield(); foreach (var item in source) { yield return item; From 98b9d86a5cf000abe2686e77abc9e94a8c53396f Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Mon, 1 Jun 2026 14:25:38 +0100 Subject: [PATCH 10/11] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/SharpCompress/Compressors/LZMA/LZipStream.Async.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/SharpCompress/Compressors/LZMA/LZipStream.Async.cs b/src/SharpCompress/Compressors/LZMA/LZipStream.Async.cs index 054d5ac74..2cc7fb8d3 100644 --- a/src/SharpCompress/Compressors/LZMA/LZipStream.Async.cs +++ b/src/SharpCompress/Compressors/LZMA/LZipStream.Async.cs @@ -150,6 +150,10 @@ CancellationToken cancellationToken } var basePower = header[5] & 0x1F; var subtractionNumerator = (header[5] & 0xE0) >> 5; + if (basePower < 4 || basePower > 30) + { + return 0; + } return (1 << basePower) - (subtractionNumerator * (1 << (basePower - 4))); } finally From 4d89d856b1d449018db61f84166b138396c3c000 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 1 Jun 2026 13:27:30 +0000 Subject: [PATCH 11/11] Regenerate AotSmoke packages.lock.json with net10.0/linux-x64 section --- src/SharpCompress/packages.lock.json | 12 ++++---- .../SharpCompress.AotSmoke/packages.lock.json | 28 +++++++++++++++---- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/src/SharpCompress/packages.lock.json b/src/SharpCompress/packages.lock.json index 05ccd77c7..e1e7afa51 100644 --- a/src/SharpCompress/packages.lock.json +++ b/src/SharpCompress/packages.lock.json @@ -286,9 +286,9 @@ "net10.0": { "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[10.0.0, )", - "resolved": "10.0.0", - "contentHash": "kICGrGYEzCNI3wPzfEXcwNHgTvlvVn9yJDhSdRK+oZQy4jvYH529u7O0xf5ocQKzOMjfS07+3z9PKRIjrFMJDA==" + "requested": "[10.0.8, )", + "resolved": "10.0.8", + "contentHash": "dVbSXGIFNR5nZcv2tOLoWI+a9T4jtFd77IYjuND+QVe360qWgAF7H0WtoopYhRw/+SgpGUTyrkrh+65+ClNnfw==" }, "Microsoft.NETFramework.ReferenceAssemblies": { "type": "Direct", @@ -388,9 +388,9 @@ "net8.0": { "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[8.0.22, )", - "resolved": "8.0.22", - "contentHash": "MhcMithKEiyyNkD2ZfbDZPmcOdi0GheGfg8saEIIEfD/fol3iHmcV8TsZkD4ZYz5gdUuoX4YtlVySUU7Sxl9SQ==" + "requested": "[8.0.27, )", + "resolved": "8.0.27", + "contentHash": "rQi9TxifHRnXP7lVRZH05DxD2/XGbJp12q0ozcbrlBlBnyyzssFTH/2vLhtKWUp2CT1qVscTrcYTFiwTyKPKRg==" }, "Microsoft.NETFramework.ReferenceAssemblies": { "type": "Direct", diff --git a/tests/SharpCompress.AotSmoke/packages.lock.json b/tests/SharpCompress.AotSmoke/packages.lock.json index 33d459fa0..776cf41a2 100644 --- a/tests/SharpCompress.AotSmoke/packages.lock.json +++ b/tests/SharpCompress.AotSmoke/packages.lock.json @@ -4,15 +4,15 @@ "net10.0": { "Microsoft.DotNet.ILCompiler": { "type": "Direct", - "requested": "[10.0.0, )", - "resolved": "10.0.0", - "contentHash": "f9u8fMRROe2lS5MOOLutK6iSNTK9pC3kqd90FIn8Sk29fbZ0QDjZrBbwUkhouk/8dppC71SIEQaag0lGRTxvfA==" + "requested": "[10.0.8, )", + "resolved": "10.0.8", + "contentHash": "RJxitcN5CCyZDcPNXKLsecwKvACzmy8C1z8hGM9+hFcnPhv1jDysJFFIeUHIPWaZ6wDAfYtZcgKEtegvL2Nz8A==" }, "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[10.0.0, )", - "resolved": "10.0.0", - "contentHash": "kICGrGYEzCNI3wPzfEXcwNHgTvlvVn9yJDhSdRK+oZQy4jvYH529u7O0xf5ocQKzOMjfS07+3z9PKRIjrFMJDA==" + "requested": "[10.0.8, )", + "resolved": "10.0.8", + "contentHash": "dVbSXGIFNR5nZcv2tOLoWI+a9T4jtFd77IYjuND+QVe360qWgAF7H0WtoopYhRw/+SgpGUTyrkrh+65+ClNnfw==" }, "Microsoft.NETFramework.ReferenceAssemblies": { "type": "Direct", @@ -63,6 +63,22 @@ "sharpcompress": { "type": "Project" } + }, + "net10.0/linux-x64": { + "Microsoft.DotNet.ILCompiler": { + "type": "Direct", + "requested": "[10.0.8, )", + "resolved": "10.0.8", + "contentHash": "RJxitcN5CCyZDcPNXKLsecwKvACzmy8C1z8hGM9+hFcnPhv1jDysJFFIeUHIPWaZ6wDAfYtZcgKEtegvL2Nz8A==", + "dependencies": { + "runtime.linux-x64.Microsoft.DotNet.ILCompiler": "10.0.8" + } + }, + "runtime.linux-x64.Microsoft.DotNet.ILCompiler": { + "type": "Transitive", + "resolved": "10.0.8", + "contentHash": "0jxyi69frgaqADCnEpHE+f65NoiRTAjfjvNDMOxWV77BumQ56eMDL4ECw29DcJTqwaYJQ92PqDS6y6CiLf7kgw==" + } } } } \ No newline at end of file