BizHawk/BizHawk.Client.Common/7z/LzmaEncodeStream.cs

304 lines
10 KiB
C#

/* 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.Compression.Lzma;
namespace SevenZip
{
#if LZMA_STREAM
#if COMPRESS
/// <summary>
/// The stream which compresses data with LZMA on the fly.
/// </summary>
public class LzmaEncodeStream : Stream
{
private const int MAX_BUFFER_CAPACITY = 1 << 30; //1 Gb
private readonly MemoryStream _buffer = new MemoryStream();
private readonly int _bufferCapacity = 1 << 18; //256 kb
private readonly bool _ownOutput;
private bool _disposed;
private Encoder _lzmaEncoder;
private Stream _output;
/// <summary>
/// Initializes a new instance of the LzmaEncodeStream class.
/// </summary>
public LzmaEncodeStream()
{
_output = new MemoryStream();
_ownOutput = true;
Init();
}
/// <summary>
/// Initializes a new instance of the LzmaEncodeStream class.
/// </summary>
/// <param name="bufferCapacity">The buffer size. The bigger size, the better compression.</param>
public LzmaEncodeStream(int bufferCapacity)
{
_output = new MemoryStream();
_ownOutput = true;
if (bufferCapacity > MAX_BUFFER_CAPACITY)
{
throw new ArgumentException("Too large capacity.", "bufferCapacity");
}
_bufferCapacity = bufferCapacity;
Init();
}
/// <summary>
/// Initializes a new instance of the LzmaEncodeStream class.
/// </summary>
/// <param name="outputStream">An output stream which supports writing.</param>
public LzmaEncodeStream(Stream outputStream)
{
if (!outputStream.CanWrite)
{
throw new ArgumentException("The specified stream can not write.", "outputStream");
}
_output = outputStream;
Init();
}
/// <summary>
/// Initializes a new instance of the LzmaEncodeStream class.
/// </summary>
/// <param name="outputStream">An output stream which supports writing.</param>
/// <param name="bufferCapacity">A buffer size. The bigger size, the better compression.</param>
public LzmaEncodeStream(Stream outputStream, int bufferCapacity)
{
if (!outputStream.CanWrite)
{
throw new ArgumentException("The specified stream can not write.", "outputStream");
}
_output = outputStream;
if (bufferCapacity > 1 << 30)
{
throw new ArgumentException("Too large capacity.", "bufferCapacity");
}
_bufferCapacity = bufferCapacity;
Init();
}
/// <summary>
/// Gets a value indicating whether the current stream supports reading.
/// </summary>
public override bool CanRead
{
get
{
return false;
}
}
/// <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
{
DisposedCheck();
return _buffer.CanWrite;
}
}
/// <summary>
/// Gets the length in bytes of the output stream.
/// </summary>
public override long Length
{
get
{
DisposedCheck();
if (_output.CanSeek)
{
return _output.Length;
}
return _buffer.Position;
}
}
/// <summary>
/// Gets or sets the position within the output stream.
/// </summary>
public override long Position
{
get
{
DisposedCheck();
if (_output.CanSeek)
{
return _output.Position;
}
return _buffer.Position;
}
set
{
throw new NotSupportedException();
}
}
private void Init()
{
_buffer.Capacity = _bufferCapacity;
SevenZipCompressor.LzmaDictionarySize = _bufferCapacity;
_lzmaEncoder = new Encoder();
SevenZipCompressor.WriteLzmaProperties(_lzmaEncoder);
}
/// <summary>
/// Checked whether the class was disposed.
/// </summary>
/// <exception cref="System.ObjectDisposedException" />
private void DisposedCheck()
{
if (_disposed)
{
throw new ObjectDisposedException("SevenZipExtractor");
}
}
private void WriteChunk()
{
_lzmaEncoder.WriteCoderProperties(_output);
long streamSize = _buffer.Position;
if (_buffer.Length != _buffer.Position)
{
_buffer.SetLength(_buffer.Position);
}
_buffer.Position = 0;
for (int i = 0; i < 8; i++)
{
_output.WriteByte((byte) (streamSize >> (8*i)));
}
_lzmaEncoder.Code(_buffer, _output, -1, -1, null);
_buffer.Position = 0;
}
/// <summary>
/// Converts the LzmaEncodeStream to the LzmaDecodeStream to read data.
/// </summary>
/// <returns></returns>
public LzmaDecodeStream ToDecodeStream()
{
DisposedCheck();
Flush();
return new LzmaDecodeStream(_output);
}
/// <summary>
/// Clears all buffers for this stream and causes any buffered data to be compressed and written.
/// </summary>
public override void Flush()
{
DisposedCheck();
WriteChunk();
}
/// <summary>
/// Releases all unmanaged resources used by LzmaEncodeStream.
/// </summary>
protected override void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
Flush();
_buffer.Close();
if (_ownOutput)
{
_output.Dispose();
}
_output = null;
}
_disposed = true;
}
}
/// <summary>
/// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read.
/// </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)
{
DisposedCheck();
throw new NotSupportedException();
}
/// <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)
{
DisposedCheck();
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)
{
DisposedCheck();
throw new NotSupportedException();
}
/// <summary>
/// Writes a sequence of bytes to the current stream and compresses it 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>
public override void Write(byte[] buffer, int offset, int count)
{
DisposedCheck();
int dataLength = Math.Min(buffer.Length - offset, count);
while (_buffer.Position + dataLength >= _bufferCapacity)
{
int length = _bufferCapacity - (int) _buffer.Position;
_buffer.Write(buffer, offset, length);
offset = length + offset;
dataLength -= length;
WriteChunk();
}
_buffer.Write(buffer, offset, dataLength);
}
}
#endif
#endif
}