mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-09-23 15:34:34 +00:00
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.
This commit is contained in:
@@ -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 @@ internal sealed class BinTree : InWindow
|
||||
var cyclicBufferSize = historySize + 1;
|
||||
if (_cyclicBufferSize != cyclicBufferSize)
|
||||
{
|
||||
if (_son is not null)
|
||||
if (_son.Length != 0)
|
||||
{
|
||||
ArrayPool<uint>.Shared.Return(_son);
|
||||
}
|
||||
@@ -138,7 +138,7 @@ internal sealed class BinTree : InWindow
|
||||
}
|
||||
if (hs != _hashSizeSum)
|
||||
{
|
||||
if (_hash is not null)
|
||||
if (_hash.Length != 0)
|
||||
{
|
||||
ArrayPool<uint>.Shared.Return(_hash);
|
||||
}
|
||||
@@ -150,15 +150,15 @@ internal sealed class BinTree : InWindow
|
||||
public override void Dispose()
|
||||
{
|
||||
base.Dispose();
|
||||
if (_son is not null)
|
||||
if (_son.Length != 0)
|
||||
{
|
||||
ArrayPool<uint>.Shared.Return(_son);
|
||||
_son = null;
|
||||
_son = [];
|
||||
}
|
||||
if (_hash is not null)
|
||||
if (_hash.Length != 0)
|
||||
{
|
||||
ArrayPool<uint>.Shared.Return(_hash);
|
||||
_hash = null;
|
||||
_hash = [];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ public partial class Decoder : IAsyncDisposable
|
||||
{
|
||||
symbol =
|
||||
(symbol << 1)
|
||||
| await _decoders[symbol]
|
||||
| await _decoders[_baseIndex + symbol]
|
||||
.DecodeAsync(rangeDecoder, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
} while (symbol < 0x100);
|
||||
@@ -90,7 +90,7 @@ public partial class Decoder : IAsyncDisposable
|
||||
{
|
||||
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 partial class Decoder : IAsyncDisposable
|
||||
{
|
||||
symbol =
|
||||
(symbol << 1)
|
||||
| await _decoders[symbol]
|
||||
| await _decoders[_baseIndex + symbol]
|
||||
.DecodeAsync(rangeDecoder, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -73,14 +73,19 @@ public partial class Decoder : ICoder, ISetDecoderProperties, IDisposable
|
||||
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 partial class Decoder : ICoder, ISetDecoderProperties, IDisposable
|
||||
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 partial class Decoder : ICoder, ISetDecoderProperties, IDisposable
|
||||
{
|
||||
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 partial class Decoder : ICoder, ISetDecoderProperties, IDisposable
|
||||
}
|
||||
|
||||
private Decoder2[] _coders;
|
||||
private BitDecoder[] _models;
|
||||
private int _numPrevBits;
|
||||
private int _numPosBits;
|
||||
private uint _posMask;
|
||||
@@ -131,10 +139,11 @@ public partial class Decoder : ICoder, ISetDecoderProperties, IDisposable
|
||||
_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)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 @@ internal partial class Encoder : ICoder, ISetCoderProperties, IWriteCoderPropert
|
||||
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 @@ internal partial class Encoder : ICoder, ISetCoderProperties, IWriteCoderPropert
|
||||
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 @@ internal partial class Encoder : ICoder, ISetCoderProperties, IWriteCoderPropert
|
||||
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 @@ internal partial class Encoder : ICoder, ISetCoderProperties, IWriteCoderPropert
|
||||
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 @@ internal partial class Encoder : ICoder, ISetCoderProperties, IWriteCoderPropert
|
||||
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 @@ internal partial class Encoder : ICoder, ISetCoderProperties, IWriteCoderPropert
|
||||
{
|
||||
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 @@ internal partial class Encoder : ICoder, ISetCoderProperties, IWriteCoderPropert
|
||||
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 @@ internal partial class Encoder : ICoder, ISetCoderProperties, IWriteCoderPropert
|
||||
}
|
||||
|
||||
private Encoder2[] _coders;
|
||||
private BitEncoder[] _models;
|
||||
private int _numPrevBits;
|
||||
private int _numPosBits;
|
||||
private uint _posMask;
|
||||
@@ -215,13 +223,30 @@ internal partial class Encoder : ICoder, ISetCoderProperties, IWriteCoderPropert
|
||||
_posMask = ((uint)1 << numPosBits) - 1;
|
||||
_numPrevBits = numPrevBits;
|
||||
var numStates = (uint)1 << (_numPrevBits + _numPosBits);
|
||||
if (_models is not null)
|
||||
{
|
||||
ArrayPool<BitEncoder>.Shared.Return(_models);
|
||||
}
|
||||
_models = ArrayPool<BitEncoder>.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<BitEncoder>.Shared.Return(_models);
|
||||
_models = null;
|
||||
_coders = null;
|
||||
}
|
||||
|
||||
public void Init()
|
||||
{
|
||||
var numStates = (uint)1 << (_numPrevBits + _numPosBits);
|
||||
@@ -423,7 +448,7 @@ internal partial class Encoder : ICoder, ISetCoderProperties, IWriteCoderPropert
|
||||
|
||||
private const uint K_NUM_OPTS = 1 << 12;
|
||||
|
||||
private class Optimal
|
||||
private struct Optimal
|
||||
{
|
||||
public Base.State _state;
|
||||
|
||||
@@ -564,10 +589,6 @@ internal partial class Encoder : ICoder, ISetCoderProperties, IWriteCoderPropert
|
||||
|
||||
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 @@ internal partial class Encoder : ICoder, ISetCoderProperties, IWriteCoderPropert
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_literalEncoder.Dispose();
|
||||
_matchFinder?.Dispose();
|
||||
_matchFinder = null;
|
||||
}
|
||||
@@ -865,7 +887,7 @@ internal partial class Encoder : ICoder, ISetCoderProperties, IWriteCoderPropert
|
||||
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 @@ internal partial class Encoder : ICoder, ISetCoderProperties, IWriteCoderPropert
|
||||
{
|
||||
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 @@ internal partial class Encoder : ICoder, ISetCoderProperties, IWriteCoderPropert
|
||||
.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 @@ internal partial class Encoder : ICoder, ISetCoderProperties, IWriteCoderPropert
|
||||
}
|
||||
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 @@ internal partial class Encoder : ICoder, ISetCoderProperties, IWriteCoderPropert
|
||||
}
|
||||
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 @@ internal partial class Encoder : ICoder, ISetCoderProperties, IWriteCoderPropert
|
||||
}
|
||||
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 @@ internal partial class Encoder : ICoder, ISetCoderProperties, IWriteCoderPropert
|
||||
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 @@ internal partial class Encoder : ICoder, ISetCoderProperties, IWriteCoderPropert
|
||||
curAndLenPrice =
|
||||
nextRepMatchPrice
|
||||
+ GetRepPrice(0, lenTest2, state2, posStateNext);
|
||||
optimum = _optimum[cur + offset];
|
||||
optimum = ref _optimum[cur + offset];
|
||||
if (curAndLenPrice < optimum._price)
|
||||
{
|
||||
optimum._price = curAndLenPrice;
|
||||
|
||||
Reference in New Issue
Block a user