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