Created
November 17, 2014 05:15
-
-
Save nbevans/db695ad5e1ad02126b2e to your computer and use it in GitHub Desktop.
LzmaDecodeStream that actually works in a: streaming / chunked / blocked fashion.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* This file is part of SevenZipSharp. | |
SevenZipSharp is free software: you can redistribute it and/or modify | |
it under the terms of the GNU Lesser General Public License as published by | |
the Free Software Foundation, either version 3 of the License, or | |
(at your option) any later version. | |
SevenZipSharp is distributed in the hope that it will be useful, | |
but WITHOUT ANY WARRANTY; without even the implied warranty of | |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
GNU Lesser General Public License for more details. | |
You should have received a copy of the GNU Lesser General Public License | |
along with SevenZipSharp. If not, see <http://www.gnu.org/licenses/>. | |
*/ | |
using System; | |
using System.IO; | |
using SevenZip.Sdk; | |
using SevenZip.Sdk.Compression.Lzma; | |
namespace Singapore.Util.Lzma | |
{ | |
/// <summary> | |
/// The stream which decompresses data with LZMA on the fly. | |
/// </summary> | |
public class LzmaDecodeStream : Stream | |
{ | |
private readonly MemoryStream _buffer = new MemoryStream(); | |
private readonly Decoder _decoder = new Decoder(); | |
private readonly Stream _input; | |
private byte[] _commonProperties; | |
private bool _error; | |
private bool _firstChunkRead; | |
/// <summary> | |
/// Initializes a new instance of the LzmaDecodeStream class. | |
/// </summary> | |
/// <param name="encodedStream">A compressed stream.</param> | |
public LzmaDecodeStream(Stream encodedStream) | |
{ | |
if (!encodedStream.CanRead) | |
{ | |
throw new ArgumentException("The specified stream can not be read.", "encodedStream"); | |
} | |
_input = encodedStream; | |
} | |
/// <summary> | |
/// Gets the chunk size. | |
/// </summary> | |
public int ChunkSize | |
{ | |
get | |
{ | |
return (int) _buffer.Length; | |
} | |
} | |
/// <summary> | |
/// Gets a value indicating whether the current stream supports reading. | |
/// </summary> | |
public override bool CanRead | |
{ | |
get | |
{ | |
return true; | |
} | |
} | |
/// <summary> | |
/// Gets a value indicating whether the current stream supports seeking. | |
/// </summary> | |
public override bool CanSeek | |
{ | |
get | |
{ | |
return false; | |
} | |
} | |
/// <summary> | |
/// Gets a value indicating whether the current stream supports writing. | |
/// </summary> | |
public override bool CanWrite | |
{ | |
get | |
{ | |
return false; | |
} | |
} | |
/// <summary> | |
/// Gets the length in bytes of the output stream. | |
/// </summary> | |
public override long Length | |
{ | |
get | |
{ | |
if (_input.CanSeek) | |
{ | |
return _input.Length; | |
} | |
return _buffer.Length; | |
} | |
} | |
/// <summary> | |
/// Gets or sets the position within the output stream. | |
/// </summary> | |
public override long Position | |
{ | |
get | |
{ | |
if (_input.CanSeek) | |
{ | |
return _input.Position; | |
} | |
return _buffer.Position; | |
} | |
set | |
{ | |
throw new NotSupportedException(); | |
} | |
} | |
private void ReadChunk() | |
{ | |
long size; | |
byte[] properties; | |
try | |
{ | |
properties = SevenZipExtractor.GetLzmaProperties(_input, out size); | |
} | |
catch (Exception) | |
{ | |
_error = true; | |
return; | |
} | |
if (!_firstChunkRead) | |
{ | |
_commonProperties = properties; | |
} | |
if (_commonProperties[0] != properties[0] || | |
_commonProperties[1] != properties[1] || | |
_commonProperties[2] != properties[2] || | |
_commonProperties[3] != properties[3] || | |
_commonProperties[4] != properties[4]) | |
{ | |
_error = true; | |
return; | |
} | |
_buffer.Capacity = 1024*32; // I MODIFIED THESE LINES FROM THE ORIGINAL. IT NOW USES A 32KB BUFFER FOR ALL CHUNK READS. RATHER THAN THE 'outSize' FOR ME WAS ALWAYS RETURNING -1 AND HENCE SETLENGTH WOULD THROW AN EXCEPTION. PAIR THIS STREAM WITH A SIMPLE COPYTO() OPERATION AND YOU HAVE A WORKING STREAMED EXTRACTOR. | |
_buffer.SetLength(_buffer.Capacity); | |
_decoder.SetDecoderProperties(properties); | |
_buffer.Position = 0; | |
_decoder.Code(_input, _buffer, 0, size, null); | |
_buffer.Position = 0; | |
} | |
/// <summary> | |
/// Does nothing. | |
/// </summary> | |
public override void Flush() {} | |
/// <summary> | |
/// Reads a sequence of bytes from the current stream and decompresses data if necessary. | |
/// </summary> | |
/// <param name="buffer">An array of bytes.</param> | |
/// <param name="offset">The zero-based byte offset in buffer at which to begin storing the data read from the current stream.</param> | |
/// <param name="count">The maximum number of bytes to be read from the current stream.</param> | |
/// <returns>The total number of bytes read into the buffer.</returns> | |
public override int Read(byte[] buffer, int offset, int count) | |
{ | |
if (_error) | |
{ | |
return 0; | |
} | |
if (!_firstChunkRead) | |
{ | |
ReadChunk(); | |
_firstChunkRead = true; | |
} | |
int readCount = 0; | |
while (count > _buffer.Length - _buffer.Position && !_error) | |
{ | |
var buf = new byte[_buffer.Length - _buffer.Position]; | |
_buffer.Read(buf, 0, buf.Length); | |
buf.CopyTo(buffer, offset); | |
offset += buf.Length; | |
count -= buf.Length; | |
readCount += buf.Length; | |
ReadChunk(); | |
} | |
if (!_error) | |
{ | |
_buffer.Read(buffer, offset, count); | |
readCount += count; | |
} | |
return readCount; | |
} | |
/// <summary> | |
/// Sets the position within the current stream. | |
/// </summary> | |
/// <param name="offset">A byte offset relative to the origin parameter.</param> | |
/// <param name="origin">A value of type System.IO.SeekOrigin indicating the reference point used to obtain the new position.</param> | |
/// <returns>The new position within the current stream.</returns> | |
public override long Seek(long offset, SeekOrigin origin) | |
{ | |
throw new NotSupportedException(); | |
} | |
/// <summary> | |
/// Sets the length of the current stream. | |
/// </summary> | |
/// <param name="value">The desired length of the current stream in bytes.</param> | |
public override void SetLength(long value) | |
{ | |
throw new NotSupportedException(); | |
} | |
/// <summary> | |
/// Writes a sequence of bytes to the current stream. | |
/// </summary> | |
/// <param name="buffer">An array of bytes.</param> | |
/// <param name="offset">The zero-based byte offset in buffer at which to begin storing the data read from the current stream.</param> | |
/// <param name="count">The maximum number of bytes to be read from the current stream.</param> | |
public override void Write(byte[] buffer, int offset, int count) | |
{ | |
throw new NotSupportedException(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment