mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-09-25 00:15:15 +00:00
More LZMA conversion going, BZip2 not for now
This commit is contained in:
@@ -24,7 +24,7 @@ namespace SharpCompress.Common
|
||||
/// Set this when you want to use a custom method for all decoding operations.
|
||||
/// </summary>
|
||||
/// <returns>string Func(bytes, index, length)</returns>
|
||||
public Func<byte[], int, int, string>? CustomDecoder { get; set; }
|
||||
//public Func<byte[], int, int, string>? CustomDecoder { get; set; }
|
||||
|
||||
public ArchiveEncoding()
|
||||
: this(Encoding.Default, Encoding.Default)
|
||||
@@ -50,7 +50,12 @@ namespace SharpCompress.Common
|
||||
|
||||
public string Decode(byte[] bytes, int start, int length)
|
||||
{
|
||||
return GetDecoder().Invoke(bytes, start, length);
|
||||
return GetEncoding().GetString(bytes, start, length);
|
||||
}
|
||||
|
||||
public string Decode(ReadOnlySpan<byte> span)
|
||||
{
|
||||
return GetEncoding().GetString(span);
|
||||
}
|
||||
|
||||
public string DecodeUTF8(byte[] bytes)
|
||||
@@ -67,10 +72,5 @@ namespace SharpCompress.Common
|
||||
{
|
||||
return Forced ?? Default ?? Encoding.UTF8;
|
||||
}
|
||||
|
||||
public Func<byte[], int, int, string> GetDecoder()
|
||||
{
|
||||
return CustomDecoder ?? ((bytes, index, count) => GetEncoding().GetString(bytes, index, count));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,10 +73,10 @@ namespace SharpCompress.Common.Zip
|
||||
{
|
||||
return new Deflate64Stream(stream, CompressionMode.Decompress);
|
||||
}
|
||||
case ZipCompressionMethod.BZip2:
|
||||
/** case ZipCompressionMethod.BZip2:
|
||||
{
|
||||
return await BZip2Stream.CreateAsync(stream, CompressionMode.Decompress, false, cancellationToken);
|
||||
}
|
||||
} */
|
||||
case ZipCompressionMethod.LZMA:
|
||||
{
|
||||
if (FlagUtility.HasFlag(Header.Flags, HeaderFlags.Encrypted))
|
||||
@@ -87,7 +87,7 @@ namespace SharpCompress.Common.Zip
|
||||
reader.ReadUInt16(); //LZMA version
|
||||
var props = new byte[reader.ReadUInt16()];
|
||||
reader.Read(props, 0, props.Length);
|
||||
return new LzmaStream(props, stream,
|
||||
return await LzmaStream.CreateAsync(props, stream,
|
||||
Header.CompressedSize > 0 ? Header.CompressedSize - 4 - props.Length : -1,
|
||||
FlagUtility.HasFlag(Header.Flags, HeaderFlags.Bit1)
|
||||
? -1
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SharpCompress.Compressors.LZMA
|
||||
{
|
||||
@@ -59,7 +60,7 @@ namespace SharpCompress.Compressors.LZMA
|
||||
/// <param name="progress">
|
||||
/// callback progress reference.
|
||||
/// </param>
|
||||
void Code(Stream inStream, Stream outStream,
|
||||
ValueTask CodeAsync(Stream inStream, Stream outStream,
|
||||
Int64 inSize, Int64 outSize, ICodeProgress progress);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Buffers.Binary;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Crypto;
|
||||
using SharpCompress.IO;
|
||||
|
||||
@@ -16,26 +18,34 @@ namespace SharpCompress.Compressors.LZMA
|
||||
/// </summary>
|
||||
public sealed class LZipStream : Stream
|
||||
{
|
||||
private readonly Stream _stream;
|
||||
private readonly CountingWritableSubStream? _countingWritableSubStream;
|
||||
#nullable disable
|
||||
private Stream _stream;
|
||||
#nullable enable
|
||||
private CountingWritableSubStream? _countingWritableSubStream;
|
||||
private bool _disposed;
|
||||
private bool _finished;
|
||||
|
||||
private long _writeCount;
|
||||
|
||||
public LZipStream(Stream stream, CompressionMode mode)
|
||||
private LZipStream()
|
||||
{
|
||||
Mode = mode;
|
||||
|
||||
}
|
||||
|
||||
public static async ValueTask<LZipStream> CreateAsync(Stream stream, CompressionMode mode)
|
||||
{
|
||||
var lzip = new LZipStream();
|
||||
lzip.Mode = mode;
|
||||
|
||||
if (mode == CompressionMode.Decompress)
|
||||
{
|
||||
int dSize = ValidateAndReadSize(stream);
|
||||
int dSize = await ValidateAndReadSize(stream);
|
||||
if (dSize == 0)
|
||||
{
|
||||
throw new IOException("Not an LZip stream");
|
||||
}
|
||||
byte[] properties = GetProperties(dSize);
|
||||
_stream = new LzmaStream(properties, stream);
|
||||
lzip._stream = await LzmaStream.CreateAsync(properties, stream);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -43,9 +53,10 @@ namespace SharpCompress.Compressors.LZMA
|
||||
int dSize = 104 * 1024;
|
||||
WriteHeaderSize(stream);
|
||||
|
||||
_countingWritableSubStream = new CountingWritableSubStream(stream);
|
||||
_stream = new Crc32Stream(new LzmaStream(new LzmaEncoderProperties(true, dSize), false, _countingWritableSubStream));
|
||||
lzip._countingWritableSubStream = new CountingWritableSubStream(stream);
|
||||
lzip._stream = new Crc32Stream(new LzmaStream(new LzmaEncoderProperties(true, dSize), false, lzip._countingWritableSubStream));
|
||||
}
|
||||
return lzip;
|
||||
}
|
||||
|
||||
public void Finish()
|
||||
@@ -90,7 +101,7 @@ namespace SharpCompress.Compressors.LZMA
|
||||
}
|
||||
}
|
||||
|
||||
public CompressionMode Mode { get; }
|
||||
public CompressionMode Mode { get; private set; }
|
||||
|
||||
public override bool CanRead => Mode == CompressionMode.Decompress;
|
||||
|
||||
@@ -155,14 +166,14 @@ namespace SharpCompress.Compressors.LZMA
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream to read from. Must not be null.</param>
|
||||
/// <returns><c>true</c> if the given stream is an LZip file, <c>false</c> otherwise.</returns>
|
||||
public static bool IsLZipFile(Stream stream) => ValidateAndReadSize(stream) != 0;
|
||||
public static async ValueTask<bool> IsLZipFileAsync(Stream stream) => await ValidateAndReadSize(stream) != 0;
|
||||
|
||||
/// <summary>
|
||||
/// Reads the 6-byte header of the stream, and returns 0 if either the header
|
||||
/// couldn't be read or it isn't a validate LZIP header, or the dictionary
|
||||
/// size if it *is* a valid LZIP file.
|
||||
/// </summary>
|
||||
public static int ValidateAndReadSize(Stream stream)
|
||||
private static async ValueTask<int> ValidateAndReadSize(Stream stream)
|
||||
{
|
||||
if (stream is null)
|
||||
{
|
||||
@@ -170,8 +181,9 @@ namespace SharpCompress.Compressors.LZMA
|
||||
}
|
||||
|
||||
// Read the header
|
||||
Span<byte> header = stackalloc byte[6];
|
||||
int n = stream.Read(header);
|
||||
using var buffer = MemoryPool<byte>.Shared.Rent(6);
|
||||
var header = buffer.Memory.Slice(0,6);
|
||||
int n = await stream.ReadAsync(header);
|
||||
|
||||
// TODO: Handle reading only part of the header?
|
||||
|
||||
@@ -180,12 +192,12 @@ namespace SharpCompress.Compressors.LZMA
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (header[0] != 'L' || header[1] != 'Z' || header[2] != 'I' || header[3] != 'P' || header[4] != 1 /* version 1 */)
|
||||
if (header.Span[0] != 'L' || header.Span[1] != 'Z' || header.Span[2] != 'I' || header.Span[3] != 'P' || header.Span[4] != 1 /* version 1 */)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int basePower = header[5] & 0x1F;
|
||||
int subtractionNumerator = (header[5] & 0xE0) >> 5;
|
||||
int basePower = header.Span[5] & 0x1F;
|
||||
int subtractionNumerator = (header.Span[5] & 0xE0) >> 5;
|
||||
return (1 << basePower) - subtractionNumerator * (1 << (basePower - 4));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Compressors.LZMA.LZ;
|
||||
using SharpCompress.Compressors.LZMA.RangeCoder;
|
||||
|
||||
@@ -11,11 +10,11 @@ namespace SharpCompress.Compressors.LZMA
|
||||
{
|
||||
private class LenDecoder
|
||||
{
|
||||
private BitDecoder _choice = new BitDecoder();
|
||||
private BitDecoder _choice2 = new BitDecoder();
|
||||
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 BitTreeDecoder(Base.K_NUM_HIGH_LEN_BITS);
|
||||
private BitTreeDecoder _highCoder = new(Base.K_NUM_HIGH_LEN_BITS);
|
||||
private uint _numPosStates;
|
||||
|
||||
public void Create(uint numPosStates)
|
||||
@@ -113,12 +112,12 @@ namespace SharpCompress.Compressors.LZMA
|
||||
}
|
||||
}
|
||||
|
||||
private Decoder2[] _coders;
|
||||
private int _numPrevBits;
|
||||
private int _numPosBits;
|
||||
private uint _posMask;
|
||||
|
||||
public void Create(int numPosBits, int numPrevBits)
|
||||
private readonly Decoder2[]_coders;
|
||||
private readonly int _numPrevBits;
|
||||
private readonly int _numPosBits;
|
||||
private readonly uint _posMask;
|
||||
|
||||
public LiteralDecoder(int numPosBits, int numPrevBits)
|
||||
{
|
||||
if (_coders != null && _numPrevBits == numPrevBits &&
|
||||
_numPosBits == numPosBits)
|
||||
@@ -161,7 +160,7 @@ namespace SharpCompress.Compressors.LZMA
|
||||
}
|
||||
}
|
||||
|
||||
private OutWindow _outWindow;
|
||||
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];
|
||||
@@ -173,18 +172,18 @@ namespace SharpCompress.Compressors.LZMA
|
||||
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 BitTreeDecoder(Base.K_NUM_ALIGN_BITS);
|
||||
private BitTreeDecoder _posAlignDecoder = new(Base.K_NUM_ALIGN_BITS);
|
||||
|
||||
private readonly LenDecoder _lenDecoder = new LenDecoder();
|
||||
private readonly LenDecoder _repLenDecoder = new LenDecoder();
|
||||
private readonly LenDecoder _lenDecoder = new();
|
||||
private readonly LenDecoder _repLenDecoder = new();
|
||||
|
||||
private readonly LiteralDecoder _literalDecoder = new LiteralDecoder();
|
||||
private LiteralDecoder? _literalDecoder;
|
||||
|
||||
private int _dictionarySize;
|
||||
|
||||
private uint _posStateMask;
|
||||
|
||||
private Base.State _state = new Base.State();
|
||||
private Base.State _state = new();
|
||||
private uint _rep0, _rep1, _rep2, _rep3;
|
||||
|
||||
public Decoder()
|
||||
@@ -196,15 +195,16 @@ namespace SharpCompress.Compressors.LZMA
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateDictionary()
|
||||
private OutWindow CreateDictionary()
|
||||
{
|
||||
if (_dictionarySize < 0)
|
||||
{
|
||||
throw new InvalidParamException();
|
||||
}
|
||||
_outWindow = new OutWindow();
|
||||
var outWindow = new OutWindow();
|
||||
int blockSize = Math.Max(_dictionarySize, (1 << 12));
|
||||
_outWindow.Create(blockSize);
|
||||
outWindow.Create(blockSize);
|
||||
return outWindow;
|
||||
}
|
||||
|
||||
private void SetLiteralProperties(int lp, int lc)
|
||||
@@ -217,7 +217,7 @@ namespace SharpCompress.Compressors.LZMA
|
||||
{
|
||||
throw new InvalidParamException();
|
||||
}
|
||||
_literalDecoder.Create(lp, lc);
|
||||
_literalDecoder = new(lp, lc);
|
||||
}
|
||||
|
||||
private void SetPosBitsProperties(int pb)
|
||||
@@ -249,7 +249,7 @@ namespace SharpCompress.Compressors.LZMA
|
||||
_isRepG2Decoders[i].Init();
|
||||
}
|
||||
|
||||
_literalDecoder.Init();
|
||||
_literalDecoder?.Init();
|
||||
for (i = 0; i < Base.K_NUM_LEN_TO_POS_STATES; i++)
|
||||
{
|
||||
_posSlotDecoder[i].Init();
|
||||
@@ -272,12 +272,12 @@ namespace SharpCompress.Compressors.LZMA
|
||||
_rep3 = 0;
|
||||
}
|
||||
|
||||
public void Code(Stream inStream, Stream outStream,
|
||||
public async ValueTask CodeAsync(Stream inStream, Stream outStream,
|
||||
Int64 inSize, Int64 outSize, ICodeProgress progress)
|
||||
{
|
||||
if (_outWindow is null)
|
||||
{
|
||||
CreateDictionary();
|
||||
_outWindow = CreateDictionary();
|
||||
}
|
||||
_outWindow.Init(outStream);
|
||||
if (outSize > 0)
|
||||
@@ -290,7 +290,7 @@ namespace SharpCompress.Compressors.LZMA
|
||||
}
|
||||
|
||||
RangeCoder.Decoder rangeDecoder = new RangeCoder.Decoder();
|
||||
rangeDecoder.Init(inStream);
|
||||
await rangeDecoder.InitAsync(inStream);
|
||||
|
||||
Code(_dictionarySize, _outWindow, rangeDecoder);
|
||||
|
||||
@@ -310,6 +310,7 @@ namespace SharpCompress.Compressors.LZMA
|
||||
|
||||
internal bool Code(int dictionarySize, OutWindow outWindow, RangeCoder.Decoder rangeDecoder)
|
||||
{
|
||||
_literalDecoder ??= _literalDecoder.CheckNotNull(nameof(_literalDecoder));
|
||||
int dictionarySizeCheck = Math.Max(dictionarySize, 1);
|
||||
|
||||
outWindow.CopyPending();
|
||||
@@ -450,7 +451,7 @@ namespace SharpCompress.Compressors.LZMA
|
||||
{
|
||||
if (_outWindow is null)
|
||||
{
|
||||
CreateDictionary();
|
||||
_outWindow = CreateDictionary();
|
||||
}
|
||||
_outWindow.Train(stream);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Compressors.LZMA.LZ;
|
||||
using SharpCompress.Compressors.LZMA.RangeCoder;
|
||||
|
||||
@@ -61,7 +62,7 @@ namespace SharpCompress.Compressors.LZMA
|
||||
return (UInt32)(G_FAST_POS[pos >> 26] + 52);
|
||||
}
|
||||
|
||||
private Base.State _state = new Base.State();
|
||||
private Base.State _state = new();
|
||||
private Byte _previousByte;
|
||||
private readonly UInt32[] _repDistances = new UInt32[Base.K_NUM_REP_DISTANCES];
|
||||
|
||||
@@ -97,18 +98,18 @@ namespace SharpCompress.Compressors.LZMA
|
||||
}
|
||||
}
|
||||
|
||||
public void Encode(RangeCoder.Encoder rangeEncoder, byte symbol)
|
||||
public async ValueTask EncodeAsync(RangeCoder.Encoder rangeEncoder, byte symbol)
|
||||
{
|
||||
uint context = 1;
|
||||
for (int i = 7; i >= 0; i--)
|
||||
{
|
||||
uint bit = (uint)((symbol >> i) & 1);
|
||||
_encoders[context].Encode(rangeEncoder, bit);
|
||||
await _encoders[context].EncodeAsync(rangeEncoder, bit);
|
||||
context = (context << 1) | bit;
|
||||
}
|
||||
}
|
||||
|
||||
public void EncodeMatched(RangeCoder.Encoder rangeEncoder, byte matchByte, byte symbol)
|
||||
public async ValueTask EncodeMatchedAsync(RangeCoder.Encoder rangeEncoder, byte matchByte, byte symbol)
|
||||
{
|
||||
uint context = 1;
|
||||
bool same = true;
|
||||
@@ -122,7 +123,7 @@ namespace SharpCompress.Compressors.LZMA
|
||||
state += ((1 + matchBit) << 8);
|
||||
same = (matchBit == bit);
|
||||
}
|
||||
_encoders[state].Encode(rangeEncoder, bit);
|
||||
await _encoders[state].EncodeAsync(rangeEncoder, bit);
|
||||
context = (context << 1) | bit;
|
||||
}
|
||||
}
|
||||
@@ -196,11 +197,11 @@ namespace SharpCompress.Compressors.LZMA
|
||||
|
||||
private class LenEncoder
|
||||
{
|
||||
private BitEncoder _choice = new BitEncoder();
|
||||
private BitEncoder _choice2 = new BitEncoder();
|
||||
private BitEncoder _choice = new();
|
||||
private BitEncoder _choice2 = new();
|
||||
private readonly BitTreeEncoder[] _lowCoder = new BitTreeEncoder[Base.K_NUM_POS_STATES_ENCODING_MAX];
|
||||
private readonly BitTreeEncoder[] _midCoder = new BitTreeEncoder[Base.K_NUM_POS_STATES_ENCODING_MAX];
|
||||
private BitTreeEncoder _highCoder = new BitTreeEncoder(Base.K_NUM_HIGH_LEN_BITS);
|
||||
private BitTreeEncoder _highCoder = new(Base.K_NUM_HIGH_LEN_BITS);
|
||||
|
||||
public LenEncoder()
|
||||
{
|
||||
@@ -223,26 +224,26 @@ namespace SharpCompress.Compressors.LZMA
|
||||
_highCoder.Init();
|
||||
}
|
||||
|
||||
public void Encode(RangeCoder.Encoder rangeEncoder, UInt32 symbol, UInt32 posState)
|
||||
public virtual async ValueTask EncodeAsync(RangeCoder.Encoder rangeEncoder, UInt32 symbol, UInt32 posState)
|
||||
{
|
||||
if (symbol < Base.K_NUM_LOW_LEN_SYMBOLS)
|
||||
{
|
||||
_choice.Encode(rangeEncoder, 0);
|
||||
_lowCoder[posState].Encode(rangeEncoder, symbol);
|
||||
await _choice.EncodeAsync(rangeEncoder, 0);
|
||||
await _lowCoder[posState].EncodeAsync(rangeEncoder, symbol);
|
||||
}
|
||||
else
|
||||
{
|
||||
symbol -= Base.K_NUM_LOW_LEN_SYMBOLS;
|
||||
_choice.Encode(rangeEncoder, 1);
|
||||
await _choice.EncodeAsync(rangeEncoder, 1);
|
||||
if (symbol < Base.K_NUM_MID_LEN_SYMBOLS)
|
||||
{
|
||||
_choice2.Encode(rangeEncoder, 0);
|
||||
_midCoder[posState].Encode(rangeEncoder, symbol);
|
||||
await _choice2.EncodeAsync(rangeEncoder, 0);
|
||||
await _midCoder[posState].EncodeAsync(rangeEncoder, symbol);
|
||||
}
|
||||
else
|
||||
{
|
||||
_choice2.Encode(rangeEncoder, 1);
|
||||
_highCoder.Encode(rangeEncoder, symbol - Base.K_NUM_MID_LEN_SYMBOLS);
|
||||
await _choice2.EncodeAsync(rangeEncoder, 1);
|
||||
await _highCoder.EncodeAsync(rangeEncoder, symbol - Base.K_NUM_MID_LEN_SYMBOLS);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -309,9 +310,9 @@ namespace SharpCompress.Compressors.LZMA
|
||||
}
|
||||
}
|
||||
|
||||
public new void Encode(RangeCoder.Encoder rangeEncoder, UInt32 symbol, UInt32 posState)
|
||||
public override async ValueTask EncodeAsync(RangeCoder.Encoder rangeEncoder, UInt32 symbol, UInt32 posState)
|
||||
{
|
||||
base.Encode(rangeEncoder, symbol, posState);
|
||||
await base.EncodeAsync(rangeEncoder, symbol, posState);
|
||||
if (--_counters[posState] == 0)
|
||||
{
|
||||
UpdateTable(posState);
|
||||
@@ -361,7 +362,7 @@ namespace SharpCompress.Compressors.LZMA
|
||||
|
||||
private readonly Optimal[] _optimum = new Optimal[K_NUM_OPTS];
|
||||
private BinTree _matchFinder;
|
||||
private readonly RangeCoder.Encoder _rangeEncoder = new RangeCoder.Encoder();
|
||||
private readonly RangeCoder.Encoder _rangeEncoder = new();
|
||||
|
||||
private readonly BitEncoder[] _isMatch =
|
||||
new BitEncoder[Base.K_NUM_STATES << Base.K_NUM_POS_STATES_BITS_MAX];
|
||||
@@ -379,12 +380,12 @@ namespace SharpCompress.Compressors.LZMA
|
||||
private readonly BitEncoder[] _posEncoders =
|
||||
new BitEncoder[Base.K_NUM_FULL_DISTANCES - Base.K_END_POS_MODEL_INDEX];
|
||||
|
||||
private BitTreeEncoder _posAlignEncoder = new BitTreeEncoder(Base.K_NUM_ALIGN_BITS);
|
||||
private BitTreeEncoder _posAlignEncoder = new(Base.K_NUM_ALIGN_BITS);
|
||||
|
||||
private readonly LenPriceTableEncoder _lenEncoder = new LenPriceTableEncoder();
|
||||
private readonly LenPriceTableEncoder _repMatchLenEncoder = new LenPriceTableEncoder();
|
||||
private readonly LenPriceTableEncoder _lenEncoder = new();
|
||||
private readonly LenPriceTableEncoder _repMatchLenEncoder = new();
|
||||
|
||||
private readonly LiteralEncoder _literalEncoder = new LiteralEncoder();
|
||||
private readonly LiteralEncoder _literalEncoder = new();
|
||||
|
||||
private readonly UInt32[] _matchDistances = new UInt32[Base.K_MATCH_MAX_LEN * 2 + 2];
|
||||
|
||||
@@ -1189,40 +1190,40 @@ namespace SharpCompress.Compressors.LZMA
|
||||
return (smallDist < ((UInt32)(1) << (32 - kDif)) && bigDist >= (smallDist << kDif));
|
||||
}
|
||||
|
||||
private void WriteEndMarker(UInt32 posState)
|
||||
private async ValueTask WriteEndMarkerAsync(UInt32 posState)
|
||||
{
|
||||
if (!_writeEndMark)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isMatch[(_state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState].Encode(_rangeEncoder, 1);
|
||||
_isRep[_state._index].Encode(_rangeEncoder, 0);
|
||||
await _isMatch[(_state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState].EncodeAsync(_rangeEncoder, 1);
|
||||
await _isRep[_state._index].EncodeAsync(_rangeEncoder, 0);
|
||||
_state.UpdateMatch();
|
||||
UInt32 len = Base.K_MATCH_MIN_LEN;
|
||||
_lenEncoder.Encode(_rangeEncoder, len - Base.K_MATCH_MIN_LEN, posState);
|
||||
await _lenEncoder.EncodeAsync(_rangeEncoder, len - Base.K_MATCH_MIN_LEN, posState);
|
||||
UInt32 posSlot = (1 << Base.K_NUM_POS_SLOT_BITS) - 1;
|
||||
UInt32 lenToPosState = Base.GetLenToPosState(len);
|
||||
_posSlotEncoder[lenToPosState].Encode(_rangeEncoder, posSlot);
|
||||
await _posSlotEncoder[lenToPosState].EncodeAsync(_rangeEncoder, posSlot);
|
||||
int footerBits = 30;
|
||||
UInt32 posReduced = (((UInt32)1) << footerBits) - 1;
|
||||
_rangeEncoder.EncodeDirectBits(posReduced >> Base.K_NUM_ALIGN_BITS, footerBits - Base.K_NUM_ALIGN_BITS);
|
||||
_posAlignEncoder.ReverseEncode(_rangeEncoder, posReduced & Base.K_ALIGN_MASK);
|
||||
await _rangeEncoder.EncodeDirectBits(posReduced >> Base.K_NUM_ALIGN_BITS, footerBits - Base.K_NUM_ALIGN_BITS);
|
||||
await _posAlignEncoder.ReverseEncodeAsync(_rangeEncoder, posReduced & Base.K_ALIGN_MASK);
|
||||
}
|
||||
|
||||
private void Flush(UInt32 nowPos)
|
||||
private async ValueTask FlushAsync(UInt32 nowPos)
|
||||
{
|
||||
ReleaseMfStream();
|
||||
WriteEndMarker(nowPos & _posStateMask);
|
||||
_rangeEncoder.FlushData();
|
||||
_rangeEncoder.FlushStream();
|
||||
await WriteEndMarkerAsync(nowPos & _posStateMask);
|
||||
await _rangeEncoder.FlushData();
|
||||
await _rangeEncoder.FlushAsync();
|
||||
}
|
||||
|
||||
public void CodeOneBlock(out Int64 inSize, out Int64 outSize, out bool finished)
|
||||
public async ValueTask<(Int64, Int64, bool)> CodeOneBlockAsync()
|
||||
{
|
||||
inSize = 0;
|
||||
outSize = 0;
|
||||
finished = true;
|
||||
long inSize = 0;
|
||||
long outSize = 0;
|
||||
var finished = true;
|
||||
|
||||
if (_inStream != null)
|
||||
{
|
||||
@@ -1233,7 +1234,7 @@ namespace SharpCompress.Compressors.LZMA
|
||||
|
||||
if (_finished)
|
||||
{
|
||||
return;
|
||||
return (inSize, outSize, finished);
|
||||
}
|
||||
_finished = true;
|
||||
|
||||
@@ -1254,20 +1255,20 @@ namespace SharpCompress.Compressors.LZMA
|
||||
if (_processingMode && _matchFinder.IsDataStarved)
|
||||
{
|
||||
_finished = false;
|
||||
return;
|
||||
return (inSize, outSize, finished);
|
||||
}
|
||||
if (_matchFinder.GetNumAvailableBytes() == 0)
|
||||
{
|
||||
Flush((UInt32)_nowPos64);
|
||||
return;
|
||||
await FlushAsync((UInt32)_nowPos64);
|
||||
return (inSize, outSize, finished);
|
||||
}
|
||||
UInt32 len, numDistancePairs; // it's not used
|
||||
ReadMatchDistances(out len, out numDistancePairs);
|
||||
UInt32 posState = (UInt32)(_nowPos64) & _posStateMask;
|
||||
_isMatch[(_state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState].Encode(_rangeEncoder, 0);
|
||||
await _isMatch[(_state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState].EncodeAsync(_rangeEncoder, 0);
|
||||
_state.UpdateChar();
|
||||
Byte curByte = _matchFinder.GetIndexByte((Int32)(0 - _additionalOffset));
|
||||
_literalEncoder.GetSubCoder((UInt32)(_nowPos64), _previousByte).Encode(_rangeEncoder, curByte);
|
||||
await _literalEncoder.GetSubCoder((UInt32)(_nowPos64), _previousByte).EncodeAsync(_rangeEncoder, curByte);
|
||||
_previousByte = curByte;
|
||||
_additionalOffset--;
|
||||
_nowPos64++;
|
||||
@@ -1275,19 +1276,19 @@ namespace SharpCompress.Compressors.LZMA
|
||||
if (_processingMode && _matchFinder.IsDataStarved)
|
||||
{
|
||||
_finished = false;
|
||||
return;
|
||||
return (inSize, outSize, finished);
|
||||
}
|
||||
if (_matchFinder.GetNumAvailableBytes() == 0)
|
||||
{
|
||||
Flush((UInt32)_nowPos64);
|
||||
return;
|
||||
await FlushAsync((UInt32)_nowPos64);
|
||||
return (inSize, outSize, finished);
|
||||
}
|
||||
while (true)
|
||||
{
|
||||
if (_processingMode && _matchFinder.IsDataStarved)
|
||||
{
|
||||
_finished = false;
|
||||
return;
|
||||
return (inSize, outSize, finished);
|
||||
}
|
||||
|
||||
UInt32 pos;
|
||||
@@ -1297,51 +1298,51 @@ namespace SharpCompress.Compressors.LZMA
|
||||
UInt32 complexState = (_state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState;
|
||||
if (len == 1 && pos == 0xFFFFFFFF)
|
||||
{
|
||||
_isMatch[complexState].Encode(_rangeEncoder, 0);
|
||||
await _isMatch[complexState].EncodeAsync(_rangeEncoder, 0);
|
||||
Byte curByte = _matchFinder.GetIndexByte((Int32)(0 - _additionalOffset));
|
||||
LiteralEncoder.Encoder2 subCoder = _literalEncoder.GetSubCoder((UInt32)_nowPos64, _previousByte);
|
||||
if (!_state.IsCharState())
|
||||
{
|
||||
Byte matchByte =
|
||||
_matchFinder.GetIndexByte((Int32)(0 - _repDistances[0] - 1 - _additionalOffset));
|
||||
subCoder.EncodeMatched(_rangeEncoder, matchByte, curByte);
|
||||
await subCoder.EncodeMatchedAsync(_rangeEncoder, matchByte, curByte);
|
||||
}
|
||||
else
|
||||
{
|
||||
subCoder.Encode(_rangeEncoder, curByte);
|
||||
await subCoder.EncodeAsync(_rangeEncoder, curByte);
|
||||
}
|
||||
_previousByte = curByte;
|
||||
_state.UpdateChar();
|
||||
}
|
||||
else
|
||||
{
|
||||
_isMatch[complexState].Encode(_rangeEncoder, 1);
|
||||
await _isMatch[complexState].EncodeAsync(_rangeEncoder, 1);
|
||||
if (pos < Base.K_NUM_REP_DISTANCES)
|
||||
{
|
||||
_isRep[_state._index].Encode(_rangeEncoder, 1);
|
||||
await _isRep[_state._index].EncodeAsync(_rangeEncoder, 1);
|
||||
if (pos == 0)
|
||||
{
|
||||
_isRepG0[_state._index].Encode(_rangeEncoder, 0);
|
||||
await _isRepG0[_state._index].EncodeAsync(_rangeEncoder, 0);
|
||||
if (len == 1)
|
||||
{
|
||||
_isRep0Long[complexState].Encode(_rangeEncoder, 0);
|
||||
await _isRep0Long[complexState].EncodeAsync(_rangeEncoder, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
_isRep0Long[complexState].Encode(_rangeEncoder, 1);
|
||||
await _isRep0Long[complexState].EncodeAsync(_rangeEncoder, 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_isRepG0[_state._index].Encode(_rangeEncoder, 1);
|
||||
await _isRepG0[_state._index].EncodeAsync(_rangeEncoder, 1);
|
||||
if (pos == 1)
|
||||
{
|
||||
_isRepG1[_state._index].Encode(_rangeEncoder, 0);
|
||||
await _isRepG1[_state._index].EncodeAsync(_rangeEncoder, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
_isRepG1[_state._index].Encode(_rangeEncoder, 1);
|
||||
_isRepG2[_state._index].Encode(_rangeEncoder, pos - 2);
|
||||
await _isRepG1[_state._index].EncodeAsync(_rangeEncoder, 1);
|
||||
await _isRepG2[_state._index].EncodeAsync(_rangeEncoder, pos - 2);
|
||||
}
|
||||
}
|
||||
if (len == 1)
|
||||
@@ -1350,7 +1351,7 @@ namespace SharpCompress.Compressors.LZMA
|
||||
}
|
||||
else
|
||||
{
|
||||
_repMatchLenEncoder.Encode(_rangeEncoder, len - Base.K_MATCH_MIN_LEN, posState);
|
||||
await _repMatchLenEncoder.EncodeAsync(_rangeEncoder, len - Base.K_MATCH_MIN_LEN, posState);
|
||||
_state.UpdateRep();
|
||||
}
|
||||
UInt32 distance = _repDistances[pos];
|
||||
@@ -1365,13 +1366,13 @@ namespace SharpCompress.Compressors.LZMA
|
||||
}
|
||||
else
|
||||
{
|
||||
_isRep[_state._index].Encode(_rangeEncoder, 0);
|
||||
await _isRep[_state._index].EncodeAsync(_rangeEncoder, 0);
|
||||
_state.UpdateMatch();
|
||||
_lenEncoder.Encode(_rangeEncoder, len - Base.K_MATCH_MIN_LEN, posState);
|
||||
await _lenEncoder.EncodeAsync(_rangeEncoder, len - Base.K_MATCH_MIN_LEN, posState);
|
||||
pos -= Base.K_NUM_REP_DISTANCES;
|
||||
UInt32 posSlot = GetPosSlot(pos);
|
||||
UInt32 lenToPosState = Base.GetLenToPosState(len);
|
||||
_posSlotEncoder[lenToPosState].Encode(_rangeEncoder, posSlot);
|
||||
await _posSlotEncoder[lenToPosState].EncodeAsync(_rangeEncoder, posSlot);
|
||||
|
||||
if (posSlot >= Base.K_START_POS_MODEL_INDEX)
|
||||
{
|
||||
@@ -1381,15 +1382,15 @@ namespace SharpCompress.Compressors.LZMA
|
||||
|
||||
if (posSlot < Base.K_END_POS_MODEL_INDEX)
|
||||
{
|
||||
BitTreeEncoder.ReverseEncode(_posEncoders,
|
||||
baseVal - posSlot - 1, _rangeEncoder, footerBits,
|
||||
posReduced);
|
||||
await BitTreeEncoder.ReverseEncodeAsync(_posEncoders,
|
||||
baseVal - posSlot - 1, _rangeEncoder, footerBits,
|
||||
posReduced);
|
||||
}
|
||||
else
|
||||
{
|
||||
_rangeEncoder.EncodeDirectBits(posReduced >> Base.K_NUM_ALIGN_BITS,
|
||||
await _rangeEncoder.EncodeDirectBits(posReduced >> Base.K_NUM_ALIGN_BITS,
|
||||
footerBits - Base.K_NUM_ALIGN_BITS);
|
||||
_posAlignEncoder.ReverseEncode(_rangeEncoder, posReduced & Base.K_ALIGN_MASK);
|
||||
await _posAlignEncoder.ReverseEncodeAsync(_rangeEncoder, posReduced & Base.K_ALIGN_MASK);
|
||||
_alignPriceCount++;
|
||||
}
|
||||
}
|
||||
@@ -1421,19 +1422,19 @@ namespace SharpCompress.Compressors.LZMA
|
||||
if (_processingMode && _matchFinder.IsDataStarved)
|
||||
{
|
||||
_finished = false;
|
||||
return;
|
||||
return (inSize, outSize, finished);
|
||||
}
|
||||
if (_matchFinder.GetNumAvailableBytes() == 0)
|
||||
{
|
||||
Flush((UInt32)_nowPos64);
|
||||
return;
|
||||
await FlushAsync((UInt32)_nowPos64);
|
||||
return (inSize, outSize, finished);
|
||||
}
|
||||
|
||||
if (_nowPos64 - progressPosValuePrev >= (1 << 12))
|
||||
{
|
||||
_finished = false;
|
||||
finished = false;
|
||||
return;
|
||||
return (inSize, outSize, finished);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1488,7 +1489,7 @@ namespace SharpCompress.Compressors.LZMA
|
||||
_nowPos64 = 0;
|
||||
}
|
||||
|
||||
public void Code(Stream inStream, Stream outStream,
|
||||
public async ValueTask CodeAsync(Stream inStream, Stream outStream,
|
||||
Int64 inSize, Int64 outSize, ICodeProgress progress)
|
||||
{
|
||||
_needReleaseMfStream = false;
|
||||
@@ -1498,10 +1499,7 @@ namespace SharpCompress.Compressors.LZMA
|
||||
SetStreams(inStream, outStream, inSize, outSize);
|
||||
while (true)
|
||||
{
|
||||
Int64 processedInSize;
|
||||
Int64 processedOutSize;
|
||||
bool finished;
|
||||
CodeOneBlock(out processedInSize, out processedOutSize, out finished);
|
||||
var (processedInSize, processedOutSize, finished) = await CodeOneBlockAsync();
|
||||
if (finished)
|
||||
{
|
||||
return;
|
||||
@@ -1518,7 +1516,7 @@ namespace SharpCompress.Compressors.LZMA
|
||||
}
|
||||
}
|
||||
|
||||
public long Code(Stream inStream, bool final)
|
||||
public async ValueTask<long> CodeAsync(Stream inStream, bool final)
|
||||
{
|
||||
_matchFinder.SetStream(inStream);
|
||||
_processingMode = !final;
|
||||
@@ -1526,10 +1524,7 @@ namespace SharpCompress.Compressors.LZMA
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
Int64 processedInSize;
|
||||
Int64 processedOutSize;
|
||||
bool finished;
|
||||
CodeOneBlock(out processedInSize, out processedOutSize, out finished);
|
||||
var (processedInSize, processedOutSize, finished) = await CodeOneBlockAsync();
|
||||
if (finished)
|
||||
{
|
||||
return processedInSize;
|
||||
|
||||
@@ -1,21 +1,24 @@
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Buffers.Binary;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Compressors.LZMA.LZ;
|
||||
|
||||
namespace SharpCompress.Compressors.LZMA
|
||||
{
|
||||
public class LzmaStream : Stream
|
||||
{
|
||||
private readonly Stream _inputStream;
|
||||
private readonly long _inputSize;
|
||||
private readonly long _outputSize;
|
||||
private Stream _inputStream;
|
||||
private long _inputSize;
|
||||
private long _outputSize;
|
||||
|
||||
private readonly int _dictionarySize;
|
||||
private readonly OutWindow _outWindow = new OutWindow();
|
||||
private readonly RangeCoder.Decoder _rangeDecoder = new RangeCoder.Decoder();
|
||||
private int _dictionarySize;
|
||||
private OutWindow _outWindow = new OutWindow();
|
||||
private RangeCoder.Decoder _rangeDecoder = new RangeCoder.Decoder();
|
||||
private Decoder _decoder;
|
||||
|
||||
private long _position;
|
||||
@@ -25,70 +28,60 @@ namespace SharpCompress.Compressors.LZMA
|
||||
private long _inputPosition;
|
||||
|
||||
// LZMA2
|
||||
private readonly bool _isLzma2;
|
||||
private bool _isLzma2;
|
||||
private bool _uncompressedChunk;
|
||||
private bool _needDictReset = true;
|
||||
private bool _needProps = true;
|
||||
|
||||
private readonly Encoder _encoder;
|
||||
private bool _isDisposed;
|
||||
|
||||
private LzmaStream() {}
|
||||
|
||||
public LzmaStream(byte[] properties, Stream inputStream)
|
||||
: this(properties, inputStream, -1, -1, null, properties.Length < 5)
|
||||
public static async ValueTask<LzmaStream> CreateAsync(byte[] properties, Stream inputStream, long inputSize = -1, long outputSize = -1,
|
||||
Stream presetDictionary = null, bool? isLzma2 = null)
|
||||
{
|
||||
}
|
||||
var ls = new LzmaStream();
|
||||
ls._inputStream = inputStream;
|
||||
ls._inputSize = inputSize;
|
||||
ls._outputSize = outputSize;
|
||||
ls._isLzma2 = isLzma2 ?? properties.Length < 5;
|
||||
|
||||
public LzmaStream(byte[] properties, Stream inputStream, long inputSize)
|
||||
: this(properties, inputStream, inputSize, -1, null, properties.Length < 5)
|
||||
{
|
||||
}
|
||||
|
||||
public LzmaStream(byte[] properties, Stream inputStream, long inputSize, long outputSize)
|
||||
: this(properties, inputStream, inputSize, outputSize, null, properties.Length < 5)
|
||||
{
|
||||
}
|
||||
|
||||
public LzmaStream(byte[] properties, Stream inputStream, long inputSize, long outputSize,
|
||||
Stream presetDictionary, bool isLzma2)
|
||||
{
|
||||
_inputStream = inputStream;
|
||||
_inputSize = inputSize;
|
||||
_outputSize = outputSize;
|
||||
_isLzma2 = isLzma2;
|
||||
|
||||
if (!isLzma2)
|
||||
if (!ls._isLzma2)
|
||||
{
|
||||
_dictionarySize = BinaryPrimitives.ReadInt32LittleEndian(properties.AsSpan(1));
|
||||
_outWindow.Create(_dictionarySize);
|
||||
ls._dictionarySize = BinaryPrimitives.ReadInt32LittleEndian(properties.AsSpan(1));
|
||||
ls._outWindow.Create(ls._dictionarySize);
|
||||
if (presetDictionary != null)
|
||||
{
|
||||
_outWindow.Train(presetDictionary);
|
||||
ls._outWindow.Train(presetDictionary);
|
||||
}
|
||||
|
||||
_rangeDecoder.Init(inputStream);
|
||||
await ls._rangeDecoder.InitAsync(inputStream);
|
||||
|
||||
_decoder = new Decoder();
|
||||
_decoder.SetDecoderProperties(properties);
|
||||
Properties = properties;
|
||||
ls._decoder = new Decoder();
|
||||
ls._decoder.SetDecoderProperties(properties);
|
||||
ls.Properties = properties;
|
||||
|
||||
_availableBytes = outputSize < 0 ? long.MaxValue : outputSize;
|
||||
_rangeDecoderLimit = inputSize;
|
||||
ls._availableBytes = outputSize < 0 ? long.MaxValue : outputSize;
|
||||
ls._rangeDecoderLimit = inputSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
_dictionarySize = 2 | (properties[0] & 1);
|
||||
_dictionarySize <<= (properties[0] >> 1) + 11;
|
||||
ls. _dictionarySize = 2 | (properties[0] & 1);
|
||||
ls. _dictionarySize <<= (properties[0] >> 1) + 11;
|
||||
|
||||
_outWindow.Create(_dictionarySize);
|
||||
ls._outWindow.Create(ls._dictionarySize);
|
||||
if (presetDictionary != null)
|
||||
{
|
||||
_outWindow.Train(presetDictionary);
|
||||
_needDictReset = false;
|
||||
ls._outWindow.Train(presetDictionary);
|
||||
ls._needDictReset = false;
|
||||
}
|
||||
|
||||
Properties = new byte[1];
|
||||
_availableBytes = 0;
|
||||
ls. Properties = new byte[1];
|
||||
ls._availableBytes = 0;
|
||||
}
|
||||
|
||||
return ls;
|
||||
}
|
||||
|
||||
public LzmaStream(LzmaEncoderProperties properties, bool isLzma2, Stream outputStream)
|
||||
@@ -128,24 +121,26 @@ namespace SharpCompress.Compressors.LZMA
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
public override async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_isDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_isDisposed = true;
|
||||
if (disposing)
|
||||
if (_encoder != null)
|
||||
{
|
||||
if (_encoder != null)
|
||||
{
|
||||
_position = _encoder.Code(null, true);
|
||||
}
|
||||
_inputStream?.Dispose();
|
||||
_position = await _encoder.CodeAsync(null, true);
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
_inputStream?.DisposeAsync();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override long Length => _position + _availableBytes;
|
||||
@@ -153,6 +148,11 @@ namespace SharpCompress.Compressors.LZMA
|
||||
public override long Position { get => _position; set => throw new NotSupportedException(); }
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||
{
|
||||
if (_endReached)
|
||||
{
|
||||
@@ -166,7 +166,7 @@ namespace SharpCompress.Compressors.LZMA
|
||||
{
|
||||
if (_isLzma2)
|
||||
{
|
||||
DecodeChunkHeader();
|
||||
await DecodeChunkHeader();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -231,7 +231,7 @@ namespace SharpCompress.Compressors.LZMA
|
||||
return total;
|
||||
}
|
||||
|
||||
private void DecodeChunkHeader()
|
||||
private async ValueTask DecodeChunkHeader()
|
||||
{
|
||||
int control = _inputStream.ReadByte();
|
||||
_inputPosition++;
|
||||
@@ -283,7 +283,7 @@ namespace SharpCompress.Compressors.LZMA
|
||||
_decoder.SetDecoderProperties(Properties);
|
||||
}
|
||||
|
||||
_rangeDecoder.Init(_inputStream);
|
||||
await _rangeDecoder.InitAsync(_inputStream);
|
||||
}
|
||||
else if (control > 0x02)
|
||||
{
|
||||
@@ -307,14 +307,22 @@ namespace SharpCompress.Compressors.LZMA
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
public override async ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
if (_encoder != null)
|
||||
{
|
||||
_position = _encoder.Code(new MemoryStream(buffer, offset, count), false);
|
||||
var m = ArrayPool<byte>.Shared.Rent(buffer.Length);
|
||||
buffer.CopyTo(m.AsMemory());
|
||||
_position = await _encoder.CodeAsync(new MemoryStream(m), false);
|
||||
ArrayPool<byte>.Shared.Return(m);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] Properties { get; } = new byte[5];
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public byte[] Properties { get; private set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SharpCompress.Compressors.LZMA.RangeCoder
|
||||
{
|
||||
internal class Encoder
|
||||
internal class Encoder : IAsyncDisposable
|
||||
{
|
||||
public const uint K_TOP_VALUE = (1 << 24);
|
||||
|
||||
@@ -38,43 +40,46 @@ namespace SharpCompress.Compressors.LZMA.RangeCoder
|
||||
_cache = 0;
|
||||
}
|
||||
|
||||
public void FlushData()
|
||||
public async ValueTask FlushData()
|
||||
{
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
ShiftLow();
|
||||
await ShiftLowAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public void FlushStream()
|
||||
public Task FlushAsync()
|
||||
{
|
||||
_stream.Flush();
|
||||
return _stream.FlushAsync();
|
||||
}
|
||||
|
||||
public void CloseStream()
|
||||
public ValueTask DisposeAsync()
|
||||
{
|
||||
_stream.Dispose();
|
||||
return _stream.DisposeAsync();
|
||||
}
|
||||
|
||||
public void Encode(uint start, uint size, uint total)
|
||||
public async ValueTask EncodeAsync(uint start, uint size, uint total)
|
||||
{
|
||||
_low += start * (_range /= total);
|
||||
_range *= size;
|
||||
while (_range < K_TOP_VALUE)
|
||||
{
|
||||
_range <<= 8;
|
||||
ShiftLow();
|
||||
await ShiftLowAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public void ShiftLow()
|
||||
public async ValueTask ShiftLowAsync()
|
||||
{
|
||||
if ((uint)_low < 0xFF000000 || (uint)(_low >> 32) == 1)
|
||||
{
|
||||
using var buffer = MemoryPool<byte>.Shared.Rent(1);
|
||||
var b = buffer.Memory.Slice(0,1);
|
||||
byte temp = _cache;
|
||||
do
|
||||
{
|
||||
_stream.WriteByte((byte)(temp + (_low >> 32)));
|
||||
b.Span[0] = (byte)(temp + (_low >> 32));
|
||||
await _stream.WriteAsync(b);
|
||||
temp = 0xFF;
|
||||
}
|
||||
while (--_cacheSize != 0);
|
||||
@@ -84,7 +89,7 @@ namespace SharpCompress.Compressors.LZMA.RangeCoder
|
||||
_low = ((uint)_low) << 8;
|
||||
}
|
||||
|
||||
public void EncodeDirectBits(uint v, int numTotalBits)
|
||||
public async ValueTask EncodeDirectBits(uint v, int numTotalBits)
|
||||
{
|
||||
for (int i = numTotalBits - 1; i >= 0; i--)
|
||||
{
|
||||
@@ -96,12 +101,12 @@ namespace SharpCompress.Compressors.LZMA.RangeCoder
|
||||
if (_range < K_TOP_VALUE)
|
||||
{
|
||||
_range <<= 8;
|
||||
ShiftLow();
|
||||
await ShiftLowAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void EncodeBit(uint size0, int numTotalBits, uint symbol)
|
||||
public async ValueTask EncodeBitAsync(uint size0, int numTotalBits, uint symbol)
|
||||
{
|
||||
uint newBound = (_range >> numTotalBits) * size0;
|
||||
if (symbol == 0)
|
||||
@@ -116,7 +121,7 @@ namespace SharpCompress.Compressors.LZMA.RangeCoder
|
||||
while (_range < K_TOP_VALUE)
|
||||
{
|
||||
_range <<= 8;
|
||||
ShiftLow();
|
||||
await ShiftLowAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,7 +134,7 @@ namespace SharpCompress.Compressors.LZMA.RangeCoder
|
||||
}
|
||||
}
|
||||
|
||||
internal class Decoder
|
||||
internal class Decoder: IAsyncDisposable
|
||||
{
|
||||
public const uint K_TOP_VALUE = (1 << 24);
|
||||
public uint _range;
|
||||
@@ -139,16 +144,19 @@ namespace SharpCompress.Compressors.LZMA.RangeCoder
|
||||
public Stream _stream;
|
||||
public long _total;
|
||||
|
||||
public void Init(Stream stream)
|
||||
public async ValueTask InitAsync(Stream stream)
|
||||
{
|
||||
// Stream.Init(stream);
|
||||
_stream = stream;
|
||||
|
||||
_code = 0;
|
||||
_range = 0xFFFFFFFF;
|
||||
using var buffer = MemoryPool<byte>.Shared.Rent(1);
|
||||
var b = buffer.Memory.Slice(0,1);
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
_code = (_code << 8) | (byte)_stream.ReadByte();
|
||||
await _stream.ReadAsync(b);
|
||||
_code = (_code << 8) | b.Span[0];
|
||||
}
|
||||
_total = 5;
|
||||
}
|
||||
@@ -159,41 +167,34 @@ namespace SharpCompress.Compressors.LZMA.RangeCoder
|
||||
_stream = null;
|
||||
}
|
||||
|
||||
public void CloseStream()
|
||||
public ValueTask DisposeAsync()
|
||||
{
|
||||
_stream.Dispose();
|
||||
return _stream.DisposeAsync();
|
||||
}
|
||||
|
||||
public void Normalize()
|
||||
public async ValueTask NormalizeAsync()
|
||||
{
|
||||
using var buffer = MemoryPool<byte>.Shared.Rent(1);
|
||||
var b = buffer.Memory.Slice(0,1);
|
||||
while (_range < K_TOP_VALUE)
|
||||
{
|
||||
_code = (_code << 8) | (byte)_stream.ReadByte();
|
||||
await _stream.ReadAsync(b);
|
||||
_code = (_code << 8) | b.Span[0];
|
||||
_range <<= 8;
|
||||
_total++;
|
||||
}
|
||||
}
|
||||
|
||||
public void Normalize2()
|
||||
{
|
||||
if (_range < K_TOP_VALUE)
|
||||
{
|
||||
_code = (_code << 8) | (byte)_stream.ReadByte();
|
||||
_range <<= 8;
|
||||
_total++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public uint GetThreshold(uint total)
|
||||
{
|
||||
return _code / (_range /= total);
|
||||
}
|
||||
|
||||
public void Decode(uint start, uint size)
|
||||
public async ValueTask Decode(uint start, uint size)
|
||||
{
|
||||
_code -= start * _range;
|
||||
_range *= size;
|
||||
Normalize();
|
||||
await NormalizeAsync();
|
||||
}
|
||||
|
||||
public uint DecodeDirectBits(int numTotalBits)
|
||||
@@ -228,7 +229,7 @@ namespace SharpCompress.Compressors.LZMA.RangeCoder
|
||||
return result;
|
||||
}
|
||||
|
||||
public uint DecodeBit(uint size0, int numTotalBits)
|
||||
public async ValueTask<uint> DecodeBitAsync(uint size0, int numTotalBits)
|
||||
{
|
||||
uint newBound = (_range >> numTotalBits) * size0;
|
||||
uint symbol;
|
||||
@@ -243,7 +244,7 @@ namespace SharpCompress.Compressors.LZMA.RangeCoder
|
||||
_code -= newBound;
|
||||
_range -= newBound;
|
||||
}
|
||||
Normalize();
|
||||
await NormalizeAsync();
|
||||
return symbol;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SharpCompress.Compressors.LZMA.RangeCoder
|
||||
{
|
||||
@@ -29,7 +30,7 @@ namespace SharpCompress.Compressors.LZMA.RangeCoder
|
||||
}
|
||||
}
|
||||
|
||||
public void Encode(Encoder encoder, uint symbol)
|
||||
public async ValueTask EncodeAsync(Encoder encoder, uint symbol)
|
||||
{
|
||||
// encoder.EncodeBit(Prob, kNumBitModelTotalBits, symbol);
|
||||
// UpdateModel(symbol);
|
||||
@@ -48,7 +49,7 @@ namespace SharpCompress.Compressors.LZMA.RangeCoder
|
||||
if (encoder._range < Encoder.K_TOP_VALUE)
|
||||
{
|
||||
encoder._range <<= 8;
|
||||
encoder.ShiftLow();
|
||||
await encoder.ShiftLowAsync();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SharpCompress.Compressors.LZMA.RangeCoder
|
||||
{
|
||||
@@ -21,25 +22,25 @@ namespace SharpCompress.Compressors.LZMA.RangeCoder
|
||||
}
|
||||
}
|
||||
|
||||
public void Encode(Encoder rangeEncoder, UInt32 symbol)
|
||||
public async ValueTask EncodeAsync(Encoder rangeEncoder, UInt32 symbol)
|
||||
{
|
||||
UInt32 m = 1;
|
||||
for (int bitIndex = _numBitLevels; bitIndex > 0;)
|
||||
{
|
||||
bitIndex--;
|
||||
UInt32 bit = (symbol >> bitIndex) & 1;
|
||||
_models[m].Encode(rangeEncoder, bit);
|
||||
await _models[m].EncodeAsync(rangeEncoder, bit);
|
||||
m = (m << 1) | bit;
|
||||
}
|
||||
}
|
||||
|
||||
public void ReverseEncode(Encoder rangeEncoder, UInt32 symbol)
|
||||
public async ValueTask ReverseEncodeAsync(Encoder rangeEncoder, UInt32 symbol)
|
||||
{
|
||||
UInt32 m = 1;
|
||||
for (UInt32 i = 0; i < _numBitLevels; i++)
|
||||
{
|
||||
UInt32 bit = symbol & 1;
|
||||
_models[m].Encode(rangeEncoder, bit);
|
||||
await _models[m].EncodeAsync(rangeEncoder, bit);
|
||||
m = (m << 1) | bit;
|
||||
symbol >>= 1;
|
||||
}
|
||||
@@ -88,14 +89,14 @@ namespace SharpCompress.Compressors.LZMA.RangeCoder
|
||||
return price;
|
||||
}
|
||||
|
||||
public static void ReverseEncode(BitEncoder[] models, UInt32 startIndex,
|
||||
public static async ValueTask ReverseEncodeAsync(BitEncoder[] models, UInt32 startIndex,
|
||||
Encoder rangeEncoder, int numBitLevels, UInt32 symbol)
|
||||
{
|
||||
UInt32 m = 1;
|
||||
for (int i = 0; i < numBitLevels; i++)
|
||||
{
|
||||
UInt32 bit = symbol & 1;
|
||||
models[startIndex + m].Encode(rangeEncoder, bit);
|
||||
await models[startIndex + m].EncodeAsync(rangeEncoder, bit);
|
||||
m = (m << 1) | bit;
|
||||
symbol >>= 1;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ namespace SharpCompress.Compressors.LZMA
|
||||
internal static async ValueTask<Stream> CreateDecoderStream(CMethodId id, Stream[] inStreams, byte[] info, IPasswordProvider pass,
|
||||
long limit, CancellationToken cancellationToken)
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
switch (id._id)
|
||||
{
|
||||
case K_COPY:
|
||||
@@ -37,17 +38,17 @@ namespace SharpCompress.Compressors.LZMA
|
||||
return inStreams.Single();
|
||||
case K_LZMA:
|
||||
case K_LZMA2:
|
||||
return new LzmaStream(info, inStreams.Single(), -1, limit);
|
||||
return await LzmaStream.CreateAsync(info, inStreams.Single(), -1, limit);
|
||||
case CMethodId.K_AES_ID:
|
||||
return new AesDecoderStream(inStreams.Single(), info, pass, limit);
|
||||
case K_BCJ:
|
||||
return new BCJFilter(false, inStreams.Single());
|
||||
case K_BCJ2:
|
||||
return new Bcj2DecoderStream(inStreams, info, limit);
|
||||
case K_B_ZIP2:
|
||||
return await BZip2Stream.CreateAsync(inStreams.Single(), CompressionMode.Decompress, true, cancellationToken);
|
||||
/*case K_PPMD:
|
||||
return new PpmdStream(new PpmdProperties(info), inStreams.Single(), false);*/
|
||||
/* case K_B_ZIP2:
|
||||
return await BZip2Stream.CreateAsync(inStreams.Single(), CompressionMode.Decompress, true, cancellationToken);
|
||||
case K_PPMD:
|
||||
return new PpmdStream(new PpmdProperties(info), inStreams.Single(), false);*/
|
||||
case K_DEFLATE:
|
||||
return new DeflateStream(inStreams.Single(), CompressionMode.Decompress);
|
||||
default:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SharpCompress.Compressors.Xz.Filters
|
||||
{
|
||||
@@ -52,6 +53,6 @@ namespace SharpCompress.Compressors.Xz.Filters
|
||||
return filter;
|
||||
}
|
||||
|
||||
public abstract void SetBaseStream(Stream stream);
|
||||
public abstract ValueTask SetBaseStreamAsync(Stream stream);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Compressors.LZMA;
|
||||
|
||||
namespace SharpCompress.Compressors.Xz.Filters
|
||||
@@ -49,9 +50,9 @@ namespace SharpCompress.Compressors.Xz.Filters
|
||||
{
|
||||
}
|
||||
|
||||
public override void SetBaseStream(Stream stream)
|
||||
public override async ValueTask SetBaseStreamAsync(Stream stream)
|
||||
{
|
||||
BaseStream = new LzmaStream(new[] { _dictionarySize }, stream);
|
||||
BaseStream = await LzmaStream.CreateAsync(new[] { _dictionarySize }, stream);
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace SharpCompress.Compressors.Xz
|
||||
|
||||
if (!_streamConnected)
|
||||
{
|
||||
ConnectStream();
|
||||
await ConnectStreamAsync();
|
||||
}
|
||||
|
||||
if (!_endOfStream && _decomStream is not null)
|
||||
@@ -95,13 +95,13 @@ namespace SharpCompress.Compressors.Xz
|
||||
_crcChecked = true;
|
||||
}
|
||||
|
||||
private void ConnectStream()
|
||||
private async ValueTask ConnectStreamAsync()
|
||||
{
|
||||
_decomStream = BaseStream;
|
||||
while (Filters.Any())
|
||||
{
|
||||
BlockFilter filter = Filters.Pop();
|
||||
filter.SetBaseStream(_decomStream);
|
||||
await filter.SetBaseStreamAsync(_decomStream);
|
||||
_decomStream = filter;
|
||||
}
|
||||
_streamConnected = true;
|
||||
|
||||
@@ -56,7 +56,7 @@ namespace SharpCompress.Readers
|
||||
return GZipReader.Open(rewindableStream, options);
|
||||
}
|
||||
|
||||
rewindableStream.Rewind(false);
|
||||
/* rewindableStream.Rewind(false);
|
||||
if (await BZip2Stream.IsBZip2Async(rewindableStream, cancellationToken))
|
||||
{
|
||||
rewindableStream.Rewind(false);
|
||||
@@ -66,13 +66,13 @@ namespace SharpCompress.Readers
|
||||
rewindableStream.Rewind(true);
|
||||
return new TarReader(rewindableStream, options, CompressionType.BZip2);
|
||||
}
|
||||
}
|
||||
} */
|
||||
|
||||
rewindableStream.Rewind(false);
|
||||
if (LZipStream.IsLZipFile(rewindableStream))
|
||||
if (await LZipStream.IsLZipFileAsync(rewindableStream))
|
||||
{
|
||||
rewindableStream.Rewind(false);
|
||||
LZipStream testStream = new(new NonDisposingStream(rewindableStream), CompressionMode.Decompress);
|
||||
var testStream = await LZipStream.CreateAsync(new NonDisposingStream(rewindableStream), CompressionMode.Decompress);
|
||||
if (await TarArchive.IsTarFileAsync(testStream, cancellationToken))
|
||||
{
|
||||
rewindableStream.Rewind(true);
|
||||
|
||||
@@ -34,17 +34,17 @@ namespace SharpCompress.Readers.Tar
|
||||
var stream = await base.RequestInitialStream(cancellationToken);
|
||||
switch (compressionType)
|
||||
{
|
||||
case CompressionType.BZip2:
|
||||
/* case CompressionType.BZip2:
|
||||
{
|
||||
return await BZip2Stream.CreateAsync(stream, CompressionMode.Decompress, false, cancellationToken);
|
||||
}
|
||||
} */
|
||||
case CompressionType.GZip:
|
||||
{
|
||||
return new GZipStream(stream, CompressionMode.Decompress);
|
||||
}
|
||||
case CompressionType.LZip:
|
||||
{
|
||||
return new LZipStream(stream, CompressionMode.Decompress);
|
||||
return await LZipStream.CreateAsync(stream, CompressionMode.Decompress);
|
||||
}
|
||||
case CompressionType.Xz:
|
||||
{
|
||||
@@ -87,7 +87,7 @@ namespace SharpCompress.Readers.Tar
|
||||
throw new InvalidFormatException("Not a tar file.");
|
||||
}
|
||||
|
||||
rewindableStream.Rewind(false);
|
||||
/*rewindableStream.Rewind(false);
|
||||
if (await BZip2Stream.IsBZip2Async(rewindableStream, cancellationToken))
|
||||
{
|
||||
rewindableStream.Rewind(false);
|
||||
@@ -98,13 +98,13 @@ namespace SharpCompress.Readers.Tar
|
||||
return new TarReader(rewindableStream, options, CompressionType.BZip2);
|
||||
}
|
||||
throw new InvalidFormatException("Not a tar file.");
|
||||
}
|
||||
} */
|
||||
|
||||
rewindableStream.Rewind(false);
|
||||
if (LZipStream.IsLZipFile(rewindableStream))
|
||||
if (await LZipStream.IsLZipFileAsync(rewindableStream))
|
||||
{
|
||||
rewindableStream.Rewind(false);
|
||||
LZipStream testStream = new(rewindableStream, CompressionMode.Decompress);
|
||||
var testStream = await LZipStream.CreateAsync(rewindableStream, CompressionMode.Decompress);
|
||||
if (await TarArchive.IsTarFileAsync(testStream, cancellationToken))
|
||||
{
|
||||
rewindableStream.Rewind(true);
|
||||
|
||||
@@ -127,12 +127,15 @@ namespace SharpCompress
|
||||
yield return item;
|
||||
}
|
||||
|
||||
public static void CheckNotNull(this object obj, string name)
|
||||
public static T CheckNotNull<T>(this T? obj, string name)
|
||||
where T : class
|
||||
{
|
||||
if (obj is null)
|
||||
{
|
||||
throw new ArgumentNullException(name);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
public static void CheckNotNullOrEmpty(this string obj, string name)
|
||||
|
||||
@@ -24,6 +24,7 @@ namespace SharpCompress.Writers.Tar
|
||||
|
||||
public static async ValueTask<TarWriter> CreateAsync(Stream destination, TarWriterOptions options, CancellationToken cancellationToken = default)
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
var tw = new TarWriter(options);
|
||||
tw.finalizeArchiveOnClose = options.FinalizeArchiveOnClose;
|
||||
|
||||
@@ -39,11 +40,11 @@ namespace SharpCompress.Writers.Tar
|
||||
{
|
||||
case CompressionType.None:
|
||||
break;
|
||||
case CompressionType.BZip2:
|
||||
/* case CompressionType.BZip2:
|
||||
{
|
||||
destination = await BZip2Stream.CreateAsync(destination, CompressionMode.Compress, false, cancellationToken);
|
||||
}
|
||||
break;
|
||||
break; */
|
||||
case CompressionType.GZip:
|
||||
{
|
||||
destination = new GZipStream(destination, CompressionMode.Compress);
|
||||
@@ -51,7 +52,7 @@ namespace SharpCompress.Writers.Tar
|
||||
break;
|
||||
case CompressionType.LZip:
|
||||
{
|
||||
destination = new LZipStream(destination, CompressionMode.Compress);
|
||||
destination = await LZipStream.CreateAsync(destination, CompressionMode.Compress);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -122,11 +123,11 @@ namespace SharpCompress.Writers.Tar
|
||||
}
|
||||
switch (OutputStream)
|
||||
{
|
||||
case BZip2Stream b:
|
||||
/* case BZip2Stream b:
|
||||
{
|
||||
await b.FinishAsync(CancellationToken.None);
|
||||
break;
|
||||
}
|
||||
} */
|
||||
case LZipStream l:
|
||||
{
|
||||
l.Finish();
|
||||
|
||||
@@ -379,6 +379,7 @@ namespace SharpCompress.Writers.Zip
|
||||
|
||||
private async ValueTask<Stream> GetWriteStream(Stream writeStream, CancellationToken cancellationToken)
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
counting = new CountingWritableSubStream(writeStream);
|
||||
Stream output = counting;
|
||||
switch (zipCompressionMethod)
|
||||
@@ -391,10 +392,10 @@ namespace SharpCompress.Writers.Zip
|
||||
{
|
||||
return new DeflateStream(counting, CompressionMode.Compress, compressionLevel);
|
||||
}
|
||||
case ZipCompressionMethod.BZip2:
|
||||
/*case ZipCompressionMethod.BZip2:
|
||||
{
|
||||
return await BZip2Stream.CreateAsync(counting, CompressionMode.Compress, false, cancellationToken);
|
||||
}
|
||||
} */
|
||||
case ZipCompressionMethod.LZMA:
|
||||
{
|
||||
counting.WriteByte(9);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Compressors.LZMA;
|
||||
using Xunit;
|
||||
|
||||
@@ -7,13 +8,13 @@ namespace SharpCompress.Test.Streams
|
||||
public class LzmaStreamTests
|
||||
{
|
||||
[Fact]
|
||||
public void TestLzma2Decompress1Byte()
|
||||
public async ValueTask TestLzma2Decompress1Byte()
|
||||
{
|
||||
byte[] properties = new byte[] { 0x01 };
|
||||
byte[] compressedData = new byte[] { 0x01, 0x00, 0x00, 0x58, 0x00 };
|
||||
MemoryStream lzma2Stream = new MemoryStream(compressedData);
|
||||
|
||||
LzmaStream decompressor = new LzmaStream(properties, lzma2Stream, 5, 1);
|
||||
LzmaStream decompressor = await LzmaStream.CreateAsync(properties, lzma2Stream, 5, 1);
|
||||
Assert.Equal('X', decompressor.ReadByte());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user