Files
sharpcompress/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs
Adam Hathcock 0a727e16e8 Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-01 13:21:04 +01:00

497 lines
16 KiB
C#

#nullable disable
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Threading.Tasks;
using SharpCompress.Compressors.LZMA.LZ;
using SharpCompress.Compressors.LZMA.RangeCoder;
namespace SharpCompress.Compressors.LZMA;
public partial class Decoder : ICoder, ISetDecoderProperties, IDisposable
{
public void Dispose()
{
_outWindow?.Dispose();
_outWindow = null;
}
private partial class LenDecoder
{
private BitDecoder _choice = new();
private BitDecoder _choice2 = new();
private readonly BitTreeDecoder[] _lowCoder = new BitTreeDecoder[Base.K_NUM_POS_STATES_MAX];
private readonly BitTreeDecoder[] _midCoder = new BitTreeDecoder[Base.K_NUM_POS_STATES_MAX];
private BitTreeDecoder _highCoder = new(Base.K_NUM_HIGH_LEN_BITS);
private uint _numPosStates;
public void Create(uint numPosStates)
{
for (var posState = _numPosStates; posState < numPosStates; posState++)
{
_lowCoder[posState] = new BitTreeDecoder(Base.K_NUM_LOW_LEN_BITS);
_midCoder[posState] = new BitTreeDecoder(Base.K_NUM_MID_LEN_BITS);
}
_numPosStates = numPosStates;
}
public void Init()
{
_choice.Init();
for (uint posState = 0; posState < _numPosStates; posState++)
{
_lowCoder[posState].Init();
_midCoder[posState].Init();
}
_choice2.Init();
_highCoder.Init();
}
public uint Decode(RangeCoder.Decoder rangeDecoder, uint posState)
{
if (_choice.Decode(rangeDecoder) == 0)
{
return _lowCoder[posState].Decode(rangeDecoder);
}
var symbol = Base.K_NUM_LOW_LEN_SYMBOLS;
if (_choice2.Decode(rangeDecoder) == 0)
{
symbol += _midCoder[posState].Decode(rangeDecoder);
}
else
{
symbol += Base.K_NUM_MID_LEN_SYMBOLS;
symbol += _highCoder.Decode(rangeDecoder);
}
return symbol;
}
}
private partial class LiteralDecoder
{
private partial struct Decoder2
{
private BitDecoder[] _decoders;
private int _baseIndex;
public void Create(BitDecoder[] decoders, int baseIndex)
{
_decoders = decoders;
_baseIndex = baseIndex;
}
public void Init()
{
for (var i = 0; i < 0x300; i++)
{
_decoders[_baseIndex + i].Init();
}
}
public byte DecodeNormal(RangeCoder.Decoder rangeDecoder)
{
uint symbol = 1;
do
{
symbol = (symbol << 1) | _decoders[_baseIndex + symbol].Decode(rangeDecoder);
} while (symbol < 0x100);
return (byte)symbol;
}
public byte DecodeWithMatchByte(RangeCoder.Decoder rangeDecoder, byte matchByte)
{
uint symbol = 1;
do
{
var matchBit = (uint)(matchByte >> 7) & 1;
matchByte <<= 1;
var bit = _decoders[_baseIndex + ((1 + matchBit) << 8) + symbol]
.Decode(rangeDecoder);
symbol = (symbol << 1) | bit;
if (matchBit != bit)
{
while (symbol < 0x100)
{
symbol =
(symbol << 1) | _decoders[_baseIndex + symbol].Decode(rangeDecoder);
}
break;
}
} while (symbol < 0x100);
return (byte)symbol;
}
}
private Decoder2[] _coders;
private BitDecoder[] _models;
private int _numPrevBits;
private int _numPosBits;
private uint _posMask;
public void Create(int numPosBits, int numPrevBits)
{
if (_coders != null && _numPrevBits == numPrevBits && _numPosBits == numPosBits)
{
return;
}
_numPosBits = numPosBits;
_posMask = ((uint)1 << numPosBits) - 1;
_numPrevBits = numPrevBits;
var numStates = (uint)1 << (_numPrevBits + _numPosBits);
_models = new BitDecoder[checked((int)(numStates * 0x300))];
_coders = new Decoder2[numStates];
for (uint i = 0; i < numStates; i++)
{
_coders[i].Create(_models, checked((int)(i * 0x300)));
}
}
public void Init()
{
var numStates = (uint)1 << (_numPrevBits + _numPosBits);
for (uint i = 0; i < numStates; i++)
{
_coders[i].Init();
}
}
private uint GetState(uint pos, byte prevByte) =>
((pos & _posMask) << _numPrevBits) + (uint)(prevByte >> (8 - _numPrevBits));
public byte DecodeNormal(RangeCoder.Decoder rangeDecoder, uint pos, byte prevByte) =>
_coders[GetState(pos, prevByte)].DecodeNormal(rangeDecoder);
public byte DecodeWithMatchByte(
RangeCoder.Decoder rangeDecoder,
uint pos,
byte prevByte,
byte matchByte
) => _coders[GetState(pos, prevByte)].DecodeWithMatchByte(rangeDecoder, matchByte);
}
private OutWindow _outWindow;
private readonly BitDecoder[] _isMatchDecoders = new BitDecoder[
Base.K_NUM_STATES << Base.K_NUM_POS_STATES_BITS_MAX
];
private readonly BitDecoder[] _isRepDecoders = new BitDecoder[Base.K_NUM_STATES];
private readonly BitDecoder[] _isRepG0Decoders = new BitDecoder[Base.K_NUM_STATES];
private readonly BitDecoder[] _isRepG1Decoders = new BitDecoder[Base.K_NUM_STATES];
private readonly BitDecoder[] _isRepG2Decoders = new BitDecoder[Base.K_NUM_STATES];
private readonly BitDecoder[] _isRep0LongDecoders = new BitDecoder[
Base.K_NUM_STATES << Base.K_NUM_POS_STATES_BITS_MAX
];
private readonly BitTreeDecoder[] _posSlotDecoder = new BitTreeDecoder[
Base.K_NUM_LEN_TO_POS_STATES
];
private readonly BitDecoder[] _posDecoders = new BitDecoder[
Base.K_NUM_FULL_DISTANCES - Base.K_END_POS_MODEL_INDEX
];
private BitTreeDecoder _posAlignDecoder = new(Base.K_NUM_ALIGN_BITS);
private readonly LenDecoder _lenDecoder = new();
private readonly LenDecoder _repLenDecoder = new();
private readonly LiteralDecoder _literalDecoder = new();
private int _dictionarySize;
private uint _posStateMask;
private Base.State _state = new();
private uint _rep0,
_rep1,
_rep2,
_rep3;
public Decoder()
{
_dictionarySize = -1;
for (var i = 0; i < Base.K_NUM_LEN_TO_POS_STATES; i++)
{
_posSlotDecoder[i] = new BitTreeDecoder(Base.K_NUM_POS_SLOT_BITS);
}
}
#if !LEGACY_DOTNET
[MemberNotNull(nameof(_outWindow))]
#endif
private void CreateDictionary()
{
if (_dictionarySize < 0)
{
throw new InvalidParamException();
}
_outWindow = new OutWindow();
var blockSize = Math.Max(_dictionarySize, (1 << 12));
_outWindow.Create(blockSize);
}
private void SetLiteralProperties(int lp, int lc)
{
if (lp > 8)
{
throw new InvalidParamException();
}
if (lc > 8)
{
throw new InvalidParamException();
}
_literalDecoder.Create(lp, lc);
}
private void SetPosBitsProperties(int pb)
{
if (pb > Base.K_NUM_POS_STATES_BITS_MAX)
{
throw new InvalidParamException();
}
var numPosStates = (uint)1 << pb;
_lenDecoder.Create(numPosStates);
_repLenDecoder.Create(numPosStates);
_posStateMask = numPosStates - 1;
}
private void Init()
{
uint i;
for (i = 0; i < Base.K_NUM_STATES; i++)
{
for (uint j = 0; j <= _posStateMask; j++)
{
var index = (i << Base.K_NUM_POS_STATES_BITS_MAX) + j;
_isMatchDecoders[index].Init();
_isRep0LongDecoders[index].Init();
}
_isRepDecoders[i].Init();
_isRepG0Decoders[i].Init();
_isRepG1Decoders[i].Init();
_isRepG2Decoders[i].Init();
}
_literalDecoder.Init();
for (i = 0; i < Base.K_NUM_LEN_TO_POS_STATES; i++)
{
_posSlotDecoder[i].Init();
}
// _PosSpecDecoder.Init();
for (i = 0; i < Base.K_NUM_FULL_DISTANCES - Base.K_END_POS_MODEL_INDEX; i++)
{
_posDecoders[i].Init();
}
_lenDecoder.Init();
_repLenDecoder.Init();
_posAlignDecoder.Init();
_state.Init();
_rep0 = 0;
_rep1 = 0;
_rep2 = 0;
_rep3 = 0;
}
public void Code(
Stream inStream,
Stream outStream,
long inSize,
long outSize,
ICodeProgress progress
)
{
if (_outWindow is null)
{
CreateDictionary();
}
_outWindow.Init(outStream);
if (outSize > 0)
{
_outWindow.SetLimit(outSize);
}
else
{
_outWindow.SetLimit(long.MaxValue - _outWindow.Total);
}
var rangeDecoder = new RangeCoder.Decoder();
rangeDecoder.Init(inStream);
Code(_dictionarySize, _outWindow, rangeDecoder);
_outWindow.ReleaseStream();
rangeDecoder.ReleaseStream();
_outWindow.Dispose();
_outWindow = null;
}
internal bool Code(int dictionarySize, OutWindow outWindow, RangeCoder.Decoder rangeDecoder)
{
var dictionarySizeCheck = Math.Max(dictionarySize, 1);
outWindow.CopyPending();
while (outWindow.HasSpace)
{
var posState = (uint)outWindow.Total & _posStateMask;
if (
_isMatchDecoders[(_state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState]
.Decode(rangeDecoder) == 0
)
{
byte b;
var prevByte = outWindow.GetByte(0);
if (!_state.IsCharState())
{
b = _literalDecoder.DecodeWithMatchByte(
rangeDecoder,
(uint)outWindow.Total,
prevByte,
outWindow.GetByte((int)_rep0)
);
}
else
{
b = _literalDecoder.DecodeNormal(rangeDecoder, (uint)outWindow.Total, prevByte);
}
outWindow.PutByte(b);
_state.UpdateChar();
}
else
{
uint len;
if (_isRepDecoders[_state._index].Decode(rangeDecoder) == 1)
{
if (_isRepG0Decoders[_state._index].Decode(rangeDecoder) == 0)
{
if (
_isRep0LongDecoders[
(_state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState
]
.Decode(rangeDecoder) == 0
)
{
_state.UpdateShortRep();
outWindow.PutByte(outWindow.GetByte((int)_rep0));
continue;
}
}
else
{
uint distance;
if (_isRepG1Decoders[_state._index].Decode(rangeDecoder) == 0)
{
distance = _rep1;
}
else
{
if (_isRepG2Decoders[_state._index].Decode(rangeDecoder) == 0)
{
distance = _rep2;
}
else
{
distance = _rep3;
_rep3 = _rep2;
}
_rep2 = _rep1;
}
_rep1 = _rep0;
_rep0 = distance;
}
len = _repLenDecoder.Decode(rangeDecoder, posState) + Base.K_MATCH_MIN_LEN;
_state.UpdateRep();
}
else
{
_rep3 = _rep2;
_rep2 = _rep1;
_rep1 = _rep0;
len = Base.K_MATCH_MIN_LEN + _lenDecoder.Decode(rangeDecoder, posState);
_state.UpdateMatch();
var posSlot = _posSlotDecoder[Base.GetLenToPosState(len)].Decode(rangeDecoder);
if (posSlot >= Base.K_START_POS_MODEL_INDEX)
{
var numDirectBits = (int)((posSlot >> 1) - 1);
_rep0 = ((2 | (posSlot & 1)) << numDirectBits);
if (posSlot < Base.K_END_POS_MODEL_INDEX)
{
_rep0 += BitTreeDecoder.ReverseDecode(
_posDecoders,
_rep0 - posSlot - 1,
rangeDecoder,
numDirectBits
);
}
else
{
_rep0 += (
rangeDecoder.DecodeDirectBits(numDirectBits - Base.K_NUM_ALIGN_BITS)
<< Base.K_NUM_ALIGN_BITS
);
_rep0 += _posAlignDecoder.ReverseDecode(rangeDecoder);
}
}
else
{
_rep0 = posSlot;
}
}
if (_rep0 >= outWindow.Total || _rep0 >= dictionarySizeCheck)
{
if (_rep0 == 0xFFFFFFFF)
{
return true;
}
throw new DataErrorException();
}
outWindow.CopyBlock((int)_rep0, (int)len);
}
}
return false;
}
public void SetDecoderProperties(byte[] properties) =>
SetDecoderProperties(properties.AsSpan());
internal void SetDecoderProperties(ReadOnlySpan<byte> properties)
{
if (properties.Length < 1)
{
throw new InvalidParamException();
}
var lc = properties[0] % 9;
var remainder = properties[0] / 9;
var lp = remainder % 5;
var pb = remainder / 5;
if (pb > Base.K_NUM_POS_STATES_BITS_MAX)
{
throw new InvalidParamException();
}
SetLiteralProperties(lp, lc);
SetPosBitsProperties(pb);
Init();
if (properties.Length >= 5)
{
_dictionarySize = 0;
for (var i = 0; i < 4; i++)
{
_dictionarySize += properties[1 + i] << (i * 8);
}
}
}
public void Train(Stream stream)
{
if (_outWindow is null)
{
CreateDictionary();
}
_outWindow.Train(stream);
}
}