123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318 |
- using System;
- using System.IO;
- using SharpCompress.Compressors.LZMA.LZ;
- using SharpCompress.Converters;
- namespace SharpCompress.Compressors.LZMA
- {
- public class LzmaStream : Stream
- {
- private readonly Stream _inputStream;
- private readonly long _inputSize;
- private readonly long _outputSize;
- private readonly int _dictionarySize;
- private readonly OutWindow _outWindow = new OutWindow();
- private readonly RangeCoder.Decoder _rangeDecoder = new RangeCoder.Decoder();
- private Decoder _decoder;
- private long _position;
- private bool _endReached;
- private long _availableBytes;
- private long _rangeDecoderLimit;
- private long _inputPosition;
- // LZMA2
- private readonly bool _isLzma2;
- private bool _uncompressedChunk;
- private bool _needDictReset = true;
- private bool _needProps = true;
- private readonly Encoder _encoder;
- private bool _isDisposed;
- public LzmaStream(byte[] properties, Stream inputStream)
- : this(properties, inputStream, -1, -1, null, 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)
- {
- _dictionarySize = DataConverter.LittleEndian.GetInt32(properties, 1);
- _outWindow.Create(_dictionarySize);
- if (presetDictionary != null)
- {
- _outWindow.Train(presetDictionary);
- }
- _rangeDecoder.Init(inputStream);
- _decoder = new Decoder();
- _decoder.SetDecoderProperties(properties);
- Properties = properties;
- _availableBytes = outputSize < 0 ? long.MaxValue : outputSize;
- _rangeDecoderLimit = inputSize;
- }
- else
- {
- _dictionarySize = 2 | (properties[0] & 1);
- _dictionarySize <<= (properties[0] >> 1) + 11;
- _outWindow.Create(_dictionarySize);
- if (presetDictionary != null)
- {
- _outWindow.Train(presetDictionary);
- _needDictReset = false;
- }
- Properties = new byte[1];
- _availableBytes = 0;
- }
- }
- public LzmaStream(LzmaEncoderProperties properties, bool isLzma2, Stream outputStream)
- : this(properties, isLzma2, null, outputStream)
- {
- }
- public LzmaStream(LzmaEncoderProperties properties, bool isLzma2, Stream presetDictionary, Stream outputStream)
- {
- _isLzma2 = isLzma2;
- _availableBytes = 0;
- _endReached = true;
- if (isLzma2)
- {
- throw new NotImplementedException();
- }
- _encoder = new Encoder();
- _encoder.SetCoderProperties(properties._propIDs, properties._properties);
- MemoryStream propStream = new MemoryStream(5);
- _encoder.WriteCoderProperties(propStream);
- Properties = propStream.ToArray();
- _encoder.SetStreams(null, outputStream, -1, -1);
- if (presetDictionary != null)
- {
- _encoder.Train(presetDictionary);
- }
- }
- public override bool CanRead => _encoder == null;
- public override bool CanSeek => false;
- public override bool CanWrite => _encoder != null;
- public override void Flush()
- {
- }
- protected override void Dispose(bool disposing)
- {
- if (_isDisposed)
- {
- return;
- }
- _isDisposed = true;
- if (disposing)
- {
- if (_encoder != null)
- {
- _position = _encoder.Code(null, true);
- }
- _inputStream?.Dispose();
- }
- base.Dispose(disposing);
- }
- public override long Length => _position + _availableBytes;
- public override long Position { get => _position; set => throw new NotSupportedException(); }
- public override int Read(byte[] buffer, int offset, int count)
- {
- if (_endReached)
- {
- return 0;
- }
- int total = 0;
- while (total < count)
- {
- if (_availableBytes == 0)
- {
- if (_isLzma2)
- {
- DecodeChunkHeader();
- }
- else
- {
- _endReached = true;
- }
- if (_endReached)
- {
- break;
- }
- }
- int toProcess = count - total;
- if (toProcess > _availableBytes)
- {
- toProcess = (int)_availableBytes;
- }
- _outWindow.SetLimit(toProcess);
- if (_uncompressedChunk)
- {
- _inputPosition += _outWindow.CopyStream(_inputStream, toProcess);
- }
- else if (_decoder.Code(_dictionarySize, _outWindow, _rangeDecoder)
- && _outputSize < 0)
- {
- _availableBytes = _outWindow.AvailableBytes;
- }
- int read = _outWindow.Read(buffer, offset, toProcess);
- total += read;
- offset += read;
- _position += read;
- _availableBytes -= read;
- if (_availableBytes == 0 && !_uncompressedChunk)
- {
- _rangeDecoder.ReleaseStream();
- if (!_rangeDecoder.IsFinished || (_rangeDecoderLimit >= 0 && _rangeDecoder._total != _rangeDecoderLimit))
- {
- throw new DataErrorException();
- }
- _inputPosition += _rangeDecoder._total;
- if (_outWindow.HasPending)
- {
- throw new DataErrorException();
- }
- }
- }
- if (_endReached)
- {
- if (_inputSize >= 0 && _inputPosition != _inputSize)
- {
- throw new DataErrorException();
- }
- if (_outputSize >= 0 && _position != _outputSize)
- {
- throw new DataErrorException();
- }
- }
- return total;
- }
- private void DecodeChunkHeader()
- {
- int control = _inputStream.ReadByte();
- _inputPosition++;
- if (control == 0x00)
- {
- _endReached = true;
- return;
- }
- if (control >= 0xE0 || control == 0x01)
- {
- _needProps = true;
- _needDictReset = false;
- _outWindow.Reset();
- }
- else if (_needDictReset)
- {
- throw new DataErrorException();
- }
- if (control >= 0x80)
- {
- _uncompressedChunk = false;
- _availableBytes = (control & 0x1F) << 16;
- _availableBytes += (_inputStream.ReadByte() << 8) + _inputStream.ReadByte() + 1;
- _inputPosition += 2;
- _rangeDecoderLimit = (_inputStream.ReadByte() << 8) + _inputStream.ReadByte() + 1;
- _inputPosition += 2;
- if (control >= 0xC0)
- {
- _needProps = false;
- Properties[0] = (byte)_inputStream.ReadByte();
- _inputPosition++;
- _decoder = new Decoder();
- _decoder.SetDecoderProperties(Properties);
- }
- else if (_needProps)
- {
- throw new DataErrorException();
- }
- else if (control >= 0xA0)
- {
- _decoder = new Decoder();
- _decoder.SetDecoderProperties(Properties);
- }
- _rangeDecoder.Init(_inputStream);
- }
- else if (control > 0x02)
- {
- throw new DataErrorException();
- }
- else
- {
- _uncompressedChunk = true;
- _availableBytes = (_inputStream.ReadByte() << 8) + _inputStream.ReadByte() + 1;
- _inputPosition += 2;
- }
- }
- public override long Seek(long offset, SeekOrigin origin)
- {
- throw new NotSupportedException();
- }
- public override void SetLength(long value)
- {
- throw new NotSupportedException();
- }
- public override void Write(byte[] buffer, int offset, int count)
- {
- if (_encoder != null)
- {
- _position = _encoder.Code(new MemoryStream(buffer, offset, count), false);
- }
- }
- public byte[] Properties { get; } = new byte[5];
- }
- }
|