1 /* Copyright 2015 Google Inc. All Rights Reserved.
2 
3 Distributed under MIT license.
4 See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
5 */
6 namespace Org.Brotli.Dec
7 {
8 	/// <summary>Bit reading helpers.</summary>
9 	internal sealed class BitReader
10 	{
11 		/// <summary>
12 		/// Input byte buffer, consist of a ring-buffer and a "slack" region where bytes from the start of
13 		/// the ring-buffer are copied.
14 		/// </summary>
15 		private const int Capacity = 1024;
16 
17 		private const int Slack = 16;
18 
19 		private const int IntBufferSize = Capacity + Slack;
20 
21 		private const int ByteReadSize = Capacity << 2;
22 
23 		private const int ByteBufferSize = IntBufferSize << 2;
24 
25 		private readonly byte[] byteBuffer = new byte[ByteBufferSize];
26 
27 		private readonly int[] intBuffer = new int[IntBufferSize];
28 
29 		private readonly Org.Brotli.Dec.IntReader intReader = new Org.Brotli.Dec.IntReader();
30 
31 		private System.IO.Stream input;
32 
33 		/// <summary>Input stream is finished.</summary>
34 		private bool endOfStreamReached;
35 
36 		/// <summary>Pre-fetched bits.</summary>
37 		internal long accumulator;
38 
39 		/// <summary>Current bit-reading position in accumulator.</summary>
40 		internal int bitOffset;
41 
42 		/// <summary>Offset of next item in intBuffer.</summary>
43 		private int intOffset;
44 
45 		private int tailBytes = 0;
46 
47 		/* Number of bytes in unfinished "int" item. */
48 		/// <summary>Fills up the input buffer.</summary>
49 		/// <remarks>
50 		/// Fills up the input buffer.
51 		/// <p> No-op if there are at least 36 bytes present after current position.
52 		/// <p> After encountering the end of the input stream, 64 additional zero bytes are copied to the
53 		/// buffer.
54 		/// </remarks>
ReadMoreInput(Org.Brotli.Dec.BitReader br)55 		internal static void ReadMoreInput(Org.Brotli.Dec.BitReader br)
56 		{
57 			// TODO: Split to check and read; move read outside of decoding loop.
58 			if (br.intOffset <= Capacity - 9)
59 			{
60 				return;
61 			}
62 			if (br.endOfStreamReached)
63 			{
64 				if (IntAvailable(br) >= -2)
65 				{
66 					return;
67 				}
68 				throw new Org.Brotli.Dec.BrotliRuntimeException("No more input");
69 			}
70 			int readOffset = br.intOffset << 2;
71 			int bytesRead = ByteReadSize - readOffset;
72 			System.Array.Copy(br.byteBuffer, readOffset, br.byteBuffer, 0, bytesRead);
73 			br.intOffset = 0;
74 			try
75 			{
76 				while (bytesRead < ByteReadSize)
77 				{
78 					int len = br.input.Read(br.byteBuffer, bytesRead, ByteReadSize - bytesRead);
79 					// EOF is -1 in Java, but 0 in C#.
80 					if (len <= 0)
81 					{
82 						br.endOfStreamReached = true;
83 						br.tailBytes = bytesRead;
84 						bytesRead += 3;
85 						break;
86 					}
87 					bytesRead += len;
88 				}
89 			}
90 			catch (System.IO.IOException e)
91 			{
92 				throw new Org.Brotli.Dec.BrotliRuntimeException("Failed to read input", e);
93 			}
94 			Org.Brotli.Dec.IntReader.Convert(br.intReader, bytesRead >> 2);
95 		}
96 
CheckHealth(Org.Brotli.Dec.BitReader br, bool endOfStream)97 		internal static void CheckHealth(Org.Brotli.Dec.BitReader br, bool endOfStream)
98 		{
99 			if (!br.endOfStreamReached)
100 			{
101 				return;
102 			}
103 			int byteOffset = (br.intOffset << 2) + ((br.bitOffset + 7) >> 3) - 8;
104 			if (byteOffset > br.tailBytes)
105 			{
106 				throw new Org.Brotli.Dec.BrotliRuntimeException("Read after end");
107 			}
108 			if (endOfStream && (byteOffset != br.tailBytes))
109 			{
110 				throw new Org.Brotli.Dec.BrotliRuntimeException("Unused bytes after end");
111 			}
112 		}
113 
114 		/// <summary>Advances the Read buffer by 5 bytes to make room for reading next 24 bits.</summary>
FillBitWindow(Org.Brotli.Dec.BitReader br)115 		internal static void FillBitWindow(Org.Brotli.Dec.BitReader br)
116 		{
117 			if (br.bitOffset >= 32)
118 			{
119 				br.accumulator = ((long)br.intBuffer[br.intOffset++] << 32) | ((long)(((ulong)br.accumulator) >> 32));
120 				br.bitOffset -= 32;
121 			}
122 		}
123 
124 		/// <summary>Reads the specified number of bits from Read Buffer.</summary>
ReadBits(Org.Brotli.Dec.BitReader br, int n)125 		internal static int ReadBits(Org.Brotli.Dec.BitReader br, int n)
126 		{
127 			FillBitWindow(br);
128 			int val = (int)((long)(((ulong)br.accumulator) >> br.bitOffset)) & ((1 << n) - 1);
129 			br.bitOffset += n;
130 			return val;
131 		}
132 
133 		/// <summary>Initialize bit reader.</summary>
134 		/// <remarks>
135 		/// Initialize bit reader.
136 		/// <p> Initialisation turns bit reader to a ready state. Also a number of bytes is prefetched to
137 		/// accumulator. Because of that this method may block until enough data could be read from input.
138 		/// </remarks>
139 		/// <param name="br">BitReader POJO</param>
140 		/// <param name="input">data source</param>
Init(Org.Brotli.Dec.BitReader br, System.IO.Stream input)141 		internal static void Init(Org.Brotli.Dec.BitReader br, System.IO.Stream input)
142 		{
143 			if (br.input != null)
144 			{
145 				throw new System.InvalidOperationException("Bit reader already has associated input stream");
146 			}
147 			Org.Brotli.Dec.IntReader.Init(br.intReader, br.byteBuffer, br.intBuffer);
148 			br.input = input;
149 			br.accumulator = 0;
150 			br.bitOffset = 64;
151 			br.intOffset = Capacity;
152 			br.endOfStreamReached = false;
153 			Prepare(br);
154 		}
155 
Prepare(Org.Brotli.Dec.BitReader br)156 		private static void Prepare(Org.Brotli.Dec.BitReader br)
157 		{
158 			ReadMoreInput(br);
159 			CheckHealth(br, false);
160 			FillBitWindow(br);
161 			FillBitWindow(br);
162 		}
163 
Reload(Org.Brotli.Dec.BitReader br)164 		internal static void Reload(Org.Brotli.Dec.BitReader br)
165 		{
166 			if (br.bitOffset == 64)
167 			{
168 				Prepare(br);
169 			}
170 		}
171 
172 		/// <exception cref="System.IO.IOException"/>
Close(Org.Brotli.Dec.BitReader br)173 		internal static void Close(Org.Brotli.Dec.BitReader br)
174 		{
175 			System.IO.Stream @is = br.input;
176 			br.input = null;
177 			if (@is != null)
178 			{
179 				@is.Close();
180 			}
181 		}
182 
JumpToByteBoundary(Org.Brotli.Dec.BitReader br)183 		internal static void JumpToByteBoundary(Org.Brotli.Dec.BitReader br)
184 		{
185 			int padding = (64 - br.bitOffset) & 7;
186 			if (padding != 0)
187 			{
188 				int paddingBits = Org.Brotli.Dec.BitReader.ReadBits(br, padding);
189 				if (paddingBits != 0)
190 				{
191 					throw new Org.Brotli.Dec.BrotliRuntimeException("Corrupted padding bits");
192 				}
193 			}
194 		}
195 
IntAvailable(Org.Brotli.Dec.BitReader br)196 		internal static int IntAvailable(Org.Brotli.Dec.BitReader br)
197 		{
198 			int limit = Capacity;
199 			if (br.endOfStreamReached)
200 			{
201 				limit = (br.tailBytes + 3) >> 2;
202 			}
203 			return limit - br.intOffset;
204 		}
205 
CopyBytes(Org.Brotli.Dec.BitReader br, byte[] data, int offset, int length)206 		internal static void CopyBytes(Org.Brotli.Dec.BitReader br, byte[] data, int offset, int length)
207 		{
208 			if ((br.bitOffset & 7) != 0)
209 			{
210 				throw new Org.Brotli.Dec.BrotliRuntimeException("Unaligned copyBytes");
211 			}
212 			// Drain accumulator.
213 			while ((br.bitOffset != 64) && (length != 0))
214 			{
215 				data[offset++] = unchecked((byte)((long)(((ulong)br.accumulator) >> br.bitOffset)));
216 				br.bitOffset += 8;
217 				length--;
218 			}
219 			if (length == 0)
220 			{
221 				return;
222 			}
223 			// Get data from shadow buffer with "sizeof(int)" granularity.
224 			int copyInts = System.Math.Min(IntAvailable(br), length >> 2);
225 			if (copyInts > 0)
226 			{
227 				int readOffset = br.intOffset << 2;
228 				System.Array.Copy(br.byteBuffer, readOffset, data, offset, copyInts << 2);
229 				offset += copyInts << 2;
230 				length -= copyInts << 2;
231 				br.intOffset += copyInts;
232 			}
233 			if (length == 0)
234 			{
235 				return;
236 			}
237 			// Read tail bytes.
238 			if (IntAvailable(br) > 0)
239 			{
240 				// length = 1..3
241 				FillBitWindow(br);
242 				while (length != 0)
243 				{
244 					data[offset++] = unchecked((byte)((long)(((ulong)br.accumulator) >> br.bitOffset)));
245 					br.bitOffset += 8;
246 					length--;
247 				}
248 				CheckHealth(br, false);
249 				return;
250 			}
251 			// Now it is possible to copy bytes directly.
252 			try
253 			{
254 				while (length > 0)
255 				{
256 					int len = br.input.Read(data, offset, length);
257 					if (len == -1)
258 					{
259 						throw new Org.Brotli.Dec.BrotliRuntimeException("Unexpected end of input");
260 					}
261 					offset += len;
262 					length -= len;
263 				}
264 			}
265 			catch (System.IO.IOException e)
266 			{
267 				throw new Org.Brotli.Dec.BrotliRuntimeException("Failed to read input", e);
268 			}
269 		}
270 	}
271 }
272