diff --git a/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs b/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs index 2cafa087..62e03f94 100644 --- a/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs +++ b/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs @@ -267,10 +267,12 @@ public partial class Decoder : ICoder, ISetDecoderProperties, IDisposable { throw new InvalidParamException(); } - _outWindow = new OutWindow(); var blockSize = Math.Max(_dictionarySize, (1 << 12)); DictionarySizeLimits.Validate(blockSize, DictionarySizeLimits.Lzma, "LZMA"); - _outWindow.Create(blockSize); + // Publish only a usable window so a rejected dictionary can be retried. + var window = new OutWindow(); + window.Create(blockSize); + _outWindow = window; } private void SetLiteralProperties(int lp, int lc) diff --git a/tests/SharpCompress.Test/Streams/DictionarySizeLimitTests.cs b/tests/SharpCompress.Test/Streams/DictionarySizeLimitTests.cs index 2619761b..f9dbcd0b 100644 --- a/tests/SharpCompress.Test/Streams/DictionarySizeLimitTests.cs +++ b/tests/SharpCompress.Test/Streams/DictionarySizeLimitTests.cs @@ -119,6 +119,93 @@ public class DictionarySizeLimitTests : TestBase } } + [Theory] + [InlineData(false, false)] + [InlineData(false, true)] + [InlineData(true, false)] + [InlineData(true, true)] + public async Task Direct_Lzma_Decoder_Can_Retry_After_Limit_Rejection(bool useAsync, bool train) + { + using var limits = new DictionarySizeLimitScope(); + var expected = new byte[128]; + using var packed = new MemoryStream(); + byte[] properties; + using ( + var encoder = LzmaStream.Create(new LzmaEncoderProperties(false, 4096), false, packed) + ) + { + properties = (byte[])encoder.Properties.Clone(); + encoder.Write(expected, 0, expected.Length); + } + packed.Position = 0; + using var output = new MemoryStream(); + using var decoder = new Decoder(); + decoder.SetDecoderProperties(properties); + DictionarySizeLimits.Lzma = 4095; + + if (useAsync) + { + await Assert.ThrowsAsync(async () => + { + if (train) + { + await decoder.TrainAsync(Stream.Null); + } + else + { + await decoder.CodeAsync( + packed, + output, + packed.Length, + expected.Length, + null!, + CancellationToken.None + ); + } + }); + } + else + { + Assert.Throws(() => + { + if (train) + { + decoder.Train(Stream.Null); + } + else + { + decoder.Code(packed, output, packed.Length, expected.Length, null!); + } + }); + } + Assert.Equal(0, packed.Position); + DictionarySizeLimits.Lzma = 4096; + if (useAsync) + { + if (train) + { + await decoder.TrainAsync(Stream.Null); + } + await decoder.CodeAsync( + packed, + output, + packed.Length, + expected.Length, + null!, + CancellationToken.None + ); + } + else + { + if (train) + { + decoder.Train(Stream.Null); + } + decoder.Code(packed, output, packed.Length, expected.Length, null!); + } + Assert.Equal(expected, output.ToArray()); + } + [Theory] [InlineData(false)] [InlineData(true)]