1/* Copyright 2015 Google Inc. All Rights Reserved.
2
3Distributed under MIT license.
4See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
5*/
6namespace Org.Brotli.Dec
7{
8	/// <summary>
9	/// <see cref="System.IO.Stream"/>
10	/// decorator that decompresses brotli data.
11	/// <p> Not thread-safe.
12	/// </summary>
13	public class BrotliInputStream : System.IO.Stream
14	{
15		public const int DefaultInternalBufferSize = 16384;
16
17		/// <summary>Internal buffer used for efficient byte-by-byte reading.</summary>
18		private byte[] buffer;
19
20		/// <summary>Number of decoded but still unused bytes in internal buffer.</summary>
21		private int remainingBufferBytes;
22
23		/// <summary>Next unused byte offset.</summary>
24		private int bufferOffset;
25
26		/// <summary>Decoder state.</summary>
27		private readonly Org.Brotli.Dec.State state = new Org.Brotli.Dec.State();
28
29		/// <summary>
30		/// Creates a
31		/// <see cref="System.IO.Stream"/>
32		/// wrapper that decompresses brotli data.
33		/// <p> For byte-by-byte reading (
34		/// <see cref="ReadByte()"/>
35		/// ) internal buffer with
36		/// <see cref="DefaultInternalBufferSize"/>
37		/// size is allocated and used.
38		/// <p> Will block the thread until first kilobyte of data of source is available.
39		/// </summary>
40		/// <param name="source">underlying data source</param>
41		/// <exception cref="System.IO.IOException">in case of corrupted data or source stream problems</exception>
42		public BrotliInputStream(System.IO.Stream source)
43			: this(source, DefaultInternalBufferSize, null)
44		{
45		}
46
47		/// <summary>
48		/// Creates a
49		/// <see cref="System.IO.Stream"/>
50		/// wrapper that decompresses brotli data.
51		/// <p> For byte-by-byte reading (
52		/// <see cref="ReadByte()"/>
53		/// ) internal buffer of specified size is
54		/// allocated and used.
55		/// <p> Will block the thread until first kilobyte of data of source is available.
56		/// </summary>
57		/// <param name="source">compressed data source</param>
58		/// <param name="byteReadBufferSize">
59		/// size of internal buffer used in case of
60		/// byte-by-byte reading
61		/// </param>
62		/// <exception cref="System.IO.IOException">in case of corrupted data or source stream problems</exception>
63		public BrotliInputStream(System.IO.Stream source, int byteReadBufferSize)
64			: this(source, byteReadBufferSize, null)
65		{
66		}
67
68		/// <summary>
69		/// Creates a
70		/// <see cref="System.IO.Stream"/>
71		/// wrapper that decompresses brotli data.
72		/// <p> For byte-by-byte reading (
73		/// <see cref="ReadByte()"/>
74		/// ) internal buffer of specified size is
75		/// allocated and used.
76		/// <p> Will block the thread until first kilobyte of data of source is available.
77		/// </summary>
78		/// <param name="source">compressed data source</param>
79		/// <param name="byteReadBufferSize">
80		/// size of internal buffer used in case of
81		/// byte-by-byte reading
82		/// </param>
83		/// <param name="customDictionary">
84		/// custom dictionary data;
85		/// <see langword="null"/>
86		/// if not used
87		/// </param>
88		/// <exception cref="System.IO.IOException">in case of corrupted data or source stream problems</exception>
89		public BrotliInputStream(System.IO.Stream source, int byteReadBufferSize, byte[] customDictionary)
90		{
91			if (byteReadBufferSize <= 0)
92			{
93				throw new System.ArgumentException("Bad buffer size:" + byteReadBufferSize);
94			}
95			else if (source == null)
96			{
97				throw new System.ArgumentException("source is null");
98			}
99			this.buffer = new byte[byteReadBufferSize];
100			this.remainingBufferBytes = 0;
101			this.bufferOffset = 0;
102			try
103			{
104				Org.Brotli.Dec.State.SetInput(state, source);
105			}
106			catch (Org.Brotli.Dec.BrotliRuntimeException ex)
107			{
108				throw new System.IO.IOException("Brotli decoder initialization failed", ex);
109			}
110			if (customDictionary != null)
111			{
112				Org.Brotli.Dec.Decode.SetCustomDictionary(state, customDictionary);
113			}
114		}
115
116		/// <summary><inheritDoc/></summary>
117		/// <exception cref="System.IO.IOException"/>
118		public override void Close()
119		{
120			Org.Brotli.Dec.State.Close(state);
121		}
122
123		/// <summary><inheritDoc/></summary>
124		/// <exception cref="System.IO.IOException"/>
125		public override int ReadByte()
126		{
127			if (bufferOffset >= remainingBufferBytes)
128			{
129				remainingBufferBytes = Read(buffer, 0, buffer.Length);
130				bufferOffset = 0;
131				if (remainingBufferBytes == -1)
132				{
133					return -1;
134				}
135			}
136			return buffer[bufferOffset++] & unchecked((int)(0xFF));
137		}
138
139		/// <summary><inheritDoc/></summary>
140		/// <exception cref="System.IO.IOException"/>
141		public override int Read(byte[] destBuffer, int destOffset, int destLen)
142		{
143			if (destOffset < 0)
144			{
145				throw new System.ArgumentException("Bad offset: " + destOffset);
146			}
147			else if (destLen < 0)
148			{
149				throw new System.ArgumentException("Bad length: " + destLen);
150			}
151			else if (destOffset + destLen > destBuffer.Length)
152			{
153				throw new System.ArgumentException("Buffer overflow: " + (destOffset + destLen) + " > " + destBuffer.Length);
154			}
155			else if (destLen == 0)
156			{
157				return 0;
158			}
159			int copyLen = System.Math.Max(remainingBufferBytes - bufferOffset, 0);
160			if (copyLen != 0)
161			{
162				copyLen = System.Math.Min(copyLen, destLen);
163				System.Array.Copy(buffer, bufferOffset, destBuffer, destOffset, copyLen);
164				bufferOffset += copyLen;
165				destOffset += copyLen;
166				destLen -= copyLen;
167				if (destLen == 0)
168				{
169					return copyLen;
170				}
171			}
172			try
173			{
174				state.output = destBuffer;
175				state.outputOffset = destOffset;
176				state.outputLength = destLen;
177				state.outputUsed = 0;
178				Org.Brotli.Dec.Decode.Decompress(state);
179				if (state.outputUsed == 0)
180				{
181					return -1;
182				}
183				return state.outputUsed + copyLen;
184			}
185			catch (Org.Brotli.Dec.BrotliRuntimeException ex)
186			{
187				throw new System.IO.IOException("Brotli stream decoding failed", ex);
188			}
189		}
190		// <{[INJECTED CODE]}>
191		public override bool CanRead {
192			get {return true;}
193		}
194
195		public override bool CanSeek {
196			get {return false;}
197		}
198		public override long Length {
199			get {throw new System.NotSupportedException();}
200		}
201		public override long Position {
202			get {throw new System.NotSupportedException();}
203			set {throw new System.NotSupportedException();}
204		}
205		public override long Seek(long offset, System.IO.SeekOrigin origin) {
206			throw new System.NotSupportedException();
207		}
208		public override void SetLength(long value){
209			throw new System.NotSupportedException();
210		}
211
212		public override bool CanWrite{get{return false;}}
213		public override System.IAsyncResult BeginWrite(byte[] buffer, int offset,
214				int count, System.AsyncCallback callback, object state) {
215			throw new System.NotSupportedException();
216		}
217		public override void Write(byte[] buffer, int offset, int count) {
218			throw new System.NotSupportedException();
219		}
220
221		public override void Flush() {}
222	}
223}
224