blob: 36f8128eaf7bba74d55b0380623bdcebf1fb3ab6 [file] [log] [blame]
/* Copyright 2015 Google Inc. All Rights Reserved.
Distributed under MIT license.
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
*/
namespace Org.Brotli.Dec
{
/// <summary>
/// <see cref="System.IO.Stream"/>
/// decorator that decompresses brotli data.
/// <p> Not thread-safe.
/// </summary>
public class BrotliInputStream : System.IO.Stream
{
public const int DefaultInternalBufferSize = 16384;
/// <summary>Internal buffer used for efficient byte-by-byte reading.</summary>
private byte[] buffer;
/// <summary>Number of decoded but still unused bytes in internal buffer.</summary>
private int remainingBufferBytes;
/// <summary>Next unused byte offset.</summary>
private int bufferOffset;
/// <summary>Decoder state.</summary>
private readonly Org.Brotli.Dec.State state = new Org.Brotli.Dec.State();
/// <summary>
/// Creates a
/// <see cref="System.IO.Stream"/>
/// wrapper that decompresses brotli data.
/// <p> For byte-by-byte reading (
/// <see cref="ReadByte()"/>
/// ) internal buffer with
/// <see cref="DefaultInternalBufferSize"/>
/// size is allocated and used.
/// <p> Will block the thread until first kilobyte of data of source is available.
/// </summary>
/// <param name="source">underlying data source</param>
/// <exception cref="System.IO.IOException">in case of corrupted data or source stream problems</exception>
public BrotliInputStream(System.IO.Stream source)
: this(source, DefaultInternalBufferSize, null)
{
}
/// <summary>
/// Creates a
/// <see cref="System.IO.Stream"/>
/// wrapper that decompresses brotli data.
/// <p> For byte-by-byte reading (
/// <see cref="ReadByte()"/>
/// ) internal buffer of specified size is
/// allocated and used.
/// <p> Will block the thread until first kilobyte of data of source is available.
/// </summary>
/// <param name="source">compressed data source</param>
/// <param name="byteReadBufferSize">
/// size of internal buffer used in case of
/// byte-by-byte reading
/// </param>
/// <exception cref="System.IO.IOException">in case of corrupted data or source stream problems</exception>
public BrotliInputStream(System.IO.Stream source, int byteReadBufferSize)
: this(source, byteReadBufferSize, null)
{
}
/// <summary>
/// Creates a
/// <see cref="System.IO.Stream"/>
/// wrapper that decompresses brotli data.
/// <p> For byte-by-byte reading (
/// <see cref="ReadByte()"/>
/// ) internal buffer of specified size is
/// allocated and used.
/// <p> Will block the thread until first kilobyte of data of source is available.
/// </summary>
/// <param name="source">compressed data source</param>
/// <param name="byteReadBufferSize">
/// size of internal buffer used in case of
/// byte-by-byte reading
/// </param>
/// <param name="customDictionary">
/// custom dictionary data;
/// <see langword="null"/>
/// if not used
/// </param>
/// <exception cref="System.IO.IOException">in case of corrupted data or source stream problems</exception>
public BrotliInputStream(System.IO.Stream source, int byteReadBufferSize, byte[] customDictionary)
{
if (byteReadBufferSize <= 0)
{
throw new System.ArgumentException("Bad buffer size:" + byteReadBufferSize);
}
else if (source == null)
{
throw new System.ArgumentException("source is null");
}
this.buffer = new byte[byteReadBufferSize];
this.remainingBufferBytes = 0;
this.bufferOffset = 0;
try
{
Org.Brotli.Dec.State.SetInput(state, source);
}
catch (Org.Brotli.Dec.BrotliRuntimeException ex)
{
throw new System.IO.IOException("Brotli decoder initialization failed", ex);
}
if (customDictionary != null)
{
Org.Brotli.Dec.Decode.SetCustomDictionary(state, customDictionary);
}
}
/// <summary><inheritDoc/></summary>
/// <exception cref="System.IO.IOException"/>
public override void Close()
{
Org.Brotli.Dec.State.Close(state);
}
/// <summary><inheritDoc/></summary>
/// <exception cref="System.IO.IOException"/>
public override int ReadByte()
{
if (bufferOffset >= remainingBufferBytes)
{
remainingBufferBytes = Read(buffer, 0, buffer.Length);
bufferOffset = 0;
if (remainingBufferBytes == -1)
{
return -1;
}
}
return buffer[bufferOffset++] & unchecked((int)(0xFF));
}
/// <summary><inheritDoc/></summary>
/// <exception cref="System.IO.IOException"/>
public override int Read(byte[] destBuffer, int destOffset, int destLen)
{
if (destOffset < 0)
{
throw new System.ArgumentException("Bad offset: " + destOffset);
}
else if (destLen < 0)
{
throw new System.ArgumentException("Bad length: " + destLen);
}
else if (destOffset + destLen > destBuffer.Length)
{
throw new System.ArgumentException("Buffer overflow: " + (destOffset + destLen) + " > " + destBuffer.Length);
}
else if (destLen == 0)
{
return 0;
}
int copyLen = System.Math.Max(remainingBufferBytes - bufferOffset, 0);
if (copyLen != 0)
{
copyLen = System.Math.Min(copyLen, destLen);
System.Array.Copy(buffer, bufferOffset, destBuffer, destOffset, copyLen);
bufferOffset += copyLen;
destOffset += copyLen;
destLen -= copyLen;
if (destLen == 0)
{
return copyLen;
}
}
try
{
state.output = destBuffer;
state.outputOffset = destOffset;
state.outputLength = destLen;
state.outputUsed = 0;
Org.Brotli.Dec.Decode.Decompress(state);
if (state.outputUsed == 0)
{
return -1;
}
return state.outputUsed + copyLen;
}
catch (Org.Brotli.Dec.BrotliRuntimeException ex)
{
throw new System.IO.IOException("Brotli stream decoding failed", ex);
}
}
// <{[INJECTED CODE]}>
public override bool CanRead {
get {return true;}
}
public override bool CanSeek {
get {return false;}
}
public override long Length {
get {throw new System.NotSupportedException();}
}
public override long Position {
get {throw new System.NotSupportedException();}
set {throw new System.NotSupportedException();}
}
public override long Seek(long offset, System.IO.SeekOrigin origin) {
throw new System.NotSupportedException();
}
public override void SetLength(long value){
throw new System.NotSupportedException();
}
public override bool CanWrite{get{return false;}}
public override System.IAsyncResult BeginWrite(byte[] buffer, int offset,
int count, System.AsyncCallback callback, object state) {
throw new System.NotSupportedException();
}
public override void Write(byte[] buffer, int offset, int count) {
throw new System.NotSupportedException();
}
public override void Flush() {}
}
}