Keep LZMA decoders retryable after dictionary limit rejection

This commit is contained in:
Adam Hathcock
2026-11-13 09:28:53 +00:00
parent 09b95f1841
commit f2e4df509b
2 changed files with 91 additions and 2 deletions

View File

@@ -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)

View File

@@ -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<InvalidFormatException>(async () =>
{
if (train)
{
await decoder.TrainAsync(Stream.Null);
}
else
{
await decoder.CodeAsync(
packed,
output,
packed.Length,
expected.Length,
null!,
CancellationToken.None
);
}
});
}
else
{
Assert.Throws<InvalidFormatException>(() =>
{
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)]